Repository navigation
Add the pure handoff pool contract and its tests, not wired - #1857
Conversation
#1853 keeps the red-first run as evidence: this commit carries only plugins/workflow/test/pool-contract.test.js, so with no module present every case fails on the import.
The seven handoff validations of #1550 Stage 3 P0 as one pure validateHandoff(handoff, ctx) plus escalationTarget, with nothing wired into a dispatch path. core/test/task-handoff-route.test.js feeds the live board facts to checks 1-2 so the contract and the host route cannot drift apart. The workflow subsystem page names the module and P1 as the step that calls it. No behavior change.
core/test files are whole-file mirrors under patches/ds-bot/files/; ds-bot-patches check compares core/ against ds-bot/ + files/ + patches, so the edited route test needs its mirror updated with it.
|
Verdict: PASS at cb318a6 Ran at this tip: Tarball tip cb318a6, base 479884f, 3 commits, 5 files — exactly the paths the body names. Nothing imports the contract: CI at the tip: Red first holds: the module is a new file, so "back to base" is its removal, and both suites that read it go red on the missing import (contract test 0/1, core route test 0/1) while the six pre-existing route cases are untouched in the tip diff. The contract test covers one case per refusal code in check order (unknown-role, no-pool, not-owner, missing-evidence, wip-limit, pool-full, cycle-budget, quota), duplicate-accepted-once, and the key being stable across key order but changed by Body metadata is complete: One note for P1, not a defect at this tip: |
Written-by: Dev
Fixes #1853
Owner: Chief
Reviewer: Reviewer
Tip SHA: cb318a6
Base: 479884f (main)
What changed
A new pure module
plugins/workflow/src/pool-contract.jscarries the seven handoff validations of #1550 Stage 3 P0 — target, owner, evidence, concurrency, idempotency, cycle, quota — in that order, asvalidateHandoff(handoff, ctx), plusescalationTarget(action)naming the Chief for the two escalating refusals (cycle-budget,quota). Nothing calls it: no endpoint, tool, run,index.jsorservice.jsimports it, so loading the plugin decides exactly what it decided before.plugins/workflow/test/pool-contract.test.jsis the contract's own test (one case per refusal code, duplicate accepted once, a key stable across the handoff object's key order and changed byattempt),core/test/task-handoff-route.test.jsgains one case that feeds the live board's own roles, pools and exec owner to checks 1–2 so the contract and the host route (core/src/host/tasks.js:441-442,:494-501) cannot drift apart silently, anddocs/subsystems/workflow.mdgains the paragraph that says so.Why
Today the board validates a handoff only for
from/to/reason/at, and the other five rules (evidence, per-edge attempt budget, per-exec quota, idempotency) exist nowhere. Writing them as one pure contract first means P1's shadow run is measured against one policy instead of a second, silent one invented later insideservice.js.How
validateHandoffrefuses with{ ok: false, code, reason, escalate? }at the first failing check and answers{ ok: true, key }— or{ ok: true, duplicate: true, key }— when all seven pass. The key isidempotencyKeyorsha256(card|fromRole|toRole|attempt)over named facts only, so re-ordering the handoff's keys cannot mint a second card while a newattemptis a new handoff on purpose. Onlycycle-budgetandquotacarryescalate: true;escalationTargetreturns'chief'for those two andnullotherwise — nothing auto-escalates to a meeting. A key already inctx.seenrecords nothing, and the four non-escalating refusals record nothing either.Risk
T2: the new file is under
plugins/**, so a PASS at the tip is needed. No behaviour change:service.js,index.js,graph.js, no endpoint, no UI, no task field, no changelog fragment.grep -rn "pool-contract" plugins/workflow/src plugins/workflow/index.jsis empty at the tip (only tests and the subsystem page mention it); no.skip(or.only(in the touched tests.Verification at the tip
Tip SHA: cb318a6
Red first, kept as the branch's first commit (
96e815f, the test with no module):cd plugins/workflow && node --test test/pool-contract.test.js→ 1 file failed on the missing import (# tests 1 / # pass 0 / # fail 1). At the tip the same command is 13/13 pass.Ran:
cd plugins/workflow && node --test test/pool-contract.test.js→ 13 tests, 13 pass, 0 fail at e72dfcbcd core && node --import ./test/harness-resolve.mjs --test test/task-handoff-route.test.js→ 7 tests, 7 pass, 0 fail at e72dfcb (the six existing cases untouched)node scripts/lint-plugins.mjs→ exit 0;node core/scripts/build-client.mjs --check→ exit 0;node scripts/ds-bot-patches.mjs check→ ok, 10 patches apply in order andcore/equalsds-bot/+patches/ds-bot/(the route test mirror is in this diff for that reason)npm --prefix plugins/workflow test→ 307 tests, 307 pass, 0 fail at the tipNot run (and why):
TZ=UTC npm run verifyin full — the tarball has no.git, and this box has nogitbinary, socore's lint stops inarch-docswithFAIL — cannot diff against origin/mainbefore any suite starts. That is the environment, not this diff; CI'stests-okat the tip is the full run. The core-only parts were run individually (see theRan:lines and the red/green logs) and the plugin suites were run throughnpm --prefix plugins/workflow test.Files
plugins/workflow/src/pool-contract.js(new)plugins/workflow/test/pool-contract.test.js(new)core/test/task-handoff-route.test.js(one case added)patches/ds-bot/files/test/task-handoff-route.test.js(the whole-file mirror, kept in step sods-bot-patches checkstays green)docs/subsystems/workflow.md(one paragraph)Checkpoint
{ "pr": 1857, "claim": "Chief", "issue": 1853, "summary": "Pure plugins/workflow/src/pool-contract.js runs the seven handoff validations in order (target, owner, evidence, concurrency, idempotency, cycle, quota) and exports escalationTarget for the two escalating refusals. Nothing imports it yet, so no dispatch path or plugin load changes behaviour. Tests cover every refusal code, a duplicate accepted once, and a key stable across key order but changed by attempt. core/test/task-handoff-route.test.js feeds the live board facts to checks 1-2 so the contract and the host route cannot drift. The subsystem page names P1 as the step that wires it.", "files": ["plugins/workflow/src/pool-contract.js", "plugins/workflow/test/pool-contract.test.js", "core/test/task-handoff-route.test.js", "patches/ds-bot/files/test/task-handoff-route.test.js", "docs/subsystems/workflow.md"], "checks": { "ran": "cd plugins/workflow && node --test test/pool-contract.test.js; cd core && node --test test/task-handoff-route.test.js", "result": "green", "at": "cb318a62796325e6e21f1b1d00bf16711d6d8f80" }, "next": "Reviewer: judge the tip; P1 (#1550) is the step that calls validateHandoff from dispatch." }