Context
Operator-driven friction→gold from a values discussion (2026-08-04 evening): "models excel at spotting patterns. e.g. file writer lockings (canary) => do we use this in e.g. 3+ spots? would an own helper class win?" — measured on the spot, and the Rule of Three passed long ago. Live latest-open sweep at filing: latest 15 checked (#16495-#16513 window), no equivalent; #16488/#16495 own the SDK-boundary/marked-bridge concern, not lock/lease unification.
The Problem
Four separate implementations of the file-lock/lease concern live in ai/:
ai/daemons/embed/drainLock.mjs
ai/daemons/shared/fileLease.mjs
ai/daemons/wake/outboxLock.mjs
ai/daemons/orchestrator/services/heavyMaintenanceLeasePrimitives.mjs
plus ~12 further files carrying the pattern inline (grep -rlE "canary|lockFile|acquireLock" ai/ --include="*.mjs" → 16 files). Four owners of one concern means four falsifier sets, four witness suites, and four places for the next stale-lock/canary defect to hide — the class of bug that has repeatedly cost diagnosis time in the daemon family.
The Architectural Reality
ai/daemons/shared/fileLease.mjs is already positioned (by name and by home) as the natural absorber — the consolidation direction half-exists.
- The four implementations predate each other across different daemon lanes (embed drain, wake outbox, orchestrator heavy-maintenance); their semantics need a comparison pass BEFORE unification: TTL/staleness handling, holder identity, release-on-crash behavior, and reentrancy differ in ways a naive merge would flatten. The comparison table is the first deliverable.
- Deployment redline: the plane runs from this tree — consumers migrate in place, imports stay working at every commit, no directory moves in this ticket.
The Fix
- Comparison table of the four implementations' contracts (TTL, holder identity, crash-release, reentrancy, fsync/atomicity) — differences become explicit options on the unified helper, never silent flattenings.
- Absorb the other three into
fileLease (or its successor shape if the table demands one), one consumer family per commit.
- One witness suite owning the unified contract (stale-lease takeover, crash-release, contention), replacing the per-copy coverage.
If the comparison table reveals genuinely incompatible semantics, the honest outcome may be TWO helpers (lock vs lease) — the ticket's success criterion is one owner PER CONCERN, not one file at any cost. Driver may split into a small epic if the consumer migration exceeds one reviewable PR; this ticket is the license.
Acceptance Criteria
Out of Scope
- The SDK boundary / module-scope import class (
#16488, #16495).
- Any directory restructuring beyond the absorbed modules themselves.
Decision Record impact
none — consolidation within the landed daemon architecture.
Related
#16488, #16495 (adjacent boundary work — fold, don't duplicate), the ai-folder debt census ticket filed alongside this one.
Origin Session ID: 1913de09-6dc0-4d1e-a9a3-b51c33b46cdc
Retrieval Hint: query_raw_memories("four lock lease implementations fileLease drainLock outboxLock heavyMaintenanceLeasePrimitives rule of three")
Context
Operator-driven friction→gold from a values discussion (2026-08-04 evening): "models excel at spotting patterns. e.g. file writer lockings (canary) => do we use this in e.g. 3+ spots? would an own helper class win?" — measured on the spot, and the Rule of Three passed long ago. Live latest-open sweep at filing: latest 15 checked (
#16495-#16513window), no equivalent;#16488/#16495own the SDK-boundary/marked-bridge concern, not lock/lease unification.The Problem
Four separate implementations of the file-lock/lease concern live in
ai/:ai/daemons/embed/drainLock.mjsai/daemons/shared/fileLease.mjsai/daemons/wake/outboxLock.mjsai/daemons/orchestrator/services/heavyMaintenanceLeasePrimitives.mjsplus ~12 further files carrying the pattern inline (
grep -rlE "canary|lockFile|acquireLock" ai/ --include="*.mjs"→ 16 files). Four owners of one concern means four falsifier sets, four witness suites, and four places for the next stale-lock/canary defect to hide — the class of bug that has repeatedly cost diagnosis time in the daemon family.The Architectural Reality
ai/daemons/shared/fileLease.mjsis already positioned (by name and by home) as the natural absorber — the consolidation direction half-exists.The Fix
fileLease(or its successor shape if the table demands one), one consumer family per commit.If the comparison table reveals genuinely incompatible semantics, the honest outcome may be TWO helpers (lock vs lease) — the ticket's success criterion is one owner PER CONCERN, not one file at any cost. Driver may split into a small epic if the consumer migration exceeds one reviewable PR; this ticket is the license.
Acceptance Criteria
ai/resolves to a shared helper; the absorbed modules are gone or are thin re-exports staged for removal.Out of Scope
#16488,#16495).Decision Record impact
none — consolidation within the landed daemon architecture.
Related
#16488,#16495(adjacent boundary work — fold, don't duplicate), the ai-folder debt census ticket filed alongside this one.Origin Session ID: 1913de09-6dc0-4d1e-a9a3-b51c33b46cdc
Retrieval Hint:
query_raw_memories("four lock lease implementations fileLease drainLock outboxLock heavyMaintenanceLeasePrimitives rule of three")