Example

Friction is a contract the person wrote

See the weakest authorised intervention used, an override that changes one action only, and three overrides that still cannot raise the strength.

Sometimes the best interface adds friction. The chapter’s constraint is that a goal alone authorises nothing: the intervention, its scope, its override and its expiry are part of the permission, and only the weakest authorised form is used.

Run it

git clone https://github.com/ernanhughes/project-language.git
cd project-language
git checkout 329660f679a5a87831f91fe1430c20ac9360dda8
npm ci
npm run example:personal -- ch25

The example is cumulative. The review is an invented one and the model is a scripted stand-in.

Expected outcome

The contract permits one form of intervention, and that is the one used:

...
Chapter 25: friction is a contract Iris wrote. The goal is hers; the form of help is only what she authorised
  action "opening another paper": reminder — "You asked for a reminder before opening another paper."
  override: open it now, without further confirmation

An override changes that action and nothing else — not the goal, not the contract, not the intervention:

...
override recorded for "opening another paper" only. goal changed: false; contract revoked: false; intervention changed: false
  overrides so far: 1

And three overrides still do not authorise anything stronger; they only prompt a question:

...
override recorded for "opening another paper" only. goal changed: false; contract revoked: false; intervention changed: false
  overrides so far: 3
  Keep this reminder, change it, or remove it?

The weakest authorised form wins

This is the decision, in full. A goal with no authorised intervention yields no friction at all: the default is ordinary access.

export function decideFriction(state: PersonalState, action: string, ctx: Context): FrictionDecision {
  const reasons: string[] = [];
  const live = [...state.entries.values()]
    .filter((e): e is typeof e & { record: FrictionContract } => e.record.kind === "friction_contract" && e.record.target_action === action)
    .filter(e => { const a = assess(state, e.record.id, ctx); if (!a.applicable) reasons.push(`${e.record.id}: ${a.reasons.join("; ")}`); return a.applicable; });
  if (live.length === 0) return { intervention: "none", contract: null, message: null, override: null, reasons: [...reasons, "no applicable contract: ordinary access"], receipt: RECEIPT };
  const authorised = live.filter(e => e.record.allowed_intervention !== null);
  if (authorised.length === 0) return { intervention: "none", contract: live[0]!.record.id, message: null, override: null, reasons: [...reasons, "a goal is not an authorisation: no intervention was supplied or accepted"], receipt: RECEIPT };
  const weakest = authorised.reduce((a, b) => (strength(a.record.allowed_intervention!) <= strength(b.record.allowed_intervention!) ? a : b)).record;
  return {
    intervention: weakest.allowed_intervention!, contract: weakest.id,
    message: `You asked for ${article(weakest.allowed_intervention!)} ${weakest.allowed_intervention} before ${weakest.target_action}.`,
    override: weakest.override, reasons: [...reasons, `weakest authorised intervention of ${authorised.length}`], receipt: RECEIPT,
  };
}

And escalation is never done on the system’s initiative:

/** Escalation is never done on the system's initiative. Asking for it returns a refusal naming what would be needed. */
export function proposeEscalation(contract: FrictionContract, to: Intervention): { escalated: false; reason: string } {
  if (contract.allowed_intervention === null) return { escalated: false, reason: "a goal alone authorises no intervention; the person would have to supply one" };
  return { escalated: false, reason: strength(to) > strength(contract.allowed_intervention)
    ? `${to} is stronger than the ${contract.allowed_intervention} the person authorised; repeated overrides do not authorise it, and only a new explicit decision about that form can`
    : `${to} is not stronger than the authorised ${contract.allowed_intervention}` };
}

Mechanism and limitations

Documented: the contract’s fields, the strength ordering and the rule that an override never raises the strength are in core/personal/src/friction.ts and core/personal/src/types.ts.

Observed: the run above. A reminder was used, not a confirmation; each override was recorded against one action and changed nothing else; and a proposed escalation to a stronger form was refused with the reason that only a new explicit decision could authorise it.

Not shown. Nothing here watches or blocks anything: the module decides what may be shown beside an action, and it says so. The receipt proves which authorised intervention the contract permits for this action now; it does not prove that the intervention helps, or that the person was not distracted. The friction ordering and the time-per-token constant are declared policy, not measured. Whether people find this mediation worth the labour is the chapter’s open question and is untested.

Canonical source and the friction module .

Understand this example

Copy this prompt into your AI tool. No code runs here.

Apply this example

Copy this prompt into your AI tool. No code runs here.