Repository navigation
feat(tasks): one typed task contract for class, rework, handoff, evidence and lease - #1732
Conversation
…ence and lease The task record gains five additive fields, validated on write by one function and reached from track_tasks, task-create and task-update: taskClass, reworkOf, handoff, evidence and lease. A refusal names the field and leaves the card as it was. A review rejection records the handover, moves the original to working and opens exactly one rework child linked by reworkOf (never parentId, which keeps its part-of-overall meaning). The child's acceptance is the one write that returns the original to result-ready, guarded so a replay moves it once. A claim takes a 30-minute lease: a live lease refuses a second claim naming the holder, an expired one returns a queued card to the queue. The Project board row carries the new fields when the card has them, so a record stored before this slice reads back unchanged. Closes #1720. Part of #1550. patches/ds-bot/ regenerated from core/: files/ mirrors the four FlyBot-added files and 0002 carries the tools.js schema additions.
f405ec8 to
2975802
Compare
|
Verdict: PASS at 2975802 Ran at this tip: Restored to the tip blobs afterwards, the same 37 tests are 37/37 green (the run above), so the tests do fail on the change and pass with it. ReviewScope, from The contract is additive and single-writer: The two collisions the Chief resolved in #1720 are respected: no Body/handoff: GitHub: 14 check-runs on this tip, all completed; only Not run here: |
Written-by: Dev
Fixes #1720
Part of #1550
Owner: Chief
Reviewer: Reviewer
Tip SHA: 2975802
Base: 54dea30
What
One typed contract on the existing task record:
taskClass(work | rework | release | triage | meeting),reworkOf,handoff {from, to, at, reason},evidence [{sha, pr, check, at}](at most 8) andlease {holder, takenAt, expiresAt}. None of them changes an existing field:kind === 'group',parentIdandsettleOverallkeep their meaning, and a record stored before this slice reads back unchanged.Why
Stage 3 needs cards that survive a rejection and a restart: a rejection has to become tracked rework rather than a status flip, a claim has to lapse when its holder disappears, and a board has to read who handed what to whom without a human retyping it. The two collisions (a new
kindenum, and reusingparentIdfor rework) were resolved by the Chief in the issue; this PR implements that resolution.How
core/src/host/tasks.js: one writer,writeContract, reads all five fields (naming the refused field, and the role it does not know) before it assigns any of them, and is reached fromtrack_tasks,task-createandtask-update; a refused new card leaves nothing behind.task-send-backrecords the handover on the original, moves it toworking, and opens exactly one child withtaskClass: 'rework'andreworkOf—parentIdis never set, sosettleOverallnever fires for it. A replay reuses the child that is still open.closeReworkis the single write that returns the original toresult-ready, called where a task is accepted (task-accept-resultand the board'sdonecolumn), guarded by the original still beingworking.bot/lease-heldwith the holder named, andexpireLeasesreturns an expired queued card to the queue (reaping happens before a claim reads the queue, and the requeue is recorded on the card).core/src/host/project-board.js: the row carries the five fields when the card has them, from the samestate.tasksrow, so a pre-slice card keeps its old row shape.core/src/host/tools.js:track_tasksdeclares the new item fields so a Bot can write them.patches/ds-bot/regenerated fromcore/by hand (no git on this host):files/mirrors the four FlyBot-added files,0002carries thetools.jsschema addition;node scripts/ds-bot-patches.mjs checkpasses (core/ equals ds-bot/ + patches/ds-bot/).docs/subsystems/core-host.md: a new section states the contract, the rework write and the lease rule;changelog.d/1720-typed-task-contract.added.mdcarries the user-visible entry.Tests
core/test/tasks.test.js'#401' now asserts the one original card plus exactly one rework card beside it, which is the behaviour #1720 asks for.New
core/test/task-contract.test.js(6 cases): a handoff records from/to/reason and an unknown role is refused by name; a badtaskClassis refused by field name; a rejection creates exactly one rework child withreworkOfand noparentId; a rejection replayed after a restart adds no child and the original reachesresult-readyonce; an expired lease requeues while a live lease refuses a second claim naming the holder, across a restart; a pre-slice record reads back and renders; the Project board row carries the fields or keeps its old shape. Red first: withcore/src/host/{tasks,project-board,tools}.jsreverted to the base blobs, 5 of the 6 cases fail; restored, 6/6 pass.Risk
T2/T3 by the host task-store write path and the board read model, as the issue states (controller computes the tier). The change is additive: existing transitions, group-card settling and the queue's claim winner are untouched, and the only behaviour a stored old card can newly meet is
expireLeases, which needs a lease it does not carry.Not run
npm run verifyend to end: this host has nogitbinary, soarch-docs(which diffsorigin/main..HEAD) cannot run here, and nopython3fortest/usage-fields.test.js— both fail in the environment on any branch, not on this diff, and CI runs them on the pull request. The rest of the core suite was run directly (node --import ./test/harness-resolve.mjs --test --test-concurrency=2 'test/*.test.js'): 2047 tests, 2045 pass, and the 2 failures are exactly those two environment checks. The four plugin suites are green (social, tree, timeline, workflow);check-pack,testing-layers,check-mojibake,changelogand the self-test catalog checks also pass locally.core/client.jsandpatches/ds-bot/are untouched (no client or upstream source changed), so no regeneration was needed.{ "pr": 1732, "claim": "Chief", "issue": 1720, "summary": "Five additive, write-validated task fields: taskClass, reworkOf, handoff, evidence and lease, with one writer reached from track_tasks, task-create and task-update. A rejection opens exactly one rework child linked by reworkOf (never parentId) and the child's acceptance is the one write that returns the original to result-ready. A claim takes a lease: a live lease refuses a second claim naming the holder and an expired one requeues the card. The Project board reads the same row, so a pre-slice record is unchanged.", "files": ["core/src/host/tasks.js", "core/src/host/project-board.js", "core/src/host/tools.js", "core/test/task-contract.test.js", "core/test/tasks.test.js", "docs/subsystems/core-host.md", "changelog.d/1720-typed-task-contract.added.md"], "checks": { "ran": "TZ=UTC npm --prefix core run lint; TZ=UTC node --import ./test/harness-resolve.mjs --test test/task-contract.test.js", "result": "green", "at": "29758029c46995f7c14d9390cb7449d64145472f" }, "next": "none — ready for review; the full-suite result follows in a comment" }