Resolves #13784
Ends the kb-sync "completed" telemetry-lie: a lease-held/deferred kb-sync exited 0 and the orchestrator recorded it completed, hiding multi-day embedding stalls behind a green health signal (the silent-stall failure class in grace's epic #13755). Now a deferred run is honestly classified skipped.
Three surfaces, consuming @neo-gpt's merged #13778 captureStdoutJson spawn-boundary channel:
- Child emit (
syncKnowledgeBase.mjs) — progress logs move to stderr; stdout carries only the single JSON outcome line the channel JSON.parses ({deferred:true, reason:'heavy-maintenance-lease-held', lease} on held; {success:true, …} on complete). Mirrors the backfill-memory-summaries.mjs precedent.
- Opt-in (
taskDefinitions.mjs) — kbSync gets captureStdoutJson: true.
- Classifier (
ProcessSupervisorService.classifySuccessfulChildOutcome) — generalized the deferred-outcome check that was hardcoded to memory-summary-backfill so ANY opted-in task emitting {deferred:true, reason} is classified skipped. The backfill-specific count-logic (all-deferred) stays gated under its taskName.
Evidence: L2 (unit) below; the live silent-stall elimination is L3 (post-restart, sandbox-unreachable) → Post-Merge Validation.
Test Evidence
ProcessSupervisorService.spec.mjs — new #13784: kbSync lease-held stdout marks skipped with child reason (generalized deferred classifier): runTask('kbSync', …) with a {deferred:true, reason:'heavy-maintenance-lease-held'} child stdout → asserts the outcome is skipped (reasonCode heavy-maintenance-lease-held) and not completed. Mirrors the existing backfill lease-held test, proving the generalization. The backfill all-deferred + lease-held tests still pass (count-logic gated). CI runs the unit config (authoritative).
Post-Merge Validation
After merge + orchestrator restart: when kbSync defers behind an active heavy-maintenance holder, the orchestrator health record shows skipped (reasonCode heavy-maintenance-lease-held), not completed. A days-long embedding stall is now visible in the telemetry instead of silent — pair with the embed-drain watchdog (#13551) for full observability of the failure class.
Deltas
ai/scripts/maintenance/syncKnowledgeBase.mjs — progress → stderr; structured JSON outcome on stdout.
ai/daemons/orchestrator/taskDefinitions.mjs — kbSync captureStdoutJson: true.
ai/daemons/orchestrator/services/ProcessSupervisorService.mjs — generalized classifySuccessfulChildOutcome deferred-outcome contract.
test/.../ProcessSupervisorService.spec.mjs — kbSync lease-held → skipped test.
Sub of #13755; sibling of #13777 (backfill) / #13784's family. Authored by @neo-opus-vega (Vega), origin session d41446ed-b9c7-4d51-a933-048b3d196665.
Resolves #13784
Ends the kb-sync "completed" telemetry-lie: a lease-held/deferred kb-sync exited
0and the orchestrator recorded it completed, hiding multi-day embedding stalls behind a green health signal (the silent-stall failure class in grace's epic #13755). Now a deferred run is honestly classified skipped.Three surfaces, consuming @neo-gpt's merged #13778
captureStdoutJsonspawn-boundary channel:syncKnowledgeBase.mjs) — progress logs move to stderr; stdout carries only the single JSON outcome line the channelJSON.parses ({deferred:true, reason:'heavy-maintenance-lease-held', lease}on held;{success:true, …}on complete). Mirrors thebackfill-memory-summaries.mjsprecedent.taskDefinitions.mjs) — kbSync getscaptureStdoutJson: true.ProcessSupervisorService.classifySuccessfulChildOutcome) — generalized the deferred-outcome check that was hardcoded tomemory-summary-backfillso ANY opted-in task emitting{deferred:true, reason}is classifiedskipped. The backfill-specific count-logic (all-deferred) stays gated under its taskName.Evidence: L2 (unit) below; the live silent-stall elimination is L3 (post-restart, sandbox-unreachable) → Post-Merge Validation.
Test Evidence
ProcessSupervisorService.spec.mjs— new#13784: kbSync lease-held stdout marks skipped with child reason (generalized deferred classifier):runTask('kbSync', …)with a{deferred:true, reason:'heavy-maintenance-lease-held'}child stdout → asserts the outcome isskipped(reasonCodeheavy-maintenance-lease-held) and notcompleted. Mirrors the existing backfill lease-held test, proving the generalization. The backfillall-deferred+ lease-held tests still pass (count-logic gated). CI runs the unit config (authoritative).Post-Merge Validation
After merge + orchestrator restart: when kbSync defers behind an active heavy-maintenance holder, the orchestrator health record shows
skipped(reasonCodeheavy-maintenance-lease-held), notcompleted. A days-long embedding stall is now visible in the telemetry instead of silent — pair with the embed-drain watchdog (#13551) for full observability of the failure class.Deltas
ai/scripts/maintenance/syncKnowledgeBase.mjs— progress → stderr; structured JSON outcome on stdout.ai/daemons/orchestrator/taskDefinitions.mjs— kbSynccaptureStdoutJson: true.ai/daemons/orchestrator/services/ProcessSupervisorService.mjs— generalizedclassifySuccessfulChildOutcomedeferred-outcome contract.test/.../ProcessSupervisorService.spec.mjs— kbSync lease-held → skipped test.Sub of #13755; sibling of #13777 (backfill) / #13784's family. Authored by @neo-opus-vega (Vega), origin session d41446ed-b9c7-4d51-a933-048b3d196665.