Repository navigation
Apply the duty's grace to tasks created by hand on a duty - #173
Merged
Merged
Conversation
A task created on a duty outside the dispatcher, a one-off above all, was stamped late_after = due_date. The overdue escalation waited out the duty's grace, but the Late lens listed the owner the morning after the due date. On 18.2 a hook body can read through ctx.api, so the lifecycle hook now reads grace_days off the row's own duty and stamps due_date + grace on insert, and on the fill-a-blank update leg. Rows with no duty (the assignment fan-out, a member's own task) keep zero grace, and a stamp the planner supplies is kept without a read. The read runs as system (runAs on the hook, which scopes ctx.api only), so a creator who cannot see the duty still gets its grace. The task write itself stays authorized as its caller. test/task-hook-body.test.ts lowers the handler with the build's own extractor and runs it in QuickJS. It pins api.read as the only capability and the grace read as the only ctx.api call. Fixes #100 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JQwxbY1jQKPYfexaanSBNK
Contributor
席位核对ACCEPT — PR #173 at head
Generated by Claude Code |
Contributor
LandedSeat landing at 2026-11-02T05:50Z (duly lane seat, session
State: #100 is closed Generated by Claude Code |
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.
Requested by Warren · project thread
Fixes #100
What was wrong (re-measured on 18.2.0, main ac5b5c4)
I made a
one_offduty withgrace_days: 7and created a task on it withdue_date: 2026-11-10and no stamp. It came back withlate_after = 2026-11-10for all three creates: a member'sdata.insert, a system create, andprotocol.createData. That's zero grace. The overdue escalation waits out the week, but the Late lens listed the owner the morning after the due date. The demo's Commissioning file handover — Riverside upgrade row was the same.The fix
The card says the hook "has no engine to read
duly_dutywith". That's no longer true on 18.2: a hook body runs as(async (ctx) => { … })withctx.api, andobjectstack buildinfersapi.readfrom the call. So the fix stays insidetask.hook.ts, with no second writer on the write-once column and no new flow:dutyand adue_datebut no stamp getsdue_date + duty.grace_days. The planner uses the same rule.runAs: 'system', and exactly what it reacheshook.zod.ts,runAs), it governs the hook'sctx.apidata operations only. Condition evaluation, the readonly strip on the payload andctx.sessionare untouched, and the triggering write keeps its own context.ctx.apicall:ctx.api.object("duly_duty").findOne({ where: { id }, fields: ["grace_days"] }), whereidis the task's ownduty. Its only capability is['api.read']. Both are pinned intest/task-hook-body.test.tsagainst the build's own extractor output.nulland a zero-grace deadline, which is the defect again.Measured live on
pnpm demo(this branch). Demo member Owen Pryce reads 0 duties because seed rows carry the admin'sowner_id(#143):GET /api/v1/data/duly_duty/<commissioning duty>POST /api/v1/data/duly_taskon that duty,due_date 2026-11-10,late_after 2099-01-01sentlate_after 2026-11-17(sent value stripped, grace applied),created_by/owner_id= OwenPATCHthe admin's task created the same wayPATCHown taskPATCHthe duty'sgrace_daysPERMISSION_DENIEDThe admin's create on the same duty also gives
2026-11-17.Demo row, read through
pnpm demoCommissioning file handover — Riverside upgrade:due_date 2026-11-14→late_after 2026-11-21(was the due date). On the seeded database, every task with a duty haslate_after = due_date + grace_days(0 mismatches across 206 rows). The 7 rows with no duty keep zero grace.Tests
test/task-hook.test.ts, new#100block: member, system and protocol creates give due+7, month-end arithmetic matches the planner, a 0 or blank grace is zero grace, a row with no duty is zero grace, the planner's stamp is kept, the fill-a-blank update applies the grace, the stamp stays write-once after the duty's grace is widened, andrunAsis declared. A duty id that resolves to nothing cannot reach the hook, because the engine refuses the dangling lookup first.test/task-hook-body.test.ts(new): the handler is lowered with@objectstack/cli/hook-body'sextractHookBody(whatos builduses), swapped into the real config with the function removed, and booted soAppPluginbinds it through the QuickJS body runner. As an ablation, changing the grace expression in the lowered source turns the two grace cases red, so the body is what runs.src/translations/authored-text.ts:hook.runAsclassified asmachine('execution identity'). The i18n coverage walk refuses an unclassified slot. This line was added under the Claim amendment on A task created by hand on a duty that grants grace is stampedlate_after = due_date— the dispatcher is the only producer that appliesgrace_days#100.pnpm validate0 ·pnpm typecheck0 ·pnpm test1152/1152 ·pnpm build0. The build artifact's hook carriesbody.capabilities: ['api.read']andrunAs: 'system'.Stale doc, left for its own card
AGENTS.md rule 5 still says
late_afteris "stamped at dispatch". After this PR it is stamped at creation: by the planner for a dispatched task, and by the hook for every other one. AGENTS.md is governed, so it is not edited here.Generated by Claude Code