ADLC
Toolkit

context-handoff

Absolute context-band signals, session-terminal mutation deny (D1–D3), and the operator CLI for write/resume/bypass/repair/unlock — F3 continuity handoff.

context-handoff

ADLC phase: P4 continuity (F3) · Library + CLI: absolute context bands, session-terminal mutation deny (D1–D3), and operator handoff commands.

The ADLC lifecycle, with P4 highlighted. Read the theory for P4

What it defends against

Long sessions rot: depth, transcript size, and context-window fill climb past the point where the agent still holds the plot. context-handoff freezes the absolute band thresholds and the session-terminal deny contracts that later harness adapters and build-gate will share — so no local copy of the numbers can drift, and a deny written once cannot silently clear when a marker file disappears.

Slice 2 adds the operator/host CLI (adlc handoff …) that adapters and humans call to write finals, resume/consume denies, bypass, repair binds, and unlock. Harness adapters and build-gate migration land in later slices.

Absolute bands

Signals are OR-joined across available kinds (pct, depth, bytes). Missing kinds are ignored; present-but-malformed or out-of-domain values fail closed as hard. Floor is never subtracted from band comparators (no floor-delta bands).

Bandpctdepthbytes
warn≥ 50≥ 20≥ 128 KiB
handoff≥ 60≥ 30≥ 192 KiB
hard≥ 80≥ 40≥ 256 KiB

Advisory nags (nagSuppression) may be suppressed near hard so deny/handoff owns the signal; that path never affects mutation deny.

Deny lifecycle (D1–D3)

CodeMeaning
D1Deny active for the denier session — mutations blocked.
D2Deny sticky across re-entry for that session until a verified consume.
D3Other open denies in the store also block mutations for non-denier sessions.

After a verified other-session consume, that record leaves D3 (status=consumed); the denier remains D2. Marker vanish / store-unavailable paths fail closed unless an unbound operator bypass clears the recoverable codes.

CLI (adlc handoff)

Dry-run by default. Pass --write to mutate. Mutating commands require ADLC_MANIFEST_KEY and append durable evidence to .adlc/manifest.jsonl — missing key exits 1 (never silent success).

adlc handoff write --session <id> [--ticket <id>] [--host <h>] [--content-hash <h>] [--write] [--json]
adlc handoff resume --session <consumer> --deny-session <denier> [--write] [--json]
adlc handoff bypass --session <id> [--unbound-reason <text>] [--write] [--json]
adlc handoff repair --session <id> --ticket <id> --content-hash <h> [--write] [--json]
adlc handoff unlock --session <id> --pid <n> --started-at <iso> --host <h> --nonce <n> [--write] [--json]
CommandRole
writeFinal checkpoint + ensure deny marker / .adlc/.deny-store
resumeOther-session consume (same-session exits 2); writes resume-auth cache
bypassBound (session only) vs unbound (--unbound-reason) one-shot grant
repairPrivileged host bind of an existing open deny ticket_id+content_hash
unlockReclaim lock only when the PID is dead, the lock is this host's, and all fields match

repair never creates a deny: a missing or already-consumed marker exits 1, so the command that relaxes a deny cannot arm one. resume makes the manifest entry durable before it flips the record (outcome: authorized attests the authorization, not the persisted consume), and rolls the resume-auth cache back if anything fails — a failed resume leaves the deny open. The bypass grant printed on stdout is scoped to the calling adapter invocation; the durable proof is the context-handoff-bypass manifest entry. An explicitly empty --unbound-reason exits 1 rather than handing back the bound grant that cannot clear what the operator asked to override.

write is the refresh path, so it rebinds an open marker onto the final it writes; a refresh that would unbind one (dropping --ticket) or refresh a consumed one exits 1 instead of leaving a final and a deny that disagree. Every command that mutates a marker — write, resume, repair — runs under that session's .lock and exits 2 when a live session on this host holds it, so one deny authorizes exactly one successor. If evidence cannot be appended, the files written in that run are rolled back: un-evidenced state is un-auditable state. A hard-killed process leaves its lock behind for unlock to reclaim.

Artifact paths (under the repo root; --dir names the .adlc directory, and its final path segment must be .adlc so artifacts and evidence stay together):

  • Final: .adlc/handoffs/finals/<session_id>.json
  • Deny: .adlc/handoffs/denies/<session_id>.json + .adlc/.deny-store
  • Resume-auth: .adlc/handoffs/<session_id>.resume-auth.json
  • Lock: .adlc/handoffs/<session_id>.lock

Library usage

import { WARN_PCT, HANDOFF_PCT, HARD_PCT } from '@adlc/context-handoff/lib/thresholds.mjs';
import { evaluateBands, nagSuppression } from '@adlc/context-handoff/lib/bands.mjs';
import { evaluateMutationGate } from '@adlc/context-handoff/lib/mutation-gate.mjs';
import { ensureDenyMarker, loadDenyRecords } from '@adlc/context-handoff/lib/deny-marker.mjs';

const bands = evaluateBands({ pct: 65, depth: 12 });
// → { warn: true, handoff: true, hard: false }
node --test packages/context-handoff/test/*.test.mjs

Binding design: docs/specs/context-rot-handoff.md and the package README.

Go deeper

packages/context-handoff

On this page