A record from the Factory repository, docs/lifecycle-schedule-receipt.json, shown as committed except that its local paths appear as placeholders in angle brackets, such as <local evidence archive>, standing for files that are not published. Every hash, date and count is as recorded. The build review says what the Factory's receipts support.
{
"recordedAt": "2026-09-29T12:35:37Z",
"component": "Lifecycle schedule (src/lifecycle-schedule.ts): lifecycle increment 1",
"base": "217887b",
"branch": "factory/lifecycle-schedule",
"disposition": "implemented by Opus 5.5, tests first; an independent adversarial pass (12 failing tests, kept unchanged) found 12 defects, all fixed by Opus 5.5 as fixer; independent Astra review of the fixed revision: rounds 1 to 3 FAIL (4, 2 and 2 findings, all fixed), round 4 PASS; afterwards only status wording changed, and the site, full check and this receipt were refreshed",
"scope": "docs/lifecycle.md §9 row 1 only: the deterministic lifecycle-schedule module with an injected clock, stable occurrence IDs and journal-backed transitions, on fixtures. No live web access, model call, sign-in use, LaunchAgent (written or loaded) or other lifecycle module is built or run.",
"spec": {
"path": "<local evidence archive>",
"sha256": "c84d125badf586044f3fc57ddba22ab47d04a0da75e8649b3cd68eff8e4c86a0",
"review": "Astra round 3 PASS on the design only (<local evidence archive>)",
"repositoryCopy": "docs/lifecycle.md: status note updated for increment 1, and its one anchor into src/intelligence.ts moved from line 695 to 702, the line that now cites implicit-retries, because this increment moved one declaration above it; test/intelligence-lifecycle-adversarial.test.ts follows the anchor and still checks every anchor"
},
"acceptance": {
"day, week and DST boundaries (Europe/London default, 02:00-05:00 window)": "test/lifecycle-schedule-time.test.ts: day boundary (London and Asia/Tokyo), week boundary across the 53-week 2026 and the ledger week, DST in Europe/London (23- and 25-hour days) and Europe/Berlin (gap and overlap inside the window), zonedInstant, isoWeekOf, occurrence identity",
"a restart gives exactly one occurrence": "test/lifecycle-schedule.test.ts \"a restart gives exactly one occurrence: ...\"; test/lifecycle-schedule-processes.test.ts: six racing processes admit one occurrence, and a runner killed by SIGKILL mid-call leaves one logical occurrence; test/lifecycle-schedule-adversarial.test.ts: a reconciled segment keeps the reset, the owner blocker and the night's deadline",
"coalescing of missed occurrences": "test/lifecycle-schedule.test.ts \"missed occurrences coalesce into the next one admitted, ...\" (daily and weekly lanes, a full night inside the range) and \"a revision that moves a full night later in its ISO week ...\", plus the usage-limit and segment-limit tests; adversarial: a moved full night and a Product added by a later revision",
"an injected usage limit defers and resumes the same occurrence under distinct transition IDs, with exactly one settlement and no re-dispatch of an unknown effect": "test/lifecycle-schedule.test.ts \"an injected usage limit defers the occurrence and later resumes the same one ...\", \"a usage limit that refused the call leaves its work unsent: ...\", \"a usage limit's reset is checked: ...\", \"a usage-window observation closes the segment unless ...\", \"a deferral is for one segment: ...\" and \"a retry of a transition whose occurrence has left the state ...\"; adversarial: refusals reporting tokens used, a rejected observation on a result, deferral retries after a resume, and retries of pruned records",
"paid overage fails closed": "test/lifecycle-schedule.test.ts \"paid overage fails closed: ...\" and \"the owner's record is current for its maximum age and no longer, ...\"",
"the discovery-window ceiling refuses (never extended)": "test/lifecycle-schedule.test.ts \"the discovery-window ceiling refuses a call it cannot fit, is never extended within its week, ...\" and \"a usage above its bound is debited as observed and flagged, ...\"",
"no LaunchAgent written or loaded; a renderer is output only and tested as text": "test/lifecycle-schedule-time.test.ts \"the LaunchAgent renderer is output only: ...\" and \"the module reads time only from the injected clock and touches nothing outside its journal: ...\""
},
"ownerDecisions": {
"state": "open: every default below is a proposal, not a signed commission (S1 unsigned)",
"defaultsUsed": [
"5: Europe/London, the 02:00-05:00 window with a 20-minute stagger per Product, weekly full occurrences Thursday to Saturday two a night, the audit on Sunday 02:00, synthesis on Sunday 02:30 and the bundle on Monday 07:00",
"6: the discovery-window allowance as a Factory admission limit in estimated tokens per ISO week, never a provider reservation, never extended; no ceiling default until the shadow run (ceilingFromShadow: p95 x 1.25, rounded up)",
"A13: the owner record of each account's sign-in with paid overage off is current for 30 days"
],
"choicesTheSpecLeavesOpen": [
"deadlines of 30, 60 and 10 minutes for the audit, synthesis and bundle, and an 08:00 close for the bundle (the spec gives 10 and 45 minutes for basic and full only)",
"a slot is the lane's own start time each day: a limit that resets inside tonight's window waits for the next night's slot",
"a week's ceiling is set when its first call opens it; a later revision lowers it at once and never raises it",
"at most eight segments per occurrence, a seven-day reset horizon, two settled occurrences kept per lane in state",
"a running occurrence stopped by a usage limit or a sign-in failure is deferred before it can be settled",
"the rhythm's deadline counts once per night: a resume on a night a segment already started keeps that night's deadline",
"allowed and allowed_warning are the only open usage-window statuses; any other, recognised or not, closes the segment until its checked reset",
"a lane's occurrences are due only when their window closes after the revision naming the lane was recorded",
"a deferral names its segment, or maps to the segment an identical earlier deferral holds, else the running one",
"a closed usage window and a sign-in refusal are held per account, for every Product's occurrences, until the reset or a newer owner record",
"a call admission is identified by its runner, task, work, bound and attempt (default 1); a replay of an ended call licenses no contact (inFlight false)",
"a call admission is identified by its runner, task, work, bound and attempt (default 1); only a fresh admission licenses contact (dispatch), and a call whose dispatch is uncertain ends as lost",
"decisions read the clock inside the journal transaction; account stops only move forward; an overage observation closes the window only with a usable reset",
"a work key is counted per task; a refused (usage-limit, sign-in) call uses no call allowance unless it reports tokens used, which makes its effect unknown"
]
},
"intelligence": {
"change": "the lifecycle-schedule declaration moves unchanged (none, the same why) from LIFECYCLE_INTELLIGENCE into MODULE_INTELLIGENCE with sources [\"lifecycle-schedule.ts\"]; no task, pin, allowance or selector rule changes",
"policyDigests": "taskPolicyDigest of all eleven tasks byte-identical before (217887b) and after: policy-digests-before.json, policy-digests-after.json, policy-digests-compare.txt (diff exit 0) under the log directory",
"readme": "the README table gains the lifecycle-schedule row; the blank line after the heading is removed, as on main, to keep the section within its accepted 25 lines"
},
"testChanges": "New: test/lifecycle-schedule-time.test.ts (12), test/lifecycle-schedule.test.ts (36: 20 by the implementer, 5 added with the adversarial fixes, 5 with the Astra round-1 fixes, 2 with the round-2 fixes and 4 with the round-3 fixes, and 6 corrupt-state mutations added to an existing test), test/lifecycle-schedule-processes.test.ts (2), test/lifecycle-schedule-adversarial.test.ts (12, by the independent adversary, unchanged), helpers test/helpers/schedule.ts and test/helpers/schedule-child.ts. Two deferral expectations in test/lifecycle-schedule.test.ts gained the new bound: null field; a forged call record gained attempt: 1; one assertion that an identical re-sent admission is refused duplicate-work now asserts its replay (dispatch and inFlight false) and that the re-send as attempt 2 is refused duplicate-work; the lost-acknowledgement replay also asserts inFlight true and dispatch false; test/helpers/schedule-child.ts holds a call in flight only when its admission licenses dispatch; nothing removed or weakened. test/intelligence-lifecycle.test.ts: a landed lifecycle module must be in MODULE_INTELLIGENCE with exactly its landed files, and every other one must still be planned and not landed (the earlier test required all nine not landed). test/intelligence-lifecycle-adversarial.test.ts: the moved src/intelligence.ts anchor (695 to 702); no assertion weakened.",
"producer": {
"model": "claude-opus-5-5",
"effort": "not reported to the session",
"role": "implementer; not the verifier"
},
"verification": {
"logDirectory": "<local evidence archive>",
"redRun": {
"log": "01-red-run.log",
"logSha256": "81bd4cba0844e544a74be28aecd44edd74f043449547857fe41e56ef9e3ce5ff",
"result": "the new tests fail before the module exists"
},
"focused": {
"command": "node --test test/lifecycle-schedule-time.test.ts test/lifecycle-schedule.test.ts test/lifecycle-schedule-processes.test.ts test/lifecycle-schedule-adversarial.test.ts test/intelligence.test.ts test/intelligence-lifecycle.test.ts test/intelligence-lifecycle-adversarial.test.ts test/intelligence-adversarial.test.ts test/docs-site.test.ts test/design-tokens.test.ts",
"passed": 138,
"failed": 0,
"skipped": 0,
"exitCode": 0,
"log": "fix/44-focused.log",
"logSha256": "4afb66a5db90d97c03f398fc514a871dc22867bc0bdcc5667ac0674e63b33f2c"
},
"processRepeats": {
"command": "node --test test/lifecycle-schedule-processes.test.ts, five times",
"result": "5 of 5 runs 2/2",
"log": "04-process-repeats.log",
"logSha256": "4982dcbdc12823cd70ac6cb53e9e92452baa0e380b334fabf037d8132346d8f6"
},
"typecheck": {
"command": "npx tsc --project tsconfig.json",
"exitCode": 0,
"log": "fix/42-tsc.log",
"logSha256": "28d3b9e880a77975493dc7e359144c0295a4f694cfe0af4f928c22307bc5c320"
},
"docs": {
"command": "rm -rf site && npm run docs",
"result": "Built 60 pages and copied 13 assets into site",
"log": "fix/41-docs-build.log",
"logSha256": "41f570e2cbe7c75eba18eab29cbf3c6f2415ff32276ebc866e438985bb66c31c"
},
"full": {
"command": "npm run check",
"passed": 980,
"failed": 0,
"skipped": 0,
"exitCode": 0,
"log": "fix/43-full-check.log",
"logSha256": "a09c96caee819520eacd68e6327c8469330f972e842472729782524d4e64e4c7",
"note": "run after the post-verdict status wording and site rebuild, with this receipt at its content before the final refresh; only the receipt changed afterwards"
},
"notRun": [
"npm --prefix apps/dashboard run check: apps/dashboard is untouched"
],
"adversarial": {
"tests": "test/lifecycle-schedule-adversarial.test.ts (12), by the independent adversary; kept unchanged",
"log": "adversarial/04-adversarial-final.log",
"logSha256": "a750d06e1bcb5cc18daeee0d18b6e5f880d220329547655aa4f353336b9e4d4e",
"result": "all 12 failed against the submitted implementation (adversarial/06-full-check.log: 964 tests, 952 passed, 12 failed)",
"dispositions": "dispositions.md",
"dispositionsSha256": "e4f9a3abfb6ef08c31d7c1bfee0447584faf00eed33a4981f3b01a03c5f1979f",
"newTestsAgainstPreFix": {
"log": "fix/03-new-tests-against-pre-fix.log",
"logSha256": "eab37261151bc05f1c75643898229198cfec371ff89322a268e162897e8d28a6",
"result": "against the pre-fix source (lifecycle-schedule.before-fix.ts) the 12 adversarial tests and the 6 new or extended implementer tests fail, and the two deferral expectations that gained bound: null differ"
}
},
"preFixFull": {
"command": "npm run check",
"passed": 952,
"failed": 0,
"skipped": 0,
"exitCode": 0,
"log": "15-full-check.log",
"note": "the implementation as submitted, before the adversarial tests existed"
}
},
"measurements": {
"soakLargestStateBytes": 38761,
"worstFullOccurrenceBytes": 14207,
"worstFullOccurrence": "eight segments, 16 calls, 48 commands, seven reconciled with 280-character confirmations, every token, key and observation at its longest",
"maxStateBytes": 786432,
"log": "fix/23-measurements.log",
"script": "fix/measure.ts"
},
"review": {
"state": "passed",
"reviewer": "Astra (gpt-6-astra, high) via codex exec, read-only",
"adversarial": "12 findings, all fixed (dispositions.md)",
"rounds": [
{
"round": 1,
"verdict": "FAIL",
"report": "<local evidence archive>",
"findings": 4,
"disposition": "all four confirmed and fixed (dispositions.md, Round 1)"
},
{
"round": 2,
"verdict": "FAIL",
"report": "<local evidence archive>",
"findings": 2,
"disposition": "round 1 confirmed resolved; both new findings confirmed and fixed (dispositions.md, Round 2)"
},
{
"round": 3,
"verdict": "FAIL",
"report": "<local evidence archive>",
"findings": 2,
"disposition": "round 2 confirmed resolved; both new findings confirmed and fixed, and two adjacent classes closed (dispositions.md, Round 3)"
},
{
"round": 4,
"verdict": "PASS",
"report": "<local evidence archive>",
"findings": 0,
"disposition": "round 3 confirmed resolved; no material defect; receipt hashes and all 74 site-manifest entries checked"
}
],
"verdict": "Astra round 4 PASS on the fixed revision (<local evidence archive>)"
},
"notEstablished": [
"Any composition: no runner, CLI, collector, triage or LaunchAgent calls it; nothing runs on a schedule",
"A LaunchAgent written or loaded (S6), or sign-in from launchd (A1, L5)",
"That the live usage-limit event matches the fake one (A2), which statuses it uses, and that resetsAt is in seconds",
"The owner's S1 commission and account records; that paid overage can be turned off (A13)",
"That a call's bound is a true upper bound: it rests on transport-enforced bounds not built for these tasks",
"Authentication: records and interruption confirmations are attribution",
"Power-loss durability, other hosts, and any Signal, Capture, Assessment or customer value; all 27 epics stay open"
],
"files": {
"src/lifecycle-schedule.ts": "a480a351a2452fb0be2e3065e4178c9fcfee745a62ddcb30c4c759ebf673a101",
"src/intelligence.ts": "429136e8eec36ed50d4c31121de3f6e5b0e2ec09a09f5907fe2711672e5fbb63",
"test/lifecycle-schedule-time.test.ts": "451d27b2b0ec62486bb7368539e4e197ed519e9555853db366248bd1ef467ec3",
"test/lifecycle-schedule.test.ts": "f808cb86e75db67ededd3a918c12dc6721e2baa7f64229e98f7f7d7b598cc519",
"test/lifecycle-schedule-processes.test.ts": "fac34889f13c772117b0a5b078aef1dbd51a4dd13f88df7a97bf748a47ccf955",
"test/lifecycle-schedule-adversarial.test.ts": "38f802b559a1bdd0513c96e5e92109cce9c0a51ca2138a352f495086b4ae2186",
"test/helpers/schedule.ts": "9388a84d910e94fc66216d89e6cbdb833ef740a8e6c17d3653d2865d87147c00",
"test/helpers/schedule-child.ts": "df147b3a25da42df6cfa0b1e67e06fe2eed5604d8b96e3571994551578103865",
"test/intelligence-lifecycle.test.ts": "742ba64ff5e04a169217212de198f6ec058c73dcd47a2b34b9166bd8c4ad9b7a",
"test/intelligence-lifecycle-adversarial.test.ts": "92108b67f97a8d5b8cd5861e6114cc171173ff9fceb6d5ce1b5d36ad41829cab",
"README.md": "61c36689914590fdb645fff4e533f9acd86995d5618f070a105f69d9d1c30ef3",
"docs/lifecycle.md": "8a8c3a4b269ede22faf573711b91e61bb5de6083e801f06e72ff05bc9eecf5c0",
"docs/agents/lifecycle-schedule.md": "22056b096faf449225baecff1311b7545a41136f5180a0819320c5e40db64288",
"docs/guide/lifecycle-schedule.md": "8b7399310f9952eb33137700c67b64e4657901ac9ed414f9ecdcaa023309ae6e",
"docs/agents/intelligence.md": "2cacfce6199b769506ffcb5cf2bb4064f6cba59f0690dd4a434c3a4155ccc472",
"docs/guide/intelligence.md": "8c0268ad729212185ffe275e5d887e6aa878ef0dfd822c233cc684f82343d244",
"docs/agents/factory-model.md": "8b028edd2f3656309c61d44e6bb41f0d13cc54e08632b8ac066018597bf0cfad",
"docs/build-review.md": "bb72280bca80044170f539cbc18f9b751799ddda5ec06188416a0f063b3611f3",
"docs/guide/index.md": "43fd5bec2cb43dd5e3a50f4a3b90f6d62b213fb6602ecea60b430b1c0f948490",
"site/build-manifest.json": "ba60be879716e1fad542a75f83b3f241f7d4a1f266436d65bc14f8df3f6091ed"
},
"fixRecordedAt": "2026-09-29"
}
