Context
The Data Sync pipeline has been failing for 20 consecutive runs (#16428 alarm). The actual cause is a missing npm package. The operator-visible failure line says:
[DataSync] stage "GitHub Workflow corpus" failed under declared credential scope `reader`.
If this is an authentication failure, the stage requires a scope that grants it — never an
ambient credential.
The real error, 550 log lines earlier:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'chromadb'
imported from /home/runner/work/neo/neo/ai/services/knowledge-base/ChromaManager.mjs
Not an authentication failure. Not a credential-scope problem. A packaging one.
The Problem
buildScripts/dataSyncPipeline.mjs:547 prepends the credential-scope framing to every stage failure, unconditionally, before knowing anything about the failure's class:
error.message = `[DataSync] stage "${label}" failed under declared credential scope ` +
`\`${tokenScope}\`. If this is an authentication failure, the stage requires a scope ` +
`that grants it — never an ambient credential.\n${error.message}`;Its own comment states the problem it was built to solve — "a bare child failure reads as 'the tool is broken' when the finding is 'this stage was granted none and needs a credential'" — and that reasoning is sound. But the annotation was made unconditional, so it now produces the mirror-image defect: a packaging failure reads as an auth failure.
The If this is an authentication failure hedge is real, and it is not enough. In an operator-visible log tail, the annotation is the last line and the true error has scrolled past. The hedge is only readable by someone who already has the answer.
The annotation knows which scope the stage was granted. It does not know whether the failure had anything to do with credentials — and it is phrased as though it does.
The Architectural Reality
emitStages in buildScripts/dataSyncPipeline.mjs (:531–:549). tokenScope is a per-stage declaration; the child's exit is opaque to it. Every failure class — module resolution, ENOENT, a thrown assertion, a genuine 401 — receives the identical credential-framed prefix.
This is a diagnostic that answers a question adjacent to the one the reader is asking. The reader asks "why did this fail?"; the annotation answers "what was this stage entitled to?" Both are true statements; only one is the question.
The Fix
Classify before annotating. The child's error already carries its class:
ERR_MODULE_NOT_FOUND / MODULE_NOT_FOUND → a dependency/packaging failure. Say so, and do not lead with credentials.
ENOENT on the script path → the stage's entrypoint is missing.
- auth-shaped (
401, 403, authentication, credentials, permission, Bad credentials) → the existing credential framing is correct and should stay.
- anything unrecognized → say the class is unrecognized and state the scope as context, never as a diagnosis.
The scope annotation stays in all four cases — it is genuinely useful context. What changes is that it stops leading with a hypothesis it cannot support, and the last-line-in-the-tail slot goes to the class that was actually observed.
Deliberately preserving the original design constraint: no per-stage requiresCredential flag. The classification is derived from the observed error, so it cannot drift from a hand-maintained declaration — the same property the current annotation was built for.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
emitStages failure message |
this ticket |
leads with the observed failure class; scope stated as context |
unrecognized class ⇒ says "unrecognized", never asserts a cause |
in-source |
spec per class |
tokenScope declaration |
unchanged |
still the only scope authority |
— |
— |
no per-stage flag added |
| exit codes / deferral behaviour |
unchanged |
publishGeneratedProgressOnFailure path untouched |
— |
— |
existing pipeline specs green |
Decision Record impact
none. Message construction inside one build script.
Acceptance Criteria
Out of Scope
- The
chromadb failure itself — that is #16495 / PR #16496, which fixes the import chain.
- The
[skip ci] / ruleset interaction — separate, and ruleset config is operator-owned.
- Log verbosity or ordering elsewhere in the pipeline.
Avoided Traps
- Deleting the annotation. It solves a real problem the comment documents; the defect is that it is unconditional, not that it exists.
- A per-stage
requiresCredential flag. Explicitly rejected in the original design for a good reason: a second hand-maintained declaration beside tokenScope, free to drift. Classification from the observed error keeps that property.
- Treating the hedge as sufficient. "If this is an authentication failure" is technically honest and operationally useless when it is the last line a reader sees.
Related
#16428 — the standing alarm this misdirected diagnosis on, 20 consecutive failures
#16495 / PR #16496 — the actual chromadb cause
#16449 — same class from another angle: a diagnostic stating something adjacent to what its reader needs
Live latest-open sweep: latest 20 open issues checked at 2026-08-05T03:40Z; no equivalent found.
Origin Session ID: eeacb603-97f1-4241-9b2f-3a542cab6d2c
Retrieval Hint: query_raw_memories("dataSyncPipeline credential scope annotation misdiagnosis") · failed under declared credential scope
Context
The Data Sync pipeline has been failing for 20 consecutive runs (
#16428alarm). The actual cause is a missing npm package. The operator-visible failure line says:The real error, 550 log lines earlier:
Not an authentication failure. Not a credential-scope problem. A packaging one.
The Problem
buildScripts/dataSyncPipeline.mjs:547prepends the credential-scope framing to every stage failure, unconditionally, before knowing anything about the failure's class:error.message = `[DataSync] stage "${label}" failed under declared credential scope ` + `\`${tokenScope}\`. If this is an authentication failure, the stage requires a scope ` + `that grants it — never an ambient credential.\n${error.message}`;Its own comment states the problem it was built to solve — "a bare child failure reads as 'the tool is broken' when the finding is 'this stage was granted
noneand needs a credential'" — and that reasoning is sound. But the annotation was made unconditional, so it now produces the mirror-image defect: a packaging failure reads as an auth failure.The
If this is an authentication failurehedge is real, and it is not enough. In an operator-visible log tail, the annotation is the last line and the true error has scrolled past. The hedge is only readable by someone who already has the answer.The annotation knows which scope the stage was granted. It does not know whether the failure had anything to do with credentials — and it is phrased as though it does.
The Architectural Reality
emitStagesinbuildScripts/dataSyncPipeline.mjs(:531–:549).tokenScopeis a per-stage declaration; the child's exit is opaque to it. Every failure class — module resolution, ENOENT, a thrown assertion, a genuine 401 — receives the identical credential-framed prefix.This is a diagnostic that answers a question adjacent to the one the reader is asking. The reader asks "why did this fail?"; the annotation answers "what was this stage entitled to?" Both are true statements; only one is the question.
The Fix
Classify before annotating. The child's error already carries its class:
ERR_MODULE_NOT_FOUND/MODULE_NOT_FOUND→ a dependency/packaging failure. Say so, and do not lead with credentials.ENOENTon the script path → the stage's entrypoint is missing.401,403,authentication,credentials,permission,Bad credentials) → the existing credential framing is correct and should stay.The scope annotation stays in all four cases — it is genuinely useful context. What changes is that it stops leading with a hypothesis it cannot support, and the last-line-in-the-tail slot goes to the class that was actually observed.
Deliberately preserving the original design constraint: no per-stage
requiresCredentialflag. The classification is derived from the observed error, so it cannot drift from a hand-maintained declaration — the same property the current annotation was built for.Contract Ledger Matrix
emitStagesfailure messagetokenScopedeclarationpublishGeneratedProgressOnFailurepath untouchedDecision Record impact
none. Message construction inside one build script.Acceptance Criteria
ERR_MODULE_NOT_FOUNDchild failure produces a message naming a dependency/packaging failure, and does not lead with credential framing. Red witness: today'schromadberror.tokenScopestill appears in every case; this narrows the claim, it does not remove the context.requiresCredentialflag is introduced — classification stays derived from the observed error.Out of Scope
chromadbfailure itself — that is#16495/ PR #16496, which fixes the import chain.[skip ci]/ ruleset interaction — separate, and ruleset config is operator-owned.Avoided Traps
requiresCredentialflag. Explicitly rejected in the original design for a good reason: a second hand-maintained declaration besidetokenScope, free to drift. Classification from the observed error keeps that property.Related
#16428— the standing alarm this misdirected diagnosis on, 20 consecutive failures#16495/ PR #16496 — the actualchromadbcause#16449— same class from another angle: a diagnostic stating something adjacent to what its reader needsLive latest-open sweep: latest 20 open issues checked at
2026-08-05T03:40Z; no equivalent found.Origin Session ID: eeacb603-97f1-4241-9b2f-3a542cab6d2c
Retrieval Hint:
query_raw_memories("dataSyncPipeline credential scope annotation misdiagnosis")·failed under declared credential scope