build-gate: surface the gate verdict and return runBuild refusals as phased results (trace F1, F5) - #196
Conversation
…phased results Two fixes from docs/build-orchestrator-trace.md (PR #194), F1 and F5. F1: buildProject's build-phase result now carries `certified` (the gate's report.certified) beside `ok` (the ledger's merge_status === "ready"). Under trust_mode "advisory" the gate never certifies, so a keyless build can be ok:true and certified:false at the same time; before, that fact was only reachable through result.council. `ok` keeps its ledger meaning so advisory tests and evidence scripts are unchanged; the docstring now states both verdicts. F5: runBuild returns { error, detail, trace } with no report for PLAN_INVALID, PLAN_TAMPERED, PLAN_HASH_MISMATCH and MUTATE_FAILED. buildProject dereferenced build.report.merge_status unconditionally and threw a TypeError on any of them. It now returns { phase:"build", ok:false, certified, error, detail, trace, ... }, mirroring proposal-orchestrator.mjs. Tests: the happy path asserts certified:false with ok:true in advisory mode; a new case tampers .telos/plan.json from a council seat (the window between writePlan and runBuild) and asserts the PLAN_TAMPERED refusal is returned as a phased result with no ledger written. On the previous orchestrator that case threw "Cannot read properties of undefined (reading 'merge_status')". Verified: cd build-gate && npm test -> exit 0 (Node 22.22.2). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrjwsLBYk3FcnkneUTqqkQ
…H_MISMATCH test Review follow-ups on the F1/F5 change: - Replace the top-level `certified` with `merge_ready` = ok AND gate certified. Echoing report.certified beside ok:false on a refused build could read as "certified" for a plan the substrate just refused; one derived verdict cannot disagree with itself, and the gate's own verdict stays in council.certified. - Build both build-phase results from one shared base object so the two returns cannot drift. - Add "situation" to the docstring's phase list (it was already missing and this docstring is the one being rewritten). - Test the second refusal shape: a self-consistent substitute plan written during council review recomputes fine but is not the approved hash, so runBuild refuses with PLAN_HASH_MISMATCH, returned as a phased result. The substitution happens on the first seat call only; the council calls the seat once per model. cd build-gate && npm test -> exit 0 (Node 22.22.2). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrjwsLBYk3FcnkneUTqqkQ
|
CI on The failure is Re-running the failed job once. Generated by Claude Code |
Bring the per-node verify stage up to date with main (079e77e), which now carries #193 (repo code trace + keyless evidence fixes), #196 (gate verdict surfacing + runBuild refusals as phased results, F1/F5), and #194 (trace docs). build-orchestrator.mjs auto-merges cleanly: the verify-stage changes and the F1/F5 changes touch different regions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013rxfYqddLk2NykrPkjy1Pr
What
Follow-up to the build orchestrator trace in #194, fixing findings F1 and F5 in
build-gate/build-orchestrator.mjs. One concern:buildProject's build-phase result must not misreport the trust verdict or crash on a substrate refusal.F1:
ok:truedid not mean certified.buildProjectderivedokfrom the ledger alone (merge_status === "ready"). Undertrust_mode:"advisory"the gate never certifies, so a keyless build wasok:truewhileresult.council.certifiedwasfalseand the gate'ssafe_next_actionsaid do not merge. The build-phase result now carries a single derived verdict,merge_ready=ok && council.certified, besideok.okkeeps its ledger meaning, so the advisory-mode tests and the committed evidence scripts underdocs/runs/agentic-teams*are unchanged; an advisory build isok:true, merge_ready:false. The gate's own verdict stays where it was, inresult.council.certified. The docstring states both verdicts and now lists thesituationphase it previously omitted.F5: a
runBuildrefusal threw instead of returning.runBuildreturns{ error, detail, trace }with noreportforPLAN_INVALID,PLAN_TAMPERED,PLAN_HASH_MISMATCHandMUTATE_FAILED(merkle-dag/orchestrate.mjs:204-206,266).buildProjectdereferencedbuild.report.merge_statusunconditionally, so any of them surfaced asTypeError: Cannot read properties of undefined (reading 'merge_status'). It now returns{ phase:"build", ok:false, merge_ready:false, error, detail, trace, council, plan, advisories, situation, teams }, the same guard the lifecycle path has atproposal-orchestrator.mjs:316. Both build-phase returns are built from one shared base object.Tests (
build-gate/scripts/test-build-orchestrator.mjs):merge_ready === falsealongsideok:true, withcouncil.certified === false.PLAN_TAMPERED: a council seat caller corrupts the storedplan_hashin.telos/plan.jsonin the window betweenwritePlanandrunBuild. Asserts the refusal comes back as a phased result withok:false,merge_ready:false, noreport, an emptytrace, and noledger.jsonl. On the previous orchestrator this case throws theTypeErrorabove (reproduced before the fix).PLAN_HASH_MISMATCH: a seat caller writes a self-consistent substitute plan (same signers, changed requirements) during review, so the hash recomputes cleanly but is not the approved one. Same assertions. This is the case theauthorizedPlanHashcheck exists for.MUTATE_FAILEDis not testable throughbuildProjectin legacy mode: the adapter'srespeconly rewritesrequirements, dependencies are never mutated, and legacy plans carry no obligations or lifecycle, somutateNodehas no error path to reach.Not changed: the dead lifecycle branches (F4), triple test execution (F2) and capability packets (F6) from the trace are left for separate changes. A corrupt or unreadable
plan.json(invalid JSON) still throws fromreadPlaninsiderunBuild; that is an exception, not a substrate refusal, and the lifecycle path behaves the same way. The evidence scripts underdocs/runs/agentic-teams*recordokbut noterror; they already exit non-zero onok:false, and adding fields to committed evidence needs the evidence regenerated, so that is a separate change.Checklist
.telos/artifacts, secrets,.env*, or*.pemfiles committedTest results
Node 22.22.2, branch on top of
main39d3e9f:cd build-gate && npm test-> exit 0 (all suites incl.test-build-orchestrator16 cases,test-runtime-adaptation,test-proposal-orchestrator,breakout)PLAN_TAMPEREDscenario run standalone against the previousbuild-orchestrator.mjs->TypeError: Cannot read properties of undefined (reading 'merge_status'){ phase:"build", ok:false, merge_ready:false, error:"PLAN_TAMPERED" }🤖 Generated with Claude Code
https://claude.ai/code/session_01JrjwsLBYk3FcnkneUTqqkQ