codex-genesis-harness 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.codebase/CURRENT_STATE.md +7 -33
  2. package/.codebase/KNOWN_PROBLEMS.md +20 -1
  3. package/.codebase/MODULE_INDEX.md +15 -2
  4. package/.codebase/PIPELINE_FLOW.md +10 -2
  5. package/.codebase/RECOVERY_POINTS.md +63 -0
  6. package/.codebase/TEST_MATRIX.md +5 -1
  7. package/.codebase/memories/lessons_learned.md +42 -0
  8. package/.codebase/state.json +130 -12
  9. package/.codex/skills/genesis-harness/SKILL.md +10 -1
  10. package/.codex/skills/genesis-harness/agents/openai.yaml +1 -2
  11. package/.codex/skills/genesis-harness/references/state-machine.md +4 -1
  12. package/.codex/skills/genesis-harness/references/workflows.md +7 -1
  13. package/.codex/skills/genesis-harness/scripts/init-planning.sh +245 -13
  14. package/.codex/skills/genesis-pipeline-orchestration/SKILL.md +15 -3
  15. package/.codex-plugin/plugin.json +4 -2
  16. package/CHANGELOG.md +21 -0
  17. package/README.EN.md +44 -2
  18. package/README.VI.md +44 -2
  19. package/README.md +80 -2
  20. package/VERSION +1 -2
  21. package/bin/genesis-harness.js +2121 -21
  22. package/contracts/features/project-registry-schema.json +37 -0
  23. package/contracts/observability/agent-run-schema.json +6 -1
  24. package/features/REGISTRY.md +9 -7
  25. package/fixtures/pipeline/end-to-end-project-lifecycle-fixture.md +39 -0
  26. package/fixtures/pipeline/feature-completion-fixture.md +26 -0
  27. package/fixtures/pipeline/run-to-feature-execution-fixture.md +20 -0
  28. package/package.json +7 -2
  29. package/scripts/check-repository-hygiene.js +48 -0
  30. package/scripts/run-evals.sh +36 -3
  31. package/scripts/schema/001-init.sql +129 -0
  32. package/scripts/schema/002-story-verify.sql +9 -0
  33. package/scripts/schema/003-tool-registry.sql +15 -0
  34. package/scripts/schema/004-intervention.sql +15 -0
  35. package/scripts/transition_state.sh +32 -8
  36. package/scripts/validation_gates.sh +2 -80
  37. package/scripts/verify.sh +3 -1
  38. package/tests/fixtures/fixture-index.md +5 -0
  39. package/tests/integration/cli-smoke.test.js +403 -0
  40. package/tests/unit/repository_hygiene.test.js +17 -0
  41. package/tests/unit/state_metadata.test.js +76 -0
  42. package/tests/unit/verify_gate.test.js +25 -0
  43. package/tests/unit/workflow_contracts.test.js +90 -0
  44. package/fixtures/tts/tts-fixture-template.md +0 -14
  45. package/fixtures/videos/video-fixture-template.md +0 -14
@@ -1,85 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- echo "====================================="
5
- echo " RUNNING VALIDATION GATES "
6
- echo "====================================="
7
-
8
4
  repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
9
- FAILURES=0
10
-
11
- fail() {
12
- echo "❌ VALIDATION FAILED: $*" >&2
13
- FAILURES=$((FAILURES + 1))
14
- }
15
-
16
- echo "1. Checking for leftover debug logs and TODOs..."
17
- # Exclude node_modules, .git, .codex, scripts, .codebase, bin, and md files
18
- EXCLUDES="--exclude-dir=node_modules --exclude-dir=.git --exclude-dir=.codex --exclude-dir=scripts --exclude-dir=.codebase --exclude-dir=bin --exclude=*.md"
19
-
20
- # We check for TODO, FIXME, console.log, print( (python)
21
- # Using grep recursively
22
- if grep -rn $EXCLUDES -E 'TODO|FIXME|console\.log|print\(' "$repo_root" | grep -v 'validation_gates.sh'; then
23
- fail "Found unresolved TODOs or leftover debug logs in codebase!"
24
- else
25
- echo "✅ Codebase cleanliness gate passed."
26
- fi
27
-
28
- echo "2. Verifying Harness Integrity..."
29
- if [ -f "$repo_root/scripts/verify.sh" ]; then
30
- if bash "$repo_root/scripts/verify.sh" > /dev/null 2>&1; then
31
- echo "✅ Harness Integrity gate passed."
32
- else
33
- fail "verify.sh failed. Core files or templates are missing."
34
- fi
35
- fi
36
-
37
- echo "3. Checking for Documentation Drift (Alignment with Code changes)..."
38
- if [ -d "$repo_root/.git" ]; then
39
- # Get list of modified files in git
40
- CHANGED_FILES=$(git diff --name-only HEAD 2>/dev/null || true)
41
-
42
- if [ -n "$CHANGED_FILES" ]; then
43
- # API Drift: code under src/ modified, but API contracts not updated
44
- if echo "$CHANGED_FILES" | grep -qE "src/.*api|src/.*endpoint|src/.*route" && ! echo "$CHANGED_FILES" | grep -qE "contracts/api/|.codebase/API_CONTRACTS.md|.planning/API_DOCS.md"; then
45
- echo "⚠️ WARNING: You changed API code under src/ but did not update API_CONTRACTS.md, API_DOCS.md or contracts/api/!"
46
- fi
47
-
48
- # Database Drift: models or schemas modified, but DOMAIN_MODELS.md not updated
49
- if echo "$CHANGED_FILES" | grep -qE "model|schema|db" && ! echo "$CHANGED_FILES" | grep -qE ".codebase/DOMAIN_MODELS.md"; then
50
- echo "⚠️ WARNING: You changed database/model files but did not update DOMAIN_MODELS.md!"
51
- fi
52
-
53
- # Test Drift: test files modified, but TEST_MATRIX.md not updated
54
- if echo "$CHANGED_FILES" | grep -qE "tests/|playwright/" && ! echo "$CHANGED_FILES" | grep -qE ".codebase/TEST_MATRIX.md"; then
55
- echo "⚠️ WARNING: You changed test files but did not update TEST_MATRIX.md!"
56
- fi
57
-
58
- # Dependency Drift: package.json dependencies modified, but DEPENDENCY_GRAPH.md not updated
59
- if echo "$CHANGED_FILES" | grep -q "package.json" && ! echo "$CHANGED_FILES" | grep -q ".codebase/DEPENDENCY_GRAPH.md"; then
60
- if git diff package.json 2>/dev/null | grep -qE '^\+.*"(dependencies|devDependencies)"'; then
61
- echo "⚠️ WARNING: You updated package.json dependencies but did not update DEPENDENCY_GRAPH.md!"
62
- fi
63
- fi
64
- echo "✅ Documentation alignment check complete."
65
- fi
66
- else
67
- echo "⚠️ Git repository not found. Skipping drift check."
68
- fi
69
-
70
- echo "4. Running Pre-emptive Prompt Sentinel check..."
71
- if [ -f "$repo_root/scripts/prompt_sentinel.js" ] && [ -f "$repo_root/.codebase/CURRENT_STATE.md" ]; then
72
- # Run prompt sentinel check on the state file as a pre-flight test
73
- node "$repo_root/scripts/prompt_sentinel.js" --check "$repo_root/.codebase/CURRENT_STATE.md" --threshold 1500
74
- fi
75
-
76
-
77
5
 
78
- echo "====================================="
79
- if [ $FAILURES -gt 0 ]; then
80
- echo "❌ $FAILURES Validation Gate(s) failed."
81
- exit 1
82
- else
83
- echo "✅ All Validation Gates passed! Output is ready for production."
84
- exit 0
85
- fi
6
+ echo "Running canonical Genesis verification gate..."
7
+ exec node "$repo_root/bin/genesis-harness.js" verify-gate
package/scripts/verify.sh CHANGED
@@ -55,7 +55,7 @@ required_context_files=(
55
55
  )
56
56
 
57
57
  required_contract_roots=(api agents events ui)
58
- required_fixture_roots=(api agents pipeline render tts images videos)
58
+ required_fixture_roots=(api agents pipeline render images)
59
59
  required_test_roots=(contracts integration unit fixtures)
60
60
  required_playwright_roots=(e2e smoke visual fixtures)
61
61
  required_observability_roots=(agent-runs failures decision-logs)
@@ -180,6 +180,8 @@ verify_repository_harness() {
180
180
 
181
181
  verify_repository_harness
182
182
 
183
+ node "$repo_root/scripts/check-repository-hygiene.js"
184
+
183
185
  verify_skill_metadata() {
184
186
  local skill_dir="$1"
185
187
  local expected_name="$2"
@@ -2,3 +2,8 @@
2
2
 
3
3
  Map every fixture to the contract, test, and module it validates.
4
4
 
5
+ | Fixture | Validates | Test / Module |
6
+ |---|---|---|
7
+ | `fixtures/pipeline/run-to-feature-execution-fixture.md` | `run --idea` advances from discovery into active feature execution with resumable artifacts plus typed API/UI contract scaffolding | `tests/integration/cli-smoke.test.js`, `bin/genesis-harness.js` |
8
+ | `fixtures/pipeline/feature-completion-fixture.md` | `next` and `complete-feature` close an active feature only after executable verification and explicit evidence | `tests/integration/cli-smoke.test.js`, `bin/genesis-harness.js` |
9
+ | `fixtures/pipeline/end-to-end-project-lifecycle-fixture.md` | Multi-feature promotion, project-wide verification, release-ready handoff, idempotent completion, event history, and pipeline audit | `tests/integration/cli-smoke.test.js`, `bin/genesis-harness.js` |
@@ -24,6 +24,15 @@ function run(args, cwd = tmp) {
24
24
  });
25
25
  }
26
26
 
27
+ function runFailure(args, cwd = tmp) {
28
+ try {
29
+ run(args, cwd);
30
+ } catch (error) {
31
+ return `${error.stdout || ""}${error.stderr || ""}`;
32
+ }
33
+ assert.fail(`expected command to fail: ${args.join(" ")}`);
34
+ }
35
+
27
36
  function runPostinstall(initCwd) {
28
37
  return execFileSync(process.execPath, [cli, "postinstall"], {
29
38
  cwd: repoRoot,
@@ -69,6 +78,400 @@ assert(
69
78
  const pathOutput = run(["path"]);
70
79
  assert(pathOutput.includes("genesis-harness"), "path should include installed skill paths");
71
80
 
81
+ const initTmp = fs.mkdtempSync(path.join(os.tmpdir(), "genesis-init-smoke-"));
82
+ fs.writeFileSync(path.join(initTmp, "README.md"), "# Blank project\n");
83
+ const initOutput = run(["init", "--platform", "codex", "--yes"], initTmp);
84
+ assert(initOutput.includes("Initialization Complete"), "init should complete non-interactively");
85
+ assert(
86
+ fs.existsSync(path.join(initTmp, ".planning", "STATE.md")),
87
+ "init should create planning state for empty projects"
88
+ );
89
+ assert(
90
+ fs.existsSync(path.join(initTmp, ".codex", "skills", "genesis-harness", "SKILL.md")),
91
+ "codex init should install packaged skills into the project-local .codex/skills directory"
92
+ );
93
+ assert(
94
+ !fs.existsSync(path.join(initTmp, "codex-home", "skills", "genesis-harness")),
95
+ "codex init should not write packaged skills into CODEX_HOME"
96
+ );
97
+ assert(
98
+ !fs.existsSync(path.join(initTmp, "agents-home", "skills", "genesis-harness")),
99
+ "codex init should not write packaged skills into GENESIS_HARNESS_HOME"
100
+ );
101
+ assert(
102
+ fs.existsSync(path.join(initTmp, ".planning", "phases", "01-discovery-and-qa", "TASKS.md")),
103
+ "init should create a post-foundation discovery phase"
104
+ );
105
+ assert(
106
+ fs.existsSync(path.join(initTmp, ".codebase", "PHASE_DEPENDENCY_MAP.md")),
107
+ "init should create a phase dependency map"
108
+ );
109
+ const initState = fs.readFileSync(path.join(initTmp, ".planning", "STATE.md"), "utf8");
110
+ assert(
111
+ initState.includes("Run discovery Q&A to confirm product approach and tech stack."),
112
+ "init should route the next step to discovery Q&A"
113
+ );
114
+ const qaBrief = fs.readFileSync(path.join(initTmp, ".planning", "INIT_QA.md"), "utf8");
115
+ assert(qaBrief.includes("tech stack"), "init should seed tech stack QA prompts");
116
+ assert(qaBrief.includes("QA sign-off"), "init should seed QA approval prompts");
117
+ const roadmap = fs.readFileSync(path.join(initTmp, ".planning", "ROADMAP.md"), "utf8");
118
+ assert(roadmap.includes("01 Discovery & QA"), "init should add a discovery phase to the roadmap");
119
+
120
+ const ideaTmp = fs.mkdtempSync(path.join(os.tmpdir(), "genesis-init-idea-"));
121
+ const idea =
122
+ "Build a lightweight meal-planning app for busy families that suggests weekly menus, tracks groceries, and works on mobile first.";
123
+ const ideaInitOutput = run(["init", "--platform", "codex", "--yes", "--idea", idea], ideaTmp);
124
+ assert(ideaInitOutput.includes("Initialization Complete"), "init with idea should complete");
125
+ const projectDoc = fs.readFileSync(path.join(ideaTmp, ".planning", "PROJECT.md"), "utf8");
126
+ assert(projectDoc.includes("meal-planning app"), "init with idea should seed project summary from the idea");
127
+ const requirementsDoc = fs.readFileSync(path.join(ideaTmp, ".planning", "REQUIREMENTS.md"), "utf8");
128
+ assert(requirementsDoc.includes("weekly menus"), "init with idea should seed functional requirements from the idea");
129
+ const stackDoc = fs.readFileSync(path.join(ideaTmp, ".planning", "STACK.md"), "utf8");
130
+ assert(stackDoc.includes("Mobile-first"), "init with idea should capture stack clues from the idea");
131
+ const summaryDoc = fs.readFileSync(path.join(ideaTmp, ".planning", "SUMMARY.md"), "utf8");
132
+ assert(summaryDoc.includes("meal-planning app"), "init with idea should carry the idea into the planning summary");
133
+ const initQaDoc = fs.readFileSync(path.join(ideaTmp, ".planning", "INIT_QA.md"), "utf8");
134
+ assert(initQaDoc.includes(idea), "init with idea should embed the original user brief for QA follow-up");
135
+
136
+ const runTmp = fs.mkdtempSync(path.join(os.tmpdir(), "genesis-run-idea-"));
137
+ const runIdea =
138
+ "Create a concierge booking assistant for boutique hotels that helps staff manage guest requests from a tablet.";
139
+ const runOutput = run(
140
+ [
141
+ "run",
142
+ "--platform",
143
+ "codex",
144
+ "--yes",
145
+ "--idea",
146
+ runIdea,
147
+ "--product-approach",
148
+ "Start with a staff-facing web dashboard optimized for tablet use in the lobby.",
149
+ "--primary-user",
150
+ "Front-desk hotel staff",
151
+ "--v1-outcome",
152
+ "Staff can log, prioritize, and resolve guest requests in one queue.",
153
+ "--qa-owner",
154
+ "Operations lead",
155
+ "--backend",
156
+ "Node.js",
157
+ "--frontend",
158
+ "React",
159
+ "--database",
160
+ "PostgreSQL",
161
+ "--deployment",
162
+ "Fly.io",
163
+ "--test-strategy",
164
+ "Node integration tests and Playwright smoke tests",
165
+ "--stack-owner",
166
+ "Tech lead"
167
+ ],
168
+ runTmp
169
+ );
170
+ assert(runOutput.includes("Run pipeline complete"), "run should complete the bootstrap pipeline");
171
+ const runProjectDoc = fs.readFileSync(path.join(runTmp, ".planning", "PROJECT.md"), "utf8");
172
+ assert(runProjectDoc.includes("Front-desk hotel staff"), "run should record the primary user");
173
+ assert(runProjectDoc.includes("staff-facing web dashboard"), "run should record the chosen product approach");
174
+ const runStackDoc = fs.readFileSync(path.join(runTmp, ".planning", "STACK.md"), "utf8");
175
+ assert(runStackDoc.includes("Language: Node.js"), "run should fill backend stack details");
176
+ assert(runStackDoc.includes("Framework: React"), "run should fill frontend stack details");
177
+ const runAdrDoc = fs.readFileSync(path.join(runTmp, ".planning", "decisions", "ADR-001-tech-stack.md"), "utf8");
178
+ assert(runAdrDoc.includes("Status: Accepted"), "run should close the tech-stack ADR when answers are provided");
179
+ const runInitQaDoc = fs.readFileSync(path.join(runTmp, ".planning", "INIT_QA.md"), "utf8");
180
+ assert(runInitQaDoc.includes("Operations lead"), "run should record the QA owner");
181
+ assert(runInitQaDoc.includes("Fly.io"), "run should record deployment direction");
182
+ const runRequirementsDoc = fs.readFileSync(path.join(runTmp, ".planning", "REQUIREMENTS.md"), "utf8");
183
+ assert(
184
+ !runRequirementsDoc.includes("guest requests are handled without context loss"),
185
+ "run should not hard-code a hotel-specific user-story outcome into generic planning docs"
186
+ );
187
+ assert(
188
+ runRequirementsDoc.includes("the core workflow can be completed without context loss"),
189
+ "run should use a generic user-story outcome in seeded planning docs"
190
+ );
191
+ const runState = JSON.parse(fs.readFileSync(path.join(runTmp, ".codebase", "state.json"), "utf8"));
192
+ assert.strictEqual(runState.current_state, "IMPLEMENTATION", "run should advance the bootstrap state into feature execution");
193
+ assert(
194
+ runState.pending_tasks.includes("Implement the first feature slice"),
195
+ "run should queue the first implementation slice"
196
+ );
197
+ assert(runState.active_feature, "run should record the active feature path");
198
+ const activeFeatureDir = path.join(runTmp, runState.active_feature);
199
+ assert(fs.existsSync(activeFeatureDir), "run should scaffold the active feature directory");
200
+ assert(
201
+ fs.existsSync(path.join(activeFeatureDir, "SPEC.md")),
202
+ "run should create a feature specification before implementation"
203
+ );
204
+ assert(
205
+ fs.existsSync(path.join(activeFeatureDir, "PLAN.md")),
206
+ "run should create a feature plan before implementation"
207
+ );
208
+ assert(
209
+ fs.existsSync(path.join(activeFeatureDir, "TEST_CONTRACT.md")),
210
+ "run should create a feature test contract before implementation"
211
+ );
212
+ assert(
213
+ fs.existsSync(path.join(activeFeatureDir, "VERIFICATION.md")),
214
+ "run should create feature verification instructions before implementation"
215
+ );
216
+ const activeFeatureSpec = fs.readFileSync(path.join(activeFeatureDir, "SPEC.md"), "utf8");
217
+ assert(activeFeatureSpec.includes("Front-desk hotel staff"), "run should seed the feature spec with the primary user");
218
+ assert(activeFeatureSpec.includes("one queue"), "run should seed the feature spec with the v1 outcome");
219
+ const activeFeaturePlan = fs.readFileSync(path.join(activeFeatureDir, "PLAN.md"), "utf8");
220
+ assert(activeFeaturePlan.includes("scripts/verify.sh"), "run should seed implementation verification commands");
221
+ const activeFeatureContract = fs.readFileSync(path.join(activeFeatureDir, "TEST_CONTRACT.md"), "utf8");
222
+ assert(activeFeatureContract.includes("contracts/ui/"), "run should link the feature test contract to generated UI contracts");
223
+ assert(activeFeatureContract.includes("contracts/api/"), "run should link the feature test contract to generated API contracts");
224
+ const featureIndex = fs.readFileSync(path.join(runTmp, ".planning", "FEATURE_INDEX.md"), "utf8");
225
+ assert(featureIndex.includes(path.basename(activeFeatureDir)), "run should register the active feature in FEATURE_INDEX.md");
226
+ const generatedUiContractPath = path.join(runTmp, "contracts", "ui", path.basename(activeFeatureDir), "screen-contract.json");
227
+ assert(fs.existsSync(generatedUiContractPath), "run should scaffold a UI contract for a UI-capable first feature");
228
+ const generatedUiContract = JSON.parse(fs.readFileSync(generatedUiContractPath, "utf8"));
229
+ assert(generatedUiContract.contract_id.startsWith("UI-"), "run should generate a typed UI contract id");
230
+ assert.strictEqual(generatedUiContract.inputs.route, "/staff-queue", "run should derive a UI route for the first feature");
231
+ assert(
232
+ generatedUiContract.outputs.events.some(event => event.name === "onQueueItemSelect"),
233
+ "run should generate UI events tailored to the first feature"
234
+ );
235
+ const generatedUiFixturePath = path.join(runTmp, "playwright", "fixtures", `${path.basename(activeFeatureDir)}-ui-fixture.md`);
236
+ assert(fs.existsSync(generatedUiFixturePath), "run should scaffold a UI fixture for a UI-capable first feature");
237
+ const generatedUiFixture = fs.readFileSync(generatedUiFixturePath, "utf8");
238
+ assert(generatedUiFixture.includes("/staff-queue"), "run should seed the UI fixture with the derived route");
239
+ const generatedApiContractDir = path.join(runTmp, "contracts", "api", path.basename(activeFeatureDir));
240
+ assert(fs.existsSync(path.join(generatedApiContractDir, "request.json")), "run should scaffold an API request contract for a backend-capable first feature");
241
+ assert(fs.existsSync(path.join(generatedApiContractDir, "response.json")), "run should scaffold an API response contract for a backend-capable first feature");
242
+ const generatedApiRequest = JSON.parse(fs.readFileSync(path.join(generatedApiContractDir, "request.json"), "utf8"));
243
+ assert.strictEqual(generatedApiRequest.method, "POST", "run should generate an API method for the first feature");
244
+ assert.strictEqual(generatedApiRequest.path, "/api/staff-queue/items", "run should derive an API path for the first feature");
245
+ const generatedApiResponse = JSON.parse(fs.readFileSync(path.join(generatedApiContractDir, "response.json"), "utf8"));
246
+ assert.strictEqual(generatedApiResponse.status, 200, "run should generate an expected successful API status");
247
+ assert.strictEqual(generatedApiResponse.body.status, "queued", "run should tailor the API response body to the first feature");
248
+ const generatedApiFixturePath = path.join(runTmp, "fixtures", "api", `${path.basename(activeFeatureDir)}-api-fixture.md`);
249
+ assert(fs.existsSync(generatedApiFixturePath), "run should scaffold an API fixture for a backend-capable first feature");
250
+ const generatedApiFixture = fs.readFileSync(generatedApiFixturePath, "utf8");
251
+ assert(generatedApiFixture.includes("/api/staff-queue/items"), "run should seed the API fixture with the derived endpoint");
252
+ const runArtifactDir = path.join(runTmp, ".runs", runState.session_id);
253
+ assert(
254
+ fs.existsSync(path.join(runArtifactDir, "STATE.json")),
255
+ "run should persist a resumable state artifact for the active session"
256
+ );
257
+ const runArtifactState = JSON.parse(fs.readFileSync(path.join(runArtifactDir, "STATE.json"), "utf8"));
258
+ assert.strictEqual(
259
+ runArtifactState.current_state,
260
+ "IMPLEMENTATION",
261
+ "run artifact state should mirror the active implementation phase"
262
+ );
263
+ assert(
264
+ fs.existsSync(path.join(runArtifactDir, "DISCOVERY.json")),
265
+ "run should persist discovery answers for later resume"
266
+ );
267
+ assert(
268
+ fs.existsSync(path.join(runArtifactDir, "RESUME.md")),
269
+ "run should persist a human-readable resume brief"
270
+ );
271
+ const resumeOutput = run(["resume"], runTmp);
272
+ assert(resumeOutput.includes("Resume session:"), "resume should print the active session summary");
273
+ assert(resumeOutput.includes(runState.session_id), "resume should reference the active session id");
274
+ assert(resumeOutput.includes("IMPLEMENTATION"), "resume should show the current execution state");
275
+ assert(
276
+ resumeOutput.includes(path.basename(activeFeatureDir)),
277
+ "resume should reference the active feature directory"
278
+ );
279
+ assert(
280
+ resumeOutput.includes("Implement the first feature slice"),
281
+ "resume should direct the next implementation task"
282
+ );
283
+ const featureRegistryPath = path.join(runTmp, ".planning", "FEATURE_REGISTRY.json");
284
+ assert(
285
+ fs.existsSync(featureRegistryPath),
286
+ "run should create a machine-readable project feature registry"
287
+ );
288
+ const projectFeatureRegistry = JSON.parse(fs.readFileSync(featureRegistryPath, "utf8"));
289
+ assert.strictEqual(projectFeatureRegistry.features.length, 1, "run should register the first feature");
290
+ assert.strictEqual(
291
+ projectFeatureRegistry.features[0].status,
292
+ "in-progress",
293
+ "the scaffolded first feature should enter the execution queue"
294
+ );
295
+ assert.strictEqual(
296
+ projectFeatureRegistry.project_status,
297
+ "implementation",
298
+ "the project registry should record the implementation lifecycle stage"
299
+ );
300
+
301
+ const secondFeatureTitle = "Notify staff when a guest request changes";
302
+ const addFeatureOutput = run(
303
+ [
304
+ "add-feature",
305
+ "--title",
306
+ secondFeatureTitle,
307
+ "--slug",
308
+ "guest-request-notifications",
309
+ "--verify-cmd",
310
+ `${process.execPath} -e "process.exit(0)"`
311
+ ],
312
+ runTmp
313
+ );
314
+ assert(addFeatureOutput.includes(secondFeatureTitle), "add-feature should report the queued feature");
315
+ const registryWithQueue = JSON.parse(fs.readFileSync(featureRegistryPath, "utf8"));
316
+ assert.strictEqual(registryWithQueue.features.length, 2, "add-feature should append to the project queue");
317
+ assert.strictEqual(registryWithQueue.features[1].status, "planned", "new features should remain planned");
318
+ assert(
319
+ fs.existsSync(path.join(runTmp, registryWithQueue.features[1].path, "TASKS.md")),
320
+ "add-feature should create an execution packet for the queued feature"
321
+ );
322
+
323
+ const nextOutput = run(["next"], runTmp);
324
+ assert(nextOutput.includes(path.basename(activeFeatureDir)), "next should identify the active feature");
325
+ assert(nextOutput.includes("Add the first failing test"), "next should print the next pending action");
326
+
327
+ const missingEvidenceOutput = runFailure(
328
+ ["complete-feature", "--verify-cmd", `${process.execPath} -e "process.exit(0)"`],
329
+ runTmp
330
+ );
331
+ assert(
332
+ missingEvidenceOutput.includes("--evidence"),
333
+ "complete-feature should require explicit verification evidence"
334
+ );
335
+
336
+ const completionOutput = run(
337
+ [
338
+ "complete-feature",
339
+ "--verify-cmd",
340
+ `${process.execPath} -e "process.exit(0)"`,
341
+ "--evidence",
342
+ "CLI lifecycle smoke verification passed"
343
+ ],
344
+ runTmp
345
+ );
346
+ assert(completionOutput.includes("Feature completed"), "complete-feature should report successful completion");
347
+ const promotedState = JSON.parse(fs.readFileSync(path.join(runTmp, ".codebase", "state.json"), "utf8"));
348
+ assert.strictEqual(
349
+ promotedState.current_state,
350
+ "IMPLEMENTATION",
351
+ "complete-feature should keep the project in implementation while queued work remains"
352
+ );
353
+ assert(
354
+ promotedState.active_feature.includes("guest-request-notifications"),
355
+ "complete-feature should promote the next planned feature"
356
+ );
357
+ assert(
358
+ promotedState.metrics.time_to_verified_feature_seconds >= 0,
359
+ "complete-feature should record feature lead-time telemetry"
360
+ );
361
+ const promotedRegistry = JSON.parse(fs.readFileSync(featureRegistryPath, "utf8"));
362
+ assert.strictEqual(
363
+ promotedRegistry.features[0].status,
364
+ "verified",
365
+ "complete-feature should move the active feature to verified"
366
+ );
367
+ assert.strictEqual(
368
+ promotedRegistry.features[0].evidence,
369
+ "CLI lifecycle smoke verification passed",
370
+ "complete-feature should persist verification evidence"
371
+ );
372
+ assert.strictEqual(
373
+ promotedRegistry.features[1].status,
374
+ "in-progress",
375
+ "complete-feature should promote the next queued feature"
376
+ );
377
+ const featureIndexAfterCompletion = fs.readFileSync(path.join(runTmp, ".planning", "FEATURE_INDEX.md"), "utf8");
378
+ assert(
379
+ featureIndexAfterCompletion.includes(`| ${promotedRegistry.features[0].title} | [x] |`),
380
+ "complete-feature should mark the feature complete in FEATURE_INDEX.md"
381
+ );
382
+ const lifecycleRunLog = path.join(
383
+ runTmp,
384
+ "observability",
385
+ "agent-runs",
386
+ `${promotedState.session_id}-feature-complete.json`
387
+ );
388
+ assert(fs.existsSync(lifecycleRunLog), "complete-feature should write an observability run record");
389
+ const nextPromotedOutput = run(["next"], runTmp);
390
+ assert(nextPromotedOutput.includes(secondFeatureTitle), "next should resolve the promoted feature");
391
+
392
+ run(
393
+ [
394
+ "complete-feature",
395
+ "--verify-cmd",
396
+ `${process.execPath} -e "process.exit(0)"`,
397
+ "--evidence",
398
+ "Notification feature verification passed"
399
+ ],
400
+ runTmp
401
+ );
402
+ const verificationState = JSON.parse(fs.readFileSync(path.join(runTmp, ".codebase", "state.json"), "utf8"));
403
+ assert.strictEqual(
404
+ verificationState.current_state,
405
+ "VERIFICATION",
406
+ "completing the final feature should move the project to verification"
407
+ );
408
+ assert.strictEqual(verificationState.active_feature, "", "final feature completion should clear active work");
409
+
410
+ const prematureCompletion = runFailure(["complete-project", "--evidence", "Premature close"], runTmp);
411
+ assert(
412
+ prematureCompletion.includes("RELEASE_READY"),
413
+ "complete-project should reject projects that have not passed project verification"
414
+ );
415
+
416
+ const projectVerificationOutput = run(
417
+ [
418
+ "verify-project",
419
+ "--verify-cmd",
420
+ `${process.execPath} -e "process.exit(0)"`,
421
+ "--evidence",
422
+ "Project acceptance suite passed"
423
+ ],
424
+ runTmp
425
+ );
426
+ assert(projectVerificationOutput.includes("Project verified"), "verify-project should report success");
427
+ const releaseReadyState = JSON.parse(fs.readFileSync(path.join(runTmp, ".codebase", "state.json"), "utf8"));
428
+ assert.strictEqual(
429
+ releaseReadyState.current_state,
430
+ "RELEASE_READY",
431
+ "verify-project should move the project to release readiness"
432
+ );
433
+ assert(
434
+ fs.existsSync(path.join(runTmp, ".planning", "PROJECT_VERIFICATION.json")),
435
+ "verify-project should persist project-level proof"
436
+ );
437
+ assert(
438
+ fs.existsSync(path.join(runTmp, ".planning", "IMPLEMENTATION_HANDOFF.md")),
439
+ "verify-project should create a final implementation handoff"
440
+ );
441
+
442
+ const projectCompletionOutput = run(
443
+ ["complete-project", "--evidence", "Release readiness approved"],
444
+ runTmp
445
+ );
446
+ assert(projectCompletionOutput.includes("Project completed"), "complete-project should close release-ready work");
447
+ const completedState = JSON.parse(fs.readFileSync(path.join(runTmp, ".codebase", "state.json"), "utf8"));
448
+ assert.strictEqual(completedState.current_state, "COMPLETED", "complete-project should close project state");
449
+ const historyLength = completedState.history.length;
450
+ const eventsPath = path.join(runArtifactDir, "EVENTS.jsonl");
451
+ assert(fs.existsSync(eventsPath), "lifecycle transitions should create append-only run events");
452
+ const eventCount = fs.readFileSync(eventsPath, "utf8").trim().split("\n").length;
453
+ assert(eventCount >= 5, "event history should include queue, feature, verification, and completion transitions");
454
+
455
+ const repeatedCompletionOutput = run(
456
+ ["complete-project", "--evidence", "Release readiness approved"],
457
+ runTmp
458
+ );
459
+ assert(repeatedCompletionOutput.includes("already completed"), "complete-project should be idempotent");
460
+ const repeatedCompletionState = JSON.parse(fs.readFileSync(path.join(runTmp, ".codebase", "state.json"), "utf8"));
461
+ assert.strictEqual(
462
+ repeatedCompletionState.history.length,
463
+ historyLength,
464
+ "idempotent completion should not duplicate state history"
465
+ );
466
+ assert.strictEqual(
467
+ fs.readFileSync(eventsPath, "utf8").trim().split("\n").length,
468
+ eventCount,
469
+ "idempotent completion should not duplicate lifecycle events"
470
+ );
471
+
472
+ const auditOutput = run(["pipeline-audit"], runTmp);
473
+ assert(auditOutput.includes("Pipeline audit passed"), "pipeline-audit should validate the completed lifecycle");
474
+
72
475
  const statusOutput = run(["status"]);
73
476
  assert(statusOutput.includes("GENESIS HARNESS - STATUS REPORT"), "status should render status report");
74
477
 
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const assert = require("assert");
5
+ const path = require("path");
6
+ const { execFileSync } = require("child_process");
7
+
8
+ const repoRoot = path.resolve(__dirname, "..", "..");
9
+ const hygieneScript = path.join(repoRoot, "scripts", "check-repository-hygiene.js");
10
+
11
+ const output = execFileSync(process.execPath, [hygieneScript], {
12
+ cwd: repoRoot,
13
+ encoding: "utf8"
14
+ });
15
+
16
+ assert(output.includes("repository hygiene passed"), "hygiene gate should report success");
17
+ console.log("repository_hygiene tests passed");
@@ -0,0 +1,76 @@
1
+ const assert = require("assert");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+
5
+ const repoRoot = path.resolve(__dirname, "..", "..");
6
+ const currentStatePath = path.join(repoRoot, ".codebase", "CURRENT_STATE.md");
7
+ const repoStatePath = path.join(repoRoot, ".codebase", "state.json");
8
+
9
+ console.log("Running state metadata contract unit tests...");
10
+
11
+ const currentState = fs.readFileSync(currentStatePath, "utf8");
12
+ const repoState = JSON.parse(fs.readFileSync(repoStatePath, "utf8"));
13
+ const allowedStates = new Set([
14
+ "INIT",
15
+ "REQUIREMENTS_GATHERING",
16
+ "PLANNING",
17
+ "IMPLEMENTATION",
18
+ "VERIFICATION",
19
+ "RELEASE_READY",
20
+ "COMPLETED"
21
+ ]);
22
+
23
+ const latestSessionMatch = currentState.match(/\*\*Latest Session\*\*: `([^`]+)`/);
24
+ assert(latestSessionMatch, "CURRENT_STATE.md should declare the latest session");
25
+ assert.strictEqual(
26
+ repoState.session_id,
27
+ latestSessionMatch[1],
28
+ "state.json session_id should match CURRENT_STATE.md"
29
+ );
30
+
31
+ const ttfvMatch = currentState.match(/\*\*Time to First Verification \(TTFV\)\*\*: (\d+)s/);
32
+ assert(ttfvMatch, "CURRENT_STATE.md should declare TTFV in seconds");
33
+ assert.strictEqual(
34
+ repoState.ttfv_seconds,
35
+ Number(ttfvMatch[1]),
36
+ "state.json ttfv_seconds should match CURRENT_STATE.md"
37
+ );
38
+ assert(
39
+ repoState._comment_ttfv.includes(`${repoState.ttfv_seconds}s`),
40
+ "state.json TTFV comment should describe the same number of seconds"
41
+ );
42
+
43
+ assert(
44
+ allowedStates.has(repoState.current_state),
45
+ `state.json current_state must be one of: ${Array.from(allowedStates).join(", ")}`
46
+ );
47
+
48
+ const sessionStartedAt = Date.parse(repoState.session_started_at);
49
+ assert(!Number.isNaN(sessionStartedAt), "state.json should include a valid session_started_at timestamp");
50
+
51
+ if (repoState.current_state === "COMPLETED") {
52
+ const completedAt = Date.parse(repoState.completed_at);
53
+ assert(!Number.isNaN(completedAt), "completed state should include a valid completed_at timestamp");
54
+ assert(
55
+ completedAt >= sessionStartedAt,
56
+ "state.json completed_at must not be older than session_started_at"
57
+ );
58
+ } else {
59
+ assert(
60
+ !Object.prototype.hasOwnProperty.call(repoState, "completed_at"),
61
+ "active states should not retain a stale completed_at timestamp"
62
+ );
63
+ }
64
+
65
+ for (const transition of repoState.history || []) {
66
+ assert(
67
+ allowedStates.has(transition.from),
68
+ `history transition.from must be a legal state: ${transition.from}`
69
+ );
70
+ assert(
71
+ allowedStates.has(transition.to),
72
+ `history transition.to must be a legal state: ${transition.to}`
73
+ );
74
+ }
75
+
76
+ console.log("state metadata contract tests passed! ✓\n");
@@ -0,0 +1,25 @@
1
+ const assert = require("assert");
2
+ const path = require("path");
3
+ const { execFileSync } = require("child_process");
4
+
5
+ const repoRoot = path.resolve(__dirname, "..", "..");
6
+ const cli = path.join(repoRoot, "bin", "genesis-harness.js");
7
+
8
+ console.log("Running verify-gate contract unit tests...");
9
+
10
+ const output = execFileSync(process.execPath, [cli, "verify-gate"], {
11
+ cwd: repoRoot,
12
+ env: {
13
+ ...process.env,
14
+ GENESIS_VERIFY_GATE_SELF_TEST: "1"
15
+ },
16
+ encoding: "utf8"
17
+ });
18
+
19
+ assert(output.includes("run-evals.sh"), "verify-gate should include eval regression checks");
20
+ assert(output.includes("docs-gate"), "verify-gate should include docs drift checks");
21
+ assert(output.includes("cold-start"), "verify-gate should include cold-start readiness checks");
22
+ assert(output.includes("pack:check"), "verify-gate should include package dry-run checks");
23
+ assert(output.includes("leanctx"), "verify-gate should include lean context reporting");
24
+
25
+ console.log("verify-gate contract tests passed! ✓\n");