Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a747f64
feat(autofix): audit the approach instead of stopping on growth-budge…
wenshao Aug 16, 2026
364d28d
fix(autofix): update the artifact-list pin for the growth-audit.json …
wenshao Aug 16, 2026
f7c3c66
Merge remote-tracking branch 'origin/main' into feat/autofix-growth-a…
wenshao Aug 16, 2026
ba4eb09
fix(autofix): surface conflict verdicts past the failure.md exits and…
wenshao Aug 16, 2026
b5e8ca6
fix(autofix): harden the growth-audit verdict pipeline and park wake …
wenshao Aug 16, 2026
7bb4d2f
fix(autofix): close the verdict-pipeline forgeries and loop-generated…
wenshao Aug 17, 2026
cfde2d9
Merge branch 'main' into feat/autofix-growth-audit
qwen-code-dev-bot Aug 17, 2026
4aaee34
Merge branch 'main' into feat/autofix-growth-audit
qwen-code-dev-bot Aug 17, 2026
5430b6e
Merge remote-tracking branch 'origin/main' into feat/autofix-growth-a…
wenshao Aug 18, 2026
df5a79e
Merge branch 'main' into feat/autofix-growth-audit
qwen-code-dev-bot Aug 18, 2026
361c745
Merge branch 'main' into feat/autofix-growth-audit
qwen-code-dev-bot Aug 18, 2026
a4c984e
Merge branch 'main' into feat/autofix-growth-audit
qwen-code-dev-bot Aug 18, 2026
4653f8c
Merge remote-tracking branch 'origin/main' into feat/autofix-growth-a…
wenshao Aug 19, 2026
2c73f3d
Merge branch 'main' into feat/autofix-growth-audit
qwen-code-dev-bot Aug 20, 2026
33168c6
Merge remote-tracking branch 'origin/main' into feat/autofix-growth-a…
wenshao Aug 20, 2026
9ae9e7c
fix(ci): drop the retired divergence rationale records (af-046/af-047…
wenshao Aug 20, 2026
c2ebf0e
Merge remote-tracking branch 'origin/main' into feat/autofix-growth-a…
wenshao Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 146 additions & 25 deletions .github/scripts/run-autofix-review-verification.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ git config --file "${GIT_CONFIG_GLOBAL}" safe.directory "$(pwd)"
if [ -s /etc/gitconfig ]; then
echo "::notice::/etc/gitconfig exists but is bypassed by the gate's GIT_CONFIG_SYSTEM redirect — replicate any setting the checks need via per-job env."
fi
# Two more inherited knobs steer EXECUTION itself, and neither has a
# legitimate setter: BASH_ENV names a file every non-interactive bash
# child sources at startup — a $GITHUB_ENV plant from an earlier step
# would run inside the trusted helpers this gate spawns, past the channel
# strip below (it covers their environment, not bash's startup file) —
# and BITE_RUNNER selects the bite check's runner command, which executes
# unwrapped with the gate's full environment. Strip them with the GIT_*
# class.
unset BASH_ENV BITE_RUNNER

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Deferred to the next round — not declined. Critical-only mode is active (the window's diff is over the growth budget), so this round's batch was limited to the two standing Criticals — R1-2 (verdict-pipeline forgery entrances) and R2-6 (loop-generated park wakes) — which this commit closes. This suggestion stays open and is picked up next round.

Note: this round replaced the startup-sourcing channel itself (step-level BASH_ENV/SHELLOPTS pins + the env -i clean child, pinned in the tests); the suggested behavioral probe that PLANTS a BASH_ENV against the wrapper remains open for next round.

中文说明

延迟到下一轮处理——并非拒绝。Critical-only 模式已生效(窗口 diff 已超增长预算),因此本轮批次仅限于两个仍然成立的 Critical——R1-2(verdict 流水线伪造入口)与 R2-6(循环自产的 park 唤醒)——本次提交已将它们关闭。本建议保持开放,下一轮处理。

说明:本轮直接替换了启动时 sourcing 通道本身(step 级 BASH_ENV/SHELLOPTS 固定 + env -i 干净子进程,已在测试中固定);建议中『对 wrapper 植入 BASH_ENV』的行为探针仍留待下一轮。


# Record whether the agent left a commit FIRST — this is a ref-only
# diff, so it runs before the failure.md early-exits and covers an
Expand All @@ -60,27 +69,6 @@ if [[ "${committed_rc}" -eq 1 ]]; then
echo "committed=true" >> "${GITHUB_OUTPUT}"
fi

if [[ -f "${WORKDIR}/failure.md" && -n "$(git status --porcelain)" ]]; then
echo "❌ Agent wrote failure.md after leaving a dirty workspace:"
git status --short
cat "${WORKDIR}/failure.md"
echo "outcome=failed" >> "${GITHUB_OUTPUT}"
exit 1
fi

if [[ -f "${WORKDIR}/failure.md" ]]; then
echo "🛑 Agent aborted intentionally:"
cat "${WORKDIR}/failure.md"
echo "outcome=failed" >> "${GITHUB_OUTPUT}"
exit 1
fi

# Convention: hooks are severed at EVERY host checkout of the PR
# branch (no secret sits in this step's env, but a post-checkout
# hook still runs branch code on the host).
git config core.hooksPath /dev/null
git checkout "${BRANCH}"

GATE_LOG="${WORKDIR}/gate-output.log"
: > "${GATE_LOG}"
rm -f "${GATE_LOG}.bite"
Expand All @@ -97,6 +85,9 @@ reject_fix() {
# step means the gate itself crashed, so losing the detail file must not turn
# a deterministic rejection into an infrastructure retry.
echo "outcome=failed" >> "${GITHUB_OUTPUT}"
if [[ "${AUDIT_VERDICT_RECORDED:-false}" == 'true' ]]; then
echo "audit_verdict=${AUDIT_VERDICT}" >> "${GITHUB_OUTPUT}"
fi
if [[ "${preexisting}" == 'true' ]]; then
# NOT retryable: the repair agent is only allowed to amend this round's
# fix, and a failure that exists without the fix is outside that boundary
Expand Down Expand Up @@ -129,6 +120,102 @@ reject_fix() {
echo "::warning::could not write the gate rejection detail; the verdict stands."
exit 1
}
# Last-writer binding for the audit verdict: the record below happens
# BEFORE the branch's build/tests run, and a check can still discover the
# step-output FILE through the inherited $RUNNER_TEMP (the strip removes
# the variable, not the backing file) and append its own audit_verdict —
Comment on lines +151 to +154

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Deferred per the maintainer's split decision (option A, 2026-08-18): this finding is tracked as a follow-up in #9374 rather than fixed in this PR, and the live escalation thread carries the open decision. Leaving this open until it lands there.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Escalated for a maintainer decision — no code change to this surface this round.

The finding is verified against the code at this commit (7bb4d2f):

  1. The conclusion gate reads FIRST_CONCLUSION: '${{ steps.verify.conclusion }}' / REPAIR_CONCLUSION: '${{ steps.verify_repair.conclusion }}' (yml:5934-5935) from steps that set continue-on-error: true (yml:5657/5863). GitHub Actions semantics: conclusion is the result AFTER continue-on-error is applied, outcome is the raw result — a killed or failing gate step concludes success. The discard (yml:5980-5990) requires PASS_CONCLUSION != 'success', so it can never fire for a killed/failed pass: exactly the shape it was added to catch. The seal's comment ("a step killed mid-check never concludes success") states a false property.
  2. The repair trigger consumes raw steps.verify.outputs.retryable (yml:5715); retryable has no every-exit re-append in the gate script.
  3. GIT_CONFIG_GLOBAL appears nowhere in the gate script; the gate's own git status/git diff calls (.sh:94, 236, 459-466, 480) re-read whatever config the round left live.
  4. The discard (yml:5986-5990) clears five fields and fires only on fixed|noop; verdict bits on any other outcome pass finalize's echo untouched.
  5. The runner-script digest check runs in the step shell BEFORE the env -i child launches (yml:5693), in reach of a startup-imported BASH_FUNC_sha256sum%%.
  6. RUNNER_TEMP is forwarded unpinned into the clean child (yml:5906).

Why escalation and not a patch: four rounds of entrance-by-entrance hardening on this surface have each found new entrances in the prior round's hardening — round 3's seal itself is the broken piece this round. The finding's conclusion that the surface is unbounded under per-entrance patching matches that trajectory, and the Critical-only growth brake is engaged (window diff net src 423 / test 880 vs 400/400 budgets). The fix directions carry different scope/risk trades that are the maintainer's call:

  • A. Split — land this PR's core (growth brake + park machinery); file follow-up issues for the structural verdict-pipeline redesign (outcome-based seal, digest inside the child, config lock, step-level pinning).
  • B. Redesign in place — authorize one focused redesign round on this PR; a larger diff, and the new machinery becomes probe surface again.
  • C. Accept with residuals — reprice design §D honestly (its "outcome flips are blocked by the conclusion gate" closure currently rests on the broken seal) and track the hardening as follow-ups.

Recommendation: A — the machinery handles the non-adversarial churn it was built for; the trust-domain question deserves its own design discussion rather than a round-5 patch on an over-budget diff.

Question for the maintainer: which direction — split (A), redesign in place (B), or accept with repriced documented residuals (C)?

中文说明

升级为 maintainer 决策——本轮不对该表面做任何代码修改。

该发现已在当前 commit(7bb4d2fde)的代码上核实:

  1. conclusion gate 读取 FIRST_CONCLUSION: '${{ steps.verify.conclusion }}' / REPAIR_CONCLUSION: '${{ steps.verify_repair.conclusion }}'(yml:5934-5935),而这两个步骤都设置了 continue-on-error: true(yml:5657/5863)。GitHub Actions 语义:conclusion 是应用 continue-on-error 之后的结果,outcome 才是原始结果——被杀死或失败的 gate 步骤 conclude 为 success。丢弃逻辑(yml:5980-5990)要求 PASS_CONCLUSION != 'success',因此对被杀/失败的 pass 永不触发——恰恰是它要拦截的形状。seal 注释(「检查途中被杀死的步骤不会 conclude success」)陈述了一个错误属性。
  2. repair 触发直接消费原始 steps.verify.outputs.retryable(yml:5715);gate 脚本中 retryable 没有每退出点重追加。
  3. GIT_CONFIG_GLOBAL 在 gate 脚本中完全不出现;gate 自己的 git status/git diff 调用(.sh:94、236、459-466、480)会重新读取该轮留下的任何存活配置。
  4. 丢弃逻辑(yml:5986-5990)清除五个字段且仅对 fixed|noop 触发;其他 outcome 上的 verdict 位会原样通过 finalize 的 echo。
  5. runner 脚本的 digest 检查在步骤 shell 中、env -i 子进程启动之前运行(yml:5693),处于启动期导入的 BASH_FUNC_sha256sum%% 的可达范围内。
  6. RUNNER_TEMP 未经 pin 地转发进清洁子进程(yml:5906)。

为什么升级而不是打补丁:该表面经过四轮逐入口加固,每一轮都在上一轮的加固上发现新入口——本轮被打破的正是 round 3 的 seal。「该表面无法逐个入口封堵」的发现结论与这一轨迹吻合,且 Critical-only 增长刹车已触发(窗口净增长 src 423 / test 880,预算 400/400)。各修复方向带有不同的范围/风险权衡,属于 maintainer 决策:

  • A. 拆分——本 PR 落地核心(增长刹车 + 停泊机制);结构性 verdict 管线重设计(基于 outcome 的 seal、digest 移入子进程内部、配置加锁、步骤级 pin)转为后续 issue。
  • B. 原地重设计——在本 PR 上授权一次聚焦的重设计轮次;diff 更大,且新机制本身会再次成为探针表面。
  • C. 接受残留——诚实重估设计 §D(其「outcome 翻转被 conclusion gate 阻断」的闭环目前恰好建立在这个损坏的 seal 之上),加固作为后续事项跟踪。

建议:A——现有机制能处理其设计目标内的非对抗性 churn;信任域问题值得一场独立的设计讨论,而不是在已超预算的 diff 上做第 5 轮补丁。

向 maintainer 提问:选择哪个方向——拆分(A)、原地重设计(B)、还是接受并重估文档化残留(C)?

# step outputs are last-write-wins. Every exit past the record therefore
# re-appends the validated verdict INLINE (no function call: gate snippets
# extracted by the contract suite must stay executable standalone), so
# the gate's copy outwrites any forged append. The flag gates it: a
# verdict rejected BEFORE its record (missing, malformed, or a routing
# violation) never surfaces.
# Growth-audit verdict gate: a round tagged KISS_AUDIT (its counting window
# is over the growth budget) must carry the audit's machine-readable verdict
# — the audit IS the round's judgment of the over-budget approach, and a
# round that skipped it must not push (the rubber-stamp hole by absence).
# Sits BEFORE the failure.md early-exits below: a conflict round stops
# BLOCKED via failure.md, and its verdict must be validated and surfaced to
# GITHUB_OUTPUT before that exit writes outcome=failed — otherwise the
# conflict trail marker never posts and the idempotent park never engages.
# Also before the build/schema/footprint checks AND the no-commit/no-op
# exits further down: the verdict is required even for a no-op audit round
# whose verdict is sound with nothing left to fix. Malformed is agent
# misbehavior, not a build problem — NON-retryable, so the repair pass is
# never invoked and the next scan simply re-runs the audit.
if [[ "${KISS_AUDIT:-false}" == 'true' ]]; then
AUDIT_VERDICT=''
Comment thread
wenshao marked this conversation as resolved.
Resolved
if [[ -f "${WORKDIR}/growth-audit.json" ]]; then
Comment on lines +184 to +185

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Deferred per the maintainer's split decision (option A, 2026-08-18): this finding is tracked as a follow-up in #9374 rather than fixed in this PR, and the live escalation thread carries the open decision. Leaving this open until it lands there.

# Slurp so the document COUNT is part of validation: the per-document
# parse accepted a valid first document followed by one jq errors on
# (or shape-filters out) on the FIRST document's verdict — the gate's
# contract is a single JSON document, so reject every multi-document
# stream.
AUDIT_VERDICT="$(jq -rs '
if length != 1 then empty else .[0]
| select((.verdict // "") | IN("sound", "drift", "conflict"))
| select((.kiss.result // "") | IN("pass", "fail"))
| select((.minimal_change.result // "") | IN("pass", "fail"))
| select((.verdict != "sound")
or ((.kiss.result == "pass") and (.minimal_change.result == "pass")))
| select((.verdict != "drift")
or ((.kiss.result == "fail") or (.minimal_change.result == "fail")))
| .verdict end' "${WORKDIR}/growth-audit.json" 2> /dev/null || true)"
fi
# Anchor the parsed value (defense in depth now that slurp rejects
# multi-document streams outright).
[[ "${AUDIT_VERDICT}" =~ ^(sound|drift|conflict)$ ]] || AUDIT_VERDICT=''
if [[ -z "${AUDIT_VERDICT}" ]]; then
{
echo "Growth-audit round (this counting window is over its growth budget) without a valid growth-audit.json verdict."
echo "The audit must run BEFORE any edit this round, and the verdict file must be a single JSON document carrying verdict sound|drift|conflict plus kiss.result and minimal_change.result each pass|fail, consistent with the taxonomy (sound requires both axes pass; drift requires at least one axis fail). Re-run the audit and produce the file; do not push without it."
} >> "${GATE_LOG}"
reject_fix 'growth-audit round missing a valid growth-audit.json verdict (audit skipped or malformed)' 'false' 'false'
fi
echo "🔎 growth-audit verdict: ${AUDIT_VERDICT}"
# Conflict routing is enforced HERE, not by convention: a conflict verdict
# must STOP BLOCKED with a handoff (the only growth path to a human). A
# round that kept fixing and committed would otherwise clear the gate like
# sound/drift, push the contested code, and park the next scan on a
# handoff question that was never asked. NON-retryable: re-audit, don't
# repair.
if [[ "${AUDIT_VERDICT}" == 'conflict' && ! -f "${WORKDIR}/failure.md" && ! -s "${WORKDIR}/handoff.md" ]]; then

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Deferred to the next round — not declined. Critical-only mode is active (the window's diff is over the growth budget), so this round's batch was limited to the two standing Criticals — R1-2 (verdict-pipeline forgery entrances) and R2-6 (loop-generated park wakes) — which this commit closes. This suggestion stays open and is picked up next round.

The conflict routing condition still mixes -f (failure.md) and -s (handoff.md) existence tests.

中文说明

延迟到下一轮处理——并非拒绝。Critical-only 模式已生效(窗口 diff 已超增长预算),因此本轮批次仅限于两个仍然成立的 Critical——R1-2(verdict 流水线伪造入口)与 R2-6(循环自产的 park 唤醒)——本次提交已将它们关闭。本建议保持开放,下一轮处理。

conflict 路由条件仍混用 -f(failure.md)与 -s(handoff.md)存在性判断。

reject_fix 'growth-audit verdict is conflict but the round did not stop with a handoff; conflict must STOP BLOCKED (no push)' 'false' 'false'
fi
# The no-push half of this routing is enforced at the success exit
# below: a repair pass re-auditing to conflict LEGITIMATELY runs behind
# the first pass's commit (committed_rc=1), so the push shape cannot be
# refused here without refusing it — the refusal sits at the push
# boundary itself.
# Record the verdict the GATE validated, for the report step to consume
# via the step output. The report must NOT re-read the file itself: the
# branch's own build/tests run as the runner user after this point and
# WORKDIR is a predictable path they can write — the validated verdict is
# the only verdict that may reach the trail marker and the re-arm.
echo "audit_verdict=${AUDIT_VERDICT}" >> "${GITHUB_OUTPUT}"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Deferred per the maintainer's split decision (option A, 2026-08-18): this finding is tracked as a follow-up in #9374 rather than fixed in this PR, and the live escalation thread carries the open decision. Leaving this open until it lands there.

AUDIT_VERDICT_RECORDED='true'
fi

if [[ -f "${WORKDIR}/failure.md" && -n "$(git status --porcelain)" ]]; then
echo "❌ Agent wrote failure.md after leaving a dirty workspace:"
git status --short
cat "${WORKDIR}/failure.md"
echo "outcome=failed" >> "${GITHUB_OUTPUT}"
exit 1
fi

if [[ -f "${WORKDIR}/failure.md" ]]; then
echo "🛑 Agent aborted intentionally:"
cat "${WORKDIR}/failure.md"
echo "outcome=failed" >> "${GITHUB_OUTPUT}"
exit 1
fi

# Convention: hooks are severed at EVERY host checkout of the PR
# branch (no secret sits in this step's env, but a post-checkout
# hook still runs branch code on the host).
git config core.hooksPath /dev/null
git checkout "${BRANCH}"
baseline_also_fails() {
# A deterministic rejection is only chargeable to this round if the same
# check passes WITHOUT the round's commits. Measured counterexample, run
Expand Down Expand Up @@ -176,7 +263,7 @@ baseline_also_fails() {
local ab_log="${GATE_LOG}.baseline"
: > "${ab_log}"
rc=0
if ! "$@" >> "${ab_log}" 2>&1; then
if ! strip_runner_channels "$@" >> "${ab_log}" 2>&1; then
rc=1
fi
git restore -- . 2>> "${GATE_LOG}" || true
Expand All @@ -197,6 +284,9 @@ baseline_also_fails() {
tail -c 3000 "${GATE_LOG}" 2> /dev/null
echo '````'
} > "${WORKDIR}/gate-rejection.md" || true
if [[ "${AUDIT_VERDICT_RECORDED:-false}" == 'true' ]]; then
echo "audit_verdict=${AUDIT_VERDICT}" >> "${GITHUB_OUTPUT}"
fi
exit 1
fi
# Every retryable exit below hands the tree to the repair agent with
Expand Down Expand Up @@ -271,14 +361,28 @@ fail_signature() {
seed_dist_note() {
echo "⚠️ the baseline leg rebuilt dist/ from baseline sources — run npm run build before typecheck/tests" >> "${GATE_LOG}"
}
# Every check below runs the BRANCH's own code (npm scripts, tests, and
# their lifecycle children) with this step's inherited environment. Strip
# the runner injection channels first: a check appending to GITHUB_OUTPUT
# would overwrite the gate's own outputs last-write-wins (a forged
# audit_verdict=sound after the gate's write), GITHUB_ENV/GITHUB_PATH
# plant environment for the PAT-bearing steps that follow, and
# GITHUB_STEP_SUMMARY lets branch code forge the job summary styled as
# gate output (the display-channel sibling; qwen-triage strips it when
# running external-author branch code for the same reason). Same class the
# deferred-upsert child closes with env -i; targeted -u here because the
# checks need the ordinary environment (PATH, HOME, …) to run at all.
strip_runner_channels() {
env -u GITHUB_OUTPUT -u GITHUB_ENV -u GITHUB_PATH -u GITHUB_STEP_SUMMARY "$@"
}
run_check() {
# pipefail makes the pipeline carry the command's status, not tee's. The
# side copy holds THIS check's transcript alone — the identity comparison
# must not match diagnostics an earlier check left in the shared log.
local label="${1}"
shift
: > "${GATE_LOG}.check"
if ! "$@" 2>&1 | tee -a "${GATE_LOG}" "${GATE_LOG}.check"; then
if ! strip_runner_channels "$@" 2>&1 | tee -a "${GATE_LOG}" "${GATE_LOG}.check"; then
if baseline_also_fails "$@"; then
reject_fix "${label} (pre-existing: also fails without this round's commit)" 'true'
fi
Expand All @@ -298,7 +402,7 @@ run_check_no_ab() {
# allowlist).
local label="${1}"
shift
if ! "$@" 2>&1 | tee -a "${GATE_LOG}"; then
if ! strip_runner_channels "$@" 2>&1 | tee -a "${GATE_LOG}"; then

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Deferred to the next round (batch bound). The gap is real — the strip_runner_channels wrap inside run_check_no_ab has zero test coverage (the forge stub only fires on npm run build). This round's last-writer re-record narrows the consequence (a forged append on the typecheck/lint/test paths is outwritten at the exit), but the coverage pin the finding asks for — extending the forge stub to typecheck/lint and/or a toContain pin — is a test-only round item and is queued next.

因批次上限推迟到下一轮。该缺口成立——run_check_no_ab 内的 strip_runner_channels 包裹零测试覆盖(forge 桩只对 npm run build 触发)。本轮的最后写入者重记录已收窄其后果(typecheck/lint/test 路径上的伪造追加会在退出处被覆盖),但该发现要求的覆盖 pin——把 forge 桩扩展到 typecheck/lint 和/或加 toContain pin——是纯测试轮事项,已排入下一轮。

Comment on lines 526 to +528

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Deferred to the next round — not declined. Critical-only mode is active (the window's diff is over the growth budget), so this round's batch was limited to the two standing Criticals — R1-2 (verdict-pipeline forgery entrances) and R2-6 (loop-generated park wakes) — which this commit closes. This suggestion stays open and is picked up next round.

The strip wrap inside run_check_no_ab still has zero behavioral coverage (the forge stub only fires on npm run build).

中文说明

延迟到下一轮处理——并非拒绝。Critical-only 模式已生效(窗口 diff 已超增长预算),因此本轮批次仅限于两个仍然成立的 Critical——R1-2(verdict 流水线伪造入口)与 R2-6(循环自产的 park 唤醒)——本次提交已将它们关闭。本建议保持开放,下一轮处理。

run_check_no_ab 内部的 strip 包装仍然没有任何行为覆盖(伪造 stub 只在 npm run build 时触发)。

reject_fix "${label}"
fi
}
Expand Down Expand Up @@ -360,6 +464,9 @@ if git diff --quiet "origin/${BRANCH}...${BRANCH}"; then
cat "${WORKDIR}/no-action.md"
echo "verified_head=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}"
echo "outcome=noop" >> "${GITHUB_OUTPUT}"
if [[ "${AUDIT_VERDICT_RECORDED:-false}" == 'true' ]]; then
echo "audit_verdict=${AUDIT_VERDICT}" >> "${GITHUB_OUTPUT}"
Comment on lines 591 to +594

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Deferred to the next round — not declined. Critical-only mode is active (the window's diff is over the growth budget), so this round's batch was limited to the two standing Criticals — R1-2 (verdict-pipeline forgery entrances) and R2-6 (loop-generated park wakes) — which this commit closes. This suggestion stays open and is picked up next round.

Routing still accepts a conflict round on a non-empty handoff.md alone, and that shape can exit via the noop path without re-evaluating the handoff question.

中文说明

延迟到下一轮处理——并非拒绝。Critical-only 模式已生效(窗口 diff 已超增长预算),因此本轮批次仅限于两个仍然成立的 Critical——R1-2(verdict 流水线伪造入口)与 R2-6(循环自产的 park 唤醒)——本次提交已将它们关闭。本建议保持开放,下一轮处理。

路由仍接受仅凭非空 handoff.md 的 conflict 轮,且该形态可以走 noop 出口而不重新评估 handoff 问题。

fi
exit 0
fi
echo "❌ Branch unchanged and no no-action.md — agent produced nothing"
Expand Down Expand Up @@ -839,7 +946,7 @@ bite_runner_default() {
# $1 = workspace dir, rest = test paths relative to the workspace.
local ws="${1}"
shift
npm run test --workspace "${ws}" --if-present -- "$@"
strip_runner_channels npm run test --workspace "${ws}" --if-present -- "$@"
}
mapfile -d '' -t BITE_FILES < <(git diff --name-only -z --no-renames --diff-filter=AM "${ROUND_RANGE}" \
-- ':(glob)**/*.test.*' ':(glob)**/*.spec.*' ':(exclude,glob)**/__snapshots__/**' \
Expand Down Expand Up @@ -1008,6 +1115,9 @@ if [[ "${#BITE_FILES[@]}" -gt 0 && -n "${BITE_SRC}" ]]; then
tail -c 3000 "${GATE_LOG}" 2> /dev/null
echo '````'
} > "${WORKDIR}/gate-rejection.md" || true
if [[ "${AUDIT_VERDICT_RECORDED:-false}" == 'true' ]]; then
echo "audit_verdict=${AUDIT_VERDICT}" >> "${GITHUB_OUTPUT}"
fi
exit 1
}
git reset --quiet 2>> "${GATE_LOG}" || true
Expand Down Expand Up @@ -1058,5 +1168,16 @@ if [[ "${#BITE_FILES[@]}" -gt 0 && -n "${BITE_SRC}" ]]; then
fi
fi
assert_verification_tree
# A conflict verdict must STOP BLOCKED: completing as fixed would push the
# contested code under the PAT while the report posts the park marker —
# the exact outcome the routing check above exists to prevent. The routing
# check cannot see this shape (a planted handoff.md satisfies it), so
# refuse at the push boundary. NON-retryable: re-audit, don't repair.
if [[ "${AUDIT_VERDICT:-}" == 'conflict' ]]; then
reject_fix 'growth-audit verdict is conflict but the round completed as fixed; conflict must STOP BLOCKED (no push)' 'false' 'false'
fi
echo "verified_head=${VERIFICATION_HEAD}" >> "${GITHUB_OUTPUT}"
echo "outcome=fixed" >> "${GITHUB_OUTPUT}"
if [[ "${AUDIT_VERDICT_RECORDED:-false}" == 'true' ]]; then
echo "audit_verdict=${AUDIT_VERDICT}" >> "${GITHUB_OUTPUT}"
fi
Loading
Loading