LearnNewsExamplesServices
Frontmatter
id12613
titleRoute lifecycle wake knobs through AiConfig
stateClosed
labels
enhancementairefactoringarchitecturemodel-experience
assigneesneo-opus-grace
createdAtJun 6, 2026, 3:45 AM
updatedAtJun 6, 2026, 4:52 PM
githubUrlhttps://github.com/neomjs/neo/issues/12613
authorneo-gpt
commentsCount1
parentIssue12456
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 6, 2026, 4:52 PM

Route lifecycle wake knobs through AiConfig

Closed v13.0.0/archive-v13-0-0-chunk-16 enhancementairefactoringarchitecturemodel-experience
neo-gpt
neo-gpt commented on Jun 6, 2026, 3:45 AM

Context

Operator feedback on 2026-06-06 flagged the lifecycle idle-threshold pattern as an ADR 0019 shape error: the threshold should be an AiConfig leaf, not local process.env parsing. I V-B-A'd the current source after the #12605 review loop: PR #12605 already routes its Memory Core drift threshold through activeSessionIdleThresholdMs, but the wake/lifecycle scripts still carry separate direct env reads and hardcoded defaults.

Live latest-open sweep: checked the latest 20 open issues on 2026-06-06 via gh issue list --state open --limit 20 --json number,title,author,labels,url. Nearby open issues included #12612, #12609, #12565, #12536, #12479, #12456, #12450, #12435, #12402, #12401, #12190, #12186, #12153, #12123, #12075, #12073, #12065, #12008, #11976, and #11909; none owns this residual. Exact duplicate searches found only closed or adjacent tickets: IDLE_THRESHOLD_MS surfaced closed #10633; TRIO_WAKE_COOLDOWN_SECONDS surfaced closed #10626; NEO_TRIO_IDENTITIES surfaced closed #12438 and #10636; lifecycle idle threshold AiConfig found no issue. The Knowledge Base semantic path was attempted first but is currently unavailable (knowledgeBase collection missing), so this ticket is grounded in live GitHub plus local source evidence.

The Problem

ADR 0019 says AiConfig / child providers are the reactive Provider SSOT: leaf(default, env, type) owns env override, defaulting, and type decoding; consumers read resolved leaves at the use site. The wake lifecycle scripts still bypass that model:

  • ai/scripts/lifecycle/checkSunsetted.mjs:72 parses process.env.IDLE_THRESHOLD_MS and falls back to 10 * 60 * 1000.
  • ai/scripts/lifecycle/checkAllAgentIdle.mjs:59 parses process.env.NEO_TRIO_IDENTITIES with a hardcoded Neo-agent roster fallback.
  • ai/scripts/lifecycle/checkAllAgentIdle.mjs:62 parses process.env.IDLE_THRESHOLD_MS again.
  • ai/scripts/lifecycle/trioWakeCooldown.mjs:38 parses process.env.TRIO_WAKE_COOLDOWN_SECONDS and falls back to 600.
  • Unit tests in checkAllAgentIdle.spec.mjs and trioWakeCooldown.spec.mjs set those raw env vars directly, which keeps the env names load-bearing outside the Provider surface.

This is not the same as MCP tool argument validation: MCP tools already have OpenAPI/Zod tool shapes. The residual here is lifecycle-script configuration, so the right validation/defaulting primitive is the AiConfig Provider leaf set.

The Architectural Reality

  • ADR 0019 is the authority: read resolved leaves at use sites; never re-implement env resolution around the Provider SSOT.
  • ai/config.template.mjs already owns Tier-1 wake/orchestrator configuration, including wakeDaemonHeartbeatAlivePath, orchestrator.intervals.swarmHeartbeatMs, and orchestrator.swarmHeartbeat.{targetSource,targets}.
  • ai/daemons/orchestrator/Orchestrator.mjs already reads AiConfig.orchestrator.swarmHeartbeat.targets / targetSource and injects them into SwarmHeartbeatService.
  • ai/daemons/orchestrator/scheduling/swarmHeartbeat.mjs already contains the deployment-portable target resolver for self, active-local-team, active-subscribers, active-a2a-participants, and disabled.
  • checkAllAgentIdle.mjs currently has a second identity source via NEO_TRIO_IDENTITIES, which can drift from the resolver-backed heartbeat target contract.

The Fix

Implement a single, non-micro cleanup slice for the lifecycle wake knobs:

  1. Add one owning AiConfig leaf set for wake lifecycle policy values. Recommended owner: AiConfig.orchestrator.swarmHeartbeat or a sibling wake policy group in Tier-1 config, because these scripts are consumed by the orchestrator heartbeat lane. Do not duplicate the same value in both Tier-1 and Memory Core child config.
  2. Move the idle threshold used by checkSunsetted.mjs and checkAllAgentIdle.mjs to one resolved leaf. Preserve existing operator compatibility either by binding the leaf to the legacy env name IDLE_THRESHOLD_MS or by adding a documented deprecation bridge; do not keep direct use-site env parsing.
  3. Route all-agent identity selection through the existing swarm-heartbeat target contract where practical. If checkAllAgentIdle.mjs still needs an explicit override, make it a Provider-owned leaf and explain why it is distinct from orchestrator.swarmHeartbeat.targets / targetSource.
  4. Move trio wake cooldown TTL to an AiConfig leaf, preserving legacy TRIO_WAKE_COOLDOWN_SECONDS behavior or documenting a deprecation bridge.
  5. Update focused unit coverage so tests exercise Provider/env resolution, not raw script-local parsing. Keep subprocess coverage for the script entrypoints.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
Lifecycle idle threshold ADR 0019; checkSunsetted.mjs; checkAllAgentIdle.mjs One AiConfig leaf owns the default and env override; both scripts read the resolved value at use site Invalid/missing env falls back through the Provider leaf decoder/default, not local parseInt logic Leaf JSDoc in ai/config.template.mjs Unit tests prove env override + default; subprocess tests still pass
All-agent idle target set Epic #11829 AC2; orchestrator.swarmHeartbeat config; resolveSwarmHeartbeatTargets() Use the existing resolver-backed target contract or one Provider-owned explicit override with clear distinction External/fork profile must not silently fan out to Neo maintainer identities Leaf/helper JSDoc if a new explicit override remains Unit tests prove default, explicit override, and no hardcoded Neo roster fallback
Trio wake cooldown TTL #10626 precedent; ADR 0019 trioWakeCooldown.mjs reads a resolved AiConfig leaf for TTL seconds/ms Existing env behavior is preserved via leaf binding or explicit deprecation bridge Leaf JSDoc trioWakeCooldown.spec.mjs proves TTL default + override via Provider-owned config

Decision Record impact

aligned-with ADR 0019. This does not amend ADR 0019; it applies the sanctioned Provider-SSOT pattern to wake lifecycle policy knobs that #12438 left out of scope.

Acceptance Criteria

  • No direct process.env.IDLE_THRESHOLD_MS, process.env.NEO_TRIO_IDENTITIES, or process.env.TRIO_WAKE_COOLDOWN_SECONDS parsing remains in ai/scripts/lifecycle/*.mjs use sites.
  • checkSunsetted.mjs and checkAllAgentIdle.mjs read the same resolved AiConfig idle threshold leaf.
  • checkAllAgentIdle.mjs no longer carries a hardcoded Neo-maintainer roster fallback; it uses the existing heartbeat target contract or a Provider-owned explicit override with documented separation.
  • trioWakeCooldown.mjs reads a resolved AiConfig cooldown leaf.
  • Focused unit coverage proves defaults, env overrides, subprocess entrypoint behavior, and the no-hardcoded-roster fallback.
  • PR body documents the chosen compatibility policy for legacy env names.

Out of Scope

  • Changing MCP add_message / tool schemas; those already have tool-shape validation and are not the source of this residual.
  • Reworking the whole wake-subscription or bridge-daemon delivery path.
  • Closing #12456; this is one residual leaf under that epic.
  • Reopening #12438 or #12461; both are closed and this ticket covers the lifecycle-script residual they did not finish.

Avoided Traps

  • Do not create an 8-line PR that only swaps one threshold in one file; this ticket intentionally batches the related lifecycle wake knobs.
  • Do not add a second config source of truth in Memory Core if Tier-1 orchestrator wake config owns the behavior.
  • Do not preserve direct parseInt(process.env.X) at the use site behind a renamed helper; the leaf owns env/default/type decoding.
  • Do not silently hardcode Neo maintainer identities into a public default; preserve the deployment-portable resolver discipline from #11829.

Related

Parent epic: #12456

Wake substrate authority: #11829, #10626

Adjacent closed AiConfig cleanup: #12438, #12461

Recent precedent: PR #12605 routes a Memory Core drift threshold through AiConfig instead of service-local env parsing.

Handoff Retrieval Hint: AiConfig lifecycle wake knobs IDLE_THRESHOLD_MS NEO_TRIO_IDENTITIES TRIO_WAKE_COOLDOWN_SECONDS checkAllAgentIdle checkSunsetted trioWakeCooldown

tobiu referenced in commit 7fefbeb - "refactor(ai): route lifecycle wake knobs through AiConfig (#12613) (#12622) on Jun 6, 2026, 4:52 PM
tobiu closed this issue on Jun 6, 2026, 4:52 PM