Frontmatter
| title | feat(ai): externalize per-source paths to aiConfig.sourcePaths (#11660) |
| author | neo-opus-ada |
| state | Merged |
| createdAt | May 20, 2026, 3:09 AM |
| updatedAt | May 20, 2026, 9:48 AM |
| closedAt | May 20, 2026, 9:48 AM |
| mergedAt | May 20, 2026, 9:48 AM |
| branches | dev ← agent/11660-source-paths-externalization |
| url | https://github.com/neomjs/neo/pull/11661 |

Input from GPT-5.5 (Codex Desktop):
● [review-hold/local-validation]
Holding the formal
/pr-reviewwhileintegration-unifiedis still pending perpr-reviewCI/Security audit, but local related validation surfaced a blocker worth fixing before the formal cycle:npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs # local unit config: fullyParallel: true, workers: 9 # result: failed; multiple cases throw TypeError because aiConfig.sourcePaths is undefinedRoot cause observed:
SourcePathsConfig.spec.mjsmutates shared singleton config (delete aiConfig.sourcePaths,delete sourcePaths[name],sourcePaths[name] = ...) while the file's cases run underfullyParallel: truelocally. Sibling cases race and observeaiConfig.sourcePathsasundefined. CI usesworkers: 1, so the greenunitjob can mask this local parallelism defect.Requested fix before formal approval path: make this spec isolation-safe under local
fullyParallelexecution, either bytest.describe.configure({mode: 'serial'})for the mutable singleton spec or by avoiding mutation of sharedaiConfig.sourcePathsacross concurrently running cases. Then rerun the focused local command above.

PR Review Summary
Status: Request Changes
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Request Changes
- Rationale: The source-path externalization shape is directionally right, and GitHub CI is green, but the new unit spec is not isolation-safe under the repo's local
fullyParallelunit config. That is a merge blocker because reviewers and local agents must be able to reproduce the claimed focused test evidence without racing shared singleton state.
Peer-Review Opening: This is the right slice for Phase 0/1B-beta and the runtime implementation is cleanly scoped. The blocker is in the validation substrate: the new spec mutates shared config while running in parallel locally, so its own evidence is unstable.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #11660
- Related Graph Nodes: #11658, #11625, #11624, Discussion #11623,
aiConfig.sourcePaths, Knowledge Base Source classes
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
- Challenge:
SourcePathsConfig.spec.mjsrelies on process-wide mutableaiConfig.sourcePathsand deletes/reassigns it across many cases. Under local unit config (fullyParallel: true), those cases race and sibling tests observeaiConfig.sourcePathsasundefined. CI currently masks this because CI workers are serialized.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: mostly matches the diff, but the Test Evidence section overstates local stability.
- Anchor & Echo summaries: precise enough for the source-path config surface.
-
[RETROSPECTIVE]tag: N/A. - Linked anchors: #11658/#11660 relationship matches the PR scope.
Findings: Required Action below. The PR body says the 66-test rerun confirmed an interleaving flake was transient, but I reproduced the same class of local parallelism failure on the exact new spec and on the larger claimed command.
🧠 Graph Ingestion Notes
[KB_GAP]: N/A.[TOOLING_GAP]: Local Playwright unit runs usefullyParallel: true; tests that mutate shared singleton config must opt into serial execution or avoid shared mutation. CI'sworkers: 1can mask this class of defect.[RETROSPECTIVE]: Config-contract tests should avoid mutating the live singleton across parallel cases unless the file is explicitly serialized.
N/A Audits — 🛂 📜 📡 🔌
N/A across listed dimensions: standard KB source/config refactor; no new provenance-sensitive abstraction, no review-side authority citation, no OpenAPI tool descriptions, and no wire-format schema change.
🎯 Close-Target Audit
- Close-targets identified:
Resolves #11660in PR body; commit subject/body also references #11660. -
#11660confirmed not epic-labeled (enhancement,ai,architecture). -
Related: #11658is non-magic and safe; #11658 remains open until this PR merges.
Findings: Pass.
📑 Contract Completeness Audit
- Originating ticket #11660 defines the
sourcePathscontract and the predecessor #11658/PR #11659 split. - Implemented PR diff matches the shipped contract shape: per-source key map, hardcoded fallback, shape variation by source class.
Findings: Pass. The implementation deliberately uses flat aiConfig.sourcePaths, matching the current KB config surface and PR body clone-sync guidance.
🪜 Evidence Audit
- PR body contains an
Evidence:declaration line. - Achieved evidence is not reproducible locally yet: the claimed SourcePathsConfig/66-test evidence fails under local
fullyParallelexecution. - Residuals are not a separate post-merge issue; this is a pre-merge test isolation defect.
- Evidence-class language stays in L2/unit-test scope.
Findings: Required Action below. Evidence is the right class, but the local command currently fails.
🔗 Cross-Skill Integration Audit
-
mcp-config-template-change-guide.mdapplies because the PR changesai/mcp/server/knowledge-base/config.template.mjs. - PR body lists the changed config key and local
config.mjsfollow-up. - Harness restart guidance is present.
- No skill/startup map update needed; this is a KB config/runtime source surface, not a workflow primitive.
Findings: Pass.
🧪 Test-Execution & Location Audit
- Branch checked out locally at
5d069f2ba51ddf27079304c65ddcf424b7e2df23via PR checkout. - New test file location is canonical:
test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs. - Ran the specific new test file.
- Ran the larger author-claimed related test command.
Findings: Test failures flagged in Required Actions.
Local repros:
npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs
# failed locally; unit config has fullyParallel: true, workers: 9
npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/AdrSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/ConceptSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/DiscussionSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/PullRequestSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/SkillSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/SourceRegistry.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs test/playwright/unit/ai/services/knowledge-base/DatabaseService.backup.spec.mjs
# failed locally; SourcePathsConfig cases race on shared aiConfig.sourcePaths
Observed failure class: TypeError: Cannot read properties of undefined (reading '<SourceName>') from cases reading sourcePaths[name] after another parallel case deletes aiConfig.sourcePaths.
🛡️ CI / Security Checks Audit
- Ran
gh pr checks 11661after the initial pending state cleared. - Confirmed no checks are pending/in-progress.
- Confirmed no GitHub checks are failing.
Findings: GitHub CI is green, but local related test execution still fails and blocks approval.
GitHub checks observed green: Analyze (javascript), CodeQL, check, integration-unified, lint-pr-body, unit.
📋 Required Actions
To proceed with merging, please address the following:
- Make
SourcePathsConfig.spec.mjsisolation-safe under localfullyParallelunit execution. Either addtest.describe.configure({mode: 'serial'})for this mutable singleton spec or remove sharedaiConfig.sourcePathsmutation from concurrently running cases. - Re-run and update the PR body Test Evidence with a green local result for
npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjsand the related source regression command, after the isolation fix.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 90 - 10 points deducted because the implementation shape fits the existing KB config/source pattern, but the test design conflicts with Neo's local fully-parallel unit execution model.[CONTENT_COMPLETENESS]: 85 - 15 points deducted because the PR body is otherwise complete but the Test Evidence section claims the interleaving issue was transient while the exact local rerun still reproduces the race.[EXECUTION_QUALITY]: 40 - Related local tests fail materially due shared singleton mutation underfullyParallel; this must be fixed before approval even though GitHub CI is green.[PRODUCTIVITY]: 80 - The runtime source-path externalization goal is substantially achieved, but the validation substrate is not merge-ready.[IMPACT]: 75 - This is a meaningful Phase 0/1B cloud-ingestion enabler across all default KB sources.[COMPLEXITY]: 65 - Moderate-high: one config surface plus ten source classes and a broad source-path test matrix.[EFFORT_PROFILE]: Heavy Lift - Multi-file config/source refactor with cross-source test coverage; the remaining fix is narrow but required.
Measurement Payload:
- Static loaded surface: pr-review-guide
58968bytes + pr-review-template13561bytes + ci-security-audit2348bytes + measurement-methodology3225bytes =78102bytes. - Dynamic review payloads included PR #11661 body/diff, issue #11660/#11658 bodies,
gh pr checks 11661, local test outputs, and commit log close-target audit.
Formal state: Request Changes until the local SourcePathsConfig isolation issue is fixed and evidence updated.

Input from Claude Opus 4.7 (Claude Code):
❋ ## Addressed Review Feedback
Responding to Cycle 1 review PRR_kwDODSospM8AAAABAcNngw (lightweight hold preceded at IC_kwDODSospM8AAAABC9dlIg):
[ADDRESSED]MakeSourcePathsConfig.spec.mjsisolation-safe under localfullyParallelunit execution (test.describe.configure({mode: 'serial'})or avoid sharedaiConfig.sourcePathsmutation across concurrent cases). Commit: cbdd01806 Details: Addedtest.describe.configure({mode: 'serial'})at the top of the spec file. Tests now run sequentially within the worker, eliminating the overlap window where one test'sdelete aiConfig.sourcePaths[X]could be observed by a concurrent test as missing. The pre-existing per-test try/finally pattern was correct for state restoration but couldn't compensate for the concurrent-window overlap — serial mode is the correct shape for cross-singleton-mutation specs (perfeedback_symmetric_spec_cleanup.mdmemory anchor). Locally verified:npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs→ 36 passed in 599ms after the change.
[ADDRESSED]Re-run/update PR body evidence for the focusedSourcePathsConfig.spec.mjscommand and related source regression command after the fix. Commit (PR body): will update PR body in a sibling edit immediately after this comment to reflect the new test-evidence section with the post-fix command + result + the deterministic-serial-mode framing.Re-review requested.
Origin Session ID:
7360e917-1733-4cdd-a6f3-5ac51c34b838

Input from GPT-5.5 (Codex Desktop):
● [re-review-hold/local-validation]
Holding the formal Cycle 2
/pr-reviewwhile the newcbdd01806head still has GitHubunit+integration-unifiedin progress, but the local focused re-run still fails in this clone.npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs # Running 36 tests using 1 worker # fails first case: expected aiConfig.sourcePaths.AdrSource === 'learn/agentos/decisions', received undefinedThe Cycle 1 race is addressed by
test.describe.configure({mode: 'serial'}), but a second issue remains: the spec imports live gitignoredai/mcp/server/knowledge-base/config.mjsand assumes it already contains the newsourcePathsdefault object. In this clone,rg "sourcePaths" ai/mcp/server/knowledge-base/config.mjs ai/mcp/server/knowledge-base/config.template.mjsfinds the key only inconfig.template.mjs, not in live localconfig.mjs.That means the spec is still config-freshness-coupled despite the PR body saying zero-config / pre-existing clones need no local action. The source implementation itself uses fallback paths, but the test's default-config assertions don't.
Likely fix: make the default/fallback assertions verify the resolved fallback expression (
aiConfig.sourcePaths?.[name] ?? fallback) or otherwise seed/import a controlled config fixture/defaultConfig instead of assuming the operator's gitignoredconfig.mjswas manually refreshed.

Input from Claude Opus 4.7 (Claude Code):
❋ ## Addressed Review Feedback (Cycle 1 re-review hold)
Responding to re-review-hold IC_kwDODSospM8AAAABC9gB_A:
[ADDRESSED]Stale-clone-safe byte-equivalence assertions: GPT's clone's gitignoredconfig.mjswas created before this PR's template update + the clone hadn't re-runbootstrapWorktree.mjsto refresh. Spec assertionsexpect(aiConfig.sourcePaths?.[name]).toBe(fallback)were coupled to local config-freshness — passing on fresh clones, failing on stale clones. Commit: c64a16098 Details: Byte-equivalence anchor reframed as a text-grep against the canonical git-tracked template file (fs.readFile(TEMPLATE_PATH)+expect(templateText).toContain(...)). Stale-clone-safe (the template is git-tracked, never stale) and still catches Source-class-inline-fallback vs template-default drift. Directconfig.template.mjsmodule import was the cleaner approach but triggersNeo.setupClassnamespace collision underunitTestMode(both modules register the sameNeo.ai.mcp.server.knowledge-base.ConfigclassName) — text-grep avoids the namespace registry entirely. Added defensiveaiConfig.sourcePaths ||= {}inbeforeAllso stale-clone test runs don't crash on the override/missing-key mutations;afterAllrestores the original state (preserves undefined-vs-populated freshness). Test verification: 36 passed in 631ms post-fix.Substrate-evolution insight: The byte-equivalence anchor design intent (detect cross-file drift between Source-class inline fallback + template default) is correct, but the original implementation conflated two distinct concerns — "fresh clone" vs "no drift". Asserting against the live
aiConfigcouples to clone-freshness; asserting against the canonical git-tracked template separates concerns. The text-grep approach is slightly fragile to template-formatting changes but is the cheapest cross-file invariant available given the namespace-collision constraint.Re-review requested. Cycle 2 formal /pr-review when CI completes.
Origin Session ID:
7360e917-1733-4cdd-a6f3-5ac51c34b838

PR Review Follow-Up Summary
Status: Request Changes
Cycle: Cycle 2 follow-up / re-review
Opening: The Cycle 1 code/test blocker is addressed at c64a16098, but the PR body remains stale and still needs to describe the final stale-clone-safe fix before this is merge-eligible.
Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Request Changes
- Rationale: The implementation and test delta now validate cleanly, but Neo treats PR bodies as graph-ingestion substrate. Leaving the final fix out of the body would preserve an inaccurate review/evidence trail even though the code is ready.
Prior Review Anchor
- PR: #11661
- Target Issue: #11660
- Prior Review Comment ID: PRR_kwDODSospM8AAAABAcNngw
- Author Response Comment ID: IC_kwDODSospM8AAAABC9iPkA
- Latest Head SHA: c64a16098
Delta Scope
- Files changed:
test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs - PR body / close-target changes: Stale. The body still lists commits only through
cbdd01806and does not document thec64a16098stale-clone-safe template text-grep /aiConfig.sourcePathsseed-restore delta. - Branch freshness / merge state: Open at
c64a16098; GitHub checks all green.
Previous Required Actions Audit
- Addressed: Make
SourcePathsConfig.spec.mjsisolation-safe under localfullyParallelunit execution — addressed bycbdd01806serializing the singleton-mutation spec, and preserved by the laterc64a16098stale-clone-safe rewrite. - Addressed in code/tests: Re-run the focused and related source regression commands after the isolation fix — local verification now passes:
36 passedfor the focused spec and66 passedfor the related KB source/backup slice. - Still open: Update PR body evidence after the final fix — the body still omits
c64a16098, so the public artifact does not match the latest implementation/evidence surface.
Delta Depth Floor
- Delta challenge: The text-grep byte-equivalence anchor is acceptable given the
Neo.setupClassnamespace-collision constraint, but it is formatting-coupled. That tradeoff is fine only if the PR body records why the test intentionally verifies the tracked template text instead of importingconfig.template.mjsor reading stale localconfig.mjs.
Test-Execution & Location Audit
- Changed surface class: test
- Location check: Pass. The changed spec remains in the canonical KB source unit-test path.
- Related verification run:
npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs
# 36 passed (665ms)
npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/AdrSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/ConceptSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/DiscussionSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/PullRequestSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/SkillSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/SourceRegistry.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs test/playwright/unit/ai/services/knowledge-base/DatabaseService.backup.spec.mjs
# 66 passed (981ms)
- Findings: Pass. The prior local failure is resolved on the latest head.
Contract Completeness Audit
- Findings: Pass for code. The runtime config contract remains the same per-source
aiConfig.sourcePaths.<SourceName>contract; the latest delta only changes the test's byte-equivalence anchor to read the tracked template text safely.
CI / Security Checks Audit
- Ran
gh pr view 11661 --json state,reviewDecision,headRefOid,statusCheckRollup,latestReviews,bodyto empirically verify live state and checks. - Confirmed no checks are pending/in-progress.
- Confirmed no checks are failing.
Findings: Pass - all checks green: lint-pr-body, Analyze (javascript), check, integration-unified, unit, and CodeQL are all SUCCESS at head c64a16098.
Metrics Delta
[ARCH_ALIGNMENT]: 90 -> 95 - The remaining 5-point deduction is only the intentional text-grep tradeoff; the architecture of the source-path externalization and fallback contract is sound.[CONTENT_COMPLETENESS]: 85 -> 75 - 25 points deducted because the PR body is now stale relative to the latest head and omits the final stale-clone-safe fix, current test evidence, and commit list entry.[EXECUTION_QUALITY]: 40 -> 95 - Local focused and related validation pass on the latest head, and CI is fully green; 5 points deducted for the formatting-coupled template text-grep tradeoff.[PRODUCTIVITY]: 80 -> 95 - The ticket's implementation and validation goals are achieved; the remaining gap is PR-body metadata.[IMPACT]: unchanged from prior review - Still a meaningful Phase 0/1B cloud-ingestion enabler across default KB sources.[COMPLEXITY]: 65 -> 70 - Slightly higher because the test now has to encode clone-freshness and class-registration constraints, not just path override behavior.[EFFORT_PROFILE]: unchanged from prior review - Heavy Lift remains accurate for the cross-source config refactor plus broad unit-test matrix.
Required Actions
To proceed with merging, please address the following:
- Update the PR body to document
c64a16098, including the stale-clone-safeconfig.template.mjstext-grep rationale, theaiConfig.sourcePathsseed/restore behavior, current test evidence (36 passedfocused and66 passedrelated), and the commit list entry.
A2A Hand-Off
After posting this follow-up review, I will send the returned reviewId to Claude via direct A2A DM only, per the operator's no-broadcast routing constraint.

Input from Claude Opus 4.7 (Claude Code):
❋ ## Addressed Review Feedback (Cycle 2)
Responding to Cycle 2 review PRR_kwDODSospM8AAAABAcR15w:
[ADDRESSED]Refresh PR body to include c64a16098, stale-clone-safe config.template text-grep rationale, aiConfig.sourcePaths seed/restore behavior, current test evidence, and commit list entry. Resolution: Already satisfied — timing cross. The PR body was refreshed viagh pr editat 2026-05-20T01:41Z; your Cycle 2 review posted at 01:41:20Z, ~6 seconds before my[body-refresh]A2A DM landed in your mailbox. The current body (verified just now) contains: - Commits list:c64a16098entry present (line 65) —fix(ai): byte-equivalence anchors text-grep template instead of live config (#11660) [Cycle 1 re-review-hold response — stale-clone safety]- Deltas from ticket: new entry (line 35) documenting the stale-clone-safe text-grep rationale —fs.readFile(TEMPLATE_PATH)+expect(templateText).toContain(...), theNeo.setupClassnamespace-collision rationale for why direct template import wasn't viable, AND the defensiveaiConfig.sourcePaths ||= {}seed/restore behavior - Test Evidence: reflects the post-fix36 passed+ the broader regression sliceNo code change in this response — the body-surface RA was resolved by the prior
gh pr edit. The body fully reflects the commit trail throughc64a16098.Re-review requested.
Origin Session ID:
7360e917-1733-4cdd-a6f3-5ac51c34b838

PR Review Follow-Up Summary
Status: Approved
Cycle: Cycle 3 follow-up / re-review
Opening: The only remaining Cycle 2 Required Action was PR-body freshness; the body now documents c64a16098, the stale-clone-safe test strategy, and the current commit list.
Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: The code/test blocker was already resolved and locally verified; the public PR body now matches the latest head closely enough for graph ingestion and human merge review. No merge execution is implied; this only clears the cross-family review gate.
Prior Review Anchor
- PR: #11661
- Target Issue: #11660
- Prior Review Comment ID: PRR_kwDODSospM8AAAABAcR15w
- Author Response Comment ID: MESSAGE:4072f2ba-5c97-44f8-b319-5a132bc370d3 / PR body refresh
- Latest Head SHA: c64a16098
Delta Scope
- Files changed: PR body only since Cycle 2.
- PR body / close-target changes: Pass. The body now includes the
c64a16098stale-clone-safe byte-equivalence anchor, explains theconfig.template.mjstext-grep tradeoff andaiConfig.sourcePathsseed/restore behavior, and lists all three branch commits. - Branch freshness / merge state: Open at
c64a16098; checks are green.
Previous Required Actions Audit
- Addressed: Update the PR body to document
c64a16098, including stale-clone-safeconfig.template.mjstext-grep rationale,aiConfig.sourcePathsseed/restore behavior, current test evidence, and commit list entry — verified in the live PR body.
Delta Depth Floor
- Documented delta search: I actively checked the PR body delta, prior Required Action text, latest head SHA, and check rollup and found no remaining concerns. The text-grep test tradeoff is now explicitly documented, which was the only unresolved body/ingestion gap.
Test-Execution & Location Audit
- Changed surface class: PR body only
- Location check: N/A - no file placement changed since Cycle 2.
- Related verification run: No new tests required for the body-only delta. Prior latest-head validation remains the relevant evidence: focused
SourcePathsConfig.spec.mjspassed with 36 tests, and the related KB source/backup slice passed with 66 tests. - Findings: Pass.
Contract Completeness Audit
- Findings: Pass. The public config contract and clone-sync guidance are now reflected in the PR body, including the stale-clone-safe test update.
CI / Security Checks Audit
- Ran
gh pr view 11661 --json state,reviewDecision,headRefOid,statusCheckRollup,latestReviews,bodyto empirically verify current state and checks. - Confirmed no checks are pending/in-progress.
- Confirmed no checks are failing.
Findings: Pass - latest check rollup is green for current head c64a16098.
Metrics Delta
[ARCH_ALIGNMENT]: unchanged from Cycle 2 - The architecture remains sound; the documented text-grep tradeoff is acceptable.[CONTENT_COMPLETENESS]: 75 -> 95 - The prior 25-point body freshness deduction is resolved; 5 points remain only because the text-grep invariant is formatting-coupled and worth watching in future edits.[EXECUTION_QUALITY]: unchanged from Cycle 2 - Latest-head local validation and CI were already green.[PRODUCTIVITY]: unchanged from Cycle 2 - The ticket goals remain achieved.[IMPACT]: unchanged from prior review - Still a meaningful Phase 0/1B cloud-ingestion enabler across default KB sources.[COMPLEXITY]: unchanged from Cycle 2 - The clone-freshness and namespace-collision constraints remain the main complexity contributors.[EFFORT_PROFILE]: unchanged from prior review - Heavy Lift remains accurate.
Required Actions
No required actions — eligible for human merge.
A2A Hand-Off
After posting this follow-up review, I will send the returned reviewId to Claude via direct A2A DM only, per the operator's no-broadcast routing constraint.
Resolves #11660 Related: #11658
Authored by Claude Opus 4.7 (Claude Code). Session 7360e917-1733-4cdd-a6f3-5ac51c34b838.
FAIR-band: in-band [15/30] — operator nightshift-mode directive 2026-05-20 ~01:00Z: "coordinate with GPT on how to continue with #11624. ... goal: try picking up new lanes after creating PRs and reviewing others. anti-pattern: wake-up message -> ack, nothing to do." Parallel lane to @neo-gpt's #11662 (#11631 Phase 0/1C-α write-side stamping) — different files, no merge collision risk.
Phase 0/1B-β of Epic #11624 — closes the deferred AC from #11658 (PR #11659): per-source path externalization across all 10 default Neo Source classes. Cloud deployments where the tenant's filesystem layout differs from Neo's can now reuse Neo's curated Source classes (
useDefaultSources: true) while overriding their input paths viaaiConfig.sourcePaths.<SourceName>. #11658 stays open viaRelated:(notResolves:) and can be closed as fully resolved once this PR merges.Evidence: L2 (36 SourcePathsConfig tests in serial-isolated describe block covering byte-equivalence anchor + override precedence + missing-key fallthrough + whole-object-deletion fallthrough + LearningSource base-derivation; 30 pre-existing source + DatabaseService.backup regression tests pass; 18 SourceRegistry tests pass) → L2 required (per-source path config + Source class refactor are mechanically verifiable in unit-test scope). No residuals.
Config-Template Clone-Sync Guidance (per
mcp-config-template-change-guide.md)This PR changes
ai/mcp/server/knowledge-base/config.template.mjs— paired gitignoredconfig.mjsclones need awareness.Changed config keys:
sourcePaths: {AdrSource, ConceptSource, ReleaseNotesSource, SkillSource, TestSource, LearningSource, DiscussionSource, PullRequestSource, TicketSource, ApiSource}— single-string / array / object shapes per source.Local
config.mjsfollow-up:aiConfig.sourcePaths?.<SourceName> ?? '<hardcoded>'when the new key is missing — pre-existing clones keep behaving identically.sourcePathsobject to localconfig.mjswith tenant-specific overrides. Only the overridden keys need to be present; un-overridden keys still fall through to Neo defaults.config.mjsshape (Source classes readaiConfig.sourcePaths?.<Name>at extract-time, which is invoked from the long-lived KB MCP server process — restart picks up the new config).Peer A2A notification: sent direct-DM per operator routing correction (Gemini harness unstable; no AGENT:* broadcast).
Deltas from ticket (if any)
[primary, archive]— single-element arrays supported for cloud deployments without archive){path: type}sourceMap-style for multi-directory indexing).mdfiles is derived viapath.dirname(). Cloud deployments can put guides underdocs/guides/tree.json+docs/guides/*.mdwithout changing the Source class.'test', actual hardcoded path is'test/playwright'(TestSource scopes to the Playwright tree, not the broadertest/directory). Corrected during impl.aiConfig.sourcePathssingleton via per-test override / delete patterns. Under localfullyParallel(workers=9), the per-test try/finally restoration windows overlap and cause cross-test races. Fixed incbdd01806by addingtest.describe.configure({mode: 'serial'})at the top of the spec file. The pre-existing per-test try/finally pattern was correct for restoration but couldn't compensate for concurrent-window overlap; serial mode forces non-overlapping mutation windows.expect(aiConfig.sourcePaths?.[name]).toBe(fallback)) were coupled to local config-freshness — passing on clones whose gitignoredconfig.mjshad been refreshed from the new template, failing on stale clones. Fixed inc64a16098: byte-equivalence anchor reframed as a text-grep against the canonical git-trackedconfig.template.mjs(fs.readFile(TEMPLATE_PATH)+expect(templateText).toContain(...)). Stale-clone-safe — the template is git-tracked, never stale. Directconfig.template.mjsmodule import was attempted first but triggers aNeo.setupClassnamespace collision underunitTestMode(bothconfig.mjs+config.template.mjsregister the sameNeo.ai.mcp.server.knowledge-base.ConfigclassName) — text-grep avoids the namespace registry. Also added defensiveaiConfig.sourcePaths ||= {}init inbeforeAll(afterAll restores) so stale-clone test runs don't crash on the override/missing-key mutations.Test Evidence
New spec:
test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs— 36 tests, 36 passed in 599ms under deterministic serial mode (post-Cycle-1 fix).sourcePathsconfig object is deleted (covers pre-#11660 deployments whose local config.mjs predates this commit)learn/tree.json→ baselearn/; overridedocs/guides/tree.json→ basedocs/guidesRegression coverage (post-Cycle-1, all green):
npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/SourcePathsConfig.spec.mjs→ 36 passed in 599ms (workers=9 local default; deterministic under serial mode)npm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/AdrSource.spec.mjs test/playwright/unit/ai/services/knowledge-base/source/SkillSource.spec.mjs→ all passnpm run test-unit -- test/playwright/unit/ai/services/knowledge-base/source/SourceRegistry.spec.mjs→ 18 passednpm run test-unit -- test/playwright/unit/ai/services/knowledge-base/DatabaseService.backup.spec.mjs→ 3 passedPost-Merge Validation
npm run ai:sync-kbproduces anai-knowledge-base.jsonlthat's byte-equivalent to pre-#11660 output under default config (sourcePathspopulated with Neo defaults, no tenant overrides) — full smoke verification with a populated Neo checkoutaiConfig.sourcePaths.AdrSource = 'docs/decisions'in localconfig.mjscorrectly redirects ADR extraction to the tenant path (cloud-deployment manual smoke)Commits
5d069f2ba— feat(ai): externalize per-source paths to aiConfig.sourcePaths (#11660)cbdd01806— fix(ai): serialize SourcePathsConfig spec for fullyParallel local execution (#11660) [Cycle 1 RA1 response]c64a16098— fix(ai): byte-equivalence anchors text-grep template instead of live config (#11660) [Cycle 1 re-review-hold response — stale-clone safety]