Repository navigation
fix(pipeline): clean foreign-owner PR bodies and refuse PRs approved with intent-conformance findings - #4
Merged
Merged
Conversation
… unresolved intent mismatch When the PR targets a repository whose owner differs from the fork that was pushed, the generated body now omits the ## Intent section and every local-file evidence reference the maintainer cannot open. Inline evidence text, links, and attachments stay; same-owner bodies are unchanged. Review now tags intent-contradiction findings with the structured category intent-conformance, and the PR step refuses to create or update the pull request while a completed review still holds one (it was approved, not fixed). The branch stays pushed.
…a in foreign-owner tests
dmealing
added a commit
that referenced
this pull request
Oct 11, 2026
… 1.93 (#6) * feat(ci): local CI mode for the local fork build (#1) * feat(ci): add local CI mode for the local fork build ci_mode (global-only, default local) skips the CI step on every run, enforced by the daemon at run creation and recovery and by every run-launch request so it holds against an older running daemon. Status and axi output explain the skip via run.local_ci. The build stamps v1.72.0-local.1, refuses self-update, and suppresses the update banner. * refactor(ci): simplify local CI mode skip plumbing Route fresh and recovered skips through one executor helper, share the update-silence predicate, and pin the github-mode e2e journey to ci_mode: github. * fix: regenerate skill and document local fork build * feat(pipeline): let local CI mode own broad regression in Test (#2) * feat(pipeline): let local CI mode own broad regression in Test The Test step's prompts told every agent that remote CI owns broad regression and remains mandatory before a PR is ready. Under `ci_mode: local` no forge checks run for the change at all, so for a repository with no `commands.test` that sentence sent the broad regression pass nowhere: the evidence agent is the whole Test step, and it was instructed to defer to a gate that never runs. `testRegressionScope` now resolves the wording from the run's effective `config.CIMode`. In `github` mode every bullet is byte-identical to before. In `local` mode the evidence turn runs the repository's complete regression suite after the targeted scenarios and reports a suite failure as a finding, and no agent-facing text claims remote CI is mandatory or will catch anything. Fix rounds stay focused in both modes, because the evidence turn always follows a fix round inside the same step execution, so the suite is paid once per Test step rather than once per repair round. Lint is deliberately left alone. Its changed-file narrowing is a scoping preference, not a deferral: it never claims another gate will catch what it skips, and widening it would surface pre-existing debt in untouched files as churn on every run. Repositories that want a deterministic repo-wide static gate set `commands.lint`, which is unchanged. * no-mistakes(review): Sync stale broad-regression doctrine docs to ci_mode * feat(test): test.live_evidence=false skips the evidence agent on a passing baseline A repository whose commands.test is its whole regression and integration suite gets no value from the live-evidence agent once that suite passes, and pays for it in wall clock: on one repository the agent was ~19 of the test step's ~27 minutes. test.live_evidence: false skips the agent only when commands.test ran and passed; a failing baseline still gets the agent to diagnose it. Trusted-only, like test.instructions: a pushed branch cannot switch off validation of itself. The resolved field is SkipLiveEvidence so a zero-value config keeps the agent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Kbw3LMGDkS8qHefoc7uGE * fix(pipeline): clean foreign-owner PR bodies and refuse PRs approved with intent-conformance findings (#4) * fix(pipeline): keep foreign-owner PR bodies clean and refuse PRs with unresolved intent mismatch When the PR targets a repository whose owner differs from the fork that was pushed, the generated body now omits the ## Intent section and every local-file evidence reference the maintainer cannot open. Inline evidence text, links, and attachments stay; same-owner bodies are unchanged. Review now tags intent-contradiction findings with the structured category intent-conformance, and the PR step refuses to create or update the pull request while a completed review still holds one (it was approved, not fixed). The branch stays pushed. * test(pipeline): share the PR drafting stub across foreign-owner tests * docs(agents): record foreign-owner PR body and intent-mismatch refusal owners * docs(skills): move PR body and intent-mismatch notes into owning skills * test(pipeline): reuse step-seeding helpers and parse the review schema in foreign-owner tests * feat(daemon): fail dead runs and reap worktree-anchored orphans (#3) A run could stay `running` forever once its executor could no longer make progress - a deleted worktree, a wedged agent whose exit was never observed, or a lost terminal write - indistinguishable from real work to anything polling `axi status` (observed as a 12+ hour poll loop against a run that had already merged). Separately, grandchildren that escape the per-command process group (a backgrounded dev server) both leaked and defeated `git worktree remove --force` by re-creating files mid-delete, so the process leak and the undeletable worktree were the same bug. - A 1-minute daemon watchdog declares an active run dead (terminal `failed`, error prefixed `dead run: `) when its worktree is gone, its recorded agent process died unobserved, a running/fixing non-CI step is silent past the new `step_stall_timeout` (default 1h, keyword-disable supported), or an active row has no live executor past a grace. Parked `awaiting_agent` runs and the CI monitor stay exempt by design. - Configured `commands.*` invocations heartbeat step activity while the daemon waits on them: their output is captured buffered, so a healthy long-running test suite would otherwise look dead and be stall-killed. - Rows inside startRun setup are tracked (`RunManager.settingUp`) so a long worktree checkout/fetch is never mistaken for an orphaned row. - `axi status` reports machine-readable `liveness: ok|stalled|dead` (plus `liveness_reason` carrying the recovery command) on non-terminal runs, covering the stopped-daemon case the watchdog cannot. - Run worktree removal goes through `removeRunWorktree`, which reaps processes anchored to the worktree (cwd via /proc, worktree path in argv via ps) before removal; Windows reaping is a documented no-op. - The user-level skill now owns background-watcher discipline (finite watchers, run-id anchoring via `axi status --run <id>`, acting on `liveness`) alongside the new polling guidance. Claude-Session: https://claude.ai/code/session_014VNhEaL6dpACh5TqrN8M6D Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * fix(agent): report claude token usage and cost from the result event (#5) * fix(agent): take claude token usage from the result event Per-message usage on assistant stream-json events is partial and repeats across events of one message, so summing it undercounted output and double-counted cache reads. Use the final result event's usage; keep the assistant sum only as a fallback when the result carries no usage. * no-mistakes(review): Capture claude result costUSD; fall back on zero usage * test: align fork tests with v1.93 usage and PR summary signatures * test: adapt fork tests to v1.93 gate-path and CI-mode defaults * fix: run e2e harness in github CI mode and leave Intent publication to pr.publish_intent * chore: stamp local build version v1.93.0-local.1 * no-mistakes(review): Fix watchdog worktree path, command heartbeat, foreign-owner Intent * no-mistakes(review): Heartbeat base-attribution commands; clarify foreign-owner Intent docs * no-mistakes(document): Document foreign-owner Intent rule and test.live_evidence --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Make the gate stop writing bad pull request descriptions for repositories we don't own.
Context: the gate pasted a stale task intent verbatim into kunchenguid/firstmate#6488's description, plus a "🚨 High ... needs maintainer decision" risk line and an unresolved review error, and still opened the PR. kunchenguid/axi#230's description got internal history, a stray directive line and local evidence file paths (
~/.no-mistakes/evidence/...) the maintainer cannot open.What Changed
isForeignOwnerPR) — now omit the## Intentsection (prBodyIntent, shared by both intent paths) and every local-file evidence reference the maintainer cannot open (testingSummaryOptions.omitLocalPaths); an artifact with only a local path is dropped, while inline evidence text, links, and attachments stay. Same-owner PR bodies are unchanged.category: intent-conformanceon contradiction findings (added to the review findings schema andtypes.FindingCategoryIntentConformance), and the PR step fails instead of opening or updating the PR while the completed review still holds one (refuseUnresolvedIntentConformance) — the branch stays pushed.docs/src/content/docs/reference/pipeline-steps.md, moved the PR-body and intent-mismatch notes into the owning skill files, and added regressions ininternal/pipeline/steps/pr_upstream_test.go.Validation Notes
make lint: generated-skill drift check plusgo vet ./..., clean on the PR head). The gate's lint step could not run because the gate service's Go toolchain is too old to parsego 1.25.0ingo.mod.TestRebaseStep_NonConflictFailureWithRebaseMetadataReturnsErrorandTestRebaseStep_FixModeNonConflictFailureReturnsErrorfail onorigin/localtoo and are not caused by this change (it touches no rebase code). They fail on hosts whose global git config setsrebase.autostash=true, because the tests do not isolate git config; they pass with that setting neutralized.Risk Assessment
✅ Low: Well-bounded change: deterministic omissions sit at single choke points with byte-identical same-owner behavior pinned by tests, the PR refusal is fail-closed, keyed on a structured category that survives the findings wire, and verified against both normal and daemon-recovery approval paths; the only finding is an adjacent pre-existing link gap noted as informational.
Testing
No scenario was driven against the live running product, so all six are untested. What was done is unit-level: the branch's scenario tests and temporary evidence probes called the real buildPRContent/Execute code with a real git repo and run DB, but the forge boundary was the repo's fake gh, so every PR body, diff, refusal message, and 'pr create' call exists only as rendered output or a recorded stub invocation. The full
go test -race ./...regression suite ran twice: under the real host environment it failed only TestRebaseStep_FixModeNonConflictFailureReturnsError and TestRebaseStep_NonConflictFailureWithRebaseMetadataReturnsError, which were proven environmental (host ~/.gitconfig sets rebase.autostash=true; both pass 3/3 with the config neutralized; the branch diff touches no rebase files); the second run's only failure was TestCIStep_CommitAndPush_GitCommandsUseStandardCredentialEnv, an artifact of the synthetic GIT_CONFIG_GLOBAL, and it passes under the real environment of run 1. No LLM API calls were made. A live drive of any scenario requires a full gate run whose agent steps bill metered LLM spend (needing Doug's pre-approval with a dollar figure) plus an authorized publish to a real repository; neither was authorized, so none was attempted.Evidence: Foreign-owner PR body (rendered in-process by PRStep code)
Source: Foreign-owner PR body (rendered in-process by PRStep code) (local file:
~/.no-mistakes/evidence/01M416CSGV9W8P8HPQDCFPZM5Y/pr-body-foreign-owner.md)Evidence: Diff: what a foreign maintainer no longer receives
# Lines the same-owner body carries that the foreign-owner body drops - stale task intent the maintainer never saw - ## Intent - - Evidence: Local only log (local file: <code>~/.no-mistakes/evidence/.../missing.bin</code>)Evidence: Same-owner PR body (unchanged behavior)
Source: Same-owner PR body (unchanged behavior) (local file:
~/.no-mistakes/evidence/01M416CSGV9W8P8HPQDCFPZM5Y/pr-body-same-owner.md)Evidence: Refusal message: unresolved intent-conformance finding
refusing to open or update the pull request: review was approved with unresolved intent-conformance finding(s) review-1 - the change does not match the run's intent; fix the change or rerun with a corrected --intent (the branch stays pushed)Evidence: Refusal message: unreadable review findings (fail closed)
read review findings before opening a pull request: invalid character 'n' looking for beginning of object key stringEvidence: Scenario test run (go test -race -v)
--- PASS: TestPRBody_ForeignOwnerOmitsIntentAndLocalPathEvidence, TestPRBody_SameOwnerKeepsIntentAndLocalPathEvidence, TestPRStep_RefusesWhileApprovedIntentConformanceFindingIsUnresolved, TestPRStep_ProceedsWhenReviewHoldsNoIntentConformanceFinding, TestReviewIntentConformanceCategoryIsRequestedAndAcceptedEvidence: Full regression suite, real host env
exit=1; only failures: TestRebaseStep_NonConflictFailureWithRebaseMetadataReturnsError, TestRebaseStep_FixModeNonConflictFailureReturnsError (host rebase.autostash=true)Evidence: Full regression suite, neutral git config
Source: Full regression suite, neutral git config (local file:
~/.no-mistakes/evidence/01M416CSGV9W8P8HPQDCFPZM5Y/full-suite-race-neutral-config.log)Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
internal/pipeline/steps/prsummary.go:450- Pre-existing, adjacent to the new foreign-owner rule: repo-relative artifact "Evidence" links are built from the parent repository URL at the run head SHA (pr.go:412 passes sctx.Repo.UpstreamURL and sctx.Run.HeadSHA into testingSummaryOptionsForGitHub), but on a fork-routed run that SHA is pushed only to the fork, so a maintainer clicking such a link on the foreign-owner PR gets a 404 until merge (and forever for untracked worktree files). The change deliberately keeps "links" for foreign owners while dropping local-path references, and this is the one kept link class that can still be unopenable. Not introduced by this diff; a remedy (build the blob base from the fork URL when fork-routed, mirroring evidence_publish.go:54-57 which already does exactly that for published evidence) extends beyond this change's stated intent.internal/pipeline/steps/rebase_test.go:369- Both rebase non-conflict-failure tests fail on any host whose global git config sets rebase.autostash=true (this machine's ~/.gitconfig does): the dirty-tree rebase auto-stashes and succeeds, so the expected error never happens. Proven environmental, not caused by this branch: the branch diff touches no rebase files, and both tests pass 3/3 once GIT_CONFIG_GLOBAL is neutralized (and again under host-config-minus-autostash). Upstream CI passes because GitHub runners use git defaults. Fix: give the rebase test setup an isolated git config (temp HOME or empty GIT_CONFIG_GLOBAL) the way ci_commit_test.go already seeds a temp HOME with its own .gitconfig.go test -race ./internal/pipeline/steps -run 'TestPRBody_ForeignOwnerOmitsIntentAndLocalPathEvidence|TestPRBody_SameOwnerKeepsIntentAndLocalPathEvidence|TestPRStep_RefusesWhileApprovedIntentConformanceFindingIsUnresolved|TestPRStep_ProceedsWhenReviewHoldsNoIntentConformanceFinding|TestReviewIntentConformanceCategoryIsRequestedAndAccepted' -v -count=1 — all passtemporary evidence probes TestProbeForeignOwnerBodies and TestProbeRefusalMessages (real buildPRContent/Execute renders written to the evidence dir; probe file removed afterwards, worktree clean at bc5251d)go test -race ./... — complete regression suite, run 1 under the real host environmentgo test -race ./... — complete regression suite, run 2 under neutralized global git config (GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM)targeted reruns of the three git-config-sensitive tests under host-config-minus-autostash: rebase pair 3/3 pass, credential-env test passes under the real host env of run 1✅ **Document** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.