{"_id":"@adlc/context-handoff","_rev":"5-fe42399aee72c3aecd26d220e20b8c7c","name":"@adlc/context-handoff","dist-tags":{"latest":"1.11.1"},"versions":{"1.8.0":{"name":"@adlc/context-handoff","version":"1.8.0","keywords":["adlc","agentic","context-rot","handoff","f3"],"author":{"url":"@voodootikigod","name":"Chris Williams"},"license":"MIT","_id":"@adlc/context-handoff@1.8.0","maintainers":[{"name":"voodootikigod","email":"voodootikigod@gmail.com"}],"homepage":"https://github.com/voodootikigod/adlc/tree/main/packages/context-handoff#readme","bugs":{"url":"https://github.com/voodootikigod/adlc/issues"},"dist":{"shasum":"1f3fa8ea0fa1bbc672f9f2d0e9741b91386ed964","tarball":"https://registry.npmjs.org/@adlc/context-handoff/-/context-handoff-1.8.0.tgz","fileCount":9,"integrity":"sha512-9LGB18ZwQckyOLbujYCWK7f00kq4VvU7JJOoT3LiMVDR7HYdbjfmIhyQRKTbTADYbdHCilUYHJMpEKkEMBuDEA==","signatures":[{"sig":"MEUCIQDNW3xOIbGgRuAUA/vASCgLg6v9y7AnXc2TbSMqiUqViAIgQlGho0pcTAxQoWIpNyw+Fb4eKrF11LuqALfhamdGtb8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adlc%2fcontext-handoff@1.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":44719},"type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.mjs","./lib/bands.mjs":"./lib/bands.mjs","./lib/thresholds.mjs":"./lib/thresholds.mjs","./lib/deny-marker.mjs":"./lib/deny-marker.mjs","./lib/mutation-gate.mjs":"./lib/mutation-gate.mjs","./lib/deny-lifecycle.mjs":"./lib/deny-lifecycle.mjs"},"gitHead":"e6b6525502d3deb214f58cffeb6c9db4b9787e39","scripts":{"test":"node --test test/*.test.mjs"},"_npmUser":{"name":"voodootikigod","email":"voodootikigod@gmail.com"},"repository":{"url":"git+https://github.com/voodootikigod/adlc.git","type":"git","directory":"packages/context-handoff"},"_npmVersion":"11.19.0","description":"Proactive context-rot handoff — absolute band signals, session-terminal deny (D1–D3), and contract helpers for F3 continuity (slice 1).","directories":{},"_nodeVersion":"20.20.2","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/context-handoff_1.8.0_1786195757118_0.4056592731182076","host":"s3://npm-registry-packages-npm-production"}},"1.9.0":{"name":"@adlc/context-handoff","version":"1.9.0","keywords":["adlc","agentic","context-rot","handoff","f3"],"author":{"url":"@voodootikigod","name":"Chris Williams"},"license":"MIT","_id":"@adlc/context-handoff@1.9.0","maintainers":[{"name":"voodootikigod","email":"voodootikigod@gmail.com"}],"homepage":"https://github.com/voodootikigod/adlc/tree/main/packages/context-handoff#readme","bugs":{"url":"https://github.com/voodootikigod/adlc/issues"},"bin":{"handoff":"bin/handoff.mjs"},"dist":{"shasum":"406dab19a54784d975d264fdae209dc9eeef822c","tarball":"https://registry.npmjs.org/@adlc/context-handoff/-/context-handoff-1.9.0.tgz","fileCount":18,"integrity":"sha512-2XCxJMbOcdaatI5Kklpxz/kR6DJs5lreLsPVK2Y1/VoAW8RcT2Sc6ixm+EqNSG/yW8i137F3yBw8b9zbK6V9hQ==","signatures":[{"sig":"MEUCIDGAKsKMqCxYwmz5iy0oggrEf17qoyFC0U1TJQia73IcAiEA2mSf8UmZyfD2iiRO0DvlKNtQWop40aCMU9H0LB5EDxg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adlc%2fcontext-handoff@1.9.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":95473},"type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.mjs","./lib/lock.mjs":"./lib/lock.mjs","./lib/bands.mjs":"./lib/bands.mjs","./lib/final.mjs":"./lib/final.mjs","./lib/paths.mjs":"./lib/paths.mjs","./lib/thresholds.mjs":"./lib/thresholds.mjs","./lib/deny-marker.mjs":"./lib/deny-marker.mjs","./lib/resume-auth.mjs":"./lib/resume-auth.mjs","./lib/deny-persist.mjs":"./lib/deny-persist.mjs","./lib/mutation-gate.mjs":"./lib/mutation-gate.mjs","./lib/deny-lifecycle.mjs":"./lib/deny-lifecycle.mjs"},"gitHead":"a928a7eeb3c2c5078b44c181702c3220ba1a7a5b","scripts":{"test":"node --test test/*.test.mjs cli-test/*.test.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8ef0f4fb-2e41-4f97-8514-98756d21727e"}},"repository":{"url":"git+https://github.com/voodootikigod/adlc.git","type":"git","directory":"packages/context-handoff"},"_npmVersion":"11.19.0","description":"Context-rot handoff — absolute band signals, session-terminal deny (D1–D3), and operator CLI for write/resume/bypass/repair/unlock (F3 continuity).","directories":{},"_nodeVersion":"20.20.2","dependencies":{"@adlc/core":"1.9.0","@adlc/gate-manifest":"1.9.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/context-handoff_1.9.0_1786221164200_0.7641615086899156","host":"s3://npm-registry-packages-npm-production"}},"1.10.0":{"name":"@adlc/context-handoff","version":"1.10.0","keywords":["adlc","agentic","context-rot","handoff","f3"],"author":{"url":"@voodootikigod","name":"Chris Williams"},"license":"MIT","_id":"@adlc/context-handoff@1.10.0","maintainers":[{"name":"voodootikigod","email":"voodootikigod@gmail.com"}],"homepage":"https://github.com/voodootikigod/adlc/tree/main/packages/context-handoff#readme","bugs":{"url":"https://github.com/voodootikigod/adlc/issues"},"bin":{"handoff":"bin/handoff.mjs"},"dist":{"shasum":"cbf6328c98f7c666592f1da1b81b99ae068235ba","tarball":"https://registry.npmjs.org/@adlc/context-handoff/-/context-handoff-1.10.0.tgz","fileCount":18,"integrity":"sha512-5ImwBVXM0DShhKRiRqZxZaiGgBMJVCKT9sBwNXlyTr1vapwpn6A98nQF1etnPmsevukgmoekkjqzQKSaKggoLg==","signatures":[{"sig":"MEUCIQDPcjD/SiROUj+GI7h/tTmaxnI8qBlIYpr4SCc3alwv3wIgD1aX1fiegQNNPDlr+e8khX5aSZ9HIbQizOmJzln+cFc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adlc%2fcontext-handoff@1.10.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":95476},"type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.mjs","./lib/lock.mjs":"./lib/lock.mjs","./lib/bands.mjs":"./lib/bands.mjs","./lib/final.mjs":"./lib/final.mjs","./lib/paths.mjs":"./lib/paths.mjs","./lib/thresholds.mjs":"./lib/thresholds.mjs","./lib/deny-marker.mjs":"./lib/deny-marker.mjs","./lib/resume-auth.mjs":"./lib/resume-auth.mjs","./lib/deny-persist.mjs":"./lib/deny-persist.mjs","./lib/mutation-gate.mjs":"./lib/mutation-gate.mjs","./lib/deny-lifecycle.mjs":"./lib/deny-lifecycle.mjs"},"gitHead":"905d493f596a62b4bc3236f97281a43982c1e300","scripts":{"test":"node --test test/*.test.mjs cli-test/*.test.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8ef0f4fb-2e41-4f97-8514-98756d21727e"}},"repository":{"url":"git+https://github.com/voodootikigod/adlc.git","type":"git","directory":"packages/context-handoff"},"_npmVersion":"11.19.0","description":"Context-rot handoff — absolute band signals, session-terminal deny (D1–D3), and operator CLI for write/resume/bypass/repair/unlock (F3 continuity).","directories":{},"_nodeVersion":"20.20.2","dependencies":{"@adlc/core":"1.10.0","@adlc/gate-manifest":"1.10.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/context-handoff_1.10.0_1786385968432_0.6055259653697114","host":"s3://npm-registry-packages-npm-production"}},"1.11.0":{"name":"@adlc/context-handoff","version":"1.11.0","keywords":["adlc","agentic","context-rot","handoff","f3"],"author":{"url":"@voodootikigod","name":"Chris Williams"},"license":"MIT","_id":"@adlc/context-handoff@1.11.0","maintainers":[{"name":"voodootikigod","email":"voodootikigod@gmail.com"}],"homepage":"https://github.com/voodootikigod/adlc/tree/main/packages/context-handoff#readme","bugs":{"url":"https://github.com/voodootikigod/adlc/issues"},"bin":{"handoff":"bin/handoff.mjs"},"dist":{"shasum":"b00e22f153d49bf18662ad711442719ee1a99043","tarball":"https://registry.npmjs.org/@adlc/context-handoff/-/context-handoff-1.11.0.tgz","fileCount":33,"integrity":"sha512-jUlAkIiFi8+QKbyIrptCelEUeDNg9ASj6CHQu/TPT5BqZ1T0UfRxLKRp5h84wN5Jb2zu/IvUK3nNDKjfxuWpfA==","signatures":[{"sig":"MEQCIHMBXj7UVew517CtywQbJRfHpogpqJD90ex4En2kL9tMAiBhaPQaTddxRyKvj1FI+Ltm8Y3J6R5U14U458vphIschg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adlc%2fcontext-handoff@1.11.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":280665},"type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.mjs","./lib/lock.mjs":"./lib/lock.mjs","./lib/bands.mjs":"./lib/bands.mjs","./lib/brief.mjs":"./lib/brief.mjs","./lib/final.mjs":"./lib/final.mjs","./lib/paths.mjs":"./lib/paths.mjs","./lib/redact.mjs":"./lib/redact.mjs","./lib/adapter.mjs":"./lib/adapter.mjs","./lib/capture.mjs":"./lib/capture.mjs","./lib/consume.mjs":"./lib/consume.mjs","./lib/rollback.mjs":"./lib/rollback.mjs","./lib/text-cap.mjs":"./lib/text-cap.mjs","./lib/supervise.mjs":"./lib/supervise.mjs","./lib/checkpoint.mjs":"./lib/checkpoint.mjs","./lib/thresholds.mjs":"./lib/thresholds.mjs","./lib/deny-marker.mjs":"./lib/deny-marker.mjs","./lib/resume-auth.mjs":"./lib/resume-auth.mjs","./lib/bypass-grant.mjs":"./lib/bypass-grant.mjs","./lib/deny-persist.mjs":"./lib/deny-persist.mjs","./lib/secret-scrub.mjs":"./lib/secret-scrub.mjs","./lib/mutation-gate.mjs":"./lib/mutation-gate.mjs","./lib/deny-lifecycle.mjs":"./lib/deny-lifecycle.mjs","./lib/continue-inputs.mjs":"./lib/continue-inputs.mjs","./lib/supervise-runtime.mjs":"./lib/supervise-runtime.mjs","./lib/transcript-extract.mjs":"./lib/transcript-extract.mjs"},"gitHead":"1e4e6f704912c912d9b3c87f57e077a48ca78c5d","scripts":{"test":"node --test test/*.test.mjs cli-test/*.test.mjs adapter-test/*.test.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8ef0f4fb-2e41-4f97-8514-98756d21727e"}},"repository":{"url":"git+https://github.com/voodootikigod/adlc.git","type":"git","directory":"packages/context-handoff"},"_npmVersion":"11.19.0","description":"Context-rot handoff — absolute band signals, session-terminal deny (D1–D3), and operator CLI for write/resume/bypass/repair/unlock (F3 continuity).","directories":{},"_nodeVersion":"20.20.2","dependencies":{"@adlc/core":"1.11.0","@adlc/tickets":"1.11.0","@adlc/gate-manifest":"1.11.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/context-handoff_1.11.0_1787594214852_0.3249108052061356","host":"s3://npm-registry-packages-npm-production"}},"1.11.1":{"name":"@adlc/context-handoff","version":"1.11.1","description":"Context-rot handoff — absolute band signals, session-terminal deny (D1–D3), and operator CLI for write/resume/bypass/repair/unlock (F3 continuity).","type":"module","license":"MIT","author":{"name":"Chris Williams","url":"@voodootikigod"},"repository":{"type":"git","url":"git+https://github.com/voodootikigod/adlc.git","directory":"packages/context-handoff"},"homepage":"https://github.com/voodootikigod/adlc/tree/main/packages/context-handoff#readme","bugs":{"url":"https://github.com/voodootikigod/adlc/issues"},"keywords":["adlc","agentic","context-rot","handoff","f3"],"bin":{"handoff":"bin/handoff.mjs"},"exports":{".":"./lib/index.mjs","./lib/thresholds.mjs":"./lib/thresholds.mjs","./lib/bands.mjs":"./lib/bands.mjs","./lib/mutation-gate.mjs":"./lib/mutation-gate.mjs","./lib/deny-lifecycle.mjs":"./lib/deny-lifecycle.mjs","./lib/deny-marker.mjs":"./lib/deny-marker.mjs","./lib/paths.mjs":"./lib/paths.mjs","./lib/final.mjs":"./lib/final.mjs","./lib/lock.mjs":"./lib/lock.mjs","./lib/resume-auth.mjs":"./lib/resume-auth.mjs","./lib/bypass-grant.mjs":"./lib/bypass-grant.mjs","./lib/deny-persist.mjs":"./lib/deny-persist.mjs","./lib/adapter.mjs":"./lib/adapter.mjs","./lib/secret-scrub.mjs":"./lib/secret-scrub.mjs","./lib/text-cap.mjs":"./lib/text-cap.mjs","./lib/capture.mjs":"./lib/capture.mjs","./lib/brief.mjs":"./lib/brief.mjs","./lib/transcript-extract.mjs":"./lib/transcript-extract.mjs","./lib/checkpoint.mjs":"./lib/checkpoint.mjs","./lib/consume.mjs":"./lib/consume.mjs","./lib/redact.mjs":"./lib/redact.mjs","./lib/rollback.mjs":"./lib/rollback.mjs","./lib/continue-inputs.mjs":"./lib/continue-inputs.mjs","./lib/supervise.mjs":"./lib/supervise.mjs","./lib/supervise-runtime.mjs":"./lib/supervise-runtime.mjs"},"dependencies":{"@adlc/core":"1.11.1","@adlc/gate-manifest":"1.11.1","@adlc/tickets":"1.11.1"},"scripts":{"test":"node --test test/*.test.mjs cli-test/*.test.mjs adapter-test/*.test.mjs"},"engines":{"node":">=18"},"publishConfig":{"access":"public","provenance":true},"gitHead":"c5772b4ff942bec77a34454c4fc888ae4e4b2c14","_id":"@adlc/context-handoff@1.11.1","_nodeVersion":"20.20.2","_npmVersion":"11.19.1","dist":{"integrity":"sha512-MzFFSvWDIGOsNIDv9Dzt8VQKBaNHxhjZPSsM29QylGk6IWquxVxksEajFvgSLh7cM2xEapCUguuYsYORgHuRhA==","shasum":"bc08f16508d7236fdbaac492651cc0906bd85fc7","tarball":"https://registry.npmjs.org/@adlc/context-handoff/-/context-handoff-1.11.1.tgz","fileCount":34,"unpackedSize":296311,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adlc%2fcontext-handoff@1.11.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDupqu0d+9ErVlbOy0+CTWqeYvlqM5CM1mpL7+WqnXdpQIgJmhfzhAvpI7xPyOIBdVC5RCZyin/rRthBDbv/AZZx7s="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8ef0f4fb-2e41-4f97-8514-98756d21727e"}},"directories":{},"maintainers":[{"name":"voodootikigod","email":"voodootikigod@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/context-handoff_1.11.1_1788949721574_0.19291524362353507"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-08T13:29:16.988Z","modified":"2026-09-09T10:28:42.122Z","1.8.0":"2026-08-08T13:29:17.284Z","1.9.0":"2026-08-08T20:32:44.342Z","1.10.0":"2026-08-10T18:19:28.588Z","1.11.0":"2026-08-24T17:56:55.014Z","1.11.1":"2026-09-09T10:28:41.701Z"},"bugs":{"url":"https://github.com/voodootikigod/adlc/issues"},"author":{"name":"Chris Williams","url":"@voodootikigod"},"license":"MIT","homepage":"https://github.com/voodootikigod/adlc/tree/main/packages/context-handoff#readme","keywords":["adlc","agentic","context-rot","handoff","f3"],"repository":{"type":"git","url":"git+https://github.com/voodootikigod/adlc.git","directory":"packages/context-handoff"},"description":"Context-rot handoff — absolute band signals, session-terminal deny (D1–D3), and operator CLI for write/resume/bypass/repair/unlock (F3 continuity).","maintainers":[{"name":"voodootikigod","email":"voodootikigod@gmail.com"}],"readme":"# @adlc/context-handoff\n\n**ADLC phase: P4 continuity (F3)** — absolute context bands, session-terminal\nmutation deny (D1–D3), and the operator CLI for\nwrite/resume/bypass/repair/unlock/continue. Binding design:\n[`docs/specs/context-rot-handoff.md`](../../docs/specs/context-rot-handoff.md).\n\n```sh\nadlc handoff write --session <id> [--ticket <id>] [--write] [--json]\nadlc handoff resume --session <consumer> --deny-session <denier> [--write]\nadlc handoff bypass --session <id> [--unbound-reason <text>] [--write]\nadlc handoff repair --session <id> --ticket <id> --content-hash <h> [--write]\nadlc handoff unlock --session <id> --pid <n> --started-at <iso> --host <h> --nonce <n> [--write]\nadlc handoff continue --deny-session <denier> [--session <new>] [--capture-from <transcript>] [--write]\nadlc handoff supervise [--dir .adlc] -- <command> [args...]\n```\n\nMutating `--write` requires `ADLC_MANIFEST_KEY` (never silent success).\n\n`--dir` names the ledger directory and its final path segment must be `.adlc`:\nartifacts and manifest evidence share that tree, and any other name is refused\nrather than splitting them. `repair` binds a deny that already exists and is\nstill open — it never creates one. `unlock` reclaims only a lock minted on this\nhost, so a dead-looking PID from another machine cannot evict a live session.\nA `bypass` grant on stdout is scoped to the calling adapter invocation; the\ndurable proof is the `context-handoff-bypass` manifest entry; an explicitly\nempty `--unbound-reason` is refused rather than degraded to a bound grant.\n\n`write`, `resume`, and `repair` all read-modify-write a deny marker, so each\nholds that session's `.adlc/handoffs/<id>.lock` (O_EXCL, released on exit) and\nexits 2 when a live session on this host holds it. `write` rebinds the marker\nonto the final it writes — `ensureDenyMarker` is idempotent, so without that a\nrefreshed hash would wedge every later resume — and refuses to unbind or to\nrefresh a consumed deny. When the manifest append fails, the run's file\nmutations are rolled back so no bind survives that nothing attests.\n\n## Continuation\n\n`continue` is the sanctioned recovery from a handoff deny: it captures the\ndenied session, binds the final to that capture, and consumes the deny for ONE\nsuccessor. The denier is never un-denied — D2 stays sticky and the work moves to\na new session. It composes capture → write → resume in a single run under the\ndenier's lock, records `context-handoff-continue`, and rolls back every file it\ntouched when that evidence append fails.\n\nThe capture body lives at `.adlc/handoffs/content/<session_id>.md` and is\nwritten only by host-privileged code; `isProtectedHandoffPath` denies agent\nwrites to `.adlc/handoffs/content/**`, and `continue` joins the mutating\nsubcommands an agent's shell must not run under deny. `content_hash` is sha256\nover the canonicalized capture body (LF line endings, no trailing whitespace),\nso re-deriving it from disk catches an edited capture that a valid signature\ncannot.\n\nDegrades with exit 2 and nothing consumed: an unbound deny (bind it with\n`repair` first), a consumed deny, a missing or corrupt `--capture-from` source,\na successor id that already holds a resume-auth, an id that cannot be safely\nquoted in the prompt, or an active ticket that disagrees with the deny's bind.\nThe successor id comes from `--session` or is minted by the command — never from\nagent input.\n\nA `--capture-from` transcript older than `HANDOFF_MAX_AGE_HOURS` contributes no\nmodel narrative: the brief still ships, with the omission stated in it. Age is\nread from the transcript's own newest timestamp, falling back to the file mtime\nonly when no entry carries one.\n\n**The bind is enforced, not advisory.** A capture-backed record carries\n`content_kind: 'capture'` on both the final and the deny marker. Every enforcing\nadapter re-derives the capture's sha256 from disk on each mutation check, so an\nedited, oversized, or deleted capture denies with a `capture_tamper:<session>`\nreason — including for the successor that already consumed the deny. It is plain\nsha256, so a keyless hook enforces exactly what the keyed CLI does. Records\nwritten before this field exist keep their previous semantics untouched: their\nhash was never re-derivable, so there is nothing to check. `repair` clears\n`content_kind` when it rebinds a hash, which is the documented way out of a lost\ncapture.\n\n**Reading a capture back.** Use `readVerifiedCapture(root, sessionId, expectedHash)`.\nIt returns the body only when the bytes on disk still hash to the value the\ndeny record and resume-auth are bound to; missing, oversize, and altered all\nfail closed with no body. Supervisors and session-start injectors must go\nthrough it rather than reading the file — a signature proves the hash was\nauthorized, never that the file still matches it.\n\nA **keyless** injector (a harness hook, which scrubs the manifest key before it\nimports anything) gets the content bind from this function and nothing more: it\ncannot verify the resume-auth's HMAC, so it must surface a capture as advisory\ncontext and say so, never as proof that the reading session is an authorized\ncontinuation. Authorization is decided where a key exists — the supervisor, and\nthe mutation gate that re-derives the same hash on every mutation.\n\n```js\nimport { readVerifiedCapture } from '@adlc/context-handoff/lib/capture.mjs';\n\nconst got = readVerifiedCapture(root, denySessionId, denyRecord.content_hash);\nif (!got.ok) return; // absent, oversize, or edited — inject nothing\n```\n\n**Capture content is redacted.** Credentials are stripped as part of composing\nthe brief, not as a step a caller remembers — the capture is persisted AND\npasted into the successor's prompt on one path. The shapes mirror the findings\nledger's (`packages/core/lib/ledger.mjs`), which refuses to commit a finding\ncontaining one; `test/capture-redact.test.mjs` pins the two behaviourally.\nRemoved spans leave an explicit `[adlc: redacted <kind>]` so a reader knows.\nPure-hex runs are exempt at any length — a brief's job includes quoting the\nsha256 `content_hash` the successor verifies against. Best-effort, not a proof\nof secret-freedom.\n\n**One successor id, one authorization.** The resume-auth is created with\n`O_EXCL`, so two continuations of different denies naming the same successor\ncannot both believe they authorized it — the denier's lock cannot serialize\nthat, since they hold different locks. The loser degrades with exit 2 and the\nwinner's grant is untouched.\n\n**Rollback never overwrites a stranger.** Every undo is a compare-and-swap on\nthe bytes this run wrote: an artifact another writer has since taken is left\nalone and named in the error, because the failure that triggers a rollback is\noften that writer.\n\n**Capture content is fenced.** Everything the brief carries is attacker-reachable\n— a branch name, a filename in `git status`, a ticket title, the previous\nsession's own words — so each section is wrapped in `<<<UNTRUSTED-CAPTURE-DATA`\n/ `END-UNTRUSTED>>>` markers, with those delimiters stripped from the content so\nthe fence cannot be closed from inside. `bootstrap_prompt` repeats, before and\nafter the body, that fenced content is recorded data rather than instructions.\n\n## Supervision\n\n`supervise` is the zero-touch path: it wraps a harness command, and when that\nsession hits a handoff deny it performs the whole recovery the operator would\notherwise perform by hand.\n\n```sh\nADLC_MANIFEST_KEY=$KEY adlc handoff supervise -- claude --model opus\n```\n\nIt mints the first session id, polls that session's deny marker (~2 s; `fs.watch`\nis not trusted), waits for the transcript to stop growing (5 s of stability, or\nthe child exiting) so the handoff summary is captured whole, runs\n`handoff continue --write`, terminates the superseded child (SIGTERM, then\nSIGKILL after 10 s), and respawns the harness with the successor id and the\nbootstrap prompt. Successor ids come from `continue`, never from the wrapper.\n\n**The key stays with the supervisor.** `ADLC_MANIFEST_KEY` is required up front\nand reaches only the continue step; `superviseChildEnv` strips it — along with\n`ADLC_ADMIN_KEY` — from every harness child.\n\n**Contract item 24.** Every spawn also drops `CLAUDECODE`,\n`CLAUDE_CODE_CHILD_SESSION`, `CLAUDE_CODE_SESSION_ID` and\n`CLAUDE_CODE_ENTRYPOINT`. A `claude` process that inherits them treats itself as\na nested child and silently stops writing its transcript — no error, no warning,\njust a continuation with nothing to capture. The scrub is applied per spawn, not\nonce at startup.\n\n**Degrade hands the session back.** An unbound deny, a corrupt transcript, a\npayload that cannot be trusted, or a capture that no longer matches its\n`content_hash` all stop the loop with exit 2, a single warning, and the\ncopy-pasteable `handoff continue` one-liner. The child is left running and\nnothing is consumed — a degrade is a decision for the operator, and killing\ntheir session first would take it away from them.\n\n**A failing harness is a failing supervisor.** The wrapper is what a script\nwaits on, so it never reports its own success for a session that failed: exit 3\nwhen the supervised command exits non-zero or is killed, exit 4 when it could\nnot be started at all. The harness's own code is carried in the message and the\nJSON payload rather than passed through, because 1 and 2 already mean something\nabout the supervision itself. A clean exit and an operator's Ctrl-C are both 0.\n\n**A crashed intermediate is reported, not failed.** A session that writes its\ndeny and then dies on its own — non-zero, or a signal the supervisor never sends\n— still hands off: the deny already said it was being replaced, and the\nsupervisor terminates it on the ordinary path anyway, so failing the run would\nfail every successful handoff. It is named on stderr and carried in the JSON as\n`abnormalExits`, because a handoff written by a session that crashed came from\ndifferent circumstances than one that stopped when asked.\n\n**A missing transcript is not a failed continuation.** The narrative is optional\n— the deterministic brief still carries ticket, evidence and git state — so a\nsession that dies before its transcript appears is continued without one rather\nthan degraded. It is reported loudly all the same, because a missing transcript\nis what the item-24 environment scrub failing looks like from the outside.\n\n**The capture is re-verified at the injection point.** The mutation gate already\nre-derives the hash on every evaluation, but that defends mutation; the bootstrap\nprompt is read by a model before it touches a tool, so the supervisor calls\n`readVerifiedCapture` again before spawning the successor.\n\n```js\nimport { WARN_PCT, HANDOFF_PCT, HARD_PCT } from '@adlc/context-handoff/lib/thresholds.mjs';\nimport { evaluateBands } from '@adlc/context-handoff/lib/bands.mjs';\nimport { evaluateMutationGate } from '@adlc/context-handoff/lib/mutation-gate.mjs';\n```\n\n```sh\nnode --test packages/context-handoff/test/*.test.mjs\n```\n\n## Deny-store expectation\n\n`loadDenyRecords` treats a missing `denies/` as unavailable only when\n`.adlc/.deny-store` exists (JSON `{schema,sessions}` written by `ensureDenyMarker`\nafter a verified marker; sessions[] makes selective marker delete fail closed). The sentinel is a sibling of `handoffs/` so deleting `handoffs/` alone\ncannot clear expectation; full signed per-deny ledger is still deferred.\nTicket-store presence alone does not expect denies. A legacy\n`.adlc/handoffs/.deny-store` is treated as expected and self-healed to the new\npath. `evaluateMarkerOnReentry` does **not** use the global sentinel for\nper-session `marker_vanished` — callers thread `denyEverWritten`. Unbound operator bypass may clear `D0:deny_store_unavailable` and\n`D3:invalid_record`.\n\nAdvisory nags (`nagSuppression`) are suppressed when remaining-to-hard is below\n`MIN_REMAINING_TO_HARD` (near-hard / handoff zone) so deny/handoff owns the\nsignal; this never affects mutation deny.\n","readmeFilename":"README.md"}