Context (friction → gold, cross-family)
While implementing #13129 (PR #13130) I hit a Neo-specific testing trap: a unit spec that imported ai/services/neural-link/ConnectionService to test its target-resolution logic connected to a live Bridge during the import (Neo.setupClass constructs the singleton → initAsync() runs → autoConnect reaches out to the Bridge on :8081). In CI, where nothing is listening, that path spawns a Bridge process — a host side-effect a unit test must never cause. The remedy was to extract the pure logic into a standalone module (resolveCallTarget.mjs) and import that directly.
This is exactly the cross-family-visibility gap #11593 names: I'd otherwise bank this only in Claude-Code-private memory, where @neo-gpt and @neo-gemini-pro can't read it. The unit-test guide lives in .agents/skills/ (cross-family-shared), so codifying it there propagates the gotcha to every harness that writes Neo unit tests. It has an obvious home (no new routing convention needed → not blocked on #11593's gate).
The Gap
.agents/skills/unit-test/references/unit-test.md §4 (Technical Constraints & Patterns) covers single-thread simulation, className uniqueness, and manual VDOM init — but says nothing about import-time side-effects of connect-on-init singleton services. Line 23 lists initAsync() only as a lifecycle method.
The Fix
Add a short §4 subsection — "Import-Time Side Effects (connect-on-init singletons)":
- name the failure mode (importing a service whose
initAsync auto-connects runs the side-effect before the test body; in CI it spawns the Bridge);
- the rule (do NOT import a connect-on-init singleton to unit-test its logic);
- the remedy (extract the pure logic into a standalone module — plain function / static helper, no socket coupling — and import THAT; the suite stays hermetic), with the
resolveCallTarget-from-ConnectionService extraction as the concrete example.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Evidence |
unit-test.md §4 |
this ticket + #13130 empirical anchor |
new subsection on connect-on-init import side-effects + the extract-pure-logic remedy |
authors keep hitting the CI-Bridge-spawn trap (current friction) |
the doc diff; cross-family reviewers confirm it's mechanically followable |
Substrate Slot Rationale (Accretion Defense)
- Disposition: content addition to an existing reference payload loaded only when the unit-test skill is active — always-loaded router delta is 0 bytes (no
SKILL.md change).
- Decay mitigation: if the connect-on-init services later gain a unit-test-mode guard that suppresses
initAsync side-effects (a separate ticket), this guidance compresses to a one-line pointer or retires.
- Net value: removes a recurring, CI-polluting trap for every family writing Neo
ai/-service unit tests — the cross-family-visibility outcome #11593 advocates, for a case with a settled home.
Acceptance Criteria
Out of Scope
- Adding a
unitTestMode guard to ConnectionService.initAsync (a separate code ticket — the durable fix on the service side).
- Migrating other harness-private memories (that's #11593's operator-gated scope).
Related
Refs #13130 (the empirical anchor), #11593 (cross-family codification-spot context), .agents/skills/unit-test/references/unit-test.md.
Origin Session ID: 4c598c8f-d8a7-4288-9420-e825a45d310e
Retrieval Hint: "unit test connect-on-init singleton import side-effect autoConnect bridge spawn extract pure logic resolveCallTarget"
Context (friction → gold, cross-family)
While implementing #13129 (PR #13130) I hit a Neo-specific testing trap: a unit spec that imported
ai/services/neural-link/ConnectionServiceto test its target-resolution logic connected to a live Bridge during the import (Neo.setupClassconstructs the singleton →initAsync()runs →autoConnectreaches out to the Bridge on:8081). In CI, where nothing is listening, that path spawns a Bridge process — a host side-effect a unit test must never cause. The remedy was to extract the pure logic into a standalone module (resolveCallTarget.mjs) and import that directly.This is exactly the cross-family-visibility gap #11593 names: I'd otherwise bank this only in Claude-Code-private memory, where @neo-gpt and @neo-gemini-pro can't read it. The unit-test guide lives in
.agents/skills/(cross-family-shared), so codifying it there propagates the gotcha to every harness that writes Neo unit tests. It has an obvious home (no new routing convention needed → not blocked on #11593's gate).The Gap
.agents/skills/unit-test/references/unit-test.md§4 (Technical Constraints & Patterns) covers single-thread simulation, className uniqueness, and manual VDOM init — but says nothing about import-time side-effects of connect-on-init singleton services. Line 23 listsinitAsync()only as a lifecycle method.The Fix
Add a short §4 subsection — "Import-Time Side Effects (connect-on-init singletons)":
initAsyncauto-connects runs the side-effect before the test body; in CI it spawns the Bridge);resolveCallTarget-from-ConnectionServiceextraction as the concrete example.Contract Ledger
unit-test.md§4Substrate Slot Rationale (Accretion Defense)
SKILL.mdchange).initAsyncside-effects (a separate ticket), this guidance compresses to a one-line pointer or retires.ai/-service unit tests — the cross-family-visibility outcome #11593 advocates, for a case with a settled home.Acceptance Criteria
unit-test.md§4 gains a concise "Import-Time Side Effects" subsection naming the failure mode, the rule, and the extract-pure-logic remedy.lint-skill-manifestpasses; noSKILL.mdrouter change.Out of Scope
unitTestModeguard toConnectionService.initAsync(a separate code ticket — the durable fix on the service side).Related
Refs #13130 (the empirical anchor), #11593 (cross-family codification-spot context),
.agents/skills/unit-test/references/unit-test.md.Origin Session ID: 4c598c8f-d8a7-4288-9420-e825a45d310e Retrieval Hint: "unit test connect-on-init singleton import side-effect autoConnect bridge spawn extract pure logic resolveCallTarget"