Skip to content

Reference implementation for #752 — decision-request rendering for a captain decision raised outside a gate - #751

Draft
iamcxa wants to merge 6 commits into
mainfrom
iamcxa/fo-decision-request
Draft

Reference implementation for #752 — decision-request rendering for a captain decision raised outside a gate#751
iamcxa wants to merge 6 commits into
mainfrom
iamcxa/fo-decision-request

Conversation

@iamcxa

@iamcxa iamcxa commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Reference implementation for #752. Read the issue first — it holds the
problem, the reproduction, and the three open questions on direction.

This branch exists to be argued against with something concrete, not to be
merged ahead of that discussion. Per CONTRIBUTING.md, direction is settled on
the issue; opening this PR first was a mistake on my part and the earlier
proposal framing has been retracted.

What is on the branch

Two commits.

1. present-gate: a ## Decision Request template. For a captain decision
raised outside a gate — a worker halting on a declared threshold, a
contract-required choice, an unmet clarification. It records nothing: gate record stays the sole recorder and the entity's stage does not move. Three
fields carry the mechanism because a relay cannot fill them — one Recommend
stated as the action it authorizes, Derived from naming the evidence the FO
read for itself, and Outside the worker's remit naming the option the worker's
role structurally could not propose. The shared core's halt clause routes to it.

2. A live test. skills/integration/decision_request_live_test.sh, in the
shape of gate_content_live_test.sh beside it. Operator-run: it needs a live
model and is not wired into CI, same as its neighbour.

Evidence

go test ./... passes, including internal/contractlint — but that only
asserts frontmatter parses, the reference closure resolves, and deferred load
points are not dangling. It cannot tell a working template from a decorative
one. The live test is what covers this change.

Mutation, same fixture and prompt and binary and base revision (bcaa4e0b7),
only the template absent — the failure reproduces:

Recommend option 3: cut slice 1 in half and defer the expiry read.
Decision: Approve option 3?

With the template, same input:

Recommend authorizing a Go-only first slice: serve today's checkout user
through the Go subcommand and defer the unused installed-plugin shell,
skill, test, and registration surfaces
.

Which guard catches the relay, checked against the captured text rather than
assumed:

Guard Result on the mutation
1 — the recommendation reduces what is delivered passes (cut, defer)
2 — it is not one of the worker's three catches it (in half, expiry)

Guard 1 alone is insufficient: "cut slice 1 in half" reduces something and is
still the worker's option relayed back.

An earlier mutation attempt was invalid rather than negative — the control sat
35 commits behind the branch base, so its skill pinned 0.25.x against a
0.27.0-pre8+dev launcher and was blocked on version compatibility before
reading any template. Full detail in the comments below.

Known open, and deliberately not settled here

  • Field count. Six fields; three carry the mechanism. Reducible to five by
    folding Raised by into the header. Not done on this branch because the
    passing live run exercised this exact template and changing a field retires
    that evidence.
  • Home. Whether this belongs in present-gate or a separate skill is
    question 1 on the issue.

without-it unanswered

None.

…endering

At a gate the FO cannot avoid judging: the template carries a `Recommend
{approve | reject}` line, and a blank there is visible. Every other way a
captain decision reaches the captain has no template at all. The shared core
lists a blocker and a contract-required choice among the conditions that may
halt the turn, and says nothing about how to present one.

So the cheapest path is to relay. A worker halted on a declared threshold, gave
three options, and the FO passed the three on. The captain got a menu instead of
a recommendation, and the technical judgment they had delegated came back to
them.

A menu is worse than a wrong recommendation, because a worker's options carry
its role's blind spot. A worker told to build a thing cannot propose building
less -- that option does not exist inside its remit. In the case above, all
three options moved the budget and none moved the requirement; what mattered was
that one deliverable had no user yet, and nobody in the worker's seat could have
said so.

Add a `## Decision Request` template beside the gate template, with three fields
a relay cannot fill: one recommendation stated as an action, `Derived from`
naming the evidence the FO read for itself rather than the worker's summary, and
`Outside the worker's remit` naming the option the worker's role structurally
could not propose. The shared core's halt clause now routes through it.

The tell that the field exists to catch: when every option on the table moves
the budget and none moves the requirement, the list was written from inside the
requirement.

Verified: `go test ./...` passes, including `internal/contractlint`, which
structurally checks this skill.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`go test ./...` proves this skill's frontmatter parses and its references
resolve. It never renders a decision request, so nothing in the suite could
tell a working template from a decorative one.

Mirror `gate_content_live_test.sh`: drive a real first officer over a fixture
where a worker has halted on a stop number and offered three options, all of
which move the budget and none of which move the requirement. The fixture also
carries the fact the worker did not act on -- one remaining deliverable exists
for a user who does not exist yet.

Two guards on the single Recommend line, because the failure this template
exists to catch produces a well-formed Recommend line carrying a relayed
option. The first requires the recommendation to reduce what gets delivered;
the second requires it not to be one of the worker's three, since "cut slice 1
in half" satisfies the first on its own.

Operator-run, like the gate-content test beside it: it needs a live model and
is not wired into CI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@iamcxa

iamcxa commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Correcting this PR's evidence claim, and a process miss

Process — I should not have opened this PR. CONTRIBUTING.md says
improvements go up as GitHub issues rather than pull requests directly, so the
direction can be discussed before anyone writes code. I grepped that file for a
test command earlier, got no match, and never read it. Holding on how to
resolve this PR; it should not be reviewed as a proposal until that is settled.

Evidence — the original body implied coverage this change did not have. It
cited go test ./... "including internal/contractlint, which structurally
checks present-gate". That is true and it is not test coverage of this
change: contractlint asserts frontmatter parses, the reference closure resolves,
and deferred load points are not dangling. Nothing in the suite renders a
decision request, so it could not tell a working template from a decorative one.

skills/integration/decision_request_live_test.sh now covers it, mirroring
gate_content_live_test.sh beside it. Operator-run — it needs a live model and
is not wired into CI, same as its neighbour.

The fixture puts a worker at a stop-number halt with three options that all move
the budget and none of which move the requirement, plus the fact the worker did
not act on: one remaining deliverable serves a user who does not exist yet. Two
guards on the single Recommend line, because the failure this exists to catch
produces a well-formed Recommend line carrying a relayed option — the first
requires the recommendation to reduce what is delivered, the second requires it
not to be one of the worker's three, since "cut slice 1 in half" satisfies the
first on its own.

First run failed on an assertion that was too literal (registration point vs
the rendered registration surfaces); the behaviour was correct and the
assertion was wrong. It now asserts substance. Second run passes.

Minimality, stated rather than claimed. The template has six fields. Three
carry the mechanism — Recommend (single), Derived from, Outside the worker's remit. Three are context and captain authority — Raised by,
Alternatives, Decision. It can go to five by folding Raised by into the
header line. Not done here: the passing live run exercised this exact template,
and changing a field retires that evidence.

@iamcxa

iamcxa commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Mutation evidence: the template is what produces the behaviour

A passing live test proves nothing on its own, so the same fixture, prompt, and
binary were run against a checkout at the same base revision
(bcaa4e0b7) with only the ## Decision Request section absent.

With the template — the recommendation reduces what is delivered, and names
the option the worker's role could not propose:

Recommend authorizing a Go-only first slice: serve today's checkout user
through the Go subcommand and defer the unused installed-plugin shell,
skill, test, and registration surfaces
.

Outside the worker's remit: reduce today's requirement to the Go subcommand;
an implementation worker assigned plugin reachability could not remove that
requirement itself.

It also declined to certify what it could not check: "The fixture contains no
implementation diff, so its line counts cannot be independently verified here."

Without the template — same everything else — the failure reproduces exactly:

Captain decision: "Publish a document and hand out its link" — implementation.

Recommend option 3: cut slice 1 in half and defer the expiry read.

Decision: Approve option 3?

No Derived from, no Outside the worker's remit, and the recommendation is
one of the worker's own three. The deliverable with no user today is never
reached.

Which guard catches it

Checked rather than assumed, against the captured text:

Guard Result on the mutation
1 — the recommendation reduces what is delivered passes (cut, defer)
2 — it is not one of the worker's three catches it (in half, expiry)

This is why guard 1 alone is not enough: "cut slice 1 in half" reduces
something, and it is still the worker's option relayed back.

An earlier mutation run was invalid, not negative

The first attempt used ~/conductor/repos/spacedock-v1, 35 commits behind the
branch base. Its First Officer skill pins 0.25.x against a 0.27.0-pre8+dev
launcher, so it was blocked on version compatibility before reading any
template. It showed only that the test fails when the FO cannot start. The
control has to sit on the same base as the treatment; the run above does.

@iamcxa iamcxa changed the title present-gate: give a captain decision raised outside a gate its own rendering Reference implementation for #752 — decision-request rendering for a captain decision raised outside a gate Aug 21, 2026
…is answered

The dev workflow's validation stage asks how a test could pass while the
observable behavior is wrong. Here the answer was short: `Derived from` was
checked for presence only, so a first officer writing "Derived from: the
worker's report" passed the check while doing the exact thing the field exists
to stop.

The template says that evidence is cited by path, line, or command. Require a
citation a reader can open, and reject a derivation that names the worker's own
summary as its source.

Falsified rather than assumed: the three bypass forms a relaying FO would
produce -- naming the report, the summary, or its own agreement -- are all
rejected by the new guard, and the passing run's real citation
(`README.md:14`, `reading.md:10`) is still accepted. Live run passes with the
guard in place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@iamcxa

iamcxa commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Self-validation against this repository's own dev workflow

Read docs/dev/README.md and checked this branch against it, rather than
against a generic PR bar. Two findings block merge; one is fixed here.

Blocking — not mine to close

Live-lane coverage is incomplete. The Proof policy is explicit:

Required CI lanes are a function of the diff, not the FO's read of
"relatedness." ... When the diff touches a file a live lane exercises — the
shipped FO/ensign contract or a host adapter (skills/**/references/**) ...
that lane is REQUIRED green before merge ... to the host-neutral dispatch
core, every host lane.

This diff touches skills/first-officer/references/first-officer-shared-core.md
— inside skills/**/references/**, and host-neutral (the host adapters are
claude-fo-dispatch.md and codex-first-officer-runtime.md). So the mapping
requires every host lane.

claude-live and codex-live run on the PR. pi-live cannot: its condition is
github.event_name == 'workflow_dispatch' && inputs.live_cadence == 'pi', so it
skips on every pull request. Pre-existing, not introduced here — but under this
policy a host-neutral contract change needs that manual dispatch green before
merge. Flagging rather than triggering it; that run costs and the repo is not
mine to spend.

Blocking — fixed in 7a4cc5c

The validation stage asks "How could this test pass while the observable
behavior is wrong?"
The answer was short: Derived from was checked for
presence only, so a first officer writing Derived from: the worker's report
passed while doing the exact thing the field exists to stop.

The guard now requires a citation a reader can open, and rejects a derivation
that names the worker's own summary. Falsified rather than assumed:

Probe Result
Derived from: the worker's report. rejected
Derived from: the ensign's summary rejected
Derived from: I read the halt and agree. rejected
the passing run's real README.md:14 citation accepted

Live run passes with the guard in place.

Not closed here, deliberately

This work never entered the dev workflow — no entity, no ideation, no
**AC-N** items with Verified by: clauses. That matters under a rule aimed
squarely at this diff:

A change to a live journey's grader, fixture, runner, or the contract text a
journey drives
IS that runtime claim, and it belongs in an ACCEPTANCE
CRITERION — not a test-plan line.

There is no AC here, and no existing TestLiveCommon* journey was run. Adding
ACs now would be another lap around the discussion #752 exists to have:
ideation and its acceptance criteria come after direction is settled, not from
the person who already skipped that step once.

Clearing the bars this change does meet

  • No prose-grep over instruction files. The test reads no instruction file.
    It drives a real first officer and asserts over the model's output — the
    second sanctioned form, where the oracle is observed output. It can fail, and
    the mutation run above shows it failing.
  • Evidence must be able to fail — name the falsifying edit. Named (remove
    the template) and executed, on a same-base control.
  • done bar: "A contract or skill change is PASSED only when a live drive
    observed the behavior it claims."
    A live drive observed it.
  • Detached adversarial audit — partially. One adversarial edit was
    constructed on a throwaway checkout and the test caught it. A full audit would
    try other refutations; not claiming that was done.

…the tag

The shell test this replaces was modelled on `gate_content_live_test.sh`, the
only shell script among ten Go tests in `skills/integration`, referenced by
nothing and wired into no lane. Copying it put every assertion behind a live
model: the graders themselves were unchecked, and the falsification that
proved they could reject a bypass was run by hand and thrown away.

Split it the way the rest of the repo is split. `gradeDecisionRequest` is one
Go entry point returning every way a rendered message fails the contract, and
the offline table test pins it against six fixtures. The live test drives a
real first officer and feeds its final message to that same entry point, so a
grader loosened to make the live run pass loosens the recorded fixtures with
it.

Two fixtures are real output captured from the same fixture, prompt, binary,
and branch, differing only in whether present-gate carried the template -- the
control removes those 38 lines and its routing sentence and nothing else. Four
are the bypasses a relaying first officer produces, including the one that
motivated a second guard: "cut slice 1 in half" reduces something and is still
the worker's own option.

The prompt lost two hints it should never have carried. It no longer says the
halt is not a gate, and it no longer names the template -- both are the
contract's judgment, and one of them was also an asserted string. Under the
weakened prompt the first officer still reaches the template, still cites
README.md and reading.md by line, and still recommends deferring the surface
that serves a user who does not exist yet.

Recording the pair also retired two checks. Without the template the first
officer wrote "Decision request:" and a Recommend line anyway, so those
presence checks separated nothing; the absent derivation, the absent remit
account, the relayed option, and the unreached surface are what did. The
comment says so rather than leaving a reader to trust form.

Verified: `go test ./...` passes; the offline table passes; the live test
passes against this branch (121s) and fails against the control with
[missing-field:derived-from missing-field:remit
never-names-the-surface-with-no-user-today
recommendation-relays-a-worker-option].

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@iamcxa

iamcxa commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Test rewritten into this repository's two-layer shape

The shell script was modelled on gate_content_live_test.sh — which turns out
to be the only shell script among ten Go tests in skills/integration,
referenced by nothing and wired into no lane. Copying it put every assertion
behind a live model: the graders were themselves unchecked, and the
falsification that showed they reject a bypass was run by hand and discarded.

Layer File Runs
Grading skills/integration/decision_request_test.go offline, in CI, no model
Live drive skills/integration/decision_request_live_test.go (//go:build live) with -tags live, feeds its final message to the same entry point

gradeDecisionRequest is one function returning every way a rendered message
fails the contract. Both layers call it, so a grader loosened to make the live
run pass loosens the recorded fixtures with it.

The prompt lost two hints it should not have carried

Before: "...raised a captain decision. This is not a gate. Present the
decision request.
"

Both are the contract's judgment, made in the prompt — and Decision request
was also one of the asserted strings, so that assertion was grading my own
phrasing. Now the prompt says only that the worker stopped part-way through its
stage and needs a decision.

Under the weakened prompt the first officer still reaches the template on its
own:

Recommend ship only the Go subcommand to the checkout user now; defer
installed-plugin entry points until a real installed-plugin user exists
.

Derived from: README.md:16 requires staying within the declared limit.
reading.md:11 says the only waiting user can run the Go subcommand
directly, while the extra entry points serve no current user.

Outside the worker's remit: reduce the outward commitment by postponing
published-plugin access; the implementation worker could not change that
scope.

Control, tightened

The earlier control was origin/main, 35 commits from the branch base. This one
is a worktree of this branch's HEAD with the 38-line ## Decision Request
section and its routing sentence removed, and nothing else. One variable.

Recommend option 3: split slice 1 and defer the expiry read.

Decision: Approve option 3?

Graded: [missing-field:derived-from missing-field:remit never-names-the-surface-with-no-user-today recommendation-relays-a-worker-option].

Recording the pair retired two of my own checks

Without the template the first officer still wrote Decision request: and a
Recommend line. Those two presence checks separated nothing. What
separated the pair was the absent derivation, the absent remit account, the
relayed option, and the unreached surface. The code comment says so rather than
leaving a reader to trust form.

Fixtures

Two recorded (with-template.txt, without-template.txt) and four constructed
bypasses — including the one that forced a second guard: "cut slice 1 in half"
reduces something and is still the worker's own option. The hand-run
falsification is now committed as those cases.

Evidence

  • go test ./... — all pass
  • offline table — 6/6, plus an extractor guard (Derived from must stop at its
    paragraph, or a later citation would rescue an empty derivation)
  • live, this branch — pass, 121s
  • live, control — fail, with the four failures above

@iamcxa

iamcxa commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Retracting the pi-live finding

An earlier comment here called incomplete pi-live coverage a blocking finding,
reading the Proof policy's "to the host-neutral dispatch core, every host lane"
as covering Pi. That was an over-read. docs/runtime-live-ci.md — the reference
that same Proof-policy bullet points to — rules on it directly:

Pull requests still run only Sonnet and Codex; Pi is optional and is not a
merge requirement.

pi-live skips on pull requests by design: it is a manual workflow_dispatch
with live_cadence=pi, behind CI-E2E-PI environment approval, running the 17
common Pi journeys plus the front-door smoke on gpt-5.6-luna at maximum
thinking. Nothing about this branch changes that.

So there is no lane gap to close here, and no manual dispatch is owed before
merge. The blocking list from that comment is empty; the Derived from
test-strength finding beside it was real and is fixed.

…recordings

`with-template.txt` proved nothing. The order it was produced in was: write the
assertion, watch the live run fail on `registration point` where the first
officer had written `registration surfaces`, widen the assertion to fit the
output. Committing that run and asserting it grades clean asserts only that the
widening happened. `without-template.txt` carried a real claim -- a first officer
without the template relays option 3 -- but that claim is evidence for the
proposal, and it already lives on the issue.

Both are gone. What replaces them is what the recordings were standing in for:
a written case per grader, each encoding a way the grader must reject, none of
them a sample the grader was fitted to. The control's rendered text survives as
one of them, as an expected failure set rather than as a score snapshot.

The cases move out of testdata and into Go string constants. This repository's
testdata holds directory trees and SQL -- things that do not fit a literal --
while its fixtures are string builders (`dispatchRecoveryReadme`,
`autoContinueEntity`). A ten-line message fits. The live fixture builders move
untagged for the same reason the dispatch-recovery fixtures are untagged: pure
string builders are reusable without a model.

Two findings came out of doing this:

`recommendation-does-not-reduce-the-delivered-surface` was reached by no fixture
at all, recordings included. It was decorative and nothing said so.
`TestEveryGraderHasACase` now fails when a grader has no case, and was itself
falsified by deleting the only case that trips that grader.

`recommendation-relays-a-worker-option` over-fired. A live run recommended
shipping only the Go subcommand and deferring both the expiry read and
installed-plugin access -- the un-relayed surface named in the recommendation
itself -- and the guard rejected it for the word `expiry`. Relaying means the
recommendation is confined to what the worker could see, not that it mentions a
worker option at all. The guard now fires only when no un-relayed surface
appears beside it. Both directions are pinned, and the recorded control still
trips it, because it never reaches past the options it was handed.

Verified: `go test ./...` passes; the offline table is 10 cases; the live drive
passes at 80s.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@iamcxa

iamcxa commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Recordings dropped; the graders are pinned on written cases

Asked what problem the recorded fixtures solved, the honest answer was: none,
and the order they were produced in says so. The assertion was written first,
the live run failed on registration point where the first officer had written
registration surfaces, and the assertion was widened to fit the output it was
meant to judge. Committing that run and asserting it grades clean asserts only
that the widening happened.

without-template.txt carried a real claim — a first officer without the
template relays option 3 — but that is evidence for the proposal, and it already
lives on #752. Its rendered text survives in the table as an expected failure
set
, which is the form that can fail.

The cases also left testdata/. This repository's testdata holds directory
trees and SQL; its fixtures are string builders (dispatchRecoveryReadme,
autoContinueEntity). A ten-line message is a string. The live fixture builders
moved untagged for the reason dispatch_recovery_fixtures_test.go states about
its own: "pure string builders are reusable without a model."

skills/integration/ now gains two Go files and no new directory.

Two findings came out of doing it

A grader was decorative and nothing said so.
recommendation-does-not-reduce-the-delivered-surface was reached by no
fixture, recordings included. TestEveryGraderHasACase now fails when any
grader has no case — falsified by deleting the only case that trips that one.

A grader over-fired, and the live run caught it. A live first officer
recommended shipping only the Go subcommand and deferring both the expiry read
and installed-plugin access — naming, in the recommendation itself, the surface
the worker's role could not reach. The guard rejected it for the word expiry.

Relaying means the recommendation is confined to what the worker could see,
not that it mentions a worker option at all. The guard now fires only when no
un-relayed surface appears beside it.

This is the same shape of change as the registration point widening, and the
code comment says so rather than hiding it. What makes it defensible instead of
a repeat:

  • it encodes the distinction the guard always meant, rather than a wording;
  • both directions are pinned — a case that must pass and a case that must fail;
  • the recorded control still trips it, because "Recommend option 3: split slice
    1 and defer the expiry read"
    never reaches past the options it was handed.

Evidence

  • go test ./... — all pass
  • offline table — 10 cases, plus the grader-coverage meta-test and the
    Derived from extractor guard
  • live drive — passes, 80s

…omments

Three comment blocks told the story of how this test was built -- which live run
failed, on which literal, and how the assertion was widened to fit it -- and that
story is already in the commit messages that made those changes. A comment
narrating a task is a comment that goes stale the moment the task is forgotten,
and nothing in a diff reads it as history.

What stays is what a reader cannot recover: what each regex means in the
contract's terms, that this table cannot establish the template works, and why
naming a worker option is not by itself relaying.

19 comment lines removed, no rule and no case removed. Density drops from 20% to
16%, against 26% in `survey_probe_test.go` and `liveassert_unit_test.go` beside
it -- the density was never the problem, the category was.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant