Example

Deliver on a decision, not on a resolution

See a queued decision re-evaluated as a new revision when a meeting ends, a timing table that cannot raise urgency into evidence, and two receipts that never stand in for each other.

When should the AI speak? The chapter’s answer is a table, and the table’s first rule is what it is not allowed to touch: delivery takes no resolution and no relevance as input, so a more urgent item can never become a better source, and a timing change can never rewrite an earlier decision.

Run it

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

One invented afternoon. The model is a scripted stand-in; the human side of the receipt is a call to a UI function, not a screen.

Expected outcome

The same decision, re-evaluated after the meeting ends, becomes a new revision rather than an edit:

...
attn:contradiction r1 PENDING/BRIEF/QUEUE  ->  r2 PENDING/BRIEF/NOW  ->  r3 DELIVERED/BRIEF/NOW
attn:lossy         r1 PENDING/SUMMARY/QUEUE  ->  r2 PENDING/SUMMARY/NOW  ->  r3 DELIVERED/SUMMARY/NOW
attn:mechanism     r1 PENDING/BRIEF/AVAILABLE  ->  r2 PENDING/BRIEF/AVAILABLE
The BRIEF stayed a BRIEF while QUEUE became NOW. The mechanism paper stayed AVAILABLE: a calendar change does not promote an unresolved relation.

Two receipts, and neither establishes the other:

...
can the model receipt show that the contradiction was presented to a person?  false
can the human receipt show it?                                                true   (an interface call, not a screen)
did the person notice it, use it, or benefit?                                 unknown. Nothing here observes any of them.

And the timing table is reported as reasons, not as a score:

...
[urgency · computed_rule · after-break] it can wait for the next break and still arrive before the deadline
[interruption_cost · observed_context · calendar_state] the person is in a meeting
[timing · computed_rule · medium-urgency-high-cost] held for the next break; it still arrives before the deadline

Delivery takes no resolution

The timing table’s inputs are a deadline, a context and two declared flags. There is no resolution in it, so urgency cannot raise evidence.

/**
 * The delivery table. Input has no resolution, no relevance, no confidence: urgency does not raise
 * evidence, and timing cannot rewrite earlier decisions.
 */
export function schedule(i: TimingInput): Timing {
  assertContext(i.context);
  const u = urgencyOf(i); const c = costOf(i);
  const reasons = [...u.reasons, ...c.reasons];
  const brk = ctxVal(i.context, "next_break_at") ?? null;
  const digest = ctxVal(i.context, "digest_at") ?? null;
  let mode: DeliveryMode; let after: string | null = null;
  if (i.non_interruptive) { mode = "AVAILABLE"; reasons.push(R("timing", "declared_policy", "non-interruptive", "declared non-interruptive: kept within reach, not competing for attention")); }
  else if (c.cost === "LOW") {
    if (u.urgency === "LOW") { mode = digest ? "DIGEST" : "QUEUE"; after = digest; reasons.push(R("timing", "computed_rule", "low-urgency-low-cost", digest ? "waits for the scheduled digest" : "queued for the next natural break")); }
    else { mode = "NOW"; reasons.push(R("timing", "computed_rule", "cost-low", "surfacing now costs little and waiting would not help")); }
  } else if (u.urgency === "HIGH") {
    if (i.delay_exceeds_interruption) { mode = "NOW"; reasons.push(R("timing", "declared_context", "delay-exceeds-interruption", "declared: the cost of delay is larger than the cost of interrupting")); }
    else { mode = "QUEUE"; after = brk; reasons.push(R("timing", "computed_rule", "high-urgency-high-cost", "interrupting costs more than the delay: held for the nearest break")); }
  } else if (u.urgency === "MEDIUM") { mode = "QUEUE"; after = brk; reasons.push(R("timing", "computed_rule", "medium-urgency-high-cost", "held for the next break; it still arrives before the deadline")); }
  else { mode = digest ? "DIGEST" : "AVAILABLE"; after = digest; reasons.push(R("timing", "computed_rule", "low-urgency-high-cost", digest ? "gathered into the digest" : "kept available, not surfaced")); }
  return { urgency: u.urgency, cost: c.cost, mode, deliver_after: after, reasons };
}

And the context it reads is declared or observed, never inferred. An unknown value is refused rather than read as permission to interrupt:

export const CONTEXT_KEYS = ["calendar_state", "focus_mode", "next_break_at", "digest_at", "do_not_disturb"] as const;
export type ContextKey = (typeof CONTEXT_KEYS)[number];
/** Allowed values. Anything else is refused: an unknown value is not read as permission to interrupt. */
export const CONTEXT_VALUES: Readonly<Partial<Record<ContextKey, readonly string[]>>> = {
  calendar_state: ["in_meeting", "free", "unknown"],
  focus_mode: ["normal", "blockers_only"],
  do_not_disturb: ["on", "off"],
};
export interface ContextFact { key: ContextKey; value: string; origin: "declared_context" | "observed_context"; ref: string; at: string }

/** An inferred state, an unknown key, or a fact with no ref is refused. Mood, stress and boredom have no key. */
export function assertContext(facts: readonly ContextFact[]): void {
  for (const f of facts) {
    if (!(CONTEXT_KEYS as readonly string[]).includes(f.key)) throw new AttentionError("CONTEXT_KEY_NOT_ALLOWED", `${String(f.key)} is not a declared or observable context key`);
    if ((f.origin as string) !== "declared_context" && (f.origin as string) !== "observed_context") throw new AttentionError("CONTEXT_ORIGIN", `${f.key}: origin ${String(f.origin)} is not declared or observed`);
    if (!f.ref.trim()) throw new AttentionError("CONTEXT_NO_REF", `${f.key} has no ref`);
    parseTime(f.at, `${f.key}.at`);
    const allowed = CONTEXT_VALUES[f.key];
    if (allowed && !allowed.includes(f.value)) throw new AttentionError("CONTEXT_VALUE_NOT_ALLOWED", `${f.key}=${JSON.stringify(f.value)} is not one of ${allowed.join(", ")}`);
    if (!allowed) parseTime(f.value, `${f.key}.value`);
  }
}

Mechanism and limitations

Documented: the delivery table, the context keys and their allowed values, and the revision lifecycle are in core/radar/src/attention/delivery.ts.

Observed: the run above. A queued decision became a new revision when the meeting ended, keeping its BRIEF; the unresolved mechanism paper stayed AVAILABLE across a calendar change; the human receipt could show presentation and the model receipt could not; and noticed, used and helpful stayed unknown throughout.

Not shown. The urgency and interruption-cost table is a declared policy, not a measured one, and whether it is the right policy for a person is not established. Nothing here observes a screen, a person, or attention: the human receipt records an interface call. An unresolved relation is never pushed in this implementation, which is a tested boundary of the scripted example rather than a general theory of urgency. There is no optional override for unconfirmed deployment alerts, and none is claimed.

Canonical source and the delivery 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.