Context
Bounded leaf for #16223 AC-1 + AC-2, filed because those two ACs are the only one-PR-resolvable piece of that ticket and they are a hard prerequisite for #14418. #16223 itself is a seven-AC consumer ticket whose restore obligation it describes as "currently unimplementable as specified" — it cannot close on this work, and a PR claiming otherwise would be false.
The harm this eventually unblocks is measured, not hypothetical: on CPU-only hardware the miniSummary backfill deferred ~100% of items and burned ~2.3 CPU-cores average for days producing zero summaries, because a generation window calibrated on GPU hardware straddles the whole corpus-size distribution on CPU. #14418 (graduated from D#13873) owns the controller that widens such a window. This leaf gives that controller something to actuate.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-24T19:47Z; no equivalent found. A2A in-flight claim sweep over the last 30 messages at the same time: no [lane-claim] on this scope. resources/content grep for the env names returns only #16223, #14418, and #16374 (an unrelated, closed action-set ticket).
The Problem
The controller cannot reach this window, and the reason is a missing surface rather than a missing mechanism.
reconfigure shipped — RecoveryActuatorService lists it in DEFAULT_ACTIONS and routes it to reconfigureComposeService({knob, knobValues, …}). What it moves is a knob from a closed registry, and it moves it by delivering leaf→env bindings into a compose service. Both halves of that path are absent for this window:
- No registry entry.
RECOVERY_KNOBS in ai/services/memory-core/helpers/recoveryKnobRegistry.mjs is the closed set of knobs the actuator may turn; the miniSummary window is not in it, so isKnownKnob refuses it and no prescription can name it.
- No compose surface. Neither env name appears anywhere under
ai/deploy/ — verified with a positive control: NEO_KB_ASK_MODEL resolves in five compose files by the same search, so the absence is a measurement rather than a search artifact.
The Architectural Reality
ai/configBase.mjs — the two leaves exist with env bindings: miniSummaryTimeoutMs (30000, NEO_MC_MINI_SUMMARY_TIMEOUT_MS) and generateMiniSummaryTimeoutMs (20000, NEO_MC_GENERATE_MINI_SUMMARY_TIMEOUT_MS). Binding without surface.
ai/services/memory-core/helpers/recoveryKnobRegistry.mjs — its own module docstring already anticipates this exact case: "A knob is the unit, not a config leaf. Several leaves can only move together — a nested timeout…". This window is that sentence's referent, which is strong evidence the registry is the right home rather than a new mechanism.
container-memory-ceiling is the precedent entry to follow in shape: description, a single declared serviceKey the actuator matches against to refuse a knob/target mismatch, requires for runtime-resolved context, a selector that proposes values keyed by registry leaf path, and the leaf→env bindings the delivery reads. The controller names a semantic knob and never learns which leaf moves.
- The nesting is load-bearing and is why the pair cannot be two knobs. Per
#16223: the inner leaf sits inside buildMiniSummary, whose try/catch swallows its rejection into a falsy return and the sweep's deferred branch; the outer leaf wraps summarize(…) from outside that guard, so its rejection escapes to the sweep's catch. Widening only the inner leaf is a silent no-op at the outer bound, and crossing the outer bound flips every item from the falsy path to the thrown path — blinding any detector keyed to one branch at exactly the moment the actuation is working hardest.
The Fix
- One
RECOVERY_KNOBS entry for the window, declaring both leaves in application order with their env bindings, serviceKey: 'mc-server', and the ordered-pair invariant: the inner leaf stays strictly below the outer, and the bound is declared against the outer.
- The ceiling is relative to a measured baseline, not an absolute constant — an absolute ceiling needs recalibration on every hardware change, which is the defect
#16223 exists to remove.
- The compose surface for both env names on the
mc-server service, following the existing NEO_MC_* passthrough idiom, across the compose files that already carry that service's knobs.
Deliberately NOT here: any detector, any controller, any widening policy. Those are #14418's, and building them here would duplicate a graduated design.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
RECOVERY_KNOBS (new entry) |
ADR-0026 closed-set discipline; #16223 AC-2 |
one knob moving both leaves as an ordered pair; inner strictly below outer; bound declared against the outer; ceiling relative to a measured baseline |
an out-of-band or inverted proposal is refused by validation, never clamped — a clamp turns a terminal into an endless stream of fake-success raises |
registry JSDoc |
unit spec: ordered-pair invariant, refusal on inversion, refusal above ceiling |
mc-server compose env (NEO_MC_MINI_SUMMARY_TIMEOUT_MS, NEO_MC_GENERATE_MINI_SUMMARY_TIMEOUT_MS) |
existing NEO_MC_* passthrough idiom |
both names reach the container so reconfigureComposeService can deliver them |
absent env ⇒ the leaf default stands, unchanged from today |
compose comment |
grep asserting both names present, with a positive control |
isKnownKnob / knobLeafPaths / knob env bindings |
existing registry readers |
accept the new knob and return its ordered leaf pair |
unknown knob still returns empty and callers still gate on isKnownKnob |
— |
spec asserting the readers resolve the new knob |
| the actuator's action set |
ADR-0026 AC-9 |
unchanged — this adds a knob, never an action |
— |
— |
spec asserts DEFAULT_ACTIONS is byte-identical |
Decision Record impact
aligned-with ADR 0026 — this adds one entry to the actuator's closed knob registry, which ADR-0026 provides for explicitly; no action class is added, removed, or re-routed, and the §2.4 actuator matrix is untouched. aligned-with ADR 0019 — no leaf is declared, moved, or re-derived; the two leaves already exist and only gain a compose surface.
⚠️ Scope corrected 2026-08-24, twenty minutes after filing — SIX of seven ACs were already shipped
I filed this after a design sweep and the sweep was wrong. RECOVERY_KNOBS already contains 'minisummary-generation-window', with both leaves carrying role: 'inner' / role: 'outer' and their env bindings, an inner-strictly-below-outer invariant whose stated reason is the branch-flip this ticket describes, and a second invariant — outer-leaves-room-for-a-draining-sweep — that expresses the ceiling relative to memoryService.miniSummaryBackfillMaxRunMs and refuses on an unresolvable budget. It is covered by recoveryKnobRegistry.spec.mjs, recoveryOverrideStore.spec.mjs, and RecoveryActuatorService.spec.mjs.
So the original AC-2 through AC-6 were already satisfied, some of them more thoroughly than I specified them.
Why my sweep missed it, because the mechanism is reusable and it is not "I did not look". I grepped RECOVERY_KNOBS, opened the container-memory-ceiling entry, read it closely — and never enumerated the object's keys. One grep -oE "^ '[a-z-]+':" returns both entries. I read the first member of a collection and generalised to the collection. That is the same failure as searching a mechanism's vocabulary instead of the mechanism, one level down: the check was precise and the population was one element.
What is actually left — and it is real, verified twice: neither env name appears anywhere under ai/deploy/, positive control NEO_KB_ASK_MODEL resolving in five compose files by the same search. The knob is fully declared and fully bounded, and reconfigureComposeService still cannot deliver it, because the container never receives the variables.
This ticket is reshaped to that one deliverable. The ACs below replace the original seven.
Acceptance Criteria
Out of Scope
- The homeostatic controller, its setpoint, and any widening policy —
#14418.
- The detector that must count a timeout on both branches —
#16223 AC-3.
- The reason-scoped restore and its tally reset —
#16223 AC-4/5/6, which that ticket records as currently unimplementable as specified.
- Provider-side cancellation of timed-out requests —
#16223 AC-7.
- Any change to the leaves' default values. This exposes the knob; it does not turn it.
Avoided Traps
- Two knobs instead of one. The registry keys by knob precisely because these leaves cannot move independently; splitting them reintroduces the silent-no-op and branch-flip failure
#16223 documents.
- An absolute ceiling. Cheaper to write and wrong on the next hardware change — the exact defect the parent ticket exists to remove.
- Clamping an out-of-band proposal. Clamping hides the terminal; refusal makes it observable. The
container-memory-ceiling entry states this reasoning for its own cap and it transfers.
- Building the detector here because it is adjacent.
#14418 graduated with a cross-family disposition; duplicating it would be a parallel implementation of a decided design.
Related
#16223 (parent — this closes its AC-1 and AC-2) · #14418 (the controller that consumes this knob) · D#13873 (its graduation) · ADR 0026 · ADR 0019
Origin Session ID: cad88c79-073f-4816-aaa7-e779224f2af3
Retrieval Hint: miniSummary generation window knob registry compose surface ordered pair inner below outer relative ceiling
Context
Bounded leaf for
#16223AC-1 + AC-2, filed because those two ACs are the only one-PR-resolvable piece of that ticket and they are a hard prerequisite for#14418.#16223itself is a seven-AC consumer ticket whose restore obligation it describes as "currently unimplementable as specified" — it cannot close on this work, and a PR claiming otherwise would be false.The harm this eventually unblocks is measured, not hypothetical: on CPU-only hardware the miniSummary backfill deferred ~100% of items and burned ~2.3 CPU-cores average for days producing zero summaries, because a generation window calibrated on GPU hardware straddles the whole corpus-size distribution on CPU.
#14418(graduated fromD#13873) owns the controller that widens such a window. This leaf gives that controller something to actuate.Live latest-open sweep: checked the latest 20 open issues at 2026-08-24T19:47Z; no equivalent found. A2A in-flight claim sweep over the last 30 messages at the same time: no
[lane-claim]on this scope.resources/contentgrep for the env names returns only#16223,#14418, and#16374(an unrelated, closed action-set ticket).The Problem
The controller cannot reach this window, and the reason is a missing surface rather than a missing mechanism.
reconfigureshipped —RecoveryActuatorServicelists it inDEFAULT_ACTIONSand routes it toreconfigureComposeService({knob, knobValues, …}). What it moves is a knob from a closed registry, and it moves it by delivering leaf→env bindings into a compose service. Both halves of that path are absent for this window:RECOVERY_KNOBSinai/services/memory-core/helpers/recoveryKnobRegistry.mjsis the closed set of knobs the actuator may turn; the miniSummary window is not in it, soisKnownKnobrefuses it and no prescription can name it.ai/deploy/— verified with a positive control:NEO_KB_ASK_MODELresolves in five compose files by the same search, so the absence is a measurement rather than a search artifact.The Architectural Reality
ai/configBase.mjs— the two leaves exist with env bindings:miniSummaryTimeoutMs(30000,NEO_MC_MINI_SUMMARY_TIMEOUT_MS) andgenerateMiniSummaryTimeoutMs(20000,NEO_MC_GENERATE_MINI_SUMMARY_TIMEOUT_MS). Binding without surface.ai/services/memory-core/helpers/recoveryKnobRegistry.mjs— its own module docstring already anticipates this exact case: "A knob is the unit, not a config leaf. Several leaves can only move together — a nested timeout…". This window is that sentence's referent, which is strong evidence the registry is the right home rather than a new mechanism.container-memory-ceilingis the precedent entry to follow in shape:description, a single declaredserviceKeythe actuator matches against to refuse a knob/target mismatch,requiresfor runtime-resolved context, a selector that proposes values keyed by registry leaf path, and the leaf→env bindings the delivery reads. The controller names a semantic knob and never learns which leaf moves.#16223: the inner leaf sits insidebuildMiniSummary, whosetry/catchswallows its rejection into a falsy return and the sweep'sdeferredbranch; the outer leaf wrapssummarize(…)from outside that guard, so its rejection escapes to the sweep'scatch. Widening only the inner leaf is a silent no-op at the outer bound, and crossing the outer bound flips every item from the falsy path to the thrown path — blinding any detector keyed to one branch at exactly the moment the actuation is working hardest.The Fix
RECOVERY_KNOBSentry for the window, declaring both leaves in application order with their env bindings,serviceKey: 'mc-server', and the ordered-pair invariant: the inner leaf stays strictly below the outer, and the bound is declared against the outer.#16223exists to remove.mc-serverservice, following the existingNEO_MC_*passthrough idiom, across the compose files that already carry that service's knobs.Deliberately NOT here: any detector, any controller, any widening policy. Those are
#14418's, and building them here would duplicate a graduated design.Contract Ledger Matrix
RECOVERY_KNOBS(new entry)#16223AC-2mc-servercompose env (NEO_MC_MINI_SUMMARY_TIMEOUT_MS,NEO_MC_GENERATE_MINI_SUMMARY_TIMEOUT_MS)NEO_MC_*passthrough idiomreconfigureComposeServicecan deliver themisKnownKnob/knobLeafPaths/ knob env bindingsisKnownKnobDEFAULT_ACTIONSis byte-identicalDecision Record impact
aligned-with ADR 0026— this adds one entry to the actuator's closed knob registry, which ADR-0026 provides for explicitly; no action class is added, removed, or re-routed, and the §2.4 actuator matrix is untouched.aligned-with ADR 0019— no leaf is declared, moved, or re-derived; the two leaves already exist and only gain a compose surface.⚠️ Scope corrected 2026-08-24, twenty minutes after filing — SIX of seven ACs were already shipped
I filed this after a design sweep and the sweep was wrong.
RECOVERY_KNOBSalready contains'minisummary-generation-window', with both leaves carryingrole: 'inner'/role: 'outer'and their env bindings, aninner-strictly-below-outerinvariant whose stated reason is the branch-flip this ticket describes, and a second invariant —outer-leaves-room-for-a-draining-sweep— that expresses the ceiling relative tomemoryService.miniSummaryBackfillMaxRunMsand refuses on an unresolvable budget. It is covered byrecoveryKnobRegistry.spec.mjs,recoveryOverrideStore.spec.mjs, andRecoveryActuatorService.spec.mjs.So the original AC-2 through AC-6 were already satisfied, some of them more thoroughly than I specified them.
Why my sweep missed it, because the mechanism is reusable and it is not "I did not look". I grepped
RECOVERY_KNOBS, opened thecontainer-memory-ceilingentry, read it closely — and never enumerated the object's keys. Onegrep -oE "^ '[a-z-]+':"returns both entries. I read the first member of a collection and generalised to the collection. That is the same failure as searching a mechanism's vocabulary instead of the mechanism, one level down: the check was precise and the population was one element.What is actually left — and it is real, verified twice: neither env name appears anywhere under
ai/deploy/, positive controlNEO_KB_ASK_MODELresolving in five compose files by the same search. The knob is fully declared and fully bounded, andreconfigureComposeServicestill cannot deliver it, because the container never receives the variables.This ticket is reshaped to that one deliverable. The ACs below replace the original seven.
Acceptance Criteria
NEO_MC_MINI_SUMMARY_TIMEOUT_MSandNEO_MC_GENERATE_MINI_SUMMARY_TIMEOUT_MSreach themc-servercontainer, declared in the compose files that already carry that service's otherNEO_MC_*knobs, following the existing passthrough idiom.NEO_KB_ASK_MODELis the control used to establish the current absence.recoveryKnobRegistry.mjschanges. The knob is already declared, bounded and spec-covered; a diff there would mean this ticket re-derived what it should have read.Out of Scope
#14418.#16223AC-3.#16223AC-4/5/6, which that ticket records as currently unimplementable as specified.#16223AC-7.Avoided Traps
#16223documents.container-memory-ceilingentry states this reasoning for its own cap and it transfers.#14418graduated with a cross-family disposition; duplicating it would be a parallel implementation of a decided design.Related
#16223(parent — this closes its AC-1 and AC-2) ·#14418(the controller that consumes this knob) ·D#13873(its graduation) · ADR 0026 · ADR 0019Origin Session ID: cad88c79-073f-4816-aaa7-e779224f2af3
Retrieval Hint:
miniSummary generation window knob registry compose surface ordered pair inner below outer relative ceiling