117 โ Plans derive their structural jobs from the type
Type: reference/types/adr.md ยท Status: accepted
Status: accepted Date: 2026-10-10 Amends: ADR 113: its open items, standard handlers and plan compaction, are decided, and its consequence that every check job needs a handler no longer holds.
Amended 2026-10-10: revision 1 memory profiles and their compatibility path are removed, the schema requires version 2, and the profile's comparison-version check went with them; the analysis keeps two declared checks, the boundary's binding to acquisition and the report-check gate. The verification protocol's grammar is one layout finding for any role with verifies, its addressees taken from that list, so the apply handler enforces nothing the validator does not.
Amended 2026-10-10: a model job's judgment inputs are engine gates, not worker evidence: the engine pins and enforces them, and delivers no file, binding or reading entry for them, since on the standard path they record an empty acceptance the worker can learn nothing from. Code jobs keep receiving theirs. The opening record is a read of the boundary entry only, whose acquisition may need the caller's raw source input; later workers have its useful values as prompt bindings. Order-only reads stay delivered: order-only is a rerun rule, and the report is content. The hand-out's reading list is de-duplicated and a repeated read is refused.
Context
ADR 113 gave the engine a plan and a type. The first consumer, the
agentic-system analysis, then needed a package of handlers beside a plan
of 498 lines. Ten of its twenty-five jobs were check or apply jobs that
restated what the layout already said about their roles: the candidate,
the incumbent, the producer's attempt and answered refusal, the partners
from cites and identity, the criteria from the roles' types. Their
handlers were wrappers naming a role and a partner tuple. Every copy was a
place for the generic logic to drift, and the plan's mission content,
which roles are written in what order from what, was hard to see. Each
job instruction was half boilerplate: read order, what the inputs are,
the answers protocol, the self-check and the return line, repeated ten
times with small differences. The operator's direction (2026-10-09): the
type defines most of the type-dependent work, and code extensions are the
special cases.
The forces that recur: a per-job configuration in Python is the cheapest thing to write and the easiest to let drift from the declaration it mirrors; a second declaration shape is worth its cost only if it stays faithful to the hand-written one; a worker should not discover at run time what code already knows; and a member's producer should need as little as possible of the whole artifact.
Decision
A compact plan. The plan lists the jobs that write, one entry per
role named after the role, with its instruction, reads, max attempts,
outputs, gates and the consumer's own checks, plus the consumer's own code
jobs in full form. A loader in the reuse modules expands it into the full
plan the engine fixes at start: one check-<role> job per filled role and
one apply-<role> job per role with verifies, their inputs taken from
the layout, not from the entry's reads; identity sources as required
inputs, cited roles as optional, the producer's attempt order-only, and
criteria as the type closure of the roles plus one plan-level contracts
group. reads shapes only the model job's hand-out. A job: entry runs a
standard handler over roles. Plan keys and names are hyphenated, and the
loader refuses an underscore. The run records the compact file's digest,
and integration compares that with the shipped plan.
Standard handlers. The reuse modules ship the check, the verification application and the artifact check. A handler learns its role and partners from the job's declared inputs through an accessor; nothing is repeated in a parameters block. The consumer keeps opening, acquisition, assembly, publication and effect verification, which no declaration can derive.
A named verification protocol, fixed, not configurable. A verifying
role's document carries ## Blockers and ## Limits, each none or a
list; with several subjects every blocker starts with the role it
addresses; Blockers none accepts every subject's handed version that
validation did not refuse, otherwise each addressed subject is refused and
an unaddressed one stays unsettled. The apply handler validates the
verdict and each handed subject together, routes findings by the role
they name, and refuses a subject only for findings that appear with the
verdict placed and not without it; a finding the subject shows on its own
belongs to its check. Consumer policy enters through two extensions on an
entry: checks, functions returning findings, with their own inputs; and
feedback, one function appending to a subject's refusal. The frozen
source is a plan-level key naming the member whose source field pins
it, a run's authorization rather than shape.
The prompt is composed from its own lines. The engine's frame prints
name = value lines for the job, its inputs, outputs, parameters, the
role, the artifact and, from the layout, identity, cites and
verifies. A plan-level prompt-section names a file the engine renders
into the frame, whose placeholders are exactly those lines and nothing
else, with a paragraph marked [<name>] kept when the prompt has that
line. Run values a worker needs, the command path, the source identity,
the capture directory, are plan parameters printed as lines, so no worker
reads the opening's JSON. Each model job receives its member type and the
types of the roles it reads; the set type is not handed to workers.
Where the rules live. The member type owns what a member contains and wins over the job instruction; the worker rules own execution; a job instruction holds what the job must establish and challenge. The records contract stays a shared member-level contract that the record-citing types refer to; the boundary contract is in the boundary type; the sources contract is split between the boundary type and the worker rules.
Run identity. A layout identity source may name run, so
a member's binding to its run is a shape check in draft validation, and a
member type registers its identifier grammar so that a correction cannot
drop an identifier its accepted version declared. The analysis keeps
three declared checks: the boundary's binding to acquisition, the
report-check gate and the profile's comparison version.
Considered alternatives
Subjects declared in the plan rather than the type's verifies:
decided against in ADR 114, since the type would not notice a plan that
omitted verification.
A parameters block on code jobs naming role and partners: lets the strings drift from the inputs the accessor reads.
Widening cites to mean reads: would make the validator read
mission; reads stays in the plan.
Configurable verdict policies: one fixed protocol; a second policy when a consumer needs it. Accepting a subject another verifier accepted would also be coherent and was not taken.
Slots as their own vocabulary: the first renderer gave {output} the
output's name while the line gave its path, and the template reached the
line through a third notation; slots became the lines.
Two template files, one for answering jobs: would repeat the rest; one conditional paragraph instead.
Handing the set type to workers, with the records contract moved into it: reversed the same day, because a producer should need its member type, the types it reads and the contracts those name, and nothing about the whole artifact; the member type with its contracts is then the complete contract for a producer, testable without a set.
A list of citable records in the prompt: dropped, the writer reads the cited members anyway and the destination follows from the ID. A list of limits the synthesis must carry: deferred until a run shows the limit-not-carried refusal firing.
The coordinator judges for consumers without a verifier, and a typed artifact-run directory: left for a consumer that needs them.
Consequences
The analysis plan is 219 lines against 498; its ten mission files hold about 2,900 words from 4,950; the consumer's wrapper handlers are deleted and its Python holds opening, acquisition, assembly, publication and three declared checks. A second consumer with structural checks writes a compact plan and no handlers. Fidelity tests hold the expansion equal to a frozen copy of the hand-written plan after the decided differences, and a judgment test showed each wrapper and its standard replacement recording the same judgments before the wrappers went.
Operativity: the analysis skill starts runs through commonplace-analysis
start, which expands the compact plan; the loader, the standard handlers
and the prompt renderer are the engine's reuse modules; draft validation
consumes the run identity and the identifier grammar; the plan loader's
refusal of underscores and the type-closure test enforce the declaration
forms. Every rule a worker meets comes through the prompt, the member
type, the records contract and the worker rules.
Limits. One consumer has exercised this; the one production analysis to
publication on the compact plan and the templated prompts has not yet run,
so whether workers follow the new prompts and write link citations
correctly is untested. The standard handlers validate with the library's
parent as the project root, which holds for a source checkout and the
analysis worktrees and not for a package-installed consumer. Pinned
validation still refuses a member type with no Python rule; treating it as
schema-only waits on the type-selected checks proposal. The derived apply
job's judgment covers the verifies relation only; that a verdict was
right is the verifier's business, not the engine's.