Context
During intake on #12461, the current origin/dev evidence showed the original B3 census is no longer a direct implementation target: the KB source-class leaf closed via #12467 / PR #12469, and ProtoSource is already covered by #12504 / PR #12505. The remaining orchestrator cluster is still present and is narrow enough for a one-PR leaf.
Verification run before filing:
git grep -n -E '(aiConfig|AiConfig)[^\\n]*\\?\\.' origin/dev -- 'ai/*.mjs' 'ai/**/*.mjs'
git grep -n -E 'sourcePaths\\?\\.' origin/dev -- 'ai/*.mjs' 'ai/**/*.mjs'
gh issue list --state all --search "B3 AiConfig orchestrator" --json number,title,state,assignees,labels,url --limit 30
gh issue list --state all --search "12461 orchestrator AiConfig" --json number,title,state,assignees,labels,url --limit 30
gh pr list --state all --search "orchestrator AiConfig optional OR orchestrator B3 OR 12461" --json number,title,state,mergeStateStatus,reviewDecision,headRefName,baseRefName,mergedAt,url --limit 30
ask_knowledge_base(query='existing ticket B3 AiConfig optional chaining orchestrator #12461', type='ticket')
The Problem
ADR 0019 classifies B3 as defensive ?. on AiConfig reads. The sanctioned pattern is to read resolved Provider leaves at the use site and let genuinely missing config paths fail loud. The current orchestrator code still contains optional-chain defenses on declared config subtrees, which preserves the old read-then-defend habit and weakens the SSOT contract that #12461 is trying to retire.
The Architectural Reality
ai/config.template.mjs declares the relevant Provider-owned paths:
engines.chroma.port: ai/config.template.mjs:284-288
orchestrator.chroma.maxRuntimeMs: ai/config.template.mjs:380-381
orchestrator.swarmHeartbeat.{targetSource,targets}: ai/config.template.mjs:402-430
orchestrator.devSyncRoots: ai/config.template.mjs:468-474
orchestrator.mlx.{enabled,model,port}: ai/config.template.mjs:513-517
orchestrator.lms.{enabled,model,port}: ai/config.template.mjs:539-542
openAiCompatible.host: ai/config.template.mjs:177-179
The current defensive read sites are in:
ai/daemons/orchestrator/Orchestrator.mjs:389
ai/daemons/orchestrator/Orchestrator.mjs:406-407
ai/daemons/orchestrator/Orchestrator.mjs:447-455
ai/daemons/orchestrator/Orchestrator.mjs:511
ai/daemons/orchestrator/Orchestrator.mjs:602
ai/daemons/orchestrator/Orchestrator.mjs:620
ai/daemons/orchestrator/Orchestrator.mjs:660
ai/daemons/orchestrator/daemon.mjs:185-188
The Fix
Replace optional-chain defenses on the declared orchestrator AiConfig subtrees with direct Provider reads at the use site.
Expected examples:
AiConfig.orchestrator.swarmHeartbeat?.targets -> AiConfig.orchestrator.swarmHeartbeat.targets
AiConfig.orchestrator.mlx?.enabled -> AiConfig.orchestrator.mlx.enabled
AiConfig.engines.chroma?.port -> AiConfig.engines.chroma.port
AiConfig.orchestrator?.devSyncRoots -> AiConfig.orchestrator.devSyncRoots
Preserve legitimate leaf-value semantics. For example, orchestrator.swarmHeartbeat.targets is a declared nullable leaf, so consumer logic may still normalize an empty/null leaf value after the direct subtree read. The prohibited part is defending against the declared subtree itself being absent.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
AiConfig.orchestrator.swarmHeartbeat.{targets,targetSource} consumers |
ADR 0019 B3; ai/config.template.mjs:402-430 |
Orchestrator reads the declared subtree directly. |
Nullable leaf values keep existing consumer semantics; missing subtree fails loud. |
Source JSDoc/comments only if wording currently implies defensive fallback. |
Focused orchestrator unit coverage plus git grep proving no remaining orchestrator swarmHeartbeat?. reads. |
AiConfig.orchestrator.mlx / AiConfig.orchestrator.lms consumers |
ADR 0019 B3; ai/config.template.mjs:513-542 |
Orchestrator reads enabled, model, and port directly from declared Provider subtrees. |
No subtree fallback; invalid/missing config fails loud. |
Source comments only if needed. |
Focused orchestrator unit coverage plus git grep proving no remaining `orchestrator.(mlx |
AiConfig.engines.chroma.port, AiConfig.orchestrator.chroma.maxRuntimeMs, AiConfig.orchestrator.devSyncRoots, AiConfig.openAiCompatible.host consumers |
ADR 0019 B3; ai/config.template.mjs:284-288, :380-381, :468-474, :177-179 |
Orchestrator and daemon read declared config leaves directly. |
No hidden subtree fallback; call-site logic for task state remains unchanged. |
Source comments only if needed. |
Focused orchestrator unit coverage plus git grep proving no remaining defensive reads on these declared subtrees. |
Decision Record Impact
Aligned with ADR 0019. This ticket does not amend or supersede ADR 0019; it is a cleanup leaf that applies ADR 0019's B3 sanctioned form to the orchestrator cluster.
Acceptance Criteria
- The orchestrator cluster no longer contains defensive optional chains on declared
AiConfig subtrees in ai/daemons/orchestrator/Orchestrator.mjs or ai/daemons/orchestrator/daemon.mjs.
- Legitimate nullable leaf handling is preserved. A
null swarmHeartbeat.targets leaf still falls through to resolver semantics; the subtree read itself is direct.
- Focused unit coverage is added or updated for the changed orchestrator behavior without mutating the shared
AiConfig singleton.
- Verification includes a targeted unit command,
git diff --check, and a git grep proof for the orchestrator B3 patterns removed by this ticket.
- The implementation PR resolves this ticket and references parent #12461.
Out of Scope
- KB source classes: already closed by #12467 / PR #12469.
- ProtoSource: covered by #12504 / PR #12505.
- Memory Core, graph extraction, GitLab workflow, BaseServer, or shared vector clusters from the broader #12461 census.
- B5 / C1 restructuring of
TaskDefinitions.mjs or other pass-through config architecture.
- Changing
learn/tree.json or public Learn navigation.
Avoided Traps
- Do not implement against the whole #12461 umbrella in one PR; it explicitly calls for cluster-sized cleanup leaves.
- Do not remove meaningful nullable leaf semantics while removing subtree defense.
- Do not import Neo/AiConfig into non-entrypoint helpers as part of this B3 leaf; C1/B5 cleanup is a separate shape.
- Do not add hidden defaults such as
|| {} or ?? <literal> to compensate for removed ?. defenses.
Related
Parent: #12461
Epic: #12456
Prior resolved leaf: #12467 / PR #12469
Sibling in flight: #12504 / PR #12505
Retrieval Hint: query_raw_memories("AiConfig ADR 0019 B3 orchestrator optional chain #12461") or query_documents(query='B3 AiConfig orchestrator optional chain', type='ticket').
Context
During intake on #12461, the current
origin/devevidence showed the original B3 census is no longer a direct implementation target: the KB source-class leaf closed via #12467 / PR #12469, and ProtoSource is already covered by #12504 / PR #12505. The remaining orchestrator cluster is still present and is narrow enough for a one-PR leaf.Verification run before filing:
git grep -n -E '(aiConfig|AiConfig)[^\\n]*\\?\\.' origin/dev -- 'ai/*.mjs' 'ai/**/*.mjs'git grep -n -E 'sourcePaths\\?\\.' origin/dev -- 'ai/*.mjs' 'ai/**/*.mjs'gh issue list --state all --search "B3 AiConfig orchestrator" --json number,title,state,assignees,labels,url --limit 30gh issue list --state all --search "12461 orchestrator AiConfig" --json number,title,state,assignees,labels,url --limit 30gh pr list --state all --search "orchestrator AiConfig optional OR orchestrator B3 OR 12461" --json number,title,state,mergeStateStatus,reviewDecision,headRefName,baseRefName,mergedAt,url --limit 30ask_knowledge_base(query='existing ticket B3 AiConfig optional chaining orchestrator #12461', type='ticket')The Problem
ADR 0019 classifies B3 as defensive
?.onAiConfigreads. The sanctioned pattern is to read resolved Provider leaves at the use site and let genuinely missing config paths fail loud. The current orchestrator code still contains optional-chain defenses on declared config subtrees, which preserves the old read-then-defend habit and weakens the SSOT contract that #12461 is trying to retire.The Architectural Reality
ai/config.template.mjsdeclares the relevant Provider-owned paths:engines.chroma.port:ai/config.template.mjs:284-288orchestrator.chroma.maxRuntimeMs:ai/config.template.mjs:380-381orchestrator.swarmHeartbeat.{targetSource,targets}:ai/config.template.mjs:402-430orchestrator.devSyncRoots:ai/config.template.mjs:468-474orchestrator.mlx.{enabled,model,port}:ai/config.template.mjs:513-517orchestrator.lms.{enabled,model,port}:ai/config.template.mjs:539-542openAiCompatible.host:ai/config.template.mjs:177-179The current defensive read sites are in:
ai/daemons/orchestrator/Orchestrator.mjs:389ai/daemons/orchestrator/Orchestrator.mjs:406-407ai/daemons/orchestrator/Orchestrator.mjs:447-455ai/daemons/orchestrator/Orchestrator.mjs:511ai/daemons/orchestrator/Orchestrator.mjs:602ai/daemons/orchestrator/Orchestrator.mjs:620ai/daemons/orchestrator/Orchestrator.mjs:660ai/daemons/orchestrator/daemon.mjs:185-188The Fix
Replace optional-chain defenses on the declared orchestrator
AiConfigsubtrees with direct Provider reads at the use site.Expected examples:
AiConfig.orchestrator.swarmHeartbeat?.targets->AiConfig.orchestrator.swarmHeartbeat.targetsAiConfig.orchestrator.mlx?.enabled->AiConfig.orchestrator.mlx.enabledAiConfig.engines.chroma?.port->AiConfig.engines.chroma.portAiConfig.orchestrator?.devSyncRoots->AiConfig.orchestrator.devSyncRootsPreserve legitimate leaf-value semantics. For example,
orchestrator.swarmHeartbeat.targetsis a declared nullable leaf, so consumer logic may still normalize an empty/null leaf value after the direct subtree read. The prohibited part is defending against the declared subtree itself being absent.Contract Ledger Matrix
AiConfig.orchestrator.swarmHeartbeat.{targets,targetSource}consumersai/config.template.mjs:402-430git grepproving no remaining orchestratorswarmHeartbeat?.reads.AiConfig.orchestrator.mlx/AiConfig.orchestrator.lmsconsumersai/config.template.mjs:513-542enabled,model, andportdirectly from declared Provider subtrees.git grepproving no remaining `orchestrator.(mlxAiConfig.engines.chroma.port,AiConfig.orchestrator.chroma.maxRuntimeMs,AiConfig.orchestrator.devSyncRoots,AiConfig.openAiCompatible.hostconsumersai/config.template.mjs:284-288,:380-381,:468-474,:177-179git grepproving no remaining defensive reads on these declared subtrees.Decision Record Impact
Aligned with ADR 0019. This ticket does not amend or supersede ADR 0019; it is a cleanup leaf that applies ADR 0019's B3 sanctioned form to the orchestrator cluster.
Acceptance Criteria
AiConfigsubtrees inai/daemons/orchestrator/Orchestrator.mjsorai/daemons/orchestrator/daemon.mjs.nullswarmHeartbeat.targetsleaf still falls through to resolver semantics; the subtree read itself is direct.AiConfigsingleton.git diff --check, and agit grepproof for the orchestrator B3 patterns removed by this ticket.Out of Scope
TaskDefinitions.mjsor other pass-through config architecture.learn/tree.jsonor public Learn navigation.Avoided Traps
|| {}or?? <literal>to compensate for removed?.defenses.Related
Parent: #12461
Epic: #12456
Prior resolved leaf: #12467 / PR #12469
Sibling in flight: #12504 / PR #12505
Retrieval Hint:
query_raw_memories("AiConfig ADR 0019 B3 orchestrator optional chain #12461")orquery_documents(query='B3 AiConfig orchestrator optional chain', type='ticket').