One source, four readers, one gate
See a task choose its representation — including leaving the source unchanged — and a gate that repairs a lossy table once, then falls back to the source.
The Semantic Browser is the composition of the Part I pieces: a record, a representation chosen by task, deterministic renderings plus a model’s, all through one gate, and a margin beside the page. This example shows the two things that make it a browser rather than a viewer — the choice of representation, and the gate that decides what the reader actually receives.
Run it
git clone https://github.com/ernanhughes/project-language.git
cd project-language
git checkout 329660f679a5a87831f91fe1430c20ac9360dda8
npm ci
npm run example:ch12No model is called. The incident report is constructed, the record is written by hand, and the model answer at the end is produced by a scripted stand-in.
Expected outcome
One source, one record, four readers, four representations — and two of them leave the source alone:
...
decide_to_open → preview transforms SHOW
approve_fix → diagram + table transforms SHOW, SHOW
follow_procedure → annotated_source UNCHANGED SHOW
look_up_fact → source_highlight UNCHANGED SHOW
Leaving the source alone is an output, not a failure to produce one.The gate repairs a lossy table once and then hands back the source:
...
attempt 1 of 2 → REPAIR
attempt 2 of 2, still lossy → FALL_BACK_TO_SOURCE: the source ships, not the tableAnd the check is applied to each view separately, because pooling them lets one mask another:
...
check over diagram + table together: SURVIVED ← the diagram still carries the condition
check over the table alone, tied to its row: LOSTThe gate is a pure decision over a profile
Everything the gate does is decided by this function. The order matters: an unvalidated instrument cannot authorise a generated rendering, and out of attempts the source ships.
export type GateDecision =
| { decision: "SHOW"; why: string }
| { decision: "REPAIR"; why: string; lost: string[] }
| { decision: "FALL_BACK_TO_SOURCE"; why: string; lost: string[] };
/**
* A pure decision over a profile. Order matters:
* 1. An instrument that has not been shown to notice a loss cannot authorise a generated rendering.
* 2. Anything lost is repaired while attempts remain.
* 3. Out of attempts, the source ships. A transformation that cannot show it preserved what mattered does not.
*/
export function gateDecision(profile: Profile, attempt: number, maxAttempts: number): GateDecision {
const lost = profile.obligations.filter((o) => o.outcome !== "SURVIVED").map((o) => o.id);
if (profile.instrument !== "validated") return { decision: "FALL_BACK_TO_SOURCE", why: `the check has not been shown to notice a loss (${profile.instrument}), so its pass cannot authorise this`, lost };
if (lost.length === 0) return { decision: "SHOW", why: "every declared obligation survived" };
if (attempt < maxAttempts) return { decision: "REPAIR", why: `${lost.length} obligation(s) lost; attempt ${attempt} of ${maxAttempts}`, lost };
return { decision: "FALL_BACK_TO_SOURCE", why: `still losing ${lost.join(", ")} after ${attempt} attempt(s)`, lost };
}The representation is chosen by task, and “unchanged” is one of the outcomes:
export type Task = "decide_to_open" | "approve_fix" | "follow_procedure" | "look_up_fact";
export type Representation = "preview" | "diagram" | "table" | "annotated_source" | "source_highlight";
export interface Choice {
representations: Representation[];
/** False means the reader is given the source as it is. That is a result, not a failure. */
transforms_source: boolean;
why: string;
}Mechanism and limitations
Documented: the task-to-representation map and the gate’s three rules are in
core/browser/src/index.ts. The record’s own traceability check is Chapter 7’s
and is not restated here.
Observed: the run above. Two of the four tasks chose a representation that leaves the source unchanged. A table that had lost a row’s condition and data gap was repaired once and then handed back; the source shipped instead. The combined check over diagram and table passed while the table alone failed, which is the hole the first version of the gate had.
Not shown. Nothing here compares the browser with the plain page: the stages cost time and no benefit is claimed. The record was written by hand, so whether a model can build one that passes the traceability check is untested. The gate catches the losses its obligations name, and its check is probed with shredding as well as dropping — it does not catch a producer that copies the right phrases in order around something else, or a shift in emphasis. The margin is built but no interactive margin is shown, and source access alongside every accepted view is not shown either. The run’s own closing note records the rest: a cross-review found four further holes — the condition’s actual words, each hypothesis’s evidence, the view lifecycle, and record validation — each now a test.
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.