Frontmatter
| title | fix(data): api stores no longer auto-create a default Pipeline (#12170) |
| author | neo-opus-ada |
| state | Merged |
| createdAt | May 29, 2026, 2:04 PM |
| updatedAt | May 29, 2026, 2:22 PM |
| closedAt | May 29, 2026, 2:22 PM |
| mergedAt | May 29, 2026, 2:22 PM |
| branches | dev ← claude/inspiring-villani-0feb1f |
| url | https://github.com/neomjs/neo/pull/12171 |
🚨 Agent PR Body Lint Violation
@neo-opus-ada — your PR body on PR #12171 does not match the pull-request template structure.
Required action: read .agents/skills/pull-request/SKILL.md BEFORE editing the PR body. The skill points at:
- Minimum-viable PR body structure:
.agents/skills/pull-request/references/pull-request-workflow.md §9 - Self-Identification mandate:
.agents/skills/pull-request/references/pull-request-workflow.md §5
Do NOT compose a substitute template or hallucinate section headings. The validator checks more structural anchors than this comment names. The only reliable path to passing is reading the actual workflow file and following its structure.
Diagnostic hint: visible anchors appear present but the structural template anchors do not.
Visible anchors missing (full list)
(none — visible layer passed; invisible structural layer caught the miss)
This is the CI tool-boundary lint companion to PR #11494's MCP manage_pr_review validator and PR #11502's agent-pr-review-body-lint.yml reviewer-side lint.
Resolves #11501.
Authored by Claude Opus 4.8 (Claude Code). Memory-core orchestrator was deactivated this session, so no Origin Session ID is available.
FAIR-band: operator-directed PR (explicit request) — not autonomous lane self-selection; swarm band-gating N/A.
Resolves #12170
api-configured stores were silently getting a default
Pipelineinstance, soStore.load()'sif (me.pipeline)branch always shadowed theelse if (me.api)remotes-api branch — anautoLoad: trueapi store never fired its RPC. Surfaced while dogfooding #12134 in a real websocket-backed remotes-api app. A one-line guard inbeforeSetPipeline()keeps api stores atpipeline === nullso they reach the api branch (which already carries the #12132/#12134 fix).Evidence: L1 (unit: api store →
pipeline === null+ non-api default preserved; api-branch reachability is deterministic oncepipeline === null) → L3 live RPC confirmation deferred to the consuming app post dependency-bump. Residual: live autoLoad ws RPC.Root cause
ClassSystemUtil.beforeSetInstance(value, Pipeline, …)mints a default instance for a falsy value (if (!config && DefaultClass) Neo.create(...)).beforeSetPipeline()only skipped that path for the url case, so every api store got a spuriousPipeline(plus itsonPipelinePushlistener) andload()never reached the api branch.The fix
src/data/Store.mjsbeforeSetPipeline(): returnnullearly whenme.apiis set — api stores load via remotes-api, never a pipeline. The url guard simplifies to!value && me.url(the!me.apiterm is now subsumed by the early return). Order-independent: Neo resolves reactive configs on demand viaconfigSymbol, some.apireads correctly regardless of config processing order.Deltas from ticket
None of substance — implements #12170's prescribed
if (me.api) return nullguard verbatim (strictapi XOR pipelineform). The!value && me.apipermissive variant (which would preserve runtime api→pipeline switching) was considered and rejected per the ticket, weighed as a ~0.001% case.Test Evidence
New
test/playwright/unit/data/StoreApiPipeline.spec.mjs— 3 cases: api →pipeline===null; api +autoLoad:true→pipeline===null; non-api → defaultPipelinepreserved.npm run test-unit -- test/playwright/unit/data/StoreApiPipeline.spec.mjs→ 3 passed (555ms)npm run test-unit -- test/playwright/unit/data/→ 56 passed (830ms) — no regression across the Store/TreeStore suitePost-Merge Validation
autoLoad: true) renders data over the websocket afternpm update neo.mjs— i.e.load()fires the RPC end-to-end.