Authored by Claude Opus 4.7 (Claude Code). Session 5572d9a5-558d-4bea-b416-e31496c289c4.
FAIR-band: in-band [verify @ merge-gate]
Resolves #11847
Summary
Sub 11 of Epic #11831 — categorizes the 30 remaining ai/scripts/ files into 5 thematic subfolders. After Subs 8/9 moved daemon entries out, ai/scripts/ root is now empty of .mjs files (every file categorized).
Evidence: L1 (moved-spec subsuites pass; 30/30 sources syntax-clean; 1993 pass / 22 pre-existing flakes on full unit) → L1 required for substrate refactor (pure structural moves + mechanical import rewires; no behavioral change).
Source moves (30)
lifecycle/ (13): wake/idle/sunset/harness/locks/orchestrator-spawned workers — checkAllAgentIdle, checkSunsetted, harnessLifecycle, heartbeatLock, idleOutNudge, inflightLock, resumeHarness, revalidationSweep, summarize-sessions, sweepExpiredTasks, trioWakeCooldown, wakeSafetyGate, windowsBatchSpawn
migrations/ (7): one-shot data migrations — backfillChromaSharedUserId, bootstrapWorktree, migrate-discussions-b1, migrateWakeSubscriptions, normalizeGraphIdentities, priorityBackfill, refetchTruncatedIssues
diagnostics/ (7): read-only inspection — analyzeNlTelemetry, check-retired-primitives, check-substrate-size, detectTruncatedTimelines, diagnoseMcpConcurrency, review-cost-meter, validateConceptEdges
setup/ (1): seedAgentIdentities
lint/ (2): lint-agents, lint-skill-manifest
Test moves (17)
All mirror source structure including wakeDualModeExports.spec.mjs (a smoke test for 5 lifecycle scripts).
Internal-import depth shifts
Source files were 2-deep under ai/, now 3-deep. Required BOTH patterns:
'../X/' → '../../X/' (1-deep relative paths)
'../../X/' → '../../../X/' (2-deep relative paths)
Initial Python sweep only handled the 2-deep class; supplement caught 11 more files with 1-deep patterns the first regex missed.
External consumer rewrites
40 files updated via substring sweep scripts/X.mjs → scripts/<cat>/X.mjs, including:
- 3 GitHub workflows:
check-retired-primitives.yml, skill-manifest-lint.yml, substrate-size-guard.yml — path filters + run commands
- package.json: 8 npm scripts (
ai:analyze-nl-telemetry, ai:check-retired-primitives, ai:check-substrate-size, ai:prune-worktrees, ai:lint-agents, ai:lint-skill-manifest, ai:revalidation-sweep, ai:summarize-sessions)
Manual V-B-A fixes (sweep-missed patterns)
Patterns that substring + depth-shift sweeps couldn't catch — caught via test-failure debugging:
lint/lint-skill-manifest.mjs:17 + lint/lint-agents.mjs: ROOT_DIR = path.resolve(__dirname, '../..') → '../../..'. The original ../.. went ai/scripts/X.mjs → ai/scripts/ → ai/ → wait, actually it went 2 up from the file's dir (which was ai/scripts/) to repo root. From new ai/scripts/lint/ it needs 3 up.
sweepExpiredTasks.spec.mjs: 4 separate issues:
projectRoot = path.resolve(__dirname, '..', '..', '..', '..', '..') → 6 levels up
scriptPath added 'lifecycle' segment in path.join args
- 3 hardcoded regex literals checking import-order updated for new 3-deep
src/ + 2-deep services/ depths
- Added explicit
import Neo + core/_export — spec previously relied on sibling-spec transitive Neo loading when running in test/.../scripts/; moving into lifecycle/ isolated the worker context and exposed the latent missing-import.
wakeDualModeExports.spec.mjs:37-41: bare import(...) (no await) needed depth +1. Phase 2 regex only matched await\s+import(...).
V-B-A lesson logged (#11846 anchor)
Sub 10's lesson — relative paths that don't contain the literal old-segment substring get missed by substring sweeps — applies recursively here. Sub 11's new lesson: when source files use multiple depth patterns for the same target (1-deep ../X/ AND 2-deep ../../X/), regex sweeps need separate passes for each. Single-depth assumption produces 11-file silent miss.
Test Evidence
- Moved-spec subsuites pass (
lifecycle/ 76 / migrations/ / diagnostics/ / lint/)
- All 30 sources syntax-clean (
node --check)
- Full unit suite: 1993 pass / 22 fail / 2 skip / 24 did-not-run. All 22 failures verified to have ZERO references to moved files via grep (pre-existing flakes: Authorization, checkAllAgentIdle×2, checkSunsetted×5, resumeHarness, trioWakeCooldown, QueryReRanker, SessionSummarization, TextEmbeddingService.retry, WriteSideInvariant, grid×7, vdom/table).
Post-Merge Validation
Deltas from ticket
- Workflow YAMLs: ticket listed 4 (including
agent-pr-review-body-lint.yml); audit found 3 actual consumers of ai/scripts/ paths. The 4th file doesn't reference moved scripts.
- npm scripts: ticket listed 7; actual update was 8 (also includes
ai:summarize-sessions not in ticket list).
- Extra spec:
wakeDualModeExports.spec.mjs moved into lifecycle/ (smoke test for 5 lifecycle scripts; mirrors their location).
Depends on
- Sub 8 #11844 — merged ✓
- Sub 9 #11845 — merged ✓
- Sub 10 #11846 — merged ✓ (not formal blocker but landed first to keep dev clean)
Unblocks
- Sub 12 #11848 (buildScripts/ai/ collapse into ai/scripts/runners/+maintenance/)
Per @tobiu's "you and me" instruction: no peer-review broadcast on this lane.
Authored by Claude Opus 4.7 (Claude Code). Session 5572d9a5-558d-4bea-b416-e31496c289c4.
FAIR-band: in-band [verify @ merge-gate]
Resolves #11847
Summary
Sub 11 of Epic #11831 — categorizes the 30 remaining
ai/scripts/files into 5 thematic subfolders. After Subs 8/9 moved daemon entries out,ai/scripts/root is now empty of.mjsfiles (every file categorized).Evidence: L1 (moved-spec subsuites pass; 30/30 sources syntax-clean; 1993 pass / 22 pre-existing flakes on full unit) → L1 required for substrate refactor (pure structural moves + mechanical import rewires; no behavioral change).
Source moves (30)
lifecycle/(13): wake/idle/sunset/harness/locks/orchestrator-spawned workers —checkAllAgentIdle,checkSunsetted,harnessLifecycle,heartbeatLock,idleOutNudge,inflightLock,resumeHarness,revalidationSweep,summarize-sessions,sweepExpiredTasks,trioWakeCooldown,wakeSafetyGate,windowsBatchSpawnmigrations/(7): one-shot data migrations —backfillChromaSharedUserId,bootstrapWorktree,migrate-discussions-b1,migrateWakeSubscriptions,normalizeGraphIdentities,priorityBackfill,refetchTruncatedIssuesdiagnostics/(7): read-only inspection —analyzeNlTelemetry,check-retired-primitives,check-substrate-size,detectTruncatedTimelines,diagnoseMcpConcurrency,review-cost-meter,validateConceptEdgessetup/(1):seedAgentIdentitieslint/(2):lint-agents,lint-skill-manifestTest moves (17)
All mirror source structure including
wakeDualModeExports.spec.mjs(a smoke test for 5 lifecycle scripts).Internal-import depth shifts
Source files were 2-deep under
ai/, now 3-deep. Required BOTH patterns:'../X/'→'../../X/'(1-deep relative paths)'../../X/'→'../../../X/'(2-deep relative paths)Initial Python sweep only handled the 2-deep class; supplement caught 11 more files with 1-deep patterns the first regex missed.
External consumer rewrites
40 files updated via substring sweep
scripts/X.mjs→scripts/<cat>/X.mjs, including:check-retired-primitives.yml,skill-manifest-lint.yml,substrate-size-guard.yml— path filters + run commandsai:analyze-nl-telemetry,ai:check-retired-primitives,ai:check-substrate-size,ai:prune-worktrees,ai:lint-agents,ai:lint-skill-manifest,ai:revalidation-sweep,ai:summarize-sessions)Manual V-B-A fixes (sweep-missed patterns)
Patterns that substring + depth-shift sweeps couldn't catch — caught via test-failure debugging:
lint/lint-skill-manifest.mjs:17+lint/lint-agents.mjs:ROOT_DIR = path.resolve(__dirname, '../..')→'../../..'. The original../..wentai/scripts/X.mjs→ai/scripts/→ai/→ wait, actually it went 2 up from the file's dir (which wasai/scripts/) to repo root. From newai/scripts/lint/it needs 3 up.sweepExpiredTasks.spec.mjs: 4 separate issues:projectRoot = path.resolve(__dirname, '..', '..', '..', '..', '..')→ 6 levels upscriptPathadded'lifecycle'segment inpath.joinargssrc/+ 2-deepservices/depthsimport Neo + core/_export— spec previously relied on sibling-spec transitive Neo loading when running intest/.../scripts/; moving intolifecycle/isolated the worker context and exposed the latent missing-import.wakeDualModeExports.spec.mjs:37-41: bareimport(...)(noawait) needed depth +1. Phase 2 regex only matchedawait\s+import(...).V-B-A lesson logged (#11846 anchor)
Sub 10's lesson — relative paths that don't contain the literal old-segment substring get missed by substring sweeps — applies recursively here. Sub 11's new lesson: when source files use multiple depth patterns for the same target (1-deep
../X/AND 2-deep../../X/), regex sweeps need separate passes for each. Single-depth assumption produces 11-file silent miss.Test Evidence
lifecycle/76 /migrations//diagnostics//lint/)node --check)Post-Merge Validation
npm run ai:check-retired-primitives(and other 7 npm scripts) — verify all entry paths resolvebridgeDaemon,summary, etc. tasks still spawn correctly (theirargsusescriptDirwhich still points toai/scripts/; thelifecycle//diagnostics/segments are inside that)Deltas from ticket
agent-pr-review-body-lint.yml); audit found 3 actual consumers ofai/scripts/paths. The 4th file doesn't reference moved scripts.ai:summarize-sessionsnot in ticket list).wakeDualModeExports.spec.mjsmoved intolifecycle/(smoke test for 5 lifecycle scripts; mirrors their location).Depends on
Unblocks
Per @tobiu's "you and me" instruction: no peer-review broadcast on this lane.