feat: RISK-ACCEPTED signs for weak evidence never a missing seal, and the ONE approval asks its questions out loud - #208
Conversation
Found in the pre-merge review of #208, not by the suite. `freeze-interview` was reopened to `build` rather than patched after close. `interview_gap` returned on the FIRST stamp whose digest matched the node. Two consequences, both wrong and in opposite directions: * a human who answered three decisions, was interrupted, and came back to answer the rest was still refused — with no way forward but editing the node to move the digest, which is the one thing that DISCARDS the answers; * had it read the last match instead, a follow-up `correct` would have been outranked by the earlier `confirm`. Every pass over the same text is now folded in order, later winning. An interview is a conversation, and two sittings about the same decisions are one interview. Fixing that exposed the second half. Each stamp recorded the FULL decision list with `unanswered` for ids that pass had not touched, so folding made every pass clobber the one before it. The stamp's answer map is now SPARSE — only what that pass actually answered. The sidecar still lists every decision, because that is the human-readable record; the stamp is the machine-readable delta. Two checks, red first, and the second is the counter-guard for the first: accumulate must not mean a stale `confirm` outranks a later `correct`. 862 passed across both roots. Twins synced, ENGINE_MD5 re-aimed. author: Tin Dang
…ing seal
Direction beat for `risk-accepted-integrity`: the frozen-contract draft and its
red checks. No engine change yet — the freeze is the human seam and this node
declares `sensitivity: security`, so the floor is `human`.
The finding, measured rather than inferred. All 16 integrity refusals in `gate`
are conditioned on `verdict == "PASS"`. Against the current engine, a Task
created seconds earlier — still carrying `goal: <one line>` and every template
section, never frozen and never briefed — reached `done` in three calls:
add run <slug> -- true
add gate <slug> RISK-ACCEPTED --reason "probing"
add done <slug>
R:UNSEALED, the post-freeze drift check, R:UNBRIEFED, the placeholder check and
R:UNDECLARED_SENSITIVE all read PASS only, so the lane meant to be the
SCRUTINISED one carries no scrutiny at all. This is #206's own lesson one
verdict over: skipping `freeze` did not FAIL the post-freeze guards, it
SWITCHED THEM OFF. That hole was closed for PASS and left open next door.
Two smaller findings fell out of the same read, both re-characterized by
execution rather than taken from notes:
* `_paths_touch` over-matches on string prefix, not path segment. The
recorded symptom (`scope: s` defeating the A17 floor) is false — probed, it
returns False. The real defect is the opposite and lives in the EXEMPTION
direction of R:UNDECLARED_SENSITIVE: `scope: src` exempts a changed
`srcfoo/secret.yaml`, and `secrets_public/x` matches `secrets/**`.
* an unrecognised `sensitivity:` degrades to the LOWEST floor. `high` and
`critical` both read `process`. This bit two real nodes on the
affordance-truth branch, which declared `high` and silently floored to
`process` instead of `plan`.
The contract splits the refusals in two: those protecting the RECORD (was it
frozen, did the contract drift, is the body still a template, did the build
touch an undeclared sensitive path) bind every verdict; those judging the
EVIDENCE (stale receipt, non-zero exit, unbound covers, coverage gap) stay
PASS-only, because signing for imperfect evidence is what the verdict is FOR.
19 checks: 13 red for the right reason, 6 green as counter-guards pinning
behavior that must NOT change — a lone refreeze still satisfies the seal, a
legitimate RISK-ACCEPTED on a frozen node still closes, a first-call gate still
hears the receipt message rather than a security refusal, and a HARD-STOP stays
recordable on an unfrozen node (refusing it would only lose the finding).
Suite: 824 passed / 13 failed / 7 skipped across both roots — the 13 are these.
Filed while dogfooding the `scaffold` beat from #207, which earned its keep: it
refused to advance this node while `gives:` still read as template, catching a
`<slug>` inside backticks that `status` alone would have shown as `direction`.
author: Tin Dang
Direction beat for `freeze-interview`: the frozen-contract draft and its red
checks. No engine change yet — the freeze is the human seam.
`freeze` already refuses an incomplete contract eight ways: template
placeholders, an unauthored `gives:`, collapsed surfaces, unswept (dimension,
surface) pairs, an unbudgeted explore. Every one of those checks the DOCUMENT.
None checks the CONVERSATION.
`## ASSUMPTIONS` is by construction a list of silences the AI filled in on the
human's behalf, each line carrying the reading taken and the cost if it is
wrong. The sweep made the AI WRITE DOWN what it decided. Nothing made it ASK.
So ADD's ONE human approval is a single stamp that says nothing about whether
the human was ever shown a single decision they are approving.
At a human floor `freeze` now refuses until every open decision has been put to
a human and answered (R:UNINTERVIEWED). Two calls, both ratified by the human
this session:
* ENFORCED, not advisory. This repo's own record is the argument: three probe
campaigns showed recommended prose gets routed around, which is why `brief`
became a checkpoint (R:UNBRIEFED) rather than advice.
* ASSUMPTIONS + REJECTS. An assumption is a silence the AI filled and a Reject
is a boundary the AI drew — both are decisions the human never made but will
be held to. The Musts came FROM the human, so re-asking them is noise, and
an `n/a` retirement already states its own reason.
Design consequence found while reading `freeze`: it computes
`authority = authority or authority_for(graph, cid)`, so an explicit
`--authority process` DOWNGRADES even a `security` node. Keying the interview on
the passed flag would therefore have shipped the guard with its own one-flag off
switch, so M5 keys it on the COMPUTED floor and E1/test pins that.
Answers persist as evidence, not a boolean: one `act: interview` stamp carrying
a digest of exactly what was interviewed, plus a `.d/interviews/<n>.md` sidecar
it references — the same sidecar shape `.d/runs/<n>.md` already uses, so no body
section is renumbered (R:RENUMBER; the engine keys sections by name and number).
Staleness follows `direction:` and `brief:`: edit an interviewed assumption and
the digest moves, so the interview re-opens. Edit a Must and it does not.
`correct` is a real verdict and does NOT complete an interview — it is cleared
by editing the item, which moves the digest and requires a fresh pass. `defer`
does complete one: a human may accept a risk knowingly.
21 checks: 18 red for the right reason (`add.interview` absent, `freeze` does
not refuse, the three skill trees do not teach it), 3 green as counter-guards
pinning behavior that must NOT change — a `process`-floor node is never
interviewed, the interview runs LAST so nobody is asked about template slots,
and the section census is unchanged.
author: Tin Dang
…ng seal
Build beat for `risk-accepted-integrity`. All 19 checks green.
`gate`'s sixteen integrity refusals were each written `verdict == "PASS"`, so
RISK-ACCEPTED walked past every one of them and `done` accepted the resulting
stamp. Measured before the fix: a Task created seconds earlier — still every
template slot, never frozen, never briefed — reached `done` in three calls.
The sixteen inline conditions are now ONE predicate over two named tuples, so
the classification is reviewable in one place instead of being re-decided at
each site:
* INTEGRITY_REFUSALS protect the RECORD — unsealed, drift, unbriefed,
placeholders, undeclared_sensitive, phantom_scope, and the three explore
equivalents. They bind every verdict that APPROVES or CLOSES.
* EVIDENCE_REFUSALS judge the RUN — stale_receipt, failed_run,
unbound_covers, hollow_explore, no_security_lens. PASS only: signing for
imperfect evidence is what RISK-ACCEPTED is FOR, and three of them already
named it as their own remedy.
* HARD-STOP is refused by neither. It never closes a task, so refusing it
would only stop a finding being written down.
`_binds` raises on an unclassified name, so a refusal added later cannot
silently inherit either tier. The verdict literal now appears exactly once in
`gate`, as `closes`.
`done` gained the seal check: any (re)freeze must precede the entitling gate. A
refreeze recorded afterwards (the re-cross pattern) is not required to.
`_paths_touch` matches whole path SEGMENTS. The dangerous direction was the
EXEMPTION clause of R:UNDECLARED_SENSITIVE, where a string-prefix read let
`scope: src` sign for a `srcfoo/` the node never declared, and
`secrets_public/` answer for `secrets/**`. An empty side now matches nothing.
`sensitivity:` floors UP when unreadable. `SENSITIVITY_FLOOR.get(v, "process")`
sent every unrecognised value to the LOWEST floor; `high` and `critical` both
read `process`, which is how two real nodes on the affordance-truth branch
floored at `process` where they meant `plan`. `new` now refuses the value
outright — deliberately breaking that function's "slots, never validation"
stance, because `sensitivity:` is not a prose slot but the enum that computes
the floor, and `init` already refuses an unknown `--profile` for the reason.
Four fixtures were corrected, none weakened; every assertion is unchanged:
* two of my own — one anchored on `depth: standard`, which `new` does not
write unless supplied, so it was a silent no-op and passed against nothing;
one used `brief()` (which COMPILES) instead of `brief_stamp()` (which
records), and one built its bundle where `gate`'s `root.parent` could not
see the git repo.
* two incumbent — `test_done_transitions_with_gate` and `_make_done` in
test_reopen.py both forged a gate stamp with no freeze, which is exactly
the record M6 now refuses. They assert that an entitling gate transitions;
that stays true, so they gained the seal the method always implied.
Four engine twins synced; ENGINE_MD5 re-aimed 1bf61710 -> 4c2a048d.
author: Tin Dang
Correcting the tier split shipped one commit ago. I classed R:UNBRIEFED as INTEGRITY; the full suite disagreed, and it was right. `test_brief_gate.py::test_non_pass_verdicts_are_never_blocked` carries `covers: M3` of the brief-gate task's FROZEN contract — "a verdict is how a node LEAVES a bad state (same stance as R:TRAP)". That Reject belongs to another task and is not this one's to weaken, so the refusal moves rather than the test. It also reads correctly on the merits. A missing brief says the BUILD ran without the compiled prompt — a fact about how the run was driven, which is what the evidence tier is for. The record itself still cannot be forged under any verdict: the seal (R:UNSEALED), the post-freeze drift check and the placeholder guard all stay INTEGRITY, so a RISK-ACCEPTED still cannot close an unfrozen, drifted or template node. Same shape as the R:T2SCAN conflict resolved during affordance-truth: when a frozen Reject from a neighbouring task contradicts a design choice here, the frozen one wins and the reasoning is recorded at the site. Twins resynced; ENGINE_MD5 re-aimed. author: Tin Dang
… first PASS
The gate refused the first PASS on three unbound referents, and was right on all
three:
* A13 and A14 were the two assumptions added to close the sweep during
authoring. Their checks were WRITTEN (the HARD-STOP counter-guard and the
backslash case) but never declared in `## CHECKS`, so the gate bound nothing
— docstring `covers:` is not the binding, the node's CHECKS section is.
* `test_no_existing_refusal_was_narrowed` was DECLARED covering R:WIDEN and
never written at all.
Writing it found the defect it was for. Rewriting sixteen inline conditions into
two named tuples is precisely the edit where a refusal quietly disappears, and
one had: R:UNFROZEN_EXPLORE was listed in INTEGRITY_REFUSALS but never dispatched
through `_binds`, because that refusal is UNCONDITIONAL — it refuses HARD-STOP
too. Routing it through `_binds` would have narrowed an existing refusal to buy
tidiness, so it comes out of the tuple with the reason recorded at the site.
The check's own arithmetic was wrong too (14 names; there are 13). Replaced with
an explicit member set rather than a smaller count — naming them pins strictly
more than counting them, and a dropped refusal now fails by name.
Refroze, re-briefed, re-ran, gated PASS at `human` on receipt 2: 20/20 checks
bound, freshness fresh.
author: Tin Dang
Build beat for `freeze-interview`. All 21 checks green; 858 passing across both
roots.
`add interview <slug>` compiles every non-`n/a` assumption and every Reject into
a numbered question carrying the reading taken and the cost if it is wrong. Bare
it is a read. With `--answer <id>=confirm|correct|defer --by "<name>"` it writes
a `.d/interviews/<n>.md` sidecar and appends one `act: interview` stamp
referencing it. At a human floor `freeze` refuses until every decision is
answered -> R:UNINTERVIEWED.
It is the LAST refusal in freeze's ladder: everything above says the contract is
unfinished, and there is no sense putting template text to a human. It is also
the only one that checks the CONVERSATION rather than the document.
Keyed on `authority_for`, never on the `--authority` argument, since the line
below it is `authority or authority_for(...)` — reading the flag would have let
`--authority process` switch the interview off on a security node.
Staleness follows `direction:` and `brief:`. `interview_gap` reads the stamp
whose digest MATCHES the current text, not the latest: revert an edit and the
earlier interview is the one that answers for this text. `correct` never
completes an interview — it is cleared by editing the item, which moves the
digest. `defer` does: a human may accept a risk knowingly.
Adding the 24th verb rippled exactly as this repo's record says it does — 15
failures across six registries and five budget pins, every one a real staleness:
* the CLI verb-set registry, the book's command reference (in `docs/`, not the
root copy I edited first), and both READMEs' "23-verb kernel" line.
* `test_no_new_verb_in_the_cli_surface` re-aimed 23 -> 24. Its claim is that
the AUTHORING-BEAT task added no verb; a later task legitimately adding one
moves the value without weakening that.
* SKILL.md went 176 -> 179. One of the five budget guards is literally
R:BUDGET_BUMP, so the addition was funded by COMPRESSING the freeze bullet
it sits beside, back to 176 exactly.
* `_open_decisions` returned a `"kind"` key, which collided with the receipt
KIND vocabulary a separate guard enumerates against the skill corpus.
Renamed to `"of"` rather than teaching the skill a word it does not mean.
Two security fixtures (`test_gate_coverage`, `test_sensitive_path_floor`) called
`add.freeze` and discarded the result, so the new refusal made them fail on
R:UNSEALED one step later. They now interview first AND assert the freeze landed
— a fixture that silently skips the one approval tests no real path.
Dogfooded on this repo's own bundle: `add interview freeze-interview` puts all
12 of its own open decisions.
author: Tin Dang
… PASS
Same pattern as the sibling node, and the gate was right again: A1, A3 and
R:SELFANSWER were unbound.
* A3's staleness was proven by a check that cited only M7 and E3.
* A1 and R:SELFANSWER had no check at all — and writing them found that my own
SKILL.md compression had left the R:SELFANSWER instruction only in
direction.md, WRAPPED ACROSS A LINE BREAK. This repo has silently unpinned a
phrase that way before, so the sentence is reflowed onto one line and the
check asserts it line by line rather than against the whole file.
`test_selfanswer_is_carried_by_prose_not_by_an_engine_claim` states the limit
plainly: `--by` is recorded verbatim, exactly as `freeze --by` is, so nothing
here VERIFIES a human. Claiming otherwise would be the worse failure — a guard
that asserts what it cannot prove is how a stamp becomes theatre. The discipline
is prose, and the check proves the prose ships.
Gated PASS at `plan` on receipt 2: 23/23 bound, freshness fresh.
Worth recording: this node's own floor is `architecture` -> `plan`, so by M5 it
was EXEMPT from the refusal it ships. The feature did not gate itself, and that
is the design — a `plan` freeze has no human in the room to interview — but it
means the first real exercise of R:UNINTERVIEWED is the next security-floored
node, not this one.
Three lessons filed to specs/method.
author: Tin Dang
Found in the pre-merge review of #208, not by the suite. `freeze-interview` was reopened to `build` rather than patched after close. `interview_gap` returned on the FIRST stamp whose digest matched the node. Two consequences, both wrong and in opposite directions: * a human who answered three decisions, was interrupted, and came back to answer the rest was still refused — with no way forward but editing the node to move the digest, which is the one thing that DISCARDS the answers; * had it read the last match instead, a follow-up `correct` would have been outranked by the earlier `confirm`. Every pass over the same text is now folded in order, later winning. An interview is a conversation, and two sittings about the same decisions are one interview. Fixing that exposed the second half. Each stamp recorded the FULL decision list with `unanswered` for ids that pass had not touched, so folding made every pass clobber the one before it. The stamp's answer map is now SPARSE — only what that pass actually answered. The sidecar still lists every decision, because that is the human-readable record; the stamp is the machine-readable delta. Two checks, red first, and the second is the counter-guard for the first: accumulate must not mean a stale `confirm` outranks a later `correct`. 862 passed across both roots. Twins synced, ENGINE_MD5 re-aimed. author: Tin Dang
The reopen reset the gate; receipt 3 re-earns it at 25/25 bound. author: Tin Dang
ecb36f5 to
9abfbd0
Compare
|
Correction to the note added to this PR body before merge. The pre-merge review note claimed There is a real defect in the same area, now measured properly. The trigger is an odd number of double-quotes, not a brace — which is precisely why The seal silently does not exist while the human is told it does. It fails closed — the gate then refuses with R:UNSEALED — so nothing is let through; the damage is a verb reporting a record it did not write, across all seven raw Filed and fixed separately before 3.3.0. |
…and 3.3.0 (#209) * fix(engine): a stamp the notary reports written is a stamp that reads back `_oneline` exists because an unbalanced `{` in a `--reason` once made the parser swallow the following stamp. That fix was correct and was applied to exactly one field. Seven writers interpolated `by:` raw. Measured on the incumbent engine: $ add freeze t --by 'O"Brien' --authority human freeze recorded at authority `human` # reported as SUCCESS stamp keys read back: ['by'] # act, authority, direction swallowed _is_frozen -> False · sealed_direction -> None The seal silently does not exist while the human is told it does. The trigger is an ODD number of `"`; a balanced pair round-trips, which is exactly why this survived every real use of the engine. Severity, stated honestly: it fails CLOSED. The gate then refuses with R:UNSEALED, so nothing is let through. The defect is that a notary whose only job is to record faithfully reported a record it did not write -> R:LIE. All seven `by:` interpolations now go through `_oneline` (freeze · brief · replan · check · interview · both gate paths), and the reason is written at the function's definition, since the fix is one careless edit away from reverting. `test_every_stamp_writer_normalises_its_by` enumerates the writers FROM THE SOURCE rather than from a hand list — a hand list is precisely how the seventh writer gets missed, which is the shape of the defect itself. Corrections to my own work, both disclosed rather than quietly fixed: * I first reported this as a BRACE bug that destroyed the whole `verified:` ledger. That was wrong. The probe behind it froze an UNAUTHORED node, so `freeze` correctly refused on template placeholders and wrote nothing — I read a correct refusal as corruption. The claim is corrected on PR #208. * M1 (`_oneline` neutralises the quote) was already TRUE before this task; `_oneline` has always replaced `"`. It stays as a regression pin, but the real rule here is M2 — the writers that never called it. * `test_no_existing_stamp_is_rewritten` first compared whole-file text, which a later write legitimately grows. It compares the parsed stamp list now. 873 passed across both roots. Twins synced, ENGINE_MD5 re-aimed. author: Tin Dang * chore(node): close stamp-field-integrity The gate refused the first PASS: M4 was declared with no check. The punctuation walk proves the property, so it now asserts the BICONDITIONAL — what the verb reports must match what the ledger holds — rather than gaining a stapled covers. Gated PASS at `plan` on receipt 2, 11/11 bound. author: Tin Dang * chore(release): 3.3.0 — the seal means something under every verdict Six version sources in lockstep: pyproject.toml · package.json · package-lock.json (2 places) · .claude-plugin/plugin.json · src/add_method/__init__.py · tooling/add.py's ENGINE. Bumping ENGINE re-aims add.py, so the four engine twins are resynced, ENGINE_MD5 re-aimed, and both dogfood bundles' `engine:`/`tooling_engine:` stamps moved to add/3.3.0 — those ride every version bump and go stale silently otherwise. CHANGELOG covers the release honestly, including that the RISK-ACCEPTED hole failed CLOSED and that the stamp defect made a verb report a record it had not written. 873 passed / 7 skipped across both roots. Tag and publish are NOT part of this commit — they remain the human call. author: Tin Dang
Two nodes, both driven through ADD, both closed on a receipt-backed gate. They are one finding at two depths: a guard written for one verdict is a guard for one verdict.
1 — RISK-ACCEPTED signs for weak evidence, never for a missing seal
#206 found that skipping
freezedid not fail the post-freeze guards, it switched them off, and closed that hole forPASS. Every one ofgate's sixteen integrity refusals was writtenverdict == "PASS", so the identical hole stayed open one verdict over. Measured before the fix — a Task created seconds earlier, still carryinggoal: <one line>and every template section, never frozen, never briefed:Three calls, from scaffold to
done, past R:UNSEALED · drift · R:UNBRIEFED · the placeholder check · R:UNDECLARED_SENSITIVE. The lane meant to be the scrutinised one carried no scrutiny at all.The sixteen inline conditions are now one predicate over two named tuples, so the classification is reviewable in one place instead of being re-decided at each site. The split is "would a verdict here be a FABRICATED record, or merely an OPTIMISTIC one?"
INTEGRITY_REFUSALSprotect the RECORD — unsealed · drift · placeholders · undeclared-sensitive · phantom-scope, plus two explore equivalents. They bind every verdict that approves or closes. Accepting a risk is not a route around the seal.EVIDENCE_REFUSALSjudge the RUN — stale receipt · failed run · unboundcovers:· open questions · missing security lens. PASS only: signing for imperfect evidence is exactly what RISK-ACCEPTED is for, and three of them already named it as their own remedy._bindsraises on an unclassified name — a refusal added later cannot silently inherit either tier.donegained the seal check (any (re)freeze must precede the entitling gate; a refreeze recorded afterwards, the re-cross pattern, is not required to). Two smaller findings fell out of the same read, both re-characterized by execution rather than taken from notes:_paths_touchover-matched on string prefix. The recorded symptom —scope: sdefeating the A17 floor — is false; probed, it returnsFalse. The real defect is the opposite and lives in the exemption direction of R:UNDECLARED_SENSITIVE:scope: srcsigned for a changedsrcfoo/secret.yaml, andsecrets_public/answered forsecrets/**. Now whole-segment.sensitivity:floored DOWN.highandcriticalboth readprocess. Two real nodes on the parent branch declaredhighand floored toprocesswhere they meantplan. It now floors tohuman, andnewrefuses the value outright — deliberately breaking that function's "slots, never validation" stance, becausesensitivity:is not a prose slot but the enum that computes the floor, andinitalready refuses an unknown--profilefor the same reason.2 — The ONE approval asks its questions out loud
freezealready refused an incomplete contract eight ways. Every one checks the document. None checked the conversation.## ASSUMPTIONSis by construction a list of silences the AI filled in on the human's behalf, each line carrying the reading taken and the cost if it is wrong. The sweep made the AI write those down. Nothing made it ask. So ADD's one human approval was a single stamp that said nothing about whether the human had seen a single decision they were approving.add interview(the 24th verb) compiles every non-n/aassumption and every Reject into a numbered question carrying the reading and the cost. Bare it is a read;--answer <id>=confirm|correct|defer --by "<name>"writes a.d/interviews/<n>.mdsidecar and oneact: interviewstamp — the same sidecar shape.d/runs/uses, so no body section is renumbered (the engine keys sections by number, which has bitten this repo).Design calls, each pinned:
--authority. The line below it isauthority or authority_for(...), so reading the flag would have shipped the guard with its own off switch:--authority processon a security node.direction:andbrief:.interview_gapreads the stamp whose digest matches the current text, not the latest — revert an edit and the earlier interview answers for it. Reword an interviewed assumption and it re-opens; edit a Must and it does not, because the Musts came from the human.correctnever completes an interview — it is cleared by editing the item, which moves the digest.deferdoes: a human may accept a risk knowingly.--byis recorded verbatim, exactly asfreeze --byis. R:SELFANSWER is shipped prose, and a check proves the prose ships — a guard that asserts what it cannot prove is how a stamp becomes theatre.Verification
The gate refused the first PASS on both nodes, and was right both times. In each case the culprit was the same class: assumptions added during authoring to close the sweep had checks written but never declared in
## CHECKS— the docstringcovers:is not the binding. Two rules had no check at all.Writing one of those missing checks found a defect on its first run: rewriting sixteen conditions into two tuples is exactly the edit where a refusal quietly vanishes, and one had —
R:UNFROZEN_EXPLOREwas listed in the integrity tuple but never dispatched, because it refuses unconditionally, HARD-STOP included. Routing it through_bindswould have narrowed an existing refusal to buy tidiness. It comes out, with the reason recorded at the site. (The check's own count was wrong too — it asserts an explicit member set now, which pins strictly more than a number.)process-floor node is never interviewed, a lone refreeze still seals, a legitimate RISK-ACCEPTED still closes, a first call still hears the receipt message, a HARD-STOP stays recordable on an unfrozen node.add interview freeze-interviewputs all 12 of that node's own open decisions.Reviewer notes
unbriefedas integrity;test_non_pass_verdicts_are_never_blockedcarries brief-gate's frozen M3 — "a verdict is how a node LEAVES a bad state." That Reject belongs to another task, so the refusal moved rather than the test. It reads correctly on the merits: a missing brief is a fact about how the run was driven, and the seal, drift and placeholder guards still bind every verdict.architecture→ floorplan, and M5 keys on the computed floor, so it was exempt from the refusal it ships. That is the design — aplanfreeze has no human in the room — but the first real exercise of R:UNINTERVIEWED will be the next security-floored node.add.freezeand discarded a refusal. They now include the seal the method always implied and assert it landed — a fixture that silently skips the one approval tests no real path.R:BUDGET_BUMP— so the addition was funded by compressing the freeze bullet beside it, back to 176 lines exactly.test_no_new_verb_in_the_cli_surfaceis re-aimed 23 → 24, not deleted: its claim is that the authoring-beat task added no verb, and a later task legitimately adding one moves the value without weakening that.freezetakesauthority or authority_for(...), so an explicit--authority processdowngrades even asecuritynode outright.Update (pre-merge review): rebased onto
mainafter #207 merged as a squash, so this now targetsmaindirectly and carries only its own 9 commits.The review found one defect, and the node was reopened to
buildrather than patched after close.interview_gapreturned on the FIRST stamp whose digest matched the node — so a human who answered three decisions, was interrupted, and came back to answer the rest was still refused, with no way forward but editing the node, which is the one action that DISCARDS the answers. Reading the last match instead would have been wrong in the opposite direction: a follow-upcorrectoutranked by the earlierconfirm.Passes over the same text now fold in order, later winning. Fixing that exposed the second half: each stamp recorded the full decision list with
unansweredfor ids that pass had not touched, so folding made every pass clobber the one before it. The stamp's answer map is now sparse — the sidecar remains the human-readable record, the stamp is the machine-readable delta. Two checks, red first, the second being the counter-guard for the first.Also found, NOT fixed here — pre-existing:
_onelineis applied to--reasonbut not to--by, onfreezeandgatealike.add freeze --by "ev{il}"writes a stamp that parses back as zero stamps: the wholeverified:ledger is lost, and that ledger's ordering IS the trust model.interviewfollows the same incumbent convention. Worth its own node.863 passed / 0 failed / 7 skipped after the rebase.