Skip to content

Settle the check judge contract and divergence corpus before porting it #720

Open

Description

@tbhb-agent

Problem

The merge scripts judge a saved check rollup with the shell function mp_judge_checks in packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh, using jq. internal/checks holds a Go evaluator, which the unattended merge path calls through agentrepo checks evaluate (ma_gates in merge-agent.sh). Moving the attended path to the Go evaluator is feature 3 of #588. It is unsafe to do before the two judges' differences are written down, because a difference can turn a failure green or a green into a refusal. The Feature that does the move is #723, and it is blocked by this Spike.

The two judges differ on the same data (read on main at d41369e7):

  • Malformed input: the shell exits 3, Go exits 5. A missing or null statusCheckRollup: the shell reads it as an empty rollup (exit 4), Go as unreadable input.
  • Unknown states: the shell reads an unknown conclusion, status or status-context state as ok. Go returns the failing outcome (exit 1) for them, not the unreadable outcome (internal/checks/checks.go:180, internal/checks/checks_test.go:82). A missing status reads as completed in the shell and as failing in Go. The shell upper-cases states before comparing, so a lowercase spelling reads like its upper-case form; Go compares exactly and treats a lowercase spelling as failing.
  • Superseded entries: both judges select the latest run of each check before reading states (internal/checks/checks.go:148, pull-request.sh:88), so an unknown state on an earlier run that a later success replaced is never read. The contract has to say whether a refusal for unknown states covers such runs.
  • Entries without a name or with an unknown __typename: the shell judges them, Go rejects them as unreadable.
  • Start times: the shell compares startedAt as strings (pull-request.sh:71, pull-request.sh:88), Go parses instants (internal/checks/checks.go:110, internal/checks/checks.go:269). A failing run at 2026-10-01T02:00:00+02:00 and a passing run at 2026-10-01T01:00:00Z of the same check make the shell choose the failure and Go choose the success. An unparseable timestamp is unreadable input in Go and a string in the shell. Tied start times: the shell takes whichever max_by returns, Go takes the worse verdict.
  • Repeated status contexts: the shell judges every entry as it stands, so an older failing entry fails the rollup. Go keeps only the newest entry per context, so a newer success hides it.

The callers also depend on the shell judge's behaviour. preflight.sh calls mp_require_reviews first and exits 75 when a review is required (preflight.sh:94; the regression at tests/skills/merge-pr/scripts/merge-workflow.bats:385 requires it), runs the unattended gates, and only then reports each check's state and name, sorted by name, without failing or still-running checks making that section refuse. squash-merge.sh prints failing or still-running checks as <state> <name> lines on standard error and refuses with a message that depends on the outcome. Both callers parse the saved response before they judge it (preflight.sh:78, squash-merge.sh:116, pull-request.sh:39), so a truncated live response can stop them with a parser error before any judge message. The Go command prints a JSON summary of the non-successful names only, with exit codes 0 to 6 (internal/agentrepo/command.go:55). The shared loader accepts agentrepo from v0.16.0 (packages/agents/source/lib/lib.sh:140), and docs/agent-skills-migration.md:9 lets a consumer run with none.

Acceptance criteria

  1. AC1: The findings comment holds a contract table with one row for each of these input classes: malformed JSON, missing or null rollup, empty rollup, unknown state, lowercase state, missing status, empty check name, unknown entry type, tied start times, a rerun that has not started, repeated status contexts, start times in different UTC offsets, an unparseable start time, and an unknown state on a superseded run. Each row states the shell judge's outcome and exit status, the chosen preflight and squash outcomes separately, and whether the change is deliberate. For every case, each caller's chosen result records its report/continuation or refusal, numeric process exit status, and exact standard output and standard error (including the diagnostic message and explicitly empty streams). These are caller process statuses, separate from the judge's status; otherwise passing prerequisites and later gates isolate the case, with merge writes stubbed. Invocation modes and earlier stops are identified as in AC7. Empty, missing and null rollups have separate cases. These fields specify findings for Replace the shell check judge in the merge scripts with the Go evaluator #723 to consume; they do not require implementing the chosen behavior in this Spike.
  2. AC2: Every row of the table cites at least one saved rollup, with the output and exit status the shell judge produced for it and the result the current Go evaluator gives for it, both recorded by running them on the head named in the comment.
  3. AC3: The comment states the sweep's domains, generation rule and expected total before reporting results. The sweep uses homogeneous ordered pairs of two entries for one check, separately for CheckRun (ordered by startedAt) and StatusContext (ordered by createdAt); it does not pair different entry types. For each CheckRun, the status domain is COMPLETED, IN_PROGRESS, PENDING, QUEUED, REQUESTED, WAITING, all six exact lowercase spellings, the literal UNKNOWN_STATE, and omission of status (14 alternatives). Its independent conclusion domain is ACTION_REQUIRED, CANCELLED, FAILURE, NEUTRAL, SKIPPED, STALE, STARTUP_FAILURE, SUCCESS, TIMED_OUT, all nine exact lowercase spellings, the literal UNKNOWN_STATE, and omission of conclusion (20 alternatives). Cross the full status × conclusion product on each entry, including combinations in which status causes a judge to ignore conclusion; do not vary one field at a time. For each StatusContext, the state domain is ERROR, EXPECTED, FAILURE, PENDING, SUCCESS, all five exact lowercase spellings, the literal UNKNOWN_STATE, and omission of state (12 alternatives). Omission means the property is absent, not null or an empty string; neither null nor empty-string state fields nor a lowercase unknown sentinel are additional sweep alternatives. Each ordered state pair takes all seven concrete timestamp pairs defined in Approach. Other fields are fixed as stated there. The expected total is 7 × ((14 × 20)^2 + 12^2) = 549808 cases: 548800 CheckRun pairs and 1008 StatusContext pairs, before any deduplication of equivalent judge results. The recorded cases cover exactly that product, and zero combinations that the shell judge reports as failing or still running are green under the table's outcomes.
  4. AC4: The comment states one general decision rule for any pair of runs of one check in which the string order and the instant order of the start times disagree. The rule is never green when either ordering would select a failing or unfinished run, and the table's row for the failing run at 2026-10-01T02:00:00+02:00 against the passing run at 2026-10-01T01:00:00Z applies it.
  5. AC5: For an unknown status, conclusion or status-context state, on the latest run and on a superseded run, the table records the chosen preflight and squash outcomes separately, each with its numeric process exit status and exact standard output and standard error, including its message or explicitly empty streams, using AC1's caller fields and boundary. It states whether validation covers every entry or only selected entries.
  6. AC6: A second table covers every exit status of the evaluator command (0 to 6), any other exit status, an absent command, malformed or non-result output, valid-looking success output with a nonzero exit status (including mapped statuses 1 and 2), and a zero exit status with missing result fields. For every case it records the evaluator's process status and output (or absence), the chosen evaluation category (green, failing, still running, empty or unreadable), and separate chosen preflight and squash results using all AC1 caller fields: report/continuation or refusal, numeric process exit status, exact standard output and standard error with the message or explicitly empty streams. Only a zero evaluator exit status with a complete success result is green; preflight continuation is not evidence of green checks. The table identifies whether an earlier stop or the check section supplies each result.
  7. AC7: A third table lists each refusal and parser stop before the shell check-reporting and judging section, including those raised by the shared loader (for example its version refusal) and by the unattended helpers it invokes (command usability and the unattended gates). For every case and relevant invocation mode it records preflight and squash results separately using AC1's caller fields: report/continuation or refusal, numeric process exit status, exact standard output and standard error with the message or explicitly empty streams, the first stop reached and whether the check section was reached. It states whether each result stays unchanged. Record the other caller's result even when that caller does not take the same stop. It states that preflight's check section reports failing and still-running checks without refusing, and that a required review still exits 75.
  8. AC8: The comment names the unattended path's existing fixtures that the table changes and the outcome, exit status, messages and skipped-check hint each gets under the table, or states that none change.
  9. AC9: The comment lists the evaluator outputs the table promises that the current evaluator does not yet give, and either names the first devtools release that gives all of them or states that no compatible release exists yet and names the issue that implements and releases it. In both cases it states how the callers behave with an older release or with none, and names the issue that installs a compatible release in consumer repositories.
  10. AC10: Non-functional: the comment records the median time of 20 runs of the shell judge and of the Go evaluator over a generated 500-check rollup, and proposes the time limit for the merge path.
  11. AC11: Each AC of this issue is answered in the findings comment, and the comment links the Feature issue for the caller migration and any further issue filed.
  12. AC12: Non-functional: before posting, the complete final findings comment and every complete final follow-up issue body, including embedded corpus and command output, each pass gitleaks, the repository's check-leaks.sh with its private-strings list, and a separate leak review of each post's full text. The findings comment records a clean result for each of the two scans and the leak review for each post. Any text changed after a check is checked again before posting.

Scope and non-goals

In scope: reading and running the two judges, recording the three tables, the corpus and the measurements, and filing follow-up issues.
Non-goal: changing the shell judge, the Go evaluator, the merge scripts or the generated skills; the findings are a comment, and any prototype stays on a throwaway branch that is deleted.
Non-goal: review-verdict parsing (#589), approval selection (#590), policy inspection and merge execution (later features of #588).
Out of scope: distributing the Go binary (#333) and installing it in consumer repositories (#544).

Approach

Build the evaluator command from main, then run the shell judge and the Go evaluator over hand-written rollups for each class of AC1 and over the generated sweep of AC3. Record every rollup with both outputs, so the Feature can commit them as fixtures. Choose each row's outcome by one rule, with no exceptions: the merge path is never greener than the shell judge for a failing or unfinished check, and a rollup that cannot be read is refused. Before running the sweep, write down its domains, generation rule and expected total (AC3). The seven ordered timestamp pairs (first, second) are: equal (2026-10-01T01:00:00Z, 2026-10-01T01:00:00Z); earlier (2026-10-01T00:00:00Z, 2026-10-01T01:00:00Z); later (the preceding pair reversed); offset-reversed (2026-10-01T02:00:00+02:00, 2026-10-01T01:00:00Z); unparseable first (INVALID_TIME, 2026-10-01T01:00:00Z); unparseable second (the preceding pair reversed); and both unparseable (INVALID_TIME, INVALID_TIME). Apply these to startedAt for CheckRuns and createdAt for StatusContexts. Keep JSON array order first then second, __typename fixed to that sub-sweep's type, one fixed nonempty check name and workflow for CheckRuns and one fixed nonempty context for StatusContexts; omit fields belonging only to the other type. Fix the head and use no required-check names or expected-head constraint for this comparative sweep; AC8 separately records unattended requirements. No other axis is multiplied into AC3; AC1's additional cases remain separate.

Trace the shared loader and the unattended helpers invoked before the check section, as well as the three callers, to fill the third table, and read the unattended fixtures under tests/skills/agent-merge-pr/ for AC8. Post the findings as one comment on this issue.

Dependencies

Native parent: #588. No blocker. The Feature that migrates the callers, #723, is blocked by this issue, by #333 and by #544.

Size

S. One findings comment and, at most, a few follow-up issues.

Verify plan

  • AC1: the table has a row for every class named in AC1, with distinct empty, missing and null rollup cases. Inspect every row's separate chosen preflight and squash report/continuation or refusal, numeric process exit status, exact standard output and standard error (message or explicitly empty streams), invocation mode and boundary, rather than accepting only a merge-path category or evaluator exit code.
  • AC2: each row cites a rollup and both recorded outputs; a reader can rerun the two commands on the cited rollup and get the recorded result.
  • AC3: independently reconstruct the specified field-level products: check all 14 status alternatives, all 20 conclusion alternatives and all 280 crossed CheckRun entries, including simultaneous lowercase/missing fields and combinations with non-completed status; check all 12 context-state alternatives. Compare the generated case keys with the complete ordered-pair × seven-timestamp set, requiring no missing or duplicate keys and exactly 548800 CheckRun and 1008 StatusContext cases (549808 total). A match between a researcher-selected total and a recorded total alone does not pass. Inspect the domain manifest and the seven timestamp pairs, then confirm zero shell-failing or still-running combinations become green under the chosen outcomes.
  • AC4: the stated general rule, the row for the two runs above, and the sweep's offset-reversed combinations (AC3), none of which shows a green outcome when either ordering selects a failing or unfinished run.
  • AC5: inspect unknown status, conclusion and context-state rows on both latest and superseded entries; require the validation-scope decision and separate chosen preflight and squash results, each with numeric process exit status and exact standard output and standard error, including its message or explicitly empty streams.
  • AC6: inspect every named case in the second table, including malformed evaluator output, success-looking output followed by exits 1 and 2, and exit 0 with missing result fields. Check the recorded evaluator status/output separately from both callers' chosen report/continuation or refusal, numeric process exit status and exact standard output and standard error (message or explicitly empty streams); confirm only a complete success result with evaluator exit 0 is green. Confirm the current evaluator's statuses against internal/agentrepo/command.go and internal/checks/checks.go on the cited head, and record the chosen caller contract as research findings.
  • AC7: confirm the third table against preflight.sh, squash-merge.sh, pull-request.sh, the shared loader and merge-agent.sh on the cited head, tracing the helpers invoked before the check section for each invocation mode, and the regression at tests/skills/merge-pr/scripts/merge-workflow.bats:385. Inspect both callers' numeric process exit statuses, exact standard output and standard error (including messages or explicitly empty streams), first stop, reachability of the check section and unchanged/change decision for every case; do not substitute a judge exit status for a caller status.
  • AC8: the list of unattended fixtures, each run on the cited head under the proposed rules.
  • AC9: the list of promised outputs the current evaluator lacks, then either the named release with evidence that it produces them or the "no compatible release yet" statement with the implementing issue, and the issue number that installs it.
  • AC10: the recorded medians and the commands that produced them.
  • AC11: the comment's answer to each AC and the links.
  • AC12: before posting, run gitleaks and the repository's check-leaks.sh with the private-strings list over the complete final findings comment and every complete final follow-up body, including embedded corpus and command output. Also obtain a separate leak review of the complete final text of each post before it is posted. Inspect the findings comment's recorded clean results for both scans and that post's leak review, covering every post; recheck any changed text. This verifies D4's three requirements.

Risks

A wrong row would let the Feature turn a failure green; AC3 and AC4 exist to catch that, and the Feature's Verify plan replays every row. The sweep might miss a class; AC1 names the known ones and the Feature adds a case for any a reviewer finds. The undo is deleting the throwaway branch; nothing else changes.

Operator authority

None

Activity

  1. updated Priority Standard and Work type Planned on Oct 25, 2026
  2. tbhb-agent commented on Oct 25, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: NOT READY — clarify observable behavior, integration verification, and rollout prerequisites

    Checked

    Gaps

    1. Acceptance criteria contain implementation prescriptions and multiple outcomes. AC4 mandates a Go command and deletion of a named function; AC5 mandates a specific command and generated implementation absence. AC1 combines contract publication with exhaustive difference accounting. Move implementation choices into Approach and separate each observable acceptance outcome.

    2. The compatibility evidence is narrower than the migration risk. AC2 checks only formerly green rollups, and its Verify entry replays existing Bats fixtures. Require contract cases for all identified differences, with expected old and chosen outcomes, including formerly failing and pending inputs. Otherwise, a failure becoming success can escape that comparison. Existing Go tests already provide readable evidence for ties, repeated contexts, unknown states, and required-check behavior (internal/checks/checks_test.go:63, internal/checks/checks_test.go:78, internal/checks/checks_test.go:94).

    3. Approach omits the caller output and exit-status transition. Shell emits per-check TSV with states and workflow-qualified names; Go emits summary JSON, with unreadable input returning 5 instead of 3 (packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh:49, internal/agentrepo/command.go:45). Preflight formats those TSV rows and reports superseded runs; merge execution uses them for failure and pending diagnostics (packages/agents/source/skills/merge-pr/shared/scripts/preflight.sh:101, packages/agents/source/skills/merge-pr/shared/scripts/squash-merge.sh:203). State which outward diagnostics remain and which deliberately change. Verify both callers’ output and refusal behavior against the branch binary.

    4. AC3 lacks merge-path failure verification. Go table cases alone do not prove callers refuse unsafe inputs or handle an unavailable command. Add integration checks for empty, unreadable, truncated, and unknown-state rollups plus command failure, asserting no merge write occurs. Define whether “unknown-state rollup” includes an unknown state in a superseded entry: Go validates verdicts after selecting latest entries (internal/checks/checks.go:148, internal/checks/checks.go:257).

    5. AC6’s benchmark does not establish its threshold by itself. Specify the benchmark invocation, measurement boundary, and explicit comparison proving a 500-check evaluation finishes under one second.

    6. Dependencies omit rollout protections required by Replace complex shell with tested Go, starting with gates and merging #588. Identify how consumers receive the required binary before updated callers, and how owner-approved protection mapping covers changed Go enforcement paths before rollout. Current configured golden and owner patterns omit those Go paths (.agents/agent-skills.conf:4, .agents/agent-skills.conf:9). If another issue supplies either prerequisite, name it and add the appropriate native blocker; otherwise distinguish work included here from a rollout hold.

    7. Generation and consumer verification are incomplete. AC5 covers generated-skill consistency, but the plan omits the full generation → token measurement → deployment sequence and consumer migration documentation. Add those checks and document adoption steps or explain “No consumer impact.” Replacing this judge also does not remove jq from the surrounding merge path (packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh:19).

    Split

    No split required. Keep the contract, evaluator adjustments, caller transition, focused regression coverage, generated output, and adoption guidance together. Reference independently delivered rollout prerequisites explicitly.

    Definition of done

    No item is fully covered by the current AC and Verify plan; implementation and merge evidence remain pending.

    • D1 Pending: AC IDs appear in Verify, but merged-head verification and PR evidence are not required.
    • D2 Pending: exact merged-head independent approval and finding disposition are not covered.
    • D3 Pending: required checks and exact-head owner approval for loosened gates are not covered.
    • D4 Pending: gitleaks, private-string leak scanning, and post leak review are absent.
    • D5 Pending: signing every commit, including the squash, is absent.
    • D6 Unmet in plan: documentation updates or an explained “No doc impact” declaration are absent.
    • D7 Pending: Assisted-by trailers and the provenance label are absent.
    • D8 Pending: S estimates size but does not verify PR limits or an exact-head exception.
    • D9 Pending: deferred findings, follow-up issue quality, and Related links are absent.
    • D10 Pending: closing reference, Done state, and final merge-commit status are absent.
    • devtools-1 Unmet in plan: mise run check, mise run cover-go-check, and mise run cover-py-check are absent.
    • devtools-2 Partially covered: AC5 checks generated consistency; ordered generation, token measurement, deployment, and committed results remain unspecified.
    • devtools-3 Unmet in plan: consumer adoption guidance or an explained “No consumer impact” declaration is absent.

    Round: 1 of 3

  3. tbhb-agent commented on Oct 25, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: NOT READY — native prerequisite and caller compatibility gaps remain

    Checked

    Round 1’s benchmark, generation sequence, adoption guidance, and identification of rollout protections are addressed. The remaining gaps follow.

    Gaps

    1. Record the stated merge prerequisite natively. Dependencies says this change “must not merge ahead of” Ship the Go binaries as release assets installed through mise's github backend #333, which remains open, but declares “Native blocked-by: none.” Add Ship the Go binaries as release assets installed through mise's github backend #333 as a native blocker. Keeping the protection update as an explicitly separate rollout hold is consistent with Replace complex shell with tested Go, starting with gates and merging #588.

    2. Resolve superseded-run reporting before deleting its implementation. Approach deletes mp_superseded, but AC5 covers only failing or running checks and their state/name output. Preflight also prints the number of omitted earlier runs, with an existing assertion requiring that message (packages/agents/source/skills/merge-pr/shared/scripts/preflight.sh:110, tests/skills/merge-pr/scripts/merge-workflow.bats:75). Require preservation through the replacement, or an explicit contract decision to change that diagnostic, with integration coverage.

    3. Verify the complete compatibility table and existing Go consumer. Approach makes the contract table a fixture, but AC2’s replay is limited to existing merge Bats rollups; AC3 adds selected cases. Require execution of every contract row against its recorded old and chosen outcomes, including malformed inputs, superseded unknown states, and head movement. Repeated status contexts already demonstrate a concrete unsafe migration: Go accepts an older ERROR followed by newer SUCCESS, while shell retains the ERROR (internal/checks/checks_test.go:78, packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh:89). Also retain regression coverage for unattended evaluation: ma_gates already invokes the same Go command with required names and expected head (packages/agents/source/skills/merge-pr/shared/scripts/merge-agent.sh:169). Shared evaluator changes must preserve that consumer’s required-check and head-binding behavior.

    4. Cover command failure beyond absence, and describe preflight accurately. AC4 covers a judge that cannot run, but its Verify entry tests only a missing command. Add a failing command stub, including partial success-looking output followed by nonzero exit, and assert no merge write. AC5 also speaks of each caller’s “refusal” status, although preflight currently reports failed checks and exits successfully (tests/skills/merge-pr/scripts/merge-workflow.bats:86); squash execution refuses with status 1 (packages/agents/source/skills/merge-pr/shared/scripts/squash-merge.sh:56). State and verify those distinct outward contracts.

    5. Finish separating observable AC from implementation. AC1 still combines contract publication with exhaustive difference accounting; AC6 combines implementation deletion with reproducible generation and names internal functions indirectly through its Verify entry. Move deletion prescriptions into Approach and give independently verifiable outcomes separate AC entries. There is room within the twelve-criterion limit.

    Split

    No split required. Keep the compatibility contract, evaluator changes, caller adapters, integration coverage, generated output, and adoption documentation together. Track #333 natively and retain the separate protection rollout hold.

    Definition of done

    Ticks indicate coverage in the proposed AC and Verify plan, not completed implementation evidence.

    • D1 Pending: AC-linked checks exist, but merged-head verification and PR evidence are not specified.
    • D2 Pending: exact merged-head independent approval and finding disposition are absent.
    • D3 Pending: required-check results and exact-head approval for loosened gates are absent.
    • D4 Pending: gitleaks, private-string scanning, and post leak reviews are absent.
    • D5 Pending: commit and squash signing are absent.
    • D6 Partially covered: contract and adoption documentation are planned; matching existing help and evaluator documentation is unspecified.
    • D7 Pending: Assisted-by trailers and the provenance label are absent.
    • D8 Pending: S estimates scope but does not verify PR size limits or an owner exception.
    • D9 Pending: follow-up issue requirements, Related links, and code references are absent.
    • D10 Pending: closing reference, Done state, and final merge-commit status are absent.
    • devtools-1 Unmet in plan: mise run check, mise run cover-go-check, and mise run cover-py-check are absent.
    • devtools-2 Approach requires ordered generation → token measurement → deployment and committed results; AC6 verifies reproducibility and generated consistency.
    • devtools-3 AC8 and its Verify entry require adoption documentation or an explained “No consumer impact” declaration.

    Round: 2 of 3

  4. tbhb-agent commented on Oct 25, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: NOT READY — compatibility and failure contracts still leave unsafe or contradictory outcomes

    Checked

    Reviewed #720, its two previous verdicts, #588, native dependencies, #333, #544, and sibling scopes #713–#722. Code reads used only origin/main at 56543d2. No files, checks, or external state were changed.

    Round 2’s native #333 blocker and superseded-run diagnostic gaps are closed. Full-table execution and unattended regression coverage are now required. Failure-command coverage and separation of the two caller contracts improved, but remain incomplete.

    Gaps

    1. AC1 and AC6 still fail the requested acceptance-criterion structure. AC1 combines publication of a contract, its contents, and how tests execute it. AC6 combines deletion of an implementation with reproducible generation. Move test machinery and deletion prescriptions into Approach/Verify; express compatibility against the contract and reproducible generated output as separate observable outcomes. This was an explicit Round 2 finding and remains unresolved.

    2. AC4’s unknown-state contract conflicts with the proposed exit mapping, and superseded unknown states remain unspecified. Go currently returns Failed/1 for an unknown status, conclusion, or context state, rather than Unreadable/5 (internal/checks/checks.go:180, internal/checks/checks_test.go:82, internal/checks/checks_test.go:120). Approach maps 1 to the ordinary failing-check path, but AC4 requires “could not be read.”

      Go also selects latest runs before judging states (internal/checks/checks.go:148, internal/checks/checks.go:155). An older unknown-state CheckRun followed by a newer successful run therefore becomes green. AC4’s unqualified “rollup with an unknown state” requires refusal, but neither the required corpus classes nor Verify explicitly cover this superseded case. State whether validation covers every entry, define the attended diagnostic mapping, and test unknown status, conclusion, and context state—including superseded entries. Preserve or explicitly account for the existing unattended interface.

    3. The compatibility corpus still omits a concrete failure-to-green difference: timestamp ordering. Shell compares startedAt strings; Go parses timestamps and compares instants (packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh:71, packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh:88, internal/checks/checks.go:110, internal/checks/checks.go:269).

      For two completed runs of the same check, a failure at 2026-10-01T02:00:00+02:00 and success at 2026-10-01T01:00:00Z make shell choose failure and Go choose success. That violates AC3, yet none of AC1’s required classes forces this case into the corpus. Invalid timestamps are another existing difference already demonstrated by internal/checks/checks_test.go:158. Add timestamp-format and ordering cases, and specify a rule that cannot weaken the shell outcome. A corpus-only difference detector cannot discover classes absent from its corpus.

    4. “Preflight never refuses” is factually wrong and could erase existing safety behavior if implemented literally. Preflight calls mp_require_reviews before reporting checks and runs unattended gates before reaching the check section (packages/agents/source/skills/merge-pr/shared/scripts/preflight.sh:94). Required reviews cause exit 75; an existing regression explicitly requires this (tests/skills/merge-pr/scripts/merge-workflow.bats:385).

      Limit AC5 and Approach to the attended check-reporting section: failing or pending checks do not themselves make that section refuse. Preserve existing review, environment, and unattended-gate refusals. Verify both the reporting behavior and these existing refusal paths.

    5. AC4’s failure messages cannot be established solely by inserting the proposed judge adapter. Both callers parse the saved response before invoking the judge. Preflight formats state and reads review status first; squash execution reads head and base fields first (packages/agents/source/skills/merge-pr/shared/scripts/preflight.sh:78, packages/agents/source/skills/merge-pr/shared/scripts/squash-merge.sh:116, packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh:39). A truncated or malformed live response can exit there with a parser error before the promised “could not be read” message.

      Missing or unusable commands can also fail earlier in ma_context, with an existing diagnostic that AC9 promises to preserve (packages/agents/source/skills/merge-pr/shared/scripts/merge-agent.sh:28, tests/skills/agent-merge-pr/workflow.bats:12). Clarify which entry points and input boundaries AC4 covers, reconcile its generic diagnostic with AC9, and ensure integration tests reach and assert the intended boundary rather than merely observing an earlier refusal.

    6. The subprocess verification still omits Round 2’s success-looking partial-output case. Verify AC4 now names an unmapped exit status and garbage output, but does not explicitly exercise a command that emits a valid-looking success result and then exits with a mapped nonzero status, such as 1 or 2. Garbage output does not establish that the adapter checks process status when parsing succeeds.

      Add this disagreement case, require nonzero status to prevent success, and assert the diagnostic and absence of a merge call. Also reject a zero-status response lacking the required result fields. The command’s result and process status are distinct parts of its interface (internal/agentrepo/command.go:68, internal/agentrepo/command.go:74, internal/agentrepo/command.go:96).

    7. Binary publication is now blocked correctly, but installation of a compatible binary is still not secured. Ship the Go binaries as release assets installed through mise's github backend #333 explicitly excludes consumer installation; feat(distribution): migrate consumer tool pins to release assets #544 owns that work. Existing adoption guidance permits no agentrepo, and the shared loader accepts releases from v0.16.0 upward (docs/agent-skills-migration.md:9, packages/agents/source/lib/lib.sh:140). Those releases do not provide the proposed diagnostic listing.

      Add feat(distribution): migrate consumer tool pins to release assets #544 as a native prerequisite, or specify and verify an alternative installation sequence. In either case, identify the release/capability required by the new callers and ensure it is installed before deployment. Publishing the first archive alone does not establish that consumers have the replacement interface. AC8’s documentation inspection does not prove installation or compatibility.

    Split

    No mandatory split identified. Keep the contract, corpus, evaluator changes, caller adapters, generated output, and adoption documentation together. Keep binary distribution, consumer installation, and the owner-protection rollout update explicitly coordinated with their separate delivery work.

    Definition of done

    The definition of done for #720 (Feature). Tick each item and give its evidence.

    • D1 The author verified each AC on the merged head, and the Verification section of the pull request cites each AC ID with its evidence: the command and its result, or a link.
    • D2 An independent reviewer approved the exact merged head, and the author resolved each finding or filed it as a follow-up.
    • D3 The required checks pass on that head, and any loosened gate has the owner's approval of that exact head.
    • D4 The leak scans come back clean: gitleaks, devtools' check-leaks.sh with the repository's private-strings list, and the leak review of each post.
    • D5 The author signed each commit, the squash included.
    • D6 The docs match the change (README, docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why.
    • D7 The commits and the squash have Assisted-by trailers, and the pull request has the provenance:agent-assisted label.
    • D8 The pull request stays within the size limits, or the owner approved its exact head over the hard limit.
    • D9 The author files follow-ups and deferred findings as issues with at least a Problem and numbered AC and lists them under the Related section of the pull request. Any work left in the code links its issue.
    • D10 The merge closes the issue through a closing reference and the board shows Done. The final Status: comment names the merge commit.
    • devtools-1 mise run check is green, and so are the coverage floors mise run cover-go-check and mise run cover-py-check, which check doesn't run.
    • devtools-2 A change to skill sources under packages/agents/source/ commits what mise run generate-skills, then mise run skill-tokens, then apm install rewrite, in that order, and check-generated-skills (part of lint) passes.
    • devtools-3 A consumer-visible change (the vendored payload, a skill, a reusable workflow, a task or a pin) has its consumer steps in docs/agent-skills-migration.md or the adoption docs, or the PR's Verification says "No consumer impact" and why.

    Round: 3 of 3

  5. changed the title [-]Port the shell check judge to Go and settle one contract[/-] [+]Settle the check judge contract and divergence corpus before porting it[/+] on Oct 25, 2026
  6. changed the issue type fromtoon Oct 25, 2026
  7. tbhb-agent commented on Oct 25, 2026

    @tbhb-agent
    ContributorAuthor

    Status: redesigned after round 3 of 3 (#720 (comment))

    Done:

    Why: round 3 found that one issue mixed a contract that was not yet settled with the migration that depends on it, and that the failure and compatibility rules were unsafe or contradictory in places. The Spike settles the contract first.

    Next: refinement round 1 of 3 for this Spike and for #723.

    Blocked on: nothing for this Spike. #723 is blocked on this Spike, #333 and #544.

  8. tbhb-agent commented on Oct 25, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: NOT READY — bound the sweep and make release and refusal evidence achievable

    Checked

    Read #720, all four comments, #588, native links, #723, #333, #544, #713 and the additional native dependent #719. Code references are to main at d41369e7. No files, checks, repository tooling, posts or operator commands were run.

    Gaps

    1. AC3 leaves the sweep’s domain undefined, and Verify AC4 does not prove its universal claim. “Every combination” does not specify which known states, which state fields, which entry types, or which timestamp values constitute the finite sweep. Status contexts use createdAt, while CheckRuns use startedAt (internal/checks/checks.go:110); a generic “start-time spelling” axis leaves that distinction to the researcher.

      Require the findings to enumerate the domains, generation rule and expected total before reporting coverage. For AC4, two example rows cannot establish the outcome for any pair whose string and instant orders disagree. Require an explicit general decision rule, supported by the defined sweep and code reasoning. Also reconcile AC3’s accepted-exception option with Approach’s unconditional “never greener” rule.

    2. AC7’s Verify entry does not cover all the refusals its table must inventory. It names the two entry scripts, pull-request.sh and the review regression, but early stops also come from the shared loader and unattended helpers. Examples include the loader’s version refusal at packages/agents/source/lib/lib.sh:151, command usability at packages/agents/source/skills/merge-pr/shared/scripts/merge-agent.sh:28, and unattended gates at packages/agents/source/skills/merge-pr/shared/scripts/merge-agent.sh:118.

      Define the boundary as the shell check-reporting/judging section, distinguish the relevant invocation modes, and require tracing the helpers invoked before it. Otherwise “each refusal” can omit existing stops while passing the listed verification. The exit-75 review regression itself is correctly identified at tests/skills/merge-pr/scripts/merge-workflow.bats:385.

    3. AC9 requires release evidence that may depend on the implementation this Spike explicitly excludes. The current evaluator can choose success for the offset example that AC4 requires to remain non-green (internal/checks/checks.go:269). Naming and demonstrating a release that implements the chosen future contract therefore cannot be assumed achievable before Replace the shell check judge in the merge scripts with the Go evaluator #723.

      Allow an explicit “no compatible release exists yet” finding. In that case, require the capability contract, the issue responsible for implementing and releasing it, and the installation sequencing through feat(distribution): migrate consumer tool pins to release assets #544. Verify that recorded decision rather than requiring a published compatible release to close an otherwise unblocked Spike.

    4. D4 has no verification step. The plan does not require gitleaks, check-leaks.sh with the repository’s private-strings list, or leak review of the findings and follow-up posts. Add those checks and their recorded results to the completion plan. The recorded corpus and command output are part of the material being published.

    Split

    Keep the Spike/Feature split. #720 should settle and record the contract and corpus; #723 should implement and verify the migration. Release production and installation remain coordinated through #333 and #544. No further split or pull request is required for this Spike.

    Definition of done

    Ticks indicate coverage by the AC and Verify plan, not completed execution.

    • D4 Unmet in plan: the two leak scans and leak review of each post are absent.
    • spike-1 Approach requires a findings comment; AC11 and Verify AC11 check its contents.
    • spike-2 AC11 requires an answer to every AC; the corresponding Verify entries require the supporting tables, recordings, references and measurements.
    • spike-3 Scope and Approach require filing follow-up issues; AC11 and Verify AC11 require links from the findings comment, including Replace the shell check judge in the merge scripts with the Go evaluator #723.

    PR items D1–D3 and D5–D10 do not apply.

    Round: 1 of 3

  9. tbhb-agent commented on Oct 25, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: NOT READY — clarify the sweep’s state fields, complete the caller contract, and finish D4 coverage

    Checked

    Read the current #720 body, all comments including the Spike’s round 1 verdict, #588, native links, #723, #333, #544 and #719. Code was read from main at d41369e7.

    • Problem: Supported by code. Shell normalization and string ordering appear at packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh:69; Go validation, state handling and instant ordering appear at internal/checks/checks.go:75, internal/checks/checks.go:180 and internal/checks/checks.go:269. Early parsing, caller reporting and exit-75 review refusal also match.
    • Acceptance criteria: Twelve correctly numbered AC<n>: entries. They describe findings artifacts or recorded experiments, with non-functional coverage in AC10 and AC12. The remaining contract and coverage gaps are below.
    • Scope and non-goals: Research, findings and follow-ups are clearly separated from implementation.
    • Approach: Comparative research is appropriate. AC4 now requires a general ordering rule, and Approach’s “never greener” rule has no exceptions.
    • Dependencies: No native blockers for the Spike. Replace the shell check judge in the merge scripts with the Go evaluator #723 is natively blocked by Settle the check judge contract and divergence corpus before porting it #720, Ship the Go binaries as release assets installed through mise's github backend #333 and feat(distribution): migrate consumer tool pins to release assets #544. AC9’s “no compatible release exists yet” option closes round 1’s release-evidence blocker.
    • Size: S is plausible for the bounded research and findings.
    • Verify plan: Every AC ID appears. AC7 now traces the loader and unattended helpers, identifies invocation modes and fixes the boundary at the shell check section. Its exit-75 regression is present at tests/skills/merge-pr/scripts/merge-workflow.bats:385.
    • Risks: Unsafe acceptance and incomplete corpus coverage are identified; no product change ships.
    • Operator authority: None is appropriate.

    Gaps

    1. AC3 still leaves the CheckRun state domain ambiguous. The two entry types and seven timestamp relations now bound much of the sweep, but a CheckRun has two independently relevant fields: status and conclusion. “Every state spelling for its type” does not specify whether their domains are crossed, or whether one field varies while the other stays fixed. The judges branch on status before reading conclusion (packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh:75; internal/checks/checks.go:184).

      Specify the per-field domains and their combination rule, including which lowercase and missing-field variants participate. Otherwise, different generators can report different expected totals while claiming full coverage. Verify AC3 should check that field-level product, rather than only agreement between the researcher’s chosen total and recorded total.

    2. The tables do not require the complete chosen caller contract that Replace the shell check judge in the merge scripts with the Go evaluator #723 consumes. AC1 requires the shell’s exit status, but only the chosen merge-path outcome. AC6 similarly maps command responses to outcome categories without requiring each caller’s process status, report or diagnostic. AC5 supplies messages and statuses for unknown states; AC7 supplies them for earlier stops. That leaves cases such as empty or missing rollups and malformed evaluator output without a required chosen status and diagnostic for each caller.

      These distinctions matter: preflight reports unreadable or empty checks at packages/agents/source/skills/merge-pr/shared/scripts/preflight.sh:104, while squash execution refuses at packages/agents/source/skills/merge-pr/shared/scripts/squash-merge.sh:215. Replace the shell check judge in the merge scripts with the Go evaluator #723 AC3 and AC4 explicitly depend on the contract naming caller behavior.

      Require the relevant table rows to record preflight and squash outcomes separately, including process status and output/message, and make Verify inspect those fields. This completes the findings artifact without prescribing implementation.

    3. Round 1’s D4 gap is only partly closed. AC12 and Verify AC12 now require gitleaks and the repository leak check with the private-strings list. Neither requires the separate leak review of each post named in D4.

      Add that review for the complete findings comment and every follow-up body, including embedded corpus and command output, and require its clean result to be recorded.

    Split

    Keep the existing split: #720 settles the contract and corpus; #723 implements the migration. Distribution and installation remain with #333 and #544. No further split or pull request is required for this Spike.

    Definition of done

    Ticks indicate coverage by the AC and Verify plan, not completed execution.

    • D4 Unmet in plan: both scans are covered by AC12 and Verify AC12, but leak review of each post is still absent.
    • spike-1 Approach requires one findings comment; AC11 and Verify AC11 inspect it.
    • spike-2 AC11 requires every AC to be answered with the corresponding tables, recordings and evidence; the gaps above must be resolved for that evidence to be sufficient.
    • spike-3 Scope and Approach require filing follow-up issues; AC11 and Verify AC11 require links from the findings comment, including Replace the shell check judge in the merge scripts with the Go evaluator #723.

    A Spike needs no pull request. PR items D1–D3 and D5–D10 do not apply.

    Round: 2 of 3

  10. tbhb-agent commented on Oct 25, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: READY — all three round 2 gaps are closed

    Checked

    Read the current #720 body, its round 2 verdict, #723, #333, #544 and #719. Code references below are from origin/main at bfd9f84.

    • Problem: Supported by the shell’s normalization and selection logic at packages/agents/source/skills/merge-pr/shared/scripts/pull-request.sh:69 and Go’s parsing, verdict and timestamp logic at internal/checks/checks.go:75, internal/checks/checks.go:180 and internal/checks/checks.go:269.
    • Acceptance criteria: AC1–AC12 remain stable, correctly numbered and within the twelve-AC limit. Each specifies an observable research artifact or measurement; AC10 and AC12 cover non-functional requirements. AC3 now defines independent field domains and crosses them on both entries. Recounting gives status 6 + 6 + 1 + 1 = 14, conclusion 9 + 9 + 1 + 1 = 20, and context state 5 + 5 + 1 + 1 = 12. Thus 7 × ((14 × 20)² + 12²) = 548800 + 1008 = 549808, exactly the stated total. Omissions and excluded alternatives are explicit.
    • Scope and non-goals: The deliverables remain findings, corpus, measurements and follow-up issues. Product implementation belongs to Replace the shell check judge in the merge scripts with the Go evaluator #723.
    • Approach: The fixed fields, ordered pairs and seven timestamp pairs make the sweep reproducible. The ordering rule, superseded-state decision and “never greener” requirement are compatible.
    • Dependencies: The Spike has no native blocker. Replace the shell check judge in the merge scripts with the Go evaluator #723 is natively blocked by this Spike, distribution in Ship the Go binaries as release assets installed through mise's github backend #333 and installation in feat(distribution): migrate consumer tool pins to release assets #544. The downstream sequence in Replace the commit and pull request workflow guards with Go hooks #719 agrees. AC9 permits documenting that no compatible release exists yet.
    • Size: S remains plausible for generated research and a findings comment; implementation is separately scoped.
    • Verify plan: Every AC ID has a named proving check. AC3 verifies the field products and complete case keys, including duplicates and omissions. AC1, AC5, AC6 and AC7 require separate preflight and squash results with numeric caller process statuses, exact stdout and stderr, messages, invocation modes and earlier-stop boundaries; their Verify entries inspect those fields. This matches the reporting/refusal distinction at packages/agents/source/skills/merge-pr/shared/scripts/preflight.sh:101 and packages/agents/source/skills/merge-pr/shared/scripts/squash-merge.sh:203. The exit-75 regression exists at tests/skills/merge-pr/scripts/merge-workflow.bats:385.
    • Risks: Unsafe acceptance, incomplete coverage and deliberate behavior changes have corresponding checks. No product change ships.
    • Operator authority: None is consistent with the research scope.

    Gaps

    None. The field-domain ambiguity, incomplete caller contracts and missing per-post leak review are closed. No new readiness gap or contradiction found.

    Split

    Keep the existing split: #720 settles the contract and corpus; #723 implements migration. Distribution and installation remain with #333 and #544.

    Definition of done

    Ticks indicate AC and Verify-plan coverage; execution remains pending.

    • D4 AC12 and Verify AC12 require gitleaks, check-leaks.sh with the private-strings list, and a separate leak review of every complete final post, with clean results recorded and changed text rechecked.
    • spike-1 Approach requires one findings comment; AC11 and Verify AC11 inspect it.
    • spike-2 AC11 requires every AC’s answer; AC1–AC10 and their Verify checks supply the evidence.
    • spike-3 Scope and Approach require filing follow-up issues; AC11 and Verify AC11 require their links in the findings comment.

    A Spike needs no pull request. PR items D1–D3 and D5–D10 do not apply.

    Round: 3 of 3

  11. added
    refinement:readyPassed the Definition of Ready; may be picked up
    and removed
    refinement:activeA refinement round is open; not ready for implementation
    on Oct 25, 2026
  12. tbhb-agent commented on Oct 26, 2026

    @tbhb-agent
    ContributorAuthor

    Findings for #720: conservative check evaluation contract

    Research head: f130498bd7383fe02f8b9d21965f555866abd98c.
    I fetched origin and used an isolated worktree. No production files
    changed. No commit, PR, merge or release forms part of this Spike.

    The recommended contract validates every entry, keeps every status
    context, and takes the worse check-run verdict selected by either
    text order or instant order. A caller accepts success only when both
    the process status and a complete, consistent result say success.

    Evidence and notation

    All source locations below refer to the research head. Let M mean
    packages/agents/source/skills/merge-pr/shared/scripts/ and L mean
    packages/agents/source/lib/. Paths written after those prefixes
    concatenate with them. This avoids ambiguous generated-copy
    references.

    • M/pull-request.sh:69-110: shell judge and superseded count.
    • internal/checks/checks.go:75-177: parse and evaluation precedence.
    • internal/checks/checks.go:180-206,257-286: verdict and selection.
    • internal/agentrepo/command.go:55-101,107-119: command protocol.
    • M/preflight.sh:40-120: preparation, review and check reporting.
    • M/squash-merge.sh:63-218: preparation and check refusal.
    • M/merge-agent.sh:19-30,118-180: unattended context and gates.
    • L/lib.sh:140-183,679-714,830-885: loader, stamps and API errors.
    • L/agent.sh:56-65: interrupted operations and detached HEAD.

    The evidence block at the end is a lossless recording, not a link to
    local files. Its metadata holds every hand-written input, both judges'
    exact stdout, stderr and numeric status, full caller recordings,
    unattended recordings, benchmark samples and the sweep manifest.
    The binary matrix records both exact outputs for every sweep key.
    The decoder reconstructs all 549808 input/output records. No fixture
    requires access to the research machine.

    P means attended preflight, S means squash with an explicit
    operator confirmation. U means unattended preflight. G means
    squash without explicit confirmation, including unattended squash.
    These are fixture invocation modes, not confirmation grants.

    For AC1 and AC6, earlier prerequisites and later gates pass, with all
    GitHub writes stubbed. P reaches its check-reporting section and
    continues to process exit 0. S either refuses there or reaches a
    successful stubbed merge and process exit 0. The check section alone
    doesn't establish that a real merge is authorized. AC7 overrides this
    boundary whenever a real earlier stop applies.

    The following executable definitions specify complete caller streams.
    m is the decoded evidence metadata. Concatenation adds no implicit
    whitespace. Every newline is explicit. Empty stderr means "".
    pfx includes the checks heading. sfx ends just before judging.
    ptail and stail include all otherwise successful later output.
    Those tails come from the recorded isolated fixture, not a proposed
    change to production diagnostics.

    r = {(x['id'], x['caller']): x for x in m['callers']}
    p = r['success', 'preflight']['stdout']
    s = r['success', 'squash-merge']['stdout']
    pfx, rest = p.split('SUCCESS        ci / test\n', 1)
    ptail = rest.split('\nsummary: all green.\n', 1)[1]
    sfx, stail = s.split('open, ready, mergeable, and green\n', 1)
    summary = {
        0: 'all green.\n',
        1: ('FAILING. Do not merge. Hand this to the '
            'fix-pr skill instead.\n'),
        2: ('still running. Wait through the watch-pr '
            'skill before merging.\n'),
    }
    err = {
        1: ('squash-merge: #7 has a failing check. '
            'Hand it to the fix-pr skill.\n'),
        2: ('squash-merge: #7 has a check still running. '
            'Wait through the watch-pr skill.\n'),
        4: ('squash-merge: #7 has no checks reported; '
            'the result is inconclusive.\n'),
        5: 'squash-merge: the checks of #7 could not be read.\n',
    }
    def line(state, name):
        return f'{state:<14} {name}\n'
    def P(code, rows=(), superseded=0):
        if code in (4, 5):
            body = ('no checks reported\n' if code == 4 else
                    'the check rollup could not be read\n')
        else:
            body = ''.join(line(state, name) for state, name
                           in sorted(rows, key=lambda x: x[1]))
            if superseded:
                body += (f'({superseded} earlier run(s) left out: '
                         'a later run of the same check replaced '
                         'each)\n')
            body += '\nsummary: ' + summary[code]
        return (0, pfx + body + ptail, '')
    def S(code, rows=()):
        if code == 0:
            return (0, sfx + 'open, ready, mergeable, and green\n'
                    + stail, '')
        body = ''.join(line(state, name) for state, name in rows)
        return (1, sfx, body + err[code])
    F = [('FAILURE', 'ci / test')]
    Q = [('QUEUED', 'ci / test')]
    C = [('ERROR', 'test'), ('SUCCESS', 'test')]

    Each tuple is (caller process status, stdout, stderr).
    P(5) is an unreadable report and continuation, not green.
    S(5) refuses with no merge call. P(4) reports empty and continues.
    S(4) refuses. P(1,...) and P(2,...) report and continue.
    S(1,...) and S(2,...) print the relevant rows and refuse.
    The failure diagnostic deliberately drops “judged by its latest run”:
    that phrase would misdescribe the conservative two-order decision.

    AC1, AC2 and AC5: input contract and recorded comparison

    Table keys are exact id values in m['hand']. That record holds the
    saved input bytes, command arguments, shell stdout/stderr/status and
    current Go stdout/stderr/status. Both commands ran on each input.
    All shell stderr streams were empty. Go stderr was empty except for
    the required-check hints explicitly recorded under AC8.

    The last column marks deliberate changes to the attended shell path.
    yes includes stricter interpretation or changed diagnostics.
    parser identifies an earlier stop, not an adapter result.

    Key Shell Go P result S result Change
    h00 3 5 parser/5 parser/5 no
    h01 4 5 P(5) S(5) yes
    h02 4 5 P(5) S(5) yes
    h03 4 4 P(4) S(4) no
    h04 0 0 P(0,OK) S(0) no
    h05 1 1 P(1,F) S(1,F) message
    h06 2 2 P(2,Q) S(2,Q) no
    h07 0 1 P(5) S(5) yes
    h08 0 1 P(5) S(5) yes
    h09 0 1 P(5) S(5) yes
    h10 0 1 P(5) S(5) yes
    h11 0 1 P(5) S(5) yes
    h12 0 1 P(5) S(5) yes
    h13 0 1 P(5) S(5) yes
    h14 0 5 P(5) S(5) yes
    h15 0 5 P(5) S(5) yes
    h16 0 1 P(1,F,1) S(1,F) yes
    h17 1 1 P(1,F,1) S(1,F) yes
    h18 2 2 P(2,Q,1) S(2,Q) no
    h19 1 0 P(1,C) S(1,C[:1]) message
    h20 1 0 P(1,F,1) S(1,F) yes
    h21 0 5 P(5) S(5) yes
    h22 0 0 P(5) S(5) yes
    h23 0 0 P(5) S(5) yes
    h24 0 0 P(5) S(5) yes

    OK = [("SUCCESS", "ci / test")]. Input keys:

    • h00: malformed.
    • h01: missing.
    • h02: null.
    • h03: empty.
    • h04: success.
    • h05: failure.
    • h06: pending.
    • h07: unknown_status.
    • h08: unknown_conclusion.
    • h09: unknown_context.
    • h10: lower_status.
    • h11: lower_conclusion.
    • h12: lower_context.
    • h13: missing_status.
    • h14: empty_name.
    • h15: unknown_type.
    • h16: tie.
    • h17: tie_reverse.
    • h18: unstarted.
    • h19: repeated_context.
    • h20: offset.
    • h21: invalid_time.
    • h22: superseded_status.
    • h23: superseded_conclusion.
    • h24: superseded_context.

    For h00, both callers exit 5 before evaluating checks. The exact
    stdout and stderr are in m['callers'] for malformed, with raw {
    as the response. Stderr is the parser diagnostic below. The preflight
    first stops at its state formatter. Squash first stops in mp_field.
    This result stays unchanged in the chosen contract.

    jq: parse error: Unfinished JSON term at EOF at line 1, column 1
    

    Missing and null rollups deliberately change from empty to unreadable.
    Their check-section reports are P(5) and S(5), not the old empty
    reports. An actual empty array remains P(4) and S(4).

    Unknown status, conclusion and context state all mean unreadable,
    even on superseded entries. Validate every raw entry before selection,
    including a conclusion that a non-completed status would otherwise
    hide. Lowercase spellings and a missing CheckRun status are also
    unreadable. This preserves Go's refusal and tightens shell acceptance.
    Don't silently uppercase them and loosen the unattended path.

    Accept the six uppercase CheckRun statuses and nine uppercase
    conclusions in AC3. An absent, null or empty conclusion is allowed and
    means pending when status is COMPLETED. An absent, null or empty
    context state means PENDING. Recognized pending statuses still ignore
    a recognized conclusion for verdict purposes. Null/empty CheckRun
    status, other field types, empty names and unknown entry types are
    unreadable. A context accepts only the five uppercase context states,
    plus the missing-value convention above. These validation rules also
    cover entries excluded by time ordering.

    AC3: finite sweep, coverage and results

    The domain manifest was written before execution. Generation order is
    CheckRun then StatusContext, first-entry index, second-entry index,
    then timestamp-pair index. CheckRun indices enumerate status as the
    outer axis and conclusion as the inner axis. Omission is absence of
    the JSON property. It isn't null or an empty string.

    • Status: COMPLETED, IN_PROGRESS, PENDING, QUEUED, REQUESTED, WAITING,
      then those six lowercase spellings, UNKNOWN_STATE, then omission.
    • Conclusion: ACTION_REQUIRED, CANCELLED, FAILURE, NEUTRAL, SKIPPED,
      STALE, STARTUP_FAILURE, SUCCESS, TIMED_OUT, then those nine
      lowercase
      spellings, UNKNOWN_STATE, then omission.
    • Context state: ERROR, EXPECTED, FAILURE, PENDING, SUCCESS, then
      those five lowercase spellings, UNKNOWN_STATE, then omission.
    • Seven timestamp pairs, in the issue's order: equal, earlier, later,
      offset-reversed, invalid first, invalid second, both invalid.
      m['manifest']['times'] records every literal timestamp.
    • Fixed head head, CheckRun workflow ci, name test, or context
      test. No required names or expected-head argument. No mixed types
      or extra fields. JSON array order is first then second.

    Expected total, fixed before running:
    7 * ((14 * 20)**2 + 12**2) = 549808.
    That is 548800 CheckRun pairs and 1008 context pairs.

    The shell batch ran the exact jq program extracted from
    mp_judge_checks. Its TSV rows went through the same failure-first,
    pending-second status reduction. The Go batch called the actual
    agentrepo.Run command handler for each input, with fresh buffers and
    no constraints. Batching avoids over a million process launches.
    The unmodified shell function and built CLI separately matched both
    streams and statuses on 103 fresh-process samples, plus every hand
    fixture. This distinguishes command recordings from an independent
    reimplementation of either judge.

    The coverage check independently reconstructed all mandated field
    products and all expected keys, compared each entry's complete fields
    with its index and checked timestamp values, order and fixed fields.
    It found exactly 549808 unique keys, zero duplicates, zero missing
    keys and zero unexpected keys. All 280 crossed CheckRun entries and
    all 12 context entries participate in both positions.

    Chosen results: 382 success, 2168 failing, 11994 pending and 535264
    unreadable. Zero shell failures or pending results become success.
    Current Go yields success for 120 shell-failing and 624 shell-pending
    cases. The chosen rule prevents all 744. The full three-way counts
    are in m['summary']['cross'], whose rows are
    [shell status, current Go status, chosen status, count].

    AC4: time ordering rule

    First reject every invalid timestamp, even on a superseded entry.
    For each CheckRun group (workflowName, name), compute both the
    shell text-order maximum and the parsed-instant maximum. Keep every
    entry tied at either maximum. Evaluate those candidates and choose
    failure over pending over success, with deterministic state/name
    ordering for equal verdicts. The selected display row represents
    that worst candidate. Count one displayed row per CheckRun group.

    Use the shell's exact text key: missing/empty start or a start that
    begins 0001 maps to 9999-12-31T23:59:59Z. For instant order,
    missing/empty start or the exact zero timestamp outranks started
    runs. Other timestamps must parse as RFC3339Nano. This preserves the
    shell's unusual zero-prefix case without letting it choose green
    over a conflicting instant-order failure.

    For any ordering disagreement, the candidate union includes both
    maxima. Its worst verdict cannot be green if either maximum fails or
    is unfinished. Ties retain all candidates, so array order cannot
    weaken the result. The argument applies to groups larger than pairs.
    For h20's offset example, the failure wins and gives P(1,F,1) and
    S(1,F). The sweep found zero unsafe offset-reversed cases.

    Keep every StatusContext entry, in its existing array order for
    otherwise equal display names. Don't discard an older failing or
    pending context. Validate createdAt, but don't use it to discard
    contexts. This preserves the shell contract for repeated contexts.

    Retain the old superseded count: number of CheckRuns minus the number
    of distinct (workflowName, name) groups. Its legacy wording remains
    exactly as in P above, including for ties. It counts collapsed display
    rows, not proof that every omitted timestamp is strictly earlier.

    AC6: evaluator process and result contract

    Keep numeric outcomes 0 through 6 and their precedence: unreadable,
    head moved, empty, missing required, failed, pending, success.
    Require an additive schema: 1, checks array and nonnegative
    integer
    superseded on every result. Each check has string state and name
    and a verdict of ok, pending or fail. Preserve existing
    outcome, sorted unique names, skipped and error semantics.
    For unreadable input require a nonempty error and empty checks.
    For missing required checks, keep the missing names in names.

    Validate the whole stdout as exactly one JSON object with no trailing
    non-whitespace bytes. Verify types, outcome/status agreement, listing
    verdicts, outcome precedence and consistency of summary names and
    counts. For success require a nonempty all-ok listing, no error or
    non-success names, and process exit 0. This rejects empty success and
    partial success-looking output. Don't infer success from an exit
    status alone or from a JSON fragment.

    Let R(n) mean the complete, internally consistent schema-1 result
    for outcome n, ending with one newline. Specific result examples are
    specified below in executable form. Unless stated otherwise evaluator
    stderr is empty. Caller stdout/stderr/status use the full P/S tuples,
    not the evaluator status. All these rows occur at the check section
    unless the early-boundary column says otherwise.

    Eval exit Eval stdout Category P S Boundary
    0 R(0) green P(0,OK) S(0) checks
    1 R(1) failing P(1,F) S(1,F) checks
    2 R(2) running P(2,Q) S(2,Q) checks
    3 R(3) failing P(1,MR+OR) S(1,MR) checks
    4 R(4) empty P(4) S(4) checks
    5 R(5) unreadable P(5) S(5) checks
    6 R(6) unreadable P(5) S(5) checks
    7, 127, 137 any unreadable P(5) S(5) checks
    other nonzero any unreadable P(5) S(5) checks
    absent absent unreadable P(5) S(5) see AC7
    0 garbage\n unreadable P(5) S(5) checks
    0 {}\n unreadable P(5) S(5) checks
    0 []\n unreadable P(5) S(5) checks
    0 help text unreadable P(5) S(5) checks
    0 {"outcome":0}\n unreadable P(5) S(5) checks
    0 R(0) + garbage unreadable P(5) S(5) checks
    1 R(0) unreadable P(5) S(5) checks
    2 R(0) unreadable P(5) S(5) checks
    other nonzero R(0) unreadable P(5) S(5) checks
    0 missing result field unreadable P(5) S(5) checks

    The absent-command row describes P/S with the loader allowing absence.
    U/G instead stop at ma_context, as AC7 specifies. Loader version
    refusals precede both. Mapped 1 or 2 with R(0) remains unreadable,
    not a valid failed/pending result. Evaluator stderr is captured and
    must never substitute for a result. Keep recognized skipped-check
    hints as described in AC8. For protocol/command failures, report the
    stable unreadable message rather than copying arbitrary subprocess
    stdout into the caller report.

    MR = [('MISSING_REQUIRED', 'test')]
    OR = [('SUCCESS', 'ci / other')]
    def R(n):
        rows = {0: [('ok', 'SUCCESS', 'ci / test')],
                1: [('fail', 'FAILURE', 'ci / test')],
                2: [('pending', 'QUEUED', 'ci / test')],
                3: [('ok', 'SUCCESS', 'ci / other')]}.get(n, [])
        value = dict(schema=1, outcome=n,
                     checks=[dict(verdict=v, state=s, name=k)
                             for v, s, k in rows], superseded=0)
        if n in (1, 2, 3):
            value['names'] = ['test']
        if n == 5:
            value['error'] = 'invalid rollup'
        return json.dumps(value, separators=(',', ':')) + '\n'

    For outcome 3, append MISSING_REQUIRED diagnostic rows for missing
    names to the display listing. The P/S row above includes both the
    reported successful check and
    the missing-check diagnostic in preflight. Squash prints only the
    missing-check diagnostic before refusing. Head-moved status 6 remains
    a
    separate evaluator outcome and refuses as unreadable in the attended
    adapter. U preserves the earlier gate's status 6.

    AC7: earlier stops, modes and exact stream rules

    No earlier gate is removed or relaxed. Full process recordings for
    malformed JSON and review/PR-state cases are in m['callers'] and
    m['early']. They include both streams, numeric status and
    reachability.
    The remaining rows below are source-traced contracts. They aren't
    claims that every external failure was injected into a live workflow.

    For the source-traced rows, use the exact source prefixes below to
    complete stdout. Messages in the diagnostic catalogue end in one
    newline. An ordinary dt_die adds no prefix. Squash's die adds
    squash-merge: . Both exit 1. Empty stdout/stderr is explicitly "".
    The table's tuple notation is (status, stdout, stderr, reached).
    pass means the other caller proceeds to P(0,OK) or S(0), with its
    full recorded stdout, empty stderr, status 0 and reached=yes.

    Pgh = '\n== gh ==\nok\n'
    Ptarget = (Pgh + '\n== target ==\nlocal branch: topic\n'
               'pull request: #7\n')
    Pstate = Ptarget + '\n== state ==\n'
    Pbefore = pfx.removesuffix('\n== checks ==\n')
    Sd = '\n--- checking SQUASH_AGENTMSG\n'
    Ss = (Sd + 'drafted for #7\n'
          '\n--- checking the review stamp\n')
    Sr = Ss + 'reviewed at head head, onto main at base\n'
    Sa = Sr + '\n--- checking the gh account\n'
    # Concrete failing subprocess fixture: exit 2, empty stdout.
    E = 'fixture failure\n'
    # U's EXIT trap, with no mail transport configured:
    def report(n, status):
        return (f'REPORT (blocker): Merge workflow for #{n} held '
                f'with status {status}. Inspect the preserved '
                'draft, review, and merge result before retrying.\n')

    With U enabled, append report(number,status) to stdout on every
    nonzero exit after pull-request.sh installed its trap. Before
    preflight assigns its number, number is unknown. During loader
    failure, the trap isn't installed, so no report appears. G appends it
    only when DEVTOOLS_UNATTENDED=1. A configured reporting transport
    keeps its existing output and failure behavior instead. This is
    orthogonal to the judge result and doesn't change the exit status.

    The table uses ordinary P/S tuples and explicitly states mode changes.
    All rows preserve their current stop and message. Source suffixes
    refer to the catalogue immediately following the table.

    First stop P tuple S tuple Modes
    loader version read (1,"",D1,no) same all
    loader version floor (1,"",D2,no) same all
    git root read (128,"",E,no) (128,"",E,no) all
    worktree readiness (1,"",D3,no) pass P/U
    argument parser pass (1,"",D4,no) S/G
    missing commit-lint pass (1,"",D5,no) S/G
    missing gh (0,PG,"",no) (1,"",D6,no) P/S
    missing jq (0,PJ,"",no) (1,"",D7,no) P/S
    viewer auth error (2,Pgh,E,no) (2,"",E,no) all
    viewer empty (1,Pgh,D8,no) (1,"",D8,no) all
    rate limit (76,Pgh,RL,no) (76,"",RL,no) all
    PR lookup error (2,Pgh,E,no) (2,"",E,no) all
    no target (0,PN,"",no) (1,"",D9,no) all
    missing draft pass (1,Sd,D10,no) S/G
    wrong draft number pass (1,Sd,D11,no) S/G
    snapshot read error (2,Ptarget,E,no) (2,Ss,E,no) all
    malformed snapshot saved h00 saved h00 P/S
    malformed fields (5,Pstate,E,no) (5,Ss,E,no) P/S
    base read error pass (2,Ss,E,no) S/G
    missing stamp pass (1,Ss,D12,no) S/G
    stale stamp pass (1,Ss,D13,no) S/G
    expected head mismatch pass (1,Ss,D14,no) S/G
    account mismatch pass (1,Sa,D15,no) S/G
    unknown mergeability saved early saved early all
    closed PR saved early saved early all
    draft PR saved early saved early all
    conflicting PR saved early saved early all
    required review saved early/75 saved early/75 all
    changes requested saved early/75 saved early/75 all
    unknown review enum saved early/1 saved early/1 all
    not CLEAN saved early saved early all
    unknown fork flag saved early saved early all

    PG is "\n== gh ==\ngh is not installed\n" followed by
    "Stop here and tell the operator.\n". PJ replaces gh with jq.
    PN is Pgh followed by the no-target block in preflight lines 66-74,
    with branch topic and an empty open-PR list. Its exact bytes are:

    PN = (Pgh + '\n== target ==\nlocal branch: topic\n'
          'pull request: NONE for this branch, and none given.\n'
          'Pass the number as the skill argument, or open one '
          'with the pr skill.\n\nopen pull requests here:\n')

    Diagnostic catalogue, using quoted Python strings for exact bytes.
    binary is the selected executable path, here is harness in the
    recorded fixture, and the example old version is v0.15.0. These are
    parameters of the existing diagnostic, not text to hardcode in #723.

    D1 = E + f'Could not read agentrepo version: {binary}\n'
    D2 = 'agentrepo found v0.15.0; required v0.16.0 or newer\n'
    D3 = ('worktree setup: mise config is untrusted. Run:\n\n'
          '  mise trust\n')
    D4 = 'squash-merge: usage: squash-merge.sh [--confirmed] [number]\n'
    D5 = ('squash-merge: commit-lint is not installed; add '
          '"go:github.com/tbhb-dev/devtools/cmd/commit-lint" = '
          '"<devtools-version>" to mise.toml [tools]\n')
    D6 = 'gh is not installed\n'
    D7 = 'jq is not installed\n'
    D8 = 'GitHub returned no viewer login\n'
    D9 = ('squash-merge: no open pull request for topic. '
          'Pass a number explicitly.\n')
    D10 = ('squash-merge: no squash message drafted. '
           'Run squash-message.sh first.\n')
    D11 = 'squash-merge: SQUASH_AGENTMSG was drafted for #8, not #7.\n'
    D12 = ('squash-merge: no independent review has cleared this '
           'message. Run it, resolve what\nit returns, then retry:'
           '\n\n  bash harness/review.sh 7\n')
    D13tail = ('squash-merge: the reviewed message and pull request '
               'are no longer the ones about to\nmerge. Review '
               'again against the current draft and branch:'
               '\n\n  bash harness/review.sh 7\n')
    D14 = 'squash-merge: Expected head differs from the reviewed head\n'
    D15 = ('squash-merge: gh is signed in as agent, not the '
           'configured other. Nothing was merged.\n')

    D1 includes the failed version command's exact stderr, empty when
    that command printed none. D3 also has source-defined alternatives,
    both P/U process exit 1
    before the check section and S/G pass: worktree setup: Vale styles are missing. Run:\n\n mise run devtools:worktree-setup\n, or
    worktree setup: could not check mise trust:\n plus captured output
    and newline. Other Vale configuration failures use
    worktree setup: Vale could not load the repository configuration:\n
    plus captured output and newline.

    D4 also covers malformed expected-head syntax with
    squash-merge: Expected head must be a full commit SHA\n.
    D13 is the exact dt_require_stamp diagnostic followed by D13tail:
    changed draft, changed bound field, changed repository configuration,
    or changed trusted policy authority. Each ends in newline. The
    field-change template is The {field} changed after the review (reviewed {old}, now {new}). Run the review script again. The other
    literal messages are in L/lib.sh:687-709. Failures reading the stamp
    or its digest keep the underlying stderr before D13tail and exit 1.

    For external reader/parser errors, E means the exact captured stderr,
    not a replacement message. Exit 2 above is the concrete external
    failure fixture, and 128 is the Git failure fixture. Other subprocess
    statuses propagate numerically unchanged. Parser status is jq's 5,
    with its actual diagnostic and any partial stdout retained. An
    arbitrary external program can emit arbitrary bytes, so this family
    is parameterized by its captured bytes and numeric status. The h00
    record gives a concrete complete parser example without that freedom.

    RL is exactly the classifier message at L/lib.sh:860-864, plus
    No automatic retry was made.\n, followed by the original error.
    Secondary, primary GraphQL and primary REST have separate messages.
    All exit 76 and retain original stderr. The first failing read decides
    which stdout prefix applies. Later API reads use their prefix at that
    point, not necessarily Pgh/S's empty prefix. No retry or budget query
    belongs to this Spike.

    U/G-specific earlier helpers follow. These modes execute ma_context.
    P and explicitly confirmed S skip it and instead take their normal
    report/merge paths, unless a common earlier gate above already stops.
    For U, ma_gates runs before the check section. For G, ma_gates first
    runs AFTER the check section and lint, at squash line 244. Don't move
    its refusal earlier while replacing only the judge.

    Context stop U status/stdout G status/stdout stderr
    operation marker 1/report(unknown,1) 1/report(7,1) C1
    detached HEAD 1/report(unknown,1) 1/report(7,1) C2
    unborn HEAD 1/report(unknown,1) 1/report(7,1) C3
    dirty worktree 1/report(unknown,1) 1/report(7,1) C4
    no configured login 1/report(unknown,1) 1/report(7,1) C5
    missing uv 1/report(unknown,1) 1/report(7,1) C6
    absent agentrepo 1/report(unknown,1) 1/report(7,1) C7
    unusable agentrepo 1/report(unknown,1) 1/report(7,1) C8
    unattended --confirmed U normal 1/report(7,1) C9

    The --confirmed flag belongs to squash. The paired U invocation is
    ordinary preflight, which reports and exits 0 with its normal stdout
    and empty stderr when its gates pass. All other context stops have
    reached=no for U/G. Plain G without the
    unattended flag has empty stdout instead of report(...). Underlying
    Git/config read failures propagate their exit status and stderr,
    with the same empty prefix and trap rule. C6 includes Bash's
    command -v uv output only if that builtin emits one in the selected
    shell. Bash in this environment emits none for the lookup.

    C1 = ('HELD: MERGE_HEAD is in progress; finish or abort it '
          'first.\n')
    # Substitute CHERRY_PICK_HEAD, REVERT_HEAD, rebase-merge,
    # or rebase-apply for the other marker cases.
    C2 = 'HELD: HEAD is detached.\n'
    C3 = ('Create the initial commit through the interactive '
          'commit skill first.\n')
    C4 = ('Commit the work first; unattended merging requires '
          'a clean tree.\n')
    C5 = 'gh_login must be configured on the machine\n'
    C6 = 'uv is required for the YAML policy reader\n'
    C7 = 'agentrepo is required for check judgement\n'
    C8 = 'a working agentrepo is required for check judgement\n'
    C9 = ('squash-merge: Unattended workflows refuse --confirmed; '
          'use pre-approval or delegation.\n')

    agent-merge-pr/shared/scripts/merge.sh sets unattended mode and
    forwards to squash. With two numeric arguments it converts the second
    into --expected-head. Its --confirmed refusal therefore comes from
    the shared squash caller after ma_context, not from the wrapper.

    Before U/G reach their initial response parsing, mp_view also runs
    ma_reviews. A malformed raw response stops at its mp_field with
    status 5, stdout Ptarget for U or Ss for G, and the exact jq stderr.
    A failed review-pages read propagates its numeric status and stderr.
    A mismatched review-page head stops with status 5 and the jq error
    PR head changed while reading review authors; the check section
    isn't reached. P/S don't take this extra reader and continue normally
    on a valid snapshot with the same paginated-review fault.

    U's pre-check gates have the following first-stop catalogue. For
    each row, U has reached=no. Its stdout is Pbefore plus any earlier
    gate output plus the trap report. The stderr is the listed diagnostic
    with newline. P and S pass on the same isolated gate-only fault.
    G reaches the checks first, then stops at the same gate after lint:
    its stdout is sfx plus the green line and the running-linters heading,
    plus any prior gate output and its optional trap report. The process
    status equals U's. This records the other caller even though its stop
    lies beyond the check-section boundary.

    First U gate stop Status Diagnostic/source
    PR state/draft/readiness 1 A1
    actual login mismatch 1 A2
    reviewer login [bot] suffix 1 A3
    no required reviewer 1 A4
    invalid reviewer type 1 A5
    exact-head approval absent 1 A6
    empty owner-review label 1 A7
    owner-review label present 1 A8
    pinned diff acquisition 1 A9
    policy inspection/evaluation 1 A10
    owner approval absent 1 A11
    requirements unavailable 1 A12
    evaluator non-success 1..6 AC6/AC8 JSON and hint
    readiness not executable 1 A13
    readiness exits nonzero 2 exact E fixture

    A ma_gates review refusal is status 1. It isn't the status-75
    mp_require_reviews refusal. That distinction stays unchanged.
    All ma_context checks run again inside ma_gates before this list.

    A1 = 'PR is not open, ready, MERGEABLE, and CLEAN\n'
    A2 = 'gh is signed in as other, not agent\n'
    A3 = ('required_reviewer must use the GraphQL login reviewer, '
          'without [bot]\n')
    A4 = 'required_reviewer must be configured on the machine\n'
    A5 = 'required_reviewer_type must be Bot or User when set\n'
    A6 = 'WAITING_REVIEW: reviewer must approve the current head\n'
    A7 = 'owner_review_label must not be empty\n'
    A8 = 'needs-owner: the PR has the owner-review label\n'
    A9 = 'Could not fetch the pinned base and head.\n'
    A11 = ('needs-owner: golden files or loosened gates require '
           'merge_owner approval on the current head\n')
    A12 = 'No required checks are known for the base\n'
    A13 = 'merge_ready_cmd must name an executable\n'

    A9 variants: unavailable repository URL, incomplete pinned histories,
    failed complete diff, or failed name-status list. Preserve the exact
    messages in M/pull-request.sh:140-151 and any preceding Git stderr.
    A10 is merge policy unreadable: {reason}\n, from policy.py's main
    exception handler, or a propagated uv/Python process failure. Reasons
    cover invalid policy patterns, repository paths, APM lock fields,
    review/findings shape, ruleset fields and missing required checks.
    No raw policy value gets added to the replacement judge diagnostic.
    Missing requirements from the policy reader use
    merge policy unreadable: No required checks are known for the base
    plus newline, rather than the later empty-output check's A12.
    For owner-required policy, the existing JSON findings precede A11 on
    stderr. A successful owner override instead prints its existing
    owner override: {login} approved {head}\n on stdout.
    Configuration parsing and Git/filesystem reads at any of these points
    retain their numeric status and exact captured streams as above.

    The malformed/required-review recordings and source trace establish
    that preflight's CHECK SECTION reports failing and running checks
    without refusing. They do not assert that preflight never refuses.
    The existing review regression is at
    tests/skills/merge-pr/scripts/merge-workflow.bats:385.

    AC8: unattended fixtures and hints

    None of the existing check-bearing fixtures under
    tests/skills/agent-merge-pr/ changes its semantic outcome, numeric
    status or stderr hint under this contract. The schema/listing fields
    are additive stdout changes. The old summary fields keep their exact
    values. The evidence includes direct evaluator replays of every
    check-shape variant with required names and expected head supplied.
    These are evaluator replays, not a claim that the entire Bats suite
    ran through a future implementation.

    Relevant fixtures: workflow.bats:76,223,233,242,253,267,
    loosens.bats:28, confirmation.bats:33-34,161, and the base success
    fixture in test_helper.bash. The missing-requirements policy case
    stops before evaluation with A12, unchanged. Missing/unusable/pinned
    runner cases in workflow.bats:12-41 keep the AC7 boundaries.

    Variant Outcome/status stdout summary stderr hint
    empty empty/4 {"outcome":4} ""
    skipped/current failed/1 test/test H(test,no)
    skipped/lagging failed/1 test/test H(test,yes)
    skipped/unknown failed/1 test/test H(test,unknown)
    skipped/missing missing/3 test/validate H(validate,yes)
    required NEUTRAL failed/1 names=test ""
    updated SUCCESS green/0 {"outcome":0} ""
    def H(name, differs):
        return (f'skipped required check: {name}\n'
                f'merge base differs from base commit: {differs}\n'
                'run: gh pr update-branch 7\n')

    In the compact rows, test/test and test/validate mean
    names/skipped, each a single-name array.

    The full byte-exact old JSON and stderr are in m['unattended'].
    Add the new fields without removing those summaries. Keep exact
    required-name matching across every workflow. Required NEUTRAL and
    SKIPPED fail, optional NEUTRAL and SKIPPED pass. Keep missing-required
    precedence and the skipped hint. Head mismatch remains 6 and missing
    expected-head data remains unreadable/5, as recorded in m['hand'].

    Go unit fixtures reordered statuses and status rerun in
    internal/checks/checks_test.go deliberately change success/0 to
    failed/1. Unknown status/conclusion/context fixtures change failed/1
    to unreadable/5. New coverage must add superseded invalid states,
    lowercase fields, offset disagreements and command protocol faults.
    These tightenings apply to both callers of the shared evaluator.

    AC9: capability and release sequence

    No compatible release exists yet. The current main implementation,
    and the latest local tag v0.22.1, don't provide the proposed schema,
    complete listing or superseded count. Main also still implements the
    unsafe context/offset selection demonstrated above. Don't invent a
    minimum released version for capabilities that don't exist.

    Missing promised outputs: all-entry validation failures for unknown
    and lowercase fields, conservative dual-order selection, all status
    contexts, full workflow-qualified listing, superseded count, schema
    marker and consistent protocol-failure adaptation. #723 implements
    these evaluator and caller changes and documents the first release
    that contains them. #333 supplies binary distribution. #544 installs
    the compatible release before its dependent callers are deployed.

    With no agentrepo, P reports unreadable/0 and S refuses unreadable/1
    at the new adapter, while U/G retain their earlier C7 refusal/1.
    A release below v0.16.0 still fails the shared loader with D2/1.
    An older accepted release lacking schema 1 or its required fields
    cannot be green: P reports unreadable/0, S refuses unreadable/1.
    U/G must validate the same result protocol at their gate and refuse
    with unreadable/5. An unusable executable can stop earlier at D1/C8.
    Don't add a second shell judge as a fallback.

    AC10: measured time and proposed limit

    Generate 500 distinct successful CheckRuns, fixed head head,
    workflow
    ci, names test-000 through test-499, all started at
    2026-10-01T01:00:00Z. Save once. Build outside the timed region.
    Time each subprocess invocation through its completion, capturing
    both streams, for 20 runs. All runs exited zero. The evidence stores
    all 40 samples, in milliseconds.

    • Shell function via Bash: median 28.179033 ms.
    • Built Go command: median 1.954330 ms.

    Commands: bash judge.sh 500.json (the unchanged sourced shell
    function) and agentrepo checks evaluate --file 500.json.
    The timer used Python time.perf_counter_ns(). These current-judge
    measurements exclude the not-yet-implemented schema validation and
    caller adapter. #723 must measure that complete boundary separately.
    Keep its proposed limit: median strictly below 1000 ms for 20 runs,
    including invocation, whole-response validation and caller verdict,
    with compilation excluded. Both existing medians are below the limit.

    AC11: ownership, open questions and follow-up work

    Every AC has a section in this comment. The migration belongs to
    #723. It should
    commit the contract, decoder and corpus before deleting the shell
    judge, then implement the additive protocol and caller adapters.
    Its tests should compare both caller streams and statuses, including
    earlier-stop reachability. Update docs/checks-evaluator.md and the
    consumer adoption documentation in that same feature.

    Further work already has owners: #333 distribution, #544 installation,
    #588 replacement-path protection, then #713 gate migration. #715,
    #719, #721 and #722 retain their existing ordering and rollout holds.
    No additional implementation issue is needed for findings already
    inside #723. No duplicate issue was filed. No feature PR is
    appropriate
    for this Spike.

    Open implementation choices that don't alter the contract: which
    release number first carries it, how #723 stores the compact sweep
    fixture, and whether its eventual diff requires the split already
    specified in #723. The evaluator precedence, validation scope,
    ordering rule, caller messages and failure mapping above are
    decisions,
    not questions deferred to the builder.

    AC12: publication checks

    PUBLICATION HOLD: the required private-strings list isn't available
    in this session. No findings comment or follow-up body has been
    posted. Gitleaks found no leaks in this complete draft and its decoded
    metadata and 549808-record corpus. The repository check-leaks.sh also
    returned zero, but warned that no private values were loaded. That
    patterns-only result does not satisfy the required private-list scan.

    A separate manual leak-review pass found no credentials, private
    identifiers, local home paths or prohibited coordination references
    in the complete draft or its decoded evidence. All fixture identifiers
    are synthetic. No follow-up post exists. Recheck the final text after
    adding the missing private-list scan result and before publication.

    Recorded evidence and decoder

    Save the following base64 block as evidence.b64. Decode it with the
    code below. The XZ payload starts with an unsigned little-endian
    8-byte metadata length, then UTF-8 JSON metadata, then four bytes per
    case: unsigned little-endian shell-dictionary index, one-byte
    Go-dictionary index, one-byte proposed evaluator outcome.
    Dictionary entries are [status, stdout, stderr].

    Uncompressed SHA-256:
    ee8e74ba494a5979c91eab8a563edf0c2ef758c2e1e0638fccd4891f70e5aa72.
    The matrix SHA-256 is in m['summary']['matrix_sha256'].

    import base64, hashlib, itertools, json, lzma, struct
    from pathlib import Path
    raw = lzma.decompress(base64.b64decode(
        Path('evidence.b64').read_text()))
    size, = struct.unpack_from('<Q', raw)
    m = json.loads(raw[8:8 + size])
    matrix = raw[8 + size:]
    assert len(matrix) == 549808 * 4
    assert hashlib.sha256(matrix).hexdigest() == (
        m['summary']['matrix_sha256'])
    Path('metadata.json').write_text(json.dumps(m, indent=2))
    d = m['manifest']
    index = 0
    seen = set()
    with Path('records.ndjson').open('w') as output:
        for kind in ('CheckRun', 'StatusContext'):
            entries = []
            if kind == 'CheckRun':
                for s, c in itertools.product(d['status'],
                                              d['conclusion']):
                    e = dict(__typename=kind,
                             workflowName='ci', name='test')
                    for k, v in [('status', s), ('conclusion', c)]:
                        if v is not None:
                            e[k] = v
                    entries.append(e)
                field = 'startedAt'
            else:
                for s in d['state']:
                    e = dict(__typename=kind, context='test')
                    if s is not None:
                        e['state'] = s
                    entries.append(e)
                field = 'createdAt'
            axes = (enumerate(entries), enumerate(entries),
                    enumerate(d['times']))
            for (i, a), (j, b), (k, (x, y)) in (
                    itertools.product(*axes)):
                key = (kind, i, j, k)
                assert key not in seen
                seen.add(key)
                rollup = dict(headRefOid='head', statusCheckRollup=[
                    dict(a, **{field: x}), dict(b, **{field: y})])
                si, gi, chosen = struct.unpack_from(
                    '<HBB', matrix, index * 4)
                record = dict(key=key, input=rollup,
                              shell=m['dictionary'][0][si],
                              go=m['dictionary'][1][gi],
                              chosen=chosen)
                output.write(json.dumps(record) + '\n')
                index += 1
    assert index == len(seen) == 549808

    The chosen outcomes are a research model. They aren't represented as
    implemented behavior. Replaying the recorded current outcomes uses
    the cited head's shell function and built command. The Go package and
    command tests passed with go test ./internal/checks ./internal/agentrepo.
    The future implementation must pass the model and caller contract.

    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
    
  13. tbhb-agent commented on Oct 31, 2026

    @tbhb-agent
    ContributorAuthor

    Findings for #720: conservative check evaluation contract

    Original recording head: f130498bd7383fe02f8b9d21965f555866abd98c.
    Fresh replay head: 44be762a34eec4a832e1ed608c77ebf188357719.

    The corpus and original caller recordings extend the existing
    research comment.
    The judge corpus, hand fixtures and unattended evaluator fixtures were
    independently replayed on the fresh head.

    The recommended contract validates every entry, keeps every status
    context, and takes the worse check-run verdict selected by either
    text order or instant order. A caller accepts success only when both
    the process status and a complete, consistent result say success.

    Evidence and notation

    Source locations in the inherited findings refer to the original recording head.
    Caller supplements below use the fresh replay head. Let M mean
    packages/agents/source/skills/merge-pr/shared/scripts/ and L mean
    packages/agents/source/lib/. Paths written after those prefixes
    concatenate with them. This avoids ambiguous generated-copy
    references.

    • M/pull-request.sh:69-110: shell judge and superseded count.
    • internal/checks/checks.go:75-177: parse and evaluation precedence.
    • internal/checks/checks.go:180-206,257-286: verdict and selection.
    • internal/agentrepo/command.go:55-101,107-119: command protocol.
    • M/preflight.sh:40-120: preparation, review and check reporting.
    • M/squash-merge.sh:63-218: preparation and check refusal.
    • M/merge-agent.sh:19-30,118-180: unattended context and gates.
    • L/lib.sh:140-183,679-714,830-885: loader, stamps and API errors.
    • L/agent.sh:56-65: interrupted operations and detached HEAD.

    The evidence block at the end is a lossless recording, not a link to
    local files. Its metadata holds every hand-written input, both judges'
    exact stdout, stderr and numeric status, full caller recordings,
    unattended recordings, benchmark samples and the sweep manifest.
    The binary matrix records both exact outputs for every sweep key.
    The decoder reconstructs all 549808 input/output records. No fixture
    requires access to the research machine.

    P means attended preflight, S means squash with an explicit
    operator confirmation. U means unattended preflight. G means
    squash without explicit confirmation, including unattended squash.
    These are fixture invocation modes, not confirmation grants.

    For AC1 and AC6, earlier prerequisites and later gates pass, with all
    GitHub writes stubbed. P reaches its check-reporting section and
    continues to process exit 0. S either refuses there or reaches a
    successful stubbed merge and process exit 0. The check section alone
    doesn't establish that a real merge is authorized. AC7 overrides this
    boundary whenever a real earlier stop applies.

    The following executable definitions specify complete caller streams.
    m is the decoded evidence metadata. Concatenation adds no implicit
    whitespace. Every newline is explicit. Empty stderr means "".
    pfx includes the checks heading. sfx ends just before judging.
    ptail and stail include all otherwise successful later output.
    Those tails come from the recorded isolated fixture, not a proposed
    change to production diagnostics.

    r = {(x['id'], x['caller']): x for x in m['callers']}
    p = r['success', 'preflight']['stdout']
    s = r['success', 'squash-merge']['stdout']
    pfx, rest = p.split('SUCCESS        ci / test\n', 1)
    ptail = rest.split('\nsummary: all green.\n', 1)[1]
    sfx, stail = s.split('open, ready, mergeable, and green\n', 1)
    summary = {
        0: 'all green.\n',
        1: ('FAILING. Do not merge. Hand this to the '
            'fix-pr skill instead.\n'),
        2: ('still running. Wait through the watch-pr '
            'skill before merging.\n'),
    }
    err = {
        1: ('squash-merge: #7 has a failing check. '
            'Hand it to the fix-pr skill.\n'),
        2: ('squash-merge: #7 has a check still running. '
            'Wait through the watch-pr skill.\n'),
        4: ('squash-merge: #7 has no checks reported; '
            'the result is inconclusive.\n'),
        5: 'squash-merge: the checks of #7 could not be read.\n',
    }
    def line(state, name):
        return f'{state:<14} {name}\n'
    def P(code, rows=(), superseded=0):
        if code in (4, 5):
            body = ('no checks reported\n' if code == 4 else
                    'the check rollup could not be read\n')
        else:
            body = ''.join(line(state, name) for state, name
                           in sorted(rows, key=lambda x: x[1]))
            if superseded:
                body += (f'({superseded} earlier run(s) left out: '
                         'a later run of the same check replaced '
                         'each)\n')
            body += '\nsummary: ' + summary[code]
        return (0, pfx + body + ptail, '')
    def S(code, rows=()):
        if code == 0:
            return (0, sfx + 'open, ready, mergeable, and green\n'
                    + stail, '')
        body = ''.join(line(state, name) for state, name in rows)
        return (1, sfx, body + err[code])
    F = [('FAILURE', 'ci / test')]
    Q = [('QUEUED', 'ci / test')]
    C = [('ERROR', 'test'), ('SUCCESS', 'test')]

    Each tuple is (caller process status, stdout, stderr).
    P(5) is an unreadable report and continuation, not green.
    S(5) refuses with no merge call. P(4) reports empty and continues.
    S(4) refuses. P(1,...) and P(2,...) report and continue.
    S(1,...) and S(2,...) print the relevant rows and refuse.
    The failure diagnostic deliberately drops “judged by its latest run”:
    that phrase would misdescribe the conservative two-order decision.

    AC1, AC2 and AC5: input contract and recorded comparison

    Table keys are exact id values in m['hand']. That record holds the
    saved input bytes, command arguments, shell stdout/stderr/status and
    current Go stdout/stderr/status. Both commands ran on each input.
    All shell stderr streams were empty. Go stderr was empty except for
    the required-check hints explicitly recorded under AC8.

    The last column marks deliberate changes to the attended shell path.
    yes includes stricter interpretation or changed diagnostics.
    parser identifies an earlier stop, not an adapter result.

    Key Shell Go P result S result Change
    h00 3 5 parser/5 parser/5 no
    h01 4 5 P(5) S(5) yes
    h02 4 5 P(5) S(5) yes
    h03 4 4 P(4) S(4) no
    h04 0 0 P(0,OK) S(0) no
    h05 1 1 P(1,F) S(1,F) message
    h06 2 2 P(2,Q) S(2,Q) no
    h07 0 1 P(5) S(5) yes
    h08 0 1 P(5) S(5) yes
    h09 0 1 P(5) S(5) yes
    h10 0 1 P(5) S(5) yes
    h11 0 1 P(5) S(5) yes
    h12 0 1 P(5) S(5) yes
    h13 0 1 P(5) S(5) yes
    h14 0 5 P(5) S(5) yes
    h15 0 5 P(5) S(5) yes
    h16 0 1 P(1,F,1) S(1,F) yes
    h17 1 1 P(1,F,1) S(1,F) yes
    h18 2 2 P(2,Q,1) S(2,Q) no
    h19 1 0 P(1,C) S(1,C[:1]) message
    h20 1 0 P(1,F,1) S(1,F) yes
    h21 0 5 P(5) S(5) yes
    h22 0 0 P(5) S(5) yes
    h23 0 0 P(5) S(5) yes
    h24 0 0 P(5) S(5) yes

    OK = [("SUCCESS", "ci / test")]. Input keys:

    • h00: malformed.
    • h01: missing.
    • h02: null.
    • h03: empty.
    • h04: success.
    • h05: failure.
    • h06: pending.
    • h07: unknown_status.
    • h08: unknown_conclusion.
    • h09: unknown_context.
    • h10: lower_status.
    • h11: lower_conclusion.
    • h12: lower_context.
    • h13: missing_status.
    • h14: empty_name.
    • h15: unknown_type.
    • h16: tie.
    • h17: tie_reverse.
    • h18: unstarted.
    • h19: repeated_context.
    • h20: offset.
    • h21: invalid_time.
    • h22: superseded_status.
    • h23: superseded_conclusion.
    • h24: superseded_context.

    For h00, both callers exit 5 before evaluating checks. The exact
    stdout and stderr are in m['callers'] for malformed, with raw {
    as the response. Stderr is the parser diagnostic below. The preflight
    first stops at its state formatter. Squash first stops in mp_field.
    This result stays unchanged in the chosen contract.

    jq: parse error: Unfinished JSON term at EOF at line 1, column 1
    

    Missing and null rollups deliberately change from empty to unreadable.
    Their check-section reports are P(5) and S(5), not the old empty
    reports. An actual empty array remains P(4) and S(4).

    Unknown status, conclusion and context state all mean unreadable,
    even on superseded entries. Validate every raw entry before selection,
    including a conclusion that a non-completed status would otherwise
    hide. Lowercase spellings and a missing CheckRun status are also
    unreadable. This preserves Go's refusal and tightens shell acceptance.
    Don't silently uppercase them and loosen the unattended path.

    Accept the six uppercase CheckRun statuses and nine uppercase
    conclusions in AC3. An absent, null or empty conclusion is allowed and
    means pending when status is COMPLETED. An absent, null or empty
    context state means PENDING. Recognized pending statuses still ignore
    a recognized conclusion for verdict purposes. Null/empty CheckRun
    status, other field types, empty names and unknown entry types are
    unreadable. A context accepts only the five uppercase context states,
    plus the missing-value convention above. These validation rules also
    cover entries excluded by time ordering.

    AC3: finite sweep, coverage and results

    The domain manifest was written before execution. Generation order is
    CheckRun then StatusContext, first-entry index, second-entry index,
    then timestamp-pair index. CheckRun indices enumerate status as the
    outer axis and conclusion as the inner axis. Omission is absence of
    the JSON property. It isn't null or an empty string.

    • Status: COMPLETED, IN_PROGRESS, PENDING, QUEUED, REQUESTED, WAITING,
      then those six lowercase spellings, UNKNOWN_STATE, then omission.
    • Conclusion: ACTION_REQUIRED, CANCELLED, FAILURE, NEUTRAL, SKIPPED,
      STALE, STARTUP_FAILURE, SUCCESS, TIMED_OUT, then those nine
      lowercase
      spellings, UNKNOWN_STATE, then omission.
    • Context state: ERROR, EXPECTED, FAILURE, PENDING, SUCCESS, then
      those five lowercase spellings, UNKNOWN_STATE, then omission.
    • Seven timestamp pairs, in the issue's order: equal, earlier, later,
      offset-reversed, invalid first, invalid second, both invalid.
      m['manifest']['times'] records every literal timestamp.
    • Fixed head head, CheckRun workflow ci, name test, or context
      test. No required names or expected-head argument. No mixed types
      or extra fields. JSON array order is first then second.

    Expected total, fixed before running:
    7 * ((14 * 20)**2 + 12**2) = 549808.
    That is 548800 CheckRun pairs and 1008 context pairs.

    The shell batch ran the exact jq program extracted from
    mp_judge_checks. Its TSV rows went through the same failure-first,
    pending-second status reduction. The Go batch called the actual
    agentrepo.Run command handler for each input, with fresh buffers and
    no constraints. Batching avoids over a million process launches.
    The unmodified shell function and built CLI separately matched both
    streams and statuses on 103 fresh-process samples, plus every hand
    fixture. This distinguishes command recordings from an independent
    reimplementation of either judge.

    The coverage check independently reconstructed all mandated field
    products and all expected keys, compared each entry's complete fields
    with its index and checked timestamp values, order and fixed fields.
    It found exactly 549808 unique keys, zero duplicates, zero missing
    keys and zero unexpected keys. All 280 crossed CheckRun entries and
    all 12 context entries participate in both positions.

    Chosen results: 382 success, 2168 failing, 11994 pending and 535264
    unreadable. Zero shell failures or pending results become success.
    Current Go yields success for 120 shell-failing and 624 shell-pending
    cases. The chosen rule prevents all 744. The full three-way counts
    are in m['summary']['cross'], whose rows are
    [shell status, current Go status, chosen status, count].

    Fresh-head verification reconstructed all mandated keys, rejected duplicates,
    and matched all 549808 shell triples, Go triples and chosen outcomes.
    All 30 hand fixtures and 101 fresh-process sweep samples also matched.
    Zero shell-failing or pending cases became green.

    AC4: time ordering rule

    First reject every invalid timestamp, even on a superseded entry.
    For each CheckRun group (workflowName, name), compute both the
    shell text-order maximum and the parsed-instant maximum. Keep every
    entry tied at either maximum. Evaluate those candidates and choose
    failure over pending over success, with deterministic state/name
    ordering for equal verdicts. The selected display row represents
    that worst candidate. Count one displayed row per CheckRun group.

    Use the shell's exact text key: missing/empty start or a start that
    begins 0001 maps to 9999-12-31T23:59:59Z. For instant order,
    missing/empty start or the exact zero timestamp outranks started
    runs. Other timestamps must parse as RFC3339Nano. This preserves the
    shell's unusual zero-prefix case without letting it choose green
    over a conflicting instant-order failure.

    For any ordering disagreement, the candidate union includes both
    maxima. Its worst verdict cannot be green if either maximum fails or
    is unfinished. Ties retain all candidates, so array order cannot
    weaken the result. The argument applies to groups larger than pairs.
    For h20's offset example, the failure wins and gives P(1,F,1) and
    S(1,F). The sweep found zero unsafe offset-reversed cases.

    Keep every StatusContext entry, in its existing array order for
    otherwise equal display names. Don't discard an older failing or
    pending context. Validate createdAt, but don't use it to discard
    contexts. This preserves the shell contract for repeated contexts.

    Retain the old superseded count: number of CheckRuns minus the number
    of distinct (workflowName, name) groups. Retaining the "earlier/later"
    diagnostic for ties is intentional: its bytes stay exactly as in P.
    The number counts collapsed display rows; it does not assert that
    every omitted timestamp is strictly earlier.

    AC6: evaluator process and result contract

    Keep numeric outcomes 0 through 6 and their precedence: unreadable,
    head moved, empty, missing required, failed, pending, success.
    Require an additive schema: 1, checks array and nonnegative
    integer
    superseded on every result. Each check has string state and name
    and a verdict of ok, pending or fail. Preserve existing
    outcome, sorted unique names, skipped and error semantics.
    For unreadable input require a nonempty error and empty checks.
    For missing required checks, keep the missing names in names.

    Validate the whole stdout as exactly one JSON object with no trailing
    non-whitespace bytes. Verify types, outcome/status agreement, listing
    verdicts, outcome precedence and consistency of summary names and
    counts. For success require a nonempty all-ok listing, no error or
    non-success names, and process exit 0. This rejects empty success and
    partial success-looking output. Don't infer success from an exit
    status alone or from a JSON fragment.

    Let R(n) mean the complete, internally consistent schema-1 result
    for outcome n, ending with one newline. Specific result examples are
    specified below in executable form. Unless stated otherwise evaluator
    stderr is empty. Caller stdout/stderr/status use the full P/S tuples,
    not the evaluator status. All these rows occur at the check section
    unless the early-boundary column says otherwise.

    Eval exit Eval stdout Category P S Boundary
    0 R(0) green P(0,OK) S(0) checks
    1 R(1) failing P(1,F) S(1,F) checks
    2 R(2) running P(2,Q) S(2,Q) checks
    3 R(3) failing P(1,MR+OR) S(1,MR) checks
    4 R(4) empty P(4) S(4) checks
    5 R(5) unreadable P(5) S(5) checks
    6 R(6) unreadable P(5) S(5) checks
    7, 127, 137 any unreadable P(5) S(5) checks
    other nonzero any unreadable P(5) S(5) checks
    absent absent unreadable P(5) S(5) see AC7
    0 garbage\n unreadable P(5) S(5) checks
    0 {}\n unreadable P(5) S(5) checks
    0 []\n unreadable P(5) S(5) checks
    0 help text unreadable P(5) S(5) checks
    0 {"outcome":0}\n unreadable P(5) S(5) checks
    0 R(0) + garbage unreadable P(5) S(5) checks
    1 R(0) unreadable P(5) S(5) checks
    2 R(0) unreadable P(5) S(5) checks
    other nonzero R(0) unreadable P(5) S(5) checks
    0 missing result field unreadable P(5) S(5) checks

    The absent-command row describes P/S with the loader allowing absence.
    U/G instead stop at ma_context, as AC7 specifies. Loader version
    refusals precede both. Mapped 1 or 2 with R(0) remains unreadable,
    not a valid failed/pending result. Evaluator stderr is captured and
    must never substitute for a result. Keep recognized skipped-check
    hints as described in AC8. For protocol/command failures, report the
    stable unreadable message rather than copying arbitrary subprocess
    stdout into the caller report.

    MR = [('MISSING_REQUIRED', 'test')]
    OR = [('SUCCESS', 'ci / other')]
    def R(n):
        rows = {0: [('ok', 'SUCCESS', 'ci / test')],
                1: [('fail', 'FAILURE', 'ci / test')],
                2: [('pending', 'QUEUED', 'ci / test')],
                3: [('ok', 'SUCCESS', 'ci / other')]}.get(n, [])
        value = dict(schema=1, outcome=n,
                     checks=[dict(verdict=v, state=s, name=k)
                             for v, s, k in rows], superseded=0)
        if n in (1, 2, 3):
            value['names'] = ['test']
        if n == 5:
            value['error'] = 'invalid rollup'
        return json.dumps(value, separators=(',', ':')) + '\n'

    For outcome 3, append MISSING_REQUIRED diagnostic rows for missing
    names to the display listing. The P/S row above includes both the
    reported successful check and
    the missing-check diagnostic in preflight. Squash prints only the
    missing-check diagnostic before refusing. Head-moved status 6 remains
    a
    separate evaluator outcome and refuses as unreadable in the attended
    adapter. U preserves the earlier gate's status 6.

    AC7: earlier stops, modes and exact stream rules

    No earlier gate is removed or relaxed. Full process recordings for
    malformed JSON and review/PR-state cases are in m['callers'] and
    m['early']. They include both streams, numeric status and
    reachability.
    The catalogue is supplemented by fresh-head caller executions for stamp,
    rate-limit, diff, policy and gate streams. Exact bytes are embedded below;
    external commands use concrete failure fixtures.

    For the source-traced rows, use the exact source prefixes below to
    complete stdout. Messages in the diagnostic catalogue end in one
    newline. An ordinary dt_die adds no prefix. Squash's die adds
    squash-merge: . Both exit 1. Empty stdout/stderr is explicitly "".
    The table's tuple notation is (status, stdout, stderr, reached).
    pass means the other caller proceeds to P(0,OK) or S(0), with its
    full recorded stdout, empty stderr, status 0 and reached=yes.

    Pgh = '\n== gh ==\nok\n'
    Ptarget = (Pgh + '\n== target ==\nlocal branch: topic\n'
               'pull request: #7\n')
    Pstate = Ptarget + '\n== state ==\n'
    Pbefore = pfx.removesuffix('\n== checks ==\n')
    Sd = '\n--- checking SQUASH_AGENTMSG\n'
    Ss = (Sd + 'drafted for #7\n'
          '\n--- checking the review stamp\n')
    Sr = Ss + 'reviewed at head head, onto main at base\n'
    Sa = Sr + '\n--- checking the gh account\n'
    # Concrete failing subprocess fixture: exit 2, empty stdout.
    E = 'fixture failure\n'
    # U's EXIT trap, with no mail transport configured:
    def report(n, status):
        return (f'REPORT (blocker): Merge workflow for #{n} held '
                f'with status {status}. Inspect the preserved '
                'draft, review, and merge result before retrying.\n')

    With U enabled, append report(number,status) to stdout on every
    nonzero exit after pull-request.sh installed its trap. Before
    preflight assigns its number, number is unknown. During loader
    failure, the trap isn't installed, so no report appears. G appends it
    only when DEVTOOLS_UNATTENDED=1. A configured reporting transport
    keeps its existing output and failure behavior instead. This is
    orthogonal to the judge result and doesn't change the exit status.

    The table uses ordinary P/S tuples and explicitly states mode changes.
    All rows preserve their current stop and message. Source suffixes
    refer to the catalogue immediately following the table.

    First stop P tuple S tuple Modes
    loader version read (1,"",D1,no) same all
    loader version floor (1,"",D2,no) same all
    git root read (128,"",E,no) (128,"",E,no) all
    worktree readiness (1,"",D3,no) pass P/U
    argument parser pass (1,"",D4,no) S/G
    missing commit-lint pass (1,"",D5,no) S/G
    missing gh (0,PG,"",no) (1,"",D6,no) P/S
    missing jq (0,PJ,"",no) (1,"",D7,no) P/S
    viewer auth error (2,Pgh,E,no) (2,"",E,no) all
    viewer empty (1,Pgh,D8,no) (1,"",D8,no) all
    rate limit (76,Pgh,RL,no) (76,"",RL,no) all
    PR lookup error (2,Pgh,E,no) (2,"",E,no) all
    no target (0,PN,"",no) (1,"",D9,no) all
    missing draft pass (1,Sd,D10,no) S/G
    wrong draft number pass (1,Sd,D11,no) S/G
    snapshot read error (2,Ptarget,E,no) (2,Ss,E,no) all
    malformed snapshot saved h00 saved h00 P/S
    malformed fields (5,Pstate,E,no) (5,Ss,E,no) P/S
    base read error pass (2,Ss,E,no) S/G
    missing stamp pass (1,Ss,D12,no) S/G
    stale stamp pass (1,Ss,D13,no) S/G
    expected head mismatch pass (1,Ss,D14,no) S/G
    account mismatch pass (1,Sa,D15,no) S/G
    unknown mergeability saved early saved early all
    closed PR saved early saved early all
    draft PR saved early saved early all
    conflicting PR saved early saved early all
    required review saved early/75 saved early/75 all
    changes requested saved early/75 saved early/75 all
    unknown review enum saved early/1 saved early/1 all
    not CLEAN saved early saved early all
    unknown fork flag saved early saved early all

    PG is "\n== gh ==\ngh is not installed\n" followed by
    "Stop here and tell the operator.\n". PJ replaces gh with jq.
    PN is Pgh followed by the no-target block in preflight lines 66-74,
    with branch topic and an empty open-PR list. Its exact bytes are:

    PN = (Pgh + '\n== target ==\nlocal branch: topic\n'
          'pull request: NONE for this branch, and none given.\n'
          'Pass the number as the skill argument, or open one '
          'with the pr skill.\n\nopen pull requests here:\n')

    Diagnostic catalogue, using quoted Python strings for exact bytes.
    The fixture executable is agentrepo, here is harness, and the
    example old version is v0.15.0. These fixture values are not text to
    hardcode in #723.

    D1 = E + 'Could not read agentrepo version: agentrepo\n'
    D2 = 'agentrepo found v0.15.0; required v0.16.0 or newer\n'
    D3 = ('worktree setup: mise config is untrusted. Run:\n\n'
          '  mise trust\n')
    D4 = 'squash-merge: usage: squash-merge.sh [--confirmed] [number]\n'
    D5 = ('squash-merge: commit-lint is not installed; add '
          '"go:github.com/tbhb-dev/devtools/cmd/commit-lint" = '
          '"<devtools-version>" to mise.toml [tools]\n')
    D6 = 'gh is not installed\n'
    D7 = 'jq is not installed\n'
    D8 = 'GitHub returned no viewer login\n'
    D9 = ('squash-merge: no open pull request for topic. '
          'Pass a number explicitly.\n')
    D10 = ('squash-merge: no squash message drafted. '
           'Run squash-message.sh first.\n')
    D11 = 'squash-merge: SQUASH_AGENTMSG was drafted for #8, not #7.\n'
    D12 = ('squash-merge: no independent review has cleared this '
           'message. Run it, resolve what\nit returns, then retry:'
           '\n\n  bash harness/review.sh 7\n')
    D13tail = ('squash-merge: the reviewed message and pull request '
               'are no longer the ones about to\nmerge. Review '
               'again against the current draft and branch:'
               '\n\n  bash harness/review.sh 7\n')
    D14 = 'squash-merge: Expected head differs from the reviewed head\n'
    D15 = ('squash-merge: gh is signed in as agent, not the '
           'configured other. Nothing was merged.\n')

    D1 includes the failed version command's exact stderr, empty when
    that command printed none. D3 also has source-defined alternatives,
    both P/U process exit 1
    before the check section and S/G pass: worktree setup: Vale styles are missing. Run:\n\n mise run devtools:worktree-setup\n, or
    worktree setup: could not check mise trust:\n plus captured output
    and newline. Other Vale configuration failures use
    worktree setup: Vale could not load the repository configuration:\n
    plus captured output and newline.

    D4 also covers malformed expected-head syntax with
    squash-merge: Expected head must be a full commit SHA\n.
    D13 is the exact dt_require_stamp diagnostic followed by D13tail:
    changed draft, changed bound field, changed repository configuration,
    or changed trusted policy authority. Each ends in newline. The
    field-change template is The {field} changed after the review (reviewed {old}, now {new}). Run the review script again. The literal prefixes and full
    caller streams are captured as stamp-draft, stamp-field,
    stamp-config and stamp-policy below. Unrelated trusted-ref movement
    with unchanged policy content/status/authority no longer refuses.
    Repository-config comparison applies only in unattended mode. Failures reading the stamp
    or its digest keep the underlying stderr before D13tail and exit 1.

    For external reader/parser errors, E means the exact captured stderr,
    not a replacement message. Exit 2 above is the concrete external
    failure fixture, and 128 is the Git failure fixture. Other subprocess
    statuses propagate numerically unchanged. Parser status is jq's 5,
    with its actual diagnostic and any partial stdout retained. An
    arbitrary external program can emit arbitrary bytes, so this family
    is parameterized by its captured bytes and numeric status. The h00
    record gives a concrete complete parser example without that freedom.

    RL is the complete stderr in rate-secondary, rate-graphql or
    rate-rest below, including the original fixture error. All statuses
    are 76; P stdout is Pgh and S stdout is explicitly empty. For U/G,
    append the trap report to stdout. Later failing reads use their prefix
    at that read.

    U/G-specific earlier helpers follow. These modes execute ma_context.
    P and explicitly confirmed S skip it and instead take their normal
    report/merge paths, unless a common earlier gate above already stops.
    For U, ma_gates runs before the check section. For G, ma_gates first
    runs AFTER the check section and lint, at squash line 244. Don't move
    its refusal earlier while replacing only the judge.

    Context stop U status/stdout G status/stdout stderr
    operation marker 1/report(unknown,1) 1/report(7,1) C1
    detached HEAD 1/report(unknown,1) 1/report(7,1) C2
    unborn HEAD 1/report(unknown,1) 1/report(7,1) C3
    dirty worktree 1/report(unknown,1) 1/report(7,1) C4
    no configured login 1/report(unknown,1) 1/report(7,1) C5
    missing uv 1/report(unknown,1) 1/report(7,1) C6
    absent agentrepo 1/report(unknown,1) 1/report(7,1) C7
    unusable agentrepo 1/report(unknown,1) 1/report(7,1) C8
    unattended --confirmed U normal 1/report(7,1) C9

    The --confirmed flag belongs to squash. The paired U invocation is
    ordinary preflight, which reports and exits 0 with its normal stdout
    and empty stderr when its gates pass. All other context stops have
    reached=no for U/G. Plain G without the
    unattended flag has empty stdout instead of report(...). Underlying
    Git/config read failures propagate their exit status and stderr,
    with the same empty prefix and trap rule. C6 includes Bash's
    command -v uv output only if that builtin emits one in the selected
    shell. Bash in this environment emits none for the lookup.

    C1 = ('HELD: MERGE_HEAD is in progress; finish or abort it '
          'first.\n')
    # Substitute CHERRY_PICK_HEAD, REVERT_HEAD, rebase-merge,
    # or rebase-apply for the other marker cases.
    C2 = 'HELD: HEAD is detached.\n'
    C3 = ('Create the initial commit through the interactive '
          'commit skill first.\n')
    C4 = ('Commit the work first; unattended merging requires '
          'a clean tree.\n')
    C5 = 'gh_login must be configured on the machine\n'
    C6 = 'uv is required for the YAML policy reader\n'
    C7 = 'agentrepo is required for check judgement\n'
    C8 = 'a working agentrepo is required for check judgement\n'
    C9 = ('squash-merge: Unattended workflows refuse --confirmed; '
          'use pre-approval or delegation.\n')

    agent-merge-pr/shared/scripts/merge.sh sets unattended mode and
    forwards to squash. With two numeric arguments it converts the second
    into --expected-head. Its --confirmed refusal therefore comes from
    the shared squash caller after ma_context, not from the wrapper.

    Before U/G reach their initial response parsing, mp_view also runs
    ma_reviews. A malformed raw response stops at its mp_field with
    status 5, stdout Ptarget for U or Ss for G, and the exact jq stderr.
    A failed review-pages read propagates its numeric status and stderr.
    A mismatched review-page head stops with status 5 and the jq error
    PR head changed while reading review authors; the check section
    isn't reached. P/S don't take this extra reader and continue normally
    on a valid snapshot with the same paginated-review fault.

    U's pre-check gates have the following first-stop catalogue.
    For these isolated faults, P and explicitly confirmed S continue with
    P(0,OK) and S(0): status 0, their full defined stdout and explicitly
    empty stderr. U stops before checks; G stops after checks and lint.
    The supplement's gate function gives exact full U/G tuples.
    Ordinary failures have no earlier gate stdout. HISTORICAL readiness
    failures add exactly {"outcome":0}\n; owner overrides add their
    recorded line before that JSON. CHOSEN FUTURE readiness failures add
    R(0) from AC6 instead. Captured rows below specify historical streams.

    First U gate stop Status Exact diagnostic/recording
    PR state/draft/readiness 1 A1
    actual login mismatch 1 A2
    reviewer login [bot] suffix 1 A3
    no required reviewer 1 A4
    invalid reviewer type 1 A5
    exact-head approval absent 1 A6
    empty owner-review label 1 A7
    owner-review label present 1 A8
    pinned diff acquisition 1 A9
    policy inspection/evaluation 1 A10
    owner approval absent 1 A11
    requirements unavailable 1 A12
    evaluator non-success 1..6 AC6/AC8 JSON and hint
    readiness not executable 1 A13
    readiness exits nonzero 2 exact E fixture

    A ma_gates review refusal is status 1. It isn't the status-75
    mp_require_reviews refusal. That distinction stays unchanged.
    All ma_context checks run again inside ma_gates before this list.

    A1 = 'PR is not open, ready, MERGEABLE, and CLEAN\n'
    A2 = 'gh is signed in as other, not agent\n'
    A3 = ('required_reviewer must use the GraphQL login reviewer, '
          'without [bot]\n')
    A4 = 'required_reviewer must be configured on the machine\n'
    A5 = 'required_reviewer_type must be Bot or User when set\n'
    A6 = 'WAITING_REVIEW: reviewer must approve the current head\n'
    A7 = 'owner_review_label must not be empty\n'
    A8 = 'needs-owner: the PR has the owner-review label\n'
    A9 = 'Could not fetch the pinned base and head.\n'
    A11 = ('needs-owner: golden files or loosened gates require '
           'merge_owner approval on the current head\n')
    A12 = 'No required checks are known for the base\n'
    A13 = 'merge_ready_cmd must name an executable\n'

    A9's five variants are diff-url, diff-history, diff-fetch,
    diff-diff and diff-names below, including the fixture Git stderr.
    A10's concrete validation cases cover empty/invalid patterns,
    repository paths, APM lock scalars, reviews, findings and applied rules.
    policy-required distinguishes the reader's missing-requirements
    message from A12's later empty-output refusal. policy-process
    records process exit 2 and E. owner-deny includes the JSON finding
    before A11; owner-override includes the finding on stderr and
    override on stdout. No raw policy value enters the judge diagnostic.

    On the fresh head ma_gates uses agentrepo merge approval.
    No qualifying approval prints nothing before A6/A11.
    approval-invalid records the missing-reviews command diagnostic
    before A6; the same command error precedes A11 for the owner check.
    Caller status remains 1, U before checks and G after checks/lint.

    The malformed/required-review recordings and source trace establish
    that preflight's CHECK SECTION reports failing and running checks
    without refusing. They do not assert that preflight never refuses.
    The existing review regression is at
    tests/skills/merge-pr/scripts/merge-workflow.bats:385.

    Exact caller supplements

    These recordings execute fresh-head preflight and squash caller bodies with
    their real stamp, rate classifier, diff and gate helpers. Prerequisite
    readers pass; external failures and merge writes are stubbed, and filesystem
    inputs use memory descriptors. Passing squash stops at the write boundary.
    The real Python policy validator and exception handler supply validation
    diagnostics; the lock-scalar case isolates that reader.
    fixture failure\n is the injected process stderr, with explicitly empty
    process stdout and exit 2. The snapshot has title test, author author,
    URL https://example.invalid/pr/7, empty commits/files/body,
    one successful ci / test CheckRun, and the values shown in stdout.

    Decode this zlib/base64 block as ac7_b64 with the Python below. Rows map each mode to
    [process_status, stdout_index, stderr_index], indexing streams.
    These are HISTORICAL current-shell recordings, with the descriptor
    label normalized to fd 3; "" is explicitly empty.
    Every nonzero row refuses. U rows have reached=no; gate G rows have
    reached=yes. Stamp/rate/review-page rows have reached=no.
    For stamp faults P/U pass; S uses G's tuple without its trap report,
    except stamp-config, which passes in S. For gate/policy/diff faults
    P/S pass as defined above. Plain G removes the trap report only.
    CHOSEN FUTURE stops and mode differences stay the same; evaluator
    stdout gains the AC6 schema-1 fields. The reconstruction below defines
    the future streams separately from the historical recordings.

    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
    bv4GBqmEJw==
    
    import base64, hashlib, json, zlib
    ac7_raw = zlib.decompress(base64.b64decode(ac7_b64))
    assert hashlib.sha256(ac7_raw).hexdigest() == (
        'cdd12dd359577feda3c694c6009995034f668968d9e3508eacd6d35de2550d26')
    ac7 = json.loads(ac7_raw)
    def captured(case, mode):
        status, out, err = ac7['rows'][case][mode]
        return status, ac7['streams'][out], ac7['streams'][err]
    
    u_before = captured('diff-url', 'U')[1].removesuffix(report(7, 1))
    g_before = captured('diff-url', 'G')[1].removesuffix(report(7, 1))
    historical_eval_ok = '{"outcome":0}\n'
    eval_ok = R(0)  # CHOSEN FUTURE success bytes, including checks/superseded.
    finding = '["fixture.txt","changes a golden file"]\n'
    override = 'owner override: owner approved head\n'
    def gate(mode, status, stderr, evaluator='', owner=False):
        out = u_before if mode == 'U' else g_before
        if owner:
            out += override
        return status, out + evaluator + report(7, status), stderr

    A1–A8 and A12 use their identically named captured rows.
    A9 uses the five diff-* rows; A10 uses policy-*;
    A11 uses owner-deny; A13 uses ready-executable.
    Readiness exit 2 uses ready-status. owner-override follows
    an approved owner finding with readiness exit 2 to record both streams.
    CHOSEN FUTURE evaluator non-success uses
    gate(mode,n,hint,R(n)) for n=1..6, with AC8's preserved stderr hint;
    every byte of the complete AC6 result precedes the trap report.
    Success evaluation before readiness prints exactly eval_ok = R(0).
    Historical evaluator summaries, including AC8's, are not future results.
    For readiness recordings, reconstruct the chosen future tuples as:

    def future_captured(case, mode):
        status, out, err = captured(case, mode)
        if case in ('ready-executable', 'ready-status', 'owner-override'):
            assert out.count(historical_eval_ok) == 1
            out = out.replace(historical_eval_ok, eval_ok)
        return status, out, err

    All other captured gate/policy/diff streams, statuses and stderr stay
    unchanged. gate also reconstructs historical evaluator streams when
    passed their historical JSON explicitly.
    For reviews-head, jq's descriptor label is normalized to fd 3
    in both prose and decoded stderr; status is 5 for both callers and neither reaches
    checks. P/S continue on the same isolated paginated-review fault.
    Other external errors retain their captured numeric status and bytes.

    AC8: unattended fixtures and hints

    None of the existing check-bearing fixtures under
    tests/skills/agent-merge-pr/ changes its semantic outcome, numeric
    status or stderr hint under this contract. The schema/listing fields
    are additive stdout changes. The old summary fields keep their exact
    values. The evidence includes direct evaluator replays of every
    check-shape variant with required names and expected head supplied.
    These are evaluator replays, not a claim that the entire Bats suite
    ran through a future implementation.

    Relevant fixtures: workflow.bats:76,223,233,242,253,267,
    loosens.bats:28, confirmation.bats:33-34,161, and the base success
    fixture in test_helper.bash. The missing-requirements policy case
    stops before evaluation with A12, unchanged. Missing/unusable/pinned
    runner cases in workflow.bats:12-41 keep the AC7 boundaries.

    Variant Outcome/status stdout summary stderr hint
    empty empty/4 {"outcome":4} ""
    skipped/current failed/1 test/test H(test,no)
    skipped/lagging failed/1 test/test H(test,yes)
    skipped/unknown failed/1 test/test H(test,unknown)
    skipped/missing missing/3 test/validate H(validate,yes)
    required NEUTRAL failed/1 names=test ""
    updated SUCCESS green/0 {"outcome":0} ""
    def H(name, differs):
        return (f'skipped required check: {name}\n'
                f'merge base differs from base commit: {differs}\n'
                'run: gh pr update-branch 7\n')

    In the compact rows, test/test and test/validate mean
    names/skipped, each a single-name array.

    The full byte-exact old JSON and stderr are in m['unattended'].
    Add the new fields without removing those summaries. Keep exact
    required-name matching across every workflow. Required NEUTRAL and
    SKIPPED fail, optional NEUTRAL and SKIPPED pass. Keep missing-required
    precedence and the skipped hint. Head mismatch remains 6 and missing
    expected-head data remains unreadable/5, as recorded in m['hand'].

    Go unit fixtures reordered statuses and status rerun in
    internal/checks/checks_test.go deliberately change success/0 to
    failed/1. Unknown status/conclusion/context fixtures change failed/1
    to unreadable/5. New coverage must add superseded invalid states,
    lowercase fields, offset disagreements and command protocol faults.
    These tightenings apply to both callers of the shared evaluator.

    AC9: capability and release sequence

    No compatible release exists yet. The fresh main implementation,
    and the latest local tag v0.23.0, don't provide the proposed schema,
    complete listing or superseded count. Main also still implements the
    unsafe context/offset selection demonstrated above. Don't invent a
    minimum released version for capabilities that don't exist.

    Missing promised outputs: all-entry validation failures for unknown
    and lowercase fields, conservative dual-order selection, all status
    contexts, full workflow-qualified listing, superseded count, schema
    marker and consistent protocol-failure adaptation. #723 implements
    these evaluator and caller changes and documents the first release
    that contains them. #333 supplies binary distribution. #544 installs
    the compatible release before its dependent callers are deployed.

    With no agentrepo, P reports unreadable/0 and S refuses unreadable/1
    at the new adapter, while U/G retain their earlier C7 refusal/1.
    A release below v0.16.0 still fails the shared loader with D2/1.
    An older accepted release lacking schema 1 or its required fields
    cannot be green: P reports unreadable/0, S refuses unreadable/1.
    U/G must validate the same result protocol at their gate and refuse
    with unreadable/5. An unusable executable can stop earlier at D1/C8.
    Don't add a second shell judge as a fallback.

    AC10: measured time and proposed limit

    Generate 500 distinct successful CheckRuns, fixed head head,
    workflow
    ci, names test-000 through test-499, all started at
    2026-10-01T01:00:00Z. Save once. Build outside the timed region.
    Time each subprocess invocation through its completion, capturing
    both streams, for 20 runs. All runs exited zero. The evidence stores
    all 40 samples, in milliseconds.

    • Original shell median: 28.179033 ms; original Go median: 1.954330 ms.
    • Fresh-head shell median: 13.267504 ms; fresh-head Go median: 1.880917 ms.

    Commands: bash judge.sh 500.json (the unchanged sourced shell
    function) and agentrepo checks evaluate --file 500.json.
    The timer used Python time.perf_counter_ns(). These current-judge
    measurements exclude the not-yet-implemented schema validation and
    caller adapter. #723 must measure that complete boundary separately.
    Keep its proposed limit: median strictly below 1000 ms for 20 runs,
    including invocation, whole-response validation and caller verdict,
    with compilation excluded. Both existing medians are below the limit.

    AC11: acceptance answers and follow-up

    AC Answer
    AC1 Contract rows distinguish each input class and both callers.
    AC2 Saved rollups include both judges' exact outputs and statuses.
    AC3 All 549808 required field-product cases replayed; zero unsafe green results.
    AC4 Union of text and instant maxima preserves the worse verdict.
    AC5 Every entry is validated, including superseded unknown states.
    AC6 Process/result table defines both callers for all required command cases.
    AC7 Earlier-stop table and historical recordings give caller statuses and streams (descriptor label normalized); separate reconstruction defines future AC6 streams. Required review still stops with 75.
    AC8 Existing unattended fixtures and their outcomes and hints are listed.
    AC9 No compatible release yet; #723 implements and releases it, #333 distributes it, #544 installs it.
    AC10 Twenty-run measurements and the proposed limit are recorded.
    AC11 This table answers each criterion and identifies the implementation dependency.
    AC12 See the final AC12 section.

    The caller migration and protocol implementation belong to
    #723,
    distribution to #333,
    installation to #544,
    and replacement-path protection to
    #588.
    No additional follow-up issue is needed.

    Recorded evidence and decoder

    Save the following base64 block as evidence.b64. Decode it with the
    code below. The XZ payload starts with an unsigned little-endian
    8-byte metadata length, then UTF-8 JSON metadata, then four bytes per
    case: unsigned little-endian shell-dictionary index, one-byte
    Go-dictionary index, one-byte proposed evaluator outcome.
    Dictionary entries are [status, stdout, stderr].

    Uncompressed SHA-256:
    ee8e74ba494a5979c91eab8a563edf0c2ef758c2e1e0638fccd4891f70e5aa72.
    The matrix SHA-256 is in m['summary']['matrix_sha256'].

    import base64, hashlib, itertools, json, lzma, struct
    from pathlib import Path
    raw = lzma.decompress(base64.b64decode(
        Path('evidence.b64').read_text()))
    size, = struct.unpack_from('<Q', raw)
    m = json.loads(raw[8:8 + size])
    matrix = raw[8 + size:]
    assert len(matrix) == 549808 * 4
    assert hashlib.sha256(matrix).hexdigest() == (
        m['summary']['matrix_sha256'])
    Path('metadata.json').write_text(json.dumps(m, indent=2))
    d = m['manifest']
    index = 0
    seen = set()
    with Path('records.ndjson').open('w') as output:
        for kind in ('CheckRun', 'StatusContext'):
            entries = []
            if kind == 'CheckRun':
                for s, c in itertools.product(d['status'],
                                              d['conclusion']):
                    e = dict(__typename=kind,
                             workflowName='ci', name='test')
                    for k, v in [('status', s), ('conclusion', c)]:
                        if v is not None:
                            e[k] = v
                    entries.append(e)
                field = 'startedAt'
            else:
                for s in d['state']:
                    e = dict(__typename=kind, context='test')
                    if s is not None:
                        e['state'] = s
                    entries.append(e)
                field = 'createdAt'
            axes = (enumerate(entries), enumerate(entries),
                    enumerate(d['times']))
            for (i, a), (j, b), (k, (x, y)) in (
                    itertools.product(*axes)):
                key = (kind, i, j, k)
                assert key not in seen
                seen.add(key)
                rollup = dict(headRefOid='head', statusCheckRollup=[
                    dict(a, **{field: x}), dict(b, **{field: y})])
                si, gi, chosen = struct.unpack_from(
                    '<HBB', matrix, index * 4)
                record = dict(key=key, input=rollup,
                              shell=m['dictionary'][0][si],
                              go=m['dictionary'][1][gi],
                              chosen=chosen)
                output.write(json.dumps(record) + '\n')
                index += 1
    assert index == len(seen) == 549808

    The chosen outcomes are a research model. They aren't represented as
    implemented behavior. Replaying the recorded current outcomes uses
    the cited head's shell function and built command. The Go package and
    command tests passed with go test ./internal/checks ./internal/agentrepo.
    The future implementation must pass the model and caller contract.

    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
    

    AC12

    Scans and leak review on the text above, before this section was added: gitleaks 8.30.1 (detect --no-git), exit 0, 0 hits; the devtools leak checker with the private-strings lists loaded, exit 0, 0 hits. Independent reviews: three rounds; the third found no blockers and a clean full-text leak review. The text above sha256 4875c68c0af60640b8f0fd23498acfebaa4462ec2ef8643af0f26d16c52a144e.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    refinement:readyPassed the Definition of Ready; may be picked up

    Type

    Fields

    Priority

    Standard

    Work type

    Planned

    Projects

    • Status
      Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions