Skip to content

CRW-582: stop an interrupted receipt test at once and write no receipt - #548

Merged
thisisjun786 merged 9 commits into
devfrom
codex/crw-582-receipt-sigint
Oct 5, 2026
Merged

thisisjun786 merged 9 commits into
devfrom
codex/crw-582-receipt-sigint

Conversation

@thisisjun786

@thisisjun786 thisisjun786 commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Change

The first SIGINT sent to the crw process during crw pabcd receipt test --session s1 -- <command> was swallowed: serve (cmd/crw/main.go:68-73) turned it into a cancelled context, but the pabcd row (cmd/crw/main.go:181) dropped that context and the receipt row passed no Context into the CLI (internal/harness/pabcd_cli.go:29-45), so the check command ran to completion and a success receipt was published. An interrupted check could therefore produce the evidence its own gate reads.

This change carries the invocation context through the pabcd dispatch into the receipt row, so the first SIGINT reaches the check command at once.

Acceptance example: with a session at phase C and a check binding, crw pabcd receipt test --session s1 -- /bin/sh -c ': > "$1"; exec sleep 30' sh <marker> used to ignore a SIGINT to the crw pid, keep waiting for the command and (after it ended) exit 0 having written .crw/evidence/s1/test-receipt.json. After this change the same SIGINT ends the run with exit code 1, prints receipt test: the command did not run to completion (terminated by signal); no receipt written, and leaves no receipt.

Files

File Change
internal/harness/pabcd.go Verb gains an optional RunContext func(ctx context.Context, ...); the former Pabcd body becomes PabcdContext(ctx, ...), which passes ctx to a row's RunContext when it is set and calls Run otherwise; Pabcd keeps its signature and calls PabcdContext(context.Background(), ...); the row match keeps a row with neither function an invalid choice
internal/harness/pabcd_cli.go the receipt row becomes {Name: "receipt", RunContext: receiptVerb}; receiptVerb(ctx, ...) passes cli.ReceiptRunOptions{Context: ctx, ...}; no other row changes
cmd/crw/main.go the pabcd row alone calls harness.PabcdContext(c.ctx, ...); serve, releaseAfterFirst and every other mode are unchanged
internal/harness/hook_test.go disclosed compile fix: TestPabcdVerbs built []Verb with positional literals, which the new field breaks; they become named-field literals with unchanged semantics (the nil row is still reported as an invalid choice, exit 2)
cmd/crw/pabcd_receipt_signal_test.go (new) real-binary test: temporary git work tree with one committed file, s1 state at phase C with a check binding, HOME/CODEX_HOME/CRW_HOME in temp dirs, one SIGINT to the crw pid, exit 1 within 10 s, both refusal strings on stdout, no receipt, and the child's home roots unchanged
contract/notes/cxc/CRW-582.json (new) replayer status file: no fixture newly passes, so the claim set is empty
docs/port-cxc/known-defects.md one appended section (parity note below)

Oracle parity

Ported from CXC v0.2.40 (3c1459acadeb1906d97c00a598e1457327ae372d), plugins/codexclaw/components/pabcd-state/src/receipt-cli.ts:75-185 for the verb and :120-137 for the run. The oracle runs the command through spawnSync, which blocks until the child exits and defers the signal: the recorder confirmed the Node process died by the deferred SIGINT (rc -2) only after the child had completed, and wrote no receipt. This port stops the child at once instead - intentionally-changed timing with the same observable outcome (no receipt, non-zero exit), recorded in the appended known-defects section.

Verification

  • Red first: the test-only commit 561c9d107 on the untouched base 13c9f41e fails with the command was still running 10 s after the SIGINT (10.83 s, go test exit 1; output saved with the task's evidence, no private path here).
  • Green: --- PASS: TestPabcdReceiptTestStopsOnFirstInterrupt (0.66s).
  • Local checks on this head (2e826b135): go test -count=1 ./cmd/crw/ ./internal/harness/ ./internal/pabcd/cli/ exit 0; make lint exit 0; go vet ./... exit 0; GOOS=darwin go vet ./... exit 0; CGO_ENABLED=0 go build ./... exit 0; BLOB_RANGE_BASE=origin/dev go run -tags dev ./cmd/crw-dev ci validate exit 0; crw-dev ci contracts exit 0; crw-dev ci plugin exit 0 with digest 0236789ec4aa1c59 (unchanged from the base, so no activation-surface file changed); go test -count=1 ./internal/contracttest/ -run TestDomain exit 0 (570 cxc subtests pass).
  • The check receipt for the C-to-D binding records exitCode 0 for the focused test command on this head.

Corpus fixtures for this unit

Fixture Entry point Green with this PR alone?
cli-help__receipt__dash_h, cli-help__receipt__dashdash_help, cli-help__receipt__test_dashdash_help, cli-help__receipt__word_help crw pabcd receipt --help (the row table) already claimed identical by the receipt-wiring issue; unchanged here
cli__receipt__not_at_phase_c_refused_plain crw pabcd receipt test guard path already claimed identical by the receipt-wiring issue; unchanged here
cli__receipt__failing_command_recorded crw pabcd orchestrate ... then crw pabcd receipt test No - it needs the orchestrate verb, which is not ported; stays pending
cli__evidence__resolve_with_receipt the evidence verb pending; not driven by this change

No corpus fixture exercises the SIGINT path (the oracle defers the signal), so this PR claims no fixture green; contract/notes/cxc/CRW-582.json registers an empty claim set.

Size

214 changed lines (202 insertions, 12 deletions) across 7 files; no generated data, fixtures or goldens.

Review findings (Devin and Codex, one run each on head 2e826b1)

Both reviews finished; three findings arrived, all anchored at internal/harness/pabcd_cli.go:41. The two blocking ones (marked below as not fixed here) were closed in the generation-2 correction section further down. Each got a code-grounded reply and is resolved on its thread; none is a security finding.

Finding Grade Disposition
Late interrupts still publish check receipts (Devin, kind bug) red Accurate residual deviation: RunReceiptCLI (internal/pabcd/cli/receipt.go:172-210) consults the context only through the command it starts, so a SIGINT that lands after the command exits - during the second capture - still publishes. Not fixed here: that file is outside this issue's edit regions, and a row-level delete-after-publish would be a racy workaround. Recorded as a residual port deviation in docs/port-cxc/known-defects.md and proposed as the first follow-up: recheck the context after the command returns and before publishing, refuse on cancellation, and add a deterministic seam so the window is testable.
Refuse receipt publication after late cancellation (Codex) P1 Same disposition as above. The hazard this issue exists for - an unfinished check manufacturing evidence - is closed on this head: a SIGINT while the check runs kills it at once and refuses certification.
Interrupted checks leave descendants running (Devin, kind bug) red Parity: ReceiptRunOptions documents that cancellation kills only the process this call started, and the oracle is weaker (its deferred signal ends Node, not the check, which runs to completion). Killing the process group is a library design change outside this issue's edit regions; listed as a follow-up proposal with its own test.

The docs-only commit after that head (the residual-deviation line above) does not touch the code the reviews read; no second review was requested.

Generation 2 correction (relay revision request del-2bea97319ae1-a1)

Head 6401c9264 (commits fa0cc18ea seam and failing test, ecd7c8238 the check, 6401c9264 the docs) closes the two blocking findings above:

  • internal/pabcd/cli/receipt.go: RunReceiptCLI calls the unexported receiptLateCancelHook (no initializer, nil in production, no package-level work at start) and then checks the caller's context at the last moment publication can still be skipped - after the command has returned and after the second source capture, immediately before the receipt directory is created - refusing with receipt test: the command did not run to completion (interrupted); no receipt written, exit code 1, nothing written.
  • internal/pabcd/cli/receipt_late_cancel_test.go (new): cancels the context through that seam after the command exits and before publication, asserting the exact text, code 1 and the absence of the receipt; red by assertion at fa0cc18ea (the run published and returned 0), green at ecd7c8238.
  • docs/port-cxc/known-defects.md: the late-interrupt line now reads port: fixed with that mechanism; the descendant case is recorded as port: kept (parity: the oracle never kills the check, and ReceiptRunOptions documents that cancellation kills only the process the call started).
  • Verify: go test -count=1 ./internal/pabcd/cli ./internal/harness ./cmd/crw exit 0; make lint, go vet ./..., CGO_ENABLED=0 go build ./..., file-scoped gofmt -l and git diff --check origin/dev...HEAD exit 0; crw-dev ci validate|contracts|plugin exit 0 (plugin digest unchanged); CRW CI run 37251599680 on this head - all 10 jobs success. Both late-interrupt threads are replied with the fix commit; no second review was requested.

Generation 3 base refresh

dev's recall-clock-seam change (PR #556) touched the recall row of cmd/crw/main.go next to the pabcd row, so the pull request conflicted in code. The refresh merged origin/dev once - merge commit b1c546c99d15fdfa3f1d6fc7f1e238b7d9921d2b, parents 6401c9264 and b32b55ea - and resolved exactly: cmd/crw/main.go keeps dev's recall row and then the three-line harness.PabcdContext pabcd row with nothing else changed in the file; docs/port-cxc/known-defects.md keeps dev's sections first and the CRW-582 section last, nothing dropped. gofmt, go build, go vet and go test -count=1 ./internal/pabcd/cli ./internal/harness ./cmd/crw pass on temporary homes. With no conflict left the pull_request CI ran automatically: run 37254049625, all 10 jobs success on this head; no review was requested, as the one Devin and one Codex review already ran. dev has since advanced (PR #558) and the only conflict now is again the appended known-defects section, which the parent unions in its merge lane.

Risks and remaining work

  • SIGTERM and SIGHUP still end the crw process without cancelling the child (pre-existing; this row only forwards SIGINT's cancellation). Out of scope here.
  • The compile-only edit to internal/harness/hook_test.go is disclosed above; its assertions are unchanged.
  • SIGINT now kills the check command with SIGKILL (exec.CommandContext's default), so a check cannot clean up - parity with the oracle is on the outcome (no receipt), not the mechanism.

Devin Review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-05T00:16:00.749375Z 2e826b1 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

Comment thread internal/harness/pabcd_cli.go
Comment thread internal/harness/pabcd_cli.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e826b135c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/harness/pabcd_cli.go
jun and others added 6 commits October 5, 2026 09:20
…sigint

# Conflicts:
#	cmd/crw/main.go
#	docs/port-cxc/known-defects.md
The coordinator refreshed the base. The only conflict was in docs/port-cxc/known-defects.md, where both sides
appended lines at the same place (1 block(s)); both sets are kept, dev's lines first, then this branch's.
@thisisjun786
thisisjun786 merged commit 3f711de into dev Oct 5, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant