---
id: 11948
title: 'Sub of #11837: workspace-safety integration test (Sub-5 AC5 follow-up)'
state: CLOSED
labels:
  - enhancement
  - ai
assignees:
  - neo-opus-ada
createdAt: '2026-05-25T01:36:08Z'
updatedAt: '2026-06-07T17:15:27Z'
githubUrl: 'https://github.com/neomjs/neo/issues/11948'
author: neo-opus-ada
commentsCount: 0
parentIssue: null
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-26T07:40:04Z'
---
# Sub of #11837: workspace-safety integration test (Sub-5 AC5 follow-up)

## Parent

#11837 — Sub 5 of Epic #11831 (External-user configurability audit). PR #11946 shipped AC1-AC3 invariant tests + AC4 audit-time verification, but AC5 (npx-neo-app workspace safety under missing-Neo-substrate conditions) was deferred because it requires runtime workspace-spawning — too heavy for unit-test scope.

## The Problem

Sub 5 AC5 requires: *"running daemons from a fresh npx-neo-app workspace must not error on missing Neo-team substrate (identityRoots entries, etc.); features that require it MUST disable cleanly with a clear log."*

PR #11946 verified at audit-time that the existing daemon code routes deployment-aware enable flags through `resolveDeploymentEnabled` / `resolveCloudOnlyEnabled` helpers (Orchestrator.mjs:347-351) — but did NOT exercise the fresh-workspace boot path. There's no test that proves:

1. A daemon spawned in a fresh npx-neo-app workspace boots without erroring on missing `ai/graph/identityRoots.mjs` entries (the SwarmHeartbeatService identity-resolver path, for example).
2. Missing Neo-team-substrate features (swarm-heartbeat target resolution, etc.) downgrade gracefully with operator-visible log lines rather than throwing.
3. The cloud-deployment profile (`AiConfig.orchestrator.deploymentMode: 'cloud'`) is the safe-default for the AC5 fresh-workspace scenario.

## The Architectural Reality

External users (forks, npx-neo-app workspace consumers, cloud tenants) do NOT have:
- Pre-populated `ai/graph/identityRoots.mjs` entries for Neo-team handles
- The gitignored `ai/config.mjs` runtime config (only the template ships)
- Pre-existing `.neo-ai-data/` substrate directories
- Operator-specific git remote / branch setup

The Sub-5 ACs assume external users CAN run daemons with the cloud-deployment-profile default disabling Neo-team-only features. AC5 is the test that proves this assumption empirically.

## The Fix

Add an integration test under `test/playwright/integration/ai/daemons/` that:

1. Creates a temporary fresh workspace directory mimicking npx-neo-app output (just `package.json` + `ai/config.template.mjs` + no `ai/config.mjs` + empty `ai/graph/identityRoots.mjs`)
2. Spawns the orchestrator daemon entry point (`ai/daemons/orchestrator/daemon.mjs`) with `NEO_AI_DEPLOYMENT_MODE=cloud`
3. Asserts the daemon boots without erroring within a reasonable timeout
4. Inspects the log output for any features that require Neo-team substrate — verifies they emit clear "feature disabled because <reason>" log lines rather than throwing

## Acceptance Criteria

- [ ] AC1 — Test exists under `test/playwright/integration/ai/daemons/` (or equivalent integration tier)
- [ ] AC2 — Fresh-workspace fixture mimics npx-neo-app output (minimal `package.json`, no gitignored configs)
- [ ] AC3 — Daemon boot under `NEO_AI_DEPLOYMENT_MODE=cloud` completes without uncaught errors
- [ ] AC4 — Log output asserted for graceful-degradation messages from Neo-team-substrate-dependent features (e.g., SwarmHeartbeatService target resolution)
- [ ] AC5 — Test runs in CI integration-unified job

## Out of Scope

- AC1-AC3 invariant tests (already shipped via PR #11946 / `Orchestrator.externalConfig.spec.mjs`)
- AC4 static usage trace (already documented in PR #11946 body)
- Fixing any failures the test surfaces (file as separate sub-tickets if discovered)

## Authority

PR #11946 review body committed to filing this follow-up. Original Sub 5 (#11837) scope explicitly enumerated AC5 as a runtime contract.

**Authored by:** [Claude Opus 4.7] (Claude Code)

## Timeline

- 2026-05-25T01:36:09Z @neo-opus-ada assigned to @neo-opus-ada
- 2026-05-25T01:36:10Z @neo-opus-ada added the `enhancement` label
- 2026-05-25T01:36:10Z @neo-opus-ada added the `ai` label
- 2026-05-25T01:37:30Z @neo-opus-ada cross-referenced by PR #11946
- 2026-05-26T07:21:15Z @neo-opus-ada cross-referenced by PR #12011
- 2026-05-26T07:22:07Z @neo-opus-ada cross-referenced by #12012
- 2026-05-26T07:40:05Z @tobiu referenced in commit `c36ff74` - "feat(orchestrator): add workspace-safety integration test (#11948) (#12011)

Sub-5 AC5 of #11837 — runtime workspace-safety probe PR #11946 deferred as too
heavy for unit-test scope. Adds two child-process spawn probes under
test/playwright/integration/ai/daemons/:

- AC3 cloud-mode boot: verifies the orchestrator daemon reaches the
  '[Orchestrator] Started.' log line in isolated NEO_AI_ORCHESTRATOR_DIR /
  NEO_AI_DB_PATH / NEO_HEARTBEAT_ALIVE_PATH paths under
  NEO_AI_DEPLOYMENT_MODE=cloud, with no UnhandledPromiseRejection / uncaught
  / init-failure surfaces.
- AC4 swarm-heartbeat resolver degrade-with-log: verifies the
  '[resolveSwarmHeartbeatTargets] selfIdentity is null — disabled' log line
  fires within a 30s pulse window in local mode with empty NEO_AGENT_IDENTITY
  + targetSource='self', proving the daemon boot path actually calls the
  resolver and survives the empty-substrate degrade gracefully.

Both probes spawn with cwd=workspaceDir to isolate cwd-relative side effects
(chroma data dir, child task PID files) from a concurrently-running operator
daemon. The sqlite DB is pre-initialized with the subset of
ai/graph/storage/SQLite.mjs::initSchema() the orchestrator's first open
requires, mimicking the post-MC-first-boot substrate shape an external user
would have.

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-26T07:40:05Z @tobiu closed this issue
- 2026-05-27T06:58:43Z @neo-opus-ada cross-referenced by PR #12085

