Context
PR #14941 is the second bounded cleanup batch under parent epic #12456. It introduces owning orchestrator.dataDir and orchestrator.dbPath leaves and removes duplicated environment/default derivations from the orchestrator daemon path.
A prior #12456 census correctly classified the daemon path as a pre-bootstrap exception because no owning leaf existed then. This ticket changes that premise deliberately: once the leaves exist, all post-bootstrap consumers must obey ADR 0019’s direct use-site-read contract. The current PR’s Config Template SSOT lint falsifies the first implementation shape because taskDefinitions.mjs captures and re-exports the resolved leaves at module scope.
This leaf exists so the one-PR batch has a native, non-epic close target. Live latest-open, local archive, and recent A2A claim sweeps immediately before creation found no equivalent ticket.
The Problem
The current orchestrator paths are re-derived from environment variables and literals in more than one module. Moving those derivations into AiConfig is correct, but replacing them with:
export const DEFAULT_DB_PATH = AiConfig.orchestrator.dbPath;
export const DEFAULT_DATA_DIR = AiConfig.orchestrator.dataDir;
would preserve ADR 0019 §3 B1 and freeze reactive provider values at import time. It also makes a non-entrypoint helper import Neo/AiConfig merely to export config primitives. Hosted CI catches both exported config values and module-scope primitive leaf capture.
The Architectural Reality
ai/config.template.mjs owns Agent OS deployment leaves and environment bindings.
ai/daemons/orchestrator/daemon.mjs is an entrypoint; it may import AiConfig and read resolved leaves after the config-freshness guard.
ai/daemons/orchestrator/taskDefinitions.mjs is a non-entrypoint helper and the existing B1/C1 precedent named by ADR 0019.
- Runtime services that consume data/database paths must read the owning leaf at the use site. A pure defaults module is allowed only for a genuine pre-bootstrap boundary and must contain literals/env names with no Neo import.
- PID/log path construction must stay lazy enough that stale overlays reach
assertConfigFresh instead of crashing before the actionable guard.
The structure-map gate confirms these are existing owners under ai/daemons/orchestrator/ and ai/daemons/orchestrator/services/. No new service or file placement is required by default.
The Fix
- Add root
orchestrator.dataDir and orchestrator.dbPath leaves with the current literal defaults and environment names.
- Remove duplicate raw environment/default derivations.
- Remove module-scope AiConfig leaf captures and config-value re-exports from
taskDefinitions.mjs.
- Migrate consumers to direct use-site reads, without value threading or hidden fallbacks.
- Keep daemon PID/log path reads post-freshness-guard and fail loud on a missing leaf.
- Add regressions proving provider refresh/override behavior reaches consumers and that the standalone daemon still boots through the intended guard path.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
AiConfig.orchestrator.dataDir |
ADR 0019 + root config template |
Owning leaf for daemon/runtime data path |
Existing literal default via leaf only |
Leaf JSDoc |
env-binding + daemon boot specs |
AiConfig.orchestrator.dbPath |
ADR 0019 + root config template |
Owning leaf for graph DB path |
Existing literal default via leaf only |
Leaf JSDoc |
env-binding + consumer specs |
| Orchestrator consumers |
ADR 0019 §3 B1 / §5 |
Direct resolved reads at use sites |
No defensive fallback |
Existing class/method JSDoc |
freshness/override regressions |
| PID/log boot path |
Config freshness guard |
Resolve after freshness assertion |
Actionable migrate-config failure |
Daemon JSDoc |
stale-overlay/boot probe |
Decision Record impact
Aligned with ADR 0019. The ticket applies its A1/B1/C1 rules and does not amend the ADR.
Acceptance Criteria
Out of Scope
- Broad cleanup of unrelated task-definition fields or scheduler behavior.
- Changing the literal path defaults or environment variable names.
- Introducing a generic config pass-along object.
- Splitting leaf declarations and consumer migration into separate PRs.
Avoided Traps
- Treating “env derivation removed” as sufficient while retaining B1 exports.
- Eager module-load path construction before config freshness is checked.
- Coupling a genuine pre-bootstrap wrapper to Neo merely to read AiConfig.
- Leaving the parent epic as the PR close target.
Related
- Parent epic: #12456
- Implementation PR: #14941
- Adjacent batches: #14935, #14942, and leaf #14956
- Mechanical lint lane: #14500
- Authority:
learn/agentos/decisions/0019-aiconfig-reactive-provider-ssot.md
Origin Session ID: de713f27-0e82-4960-b4c6-f281e0c36449
Handoff Retrieval Hints
- Memory Core:
ADR 0019 taskDefinitions DEFAULT_DB_PATH DEFAULT_DATA_DIR module-scope capture
- Exact implementation anchor: PR #14941 at
dd6211c45370223b4c58984932ee40006ab4a21e
Context
PR #14941 is the second bounded cleanup batch under parent epic #12456. It introduces owning
orchestrator.dataDirandorchestrator.dbPathleaves and removes duplicated environment/default derivations from the orchestrator daemon path.A prior #12456 census correctly classified the daemon path as a pre-bootstrap exception because no owning leaf existed then. This ticket changes that premise deliberately: once the leaves exist, all post-bootstrap consumers must obey ADR 0019’s direct use-site-read contract. The current PR’s Config Template SSOT lint falsifies the first implementation shape because
taskDefinitions.mjscaptures and re-exports the resolved leaves at module scope.This leaf exists so the one-PR batch has a native, non-epic close target. Live latest-open, local archive, and recent A2A claim sweeps immediately before creation found no equivalent ticket.
The Problem
The current orchestrator paths are re-derived from environment variables and literals in more than one module. Moving those derivations into AiConfig is correct, but replacing them with:
export const DEFAULT_DB_PATH = AiConfig.orchestrator.dbPath; export const DEFAULT_DATA_DIR = AiConfig.orchestrator.dataDir;would preserve ADR 0019 §3 B1 and freeze reactive provider values at import time. It also makes a non-entrypoint helper import Neo/AiConfig merely to export config primitives. Hosted CI catches both exported config values and module-scope primitive leaf capture.
The Architectural Reality
ai/config.template.mjsowns Agent OS deployment leaves and environment bindings.ai/daemons/orchestrator/daemon.mjsis an entrypoint; it may import AiConfig and read resolved leaves after the config-freshness guard.ai/daemons/orchestrator/taskDefinitions.mjsis a non-entrypoint helper and the existing B1/C1 precedent named by ADR 0019.assertConfigFreshinstead of crashing before the actionable guard.The structure-map gate confirms these are existing owners under
ai/daemons/orchestrator/andai/daemons/orchestrator/services/. No new service or file placement is required by default.The Fix
orchestrator.dataDirandorchestrator.dbPathleaves with the current literal defaults and environment names.taskDefinitions.mjs.Contract Ledger
AiConfig.orchestrator.dataDirAiConfig.orchestrator.dbPathDecision Record impact
Aligned with ADR 0019. The ticket applies its A1/B1/C1 rules and does not amend the ADR.
Acceptance Criteria
process.env.DEFAULT_DB_PATH/DEFAULT_DATA_DIRexport captures an AiConfig leaf at module scope.Resolves #Nfor this leaf.Out of Scope
Avoided Traps
Related
learn/agentos/decisions/0019-aiconfig-reactive-provider-ssot.mdOrigin Session ID: de713f27-0e82-4960-b4c6-f281e0c36449
Handoff Retrieval Hints
ADR 0019 taskDefinitions DEFAULT_DB_PATH DEFAULT_DATA_DIR module-scope capturedd6211c45370223b4c58984932ee40006ab4a21e