gemstack-ai 1.1.2 → 1.3.0

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/.agents/skills/gemstack-plan/SKILL.md +2 -1
  2. package/.agents/skills/gemstack-qa/SKILL.md +3 -0
  3. package/.agents/skills/gemstack-ship/SKILL.md +5 -1
  4. package/.agents/skills/gemstack-spec/SKILL.md +3 -2
  5. package/.agents/skills/gemstack-tasks/SKILL.md +4 -3
  6. package/.gemstack/state.json +7 -8
  7. package/CHANGELOG.md +75 -0
  8. package/README.md +36 -0
  9. package/RELEASE_NOTES.md +61 -0
  10. package/docs/architecture-consistency.md +14 -2
  11. package/docs/spec-driven-development.md +26 -0
  12. package/{gemstack-ai-1.1.2.tgz → gemstack-ai-1.3.0.tgz} +0 -0
  13. package/handoff.md +30 -15
  14. package/package.json +2 -2
  15. package/specs/007-mechanical-test-matrix-closure-evidence/.gemstack.json +5 -0
  16. package/specs/007-mechanical-test-matrix-closure-evidence/closure.json +59 -0
  17. package/specs/007-mechanical-test-matrix-closure-evidence/plan.md +484 -0
  18. package/specs/007-mechanical-test-matrix-closure-evidence/spec.md +597 -0
  19. package/specs/007-mechanical-test-matrix-closure-evidence/tasks.md +536 -0
  20. package/specs/008-cost-provider-safety-gates/.gemstack.json +5 -0
  21. package/specs/008-cost-provider-safety-gates/closure.json +59 -0
  22. package/specs/008-cost-provider-safety-gates/plan.md +456 -0
  23. package/specs/008-cost-provider-safety-gates/spec.md +633 -0
  24. package/specs/008-cost-provider-safety-gates/tasks.md +635 -0
  25. package/specs/009-context-capsule/closure.json +59 -0
  26. package/specs/009-context-capsule/context-capsule.json +428 -0
  27. package/specs/009-context-capsule/plan.md +663 -0
  28. package/specs/009-context-capsule/spec.md +913 -0
  29. package/specs/009-context-capsule/tasks.md +720 -0
  30. package/specs/templates/plan.md +30 -0
  31. package/specs/templates/spec.md +18 -0
  32. package/specs/templates/tasks.md +9 -0
  33. package/src/cli.js +8 -0
  34. package/src/commands/collect.js +340 -0
  35. package/src/commands/context.js +95 -0
  36. package/src/commands/ship.js +79 -0
  37. package/src/commands/verify.js +182 -6
  38. package/src/lib/closure-context.js +453 -0
  39. package/src/lib/context-capsule.js +594 -0
  40. package/src/lib/cost-ledger.js +355 -0
  41. package/src/lib/provider-boundary.js +186 -0
  42. package/src/lib/provider-registry.js +265 -0
  43. package/src/lib/runner-adapters.js +347 -0
  44. package/src/lib/safety-gates.js +277 -0
  45. package/src/lib/test-matrix.js +187 -0
@@ -0,0 +1,484 @@
1
+ # Plan de Implementación: Mechanical Test Matrix & Closure Evidence (Upgrade B)
2
+
3
+ **Feature Branch**: `007-mechanical-test-matrix-closure-evidence`
4
+ **Spec**: [`specs/007-mechanical-test-matrix-closure-evidence/spec.md`](file:///c:/CODES/Gemstack/specs/007-mechanical-test-matrix-closure-evidence/spec.md)
5
+ **Lifecycle Status**: `PLAN_COMPLETE`
6
+ **Stop Reason**: `PLAN_COMPLETE_AWAITING_REVIEW`
7
+
8
+ ---
9
+
10
+ ## 1. Primary Objective & Context
11
+
12
+ Upgrade B upgrades Gemstack from an *Architecture Consistency Engine* to an **Architecture Consistency and Mechanical Implementation Closure Engine**.
13
+ It eliminates false closure by establishing an empirical, deterministic chain of verification custody:
14
+ ```text
15
+ SPEC (gemstack-test-matrix: canonical identity & criteria)
16
+ → PLAN (gemstack-test-bindings & gemstack-closure-gates: execution & physical mapping)
17
+ → TASKS (TASK → TEST IDs & file bindings: execution tracking)
18
+ → COLLECT (gemstack collect: native execution & structured closure.json generation)
19
+ → VERIFY (gemstack verify: strictly read-only validation & staleness detection)
20
+ → SHIP (gemstack ship: fresh evidence gatekeeper & lifecycle closure)
21
+ ```
22
+
23
+ ### Invariants & Non-Negotiable Rules:
24
+ 1. **Zero External Runtime Dependencies**: Standard Node.js library exclusively (`node:fs`, `node:path`, `node:crypto`, `node:child_process`).
25
+ 2. **VERIFY is ALWAYS Read-Only**: `gemstack verify` validates existing evidence, computes in-memory status, and NEVER writes to disk, regenerates evidence, or mutates `closure.json`, `state.json`, or sidecars. There is NO `--collect` flag on verify.
26
+ 3. **Dedicated COLLECT Command**: `gemstack collect` is the explicit, mutating evidence collection action.
27
+ 4. **Git is Strictly Optional**: Non-Git repositories and dirty working trees are fully supported.
28
+ 5. **No Shell Execution**: Subprocesses execute with `shell: false` and explicit argv arrays.
29
+ 6. **Strict Roadmap Scope Isolation**:
30
+ - **Upgrade C (Cost & Provider Safety Gates)**: Billable-action protection, provider approval gates, cost-awareness before paid validations, and human approval before billable actions. Excluded from Upgrade B.
31
+ - **Upgrade D (Context Capsule / Context Compression)**: Compact deterministic project context, handoff/context efficiency, and reduced repeated reconstruction. Excluded from Upgrade B.
32
+
33
+ ---
34
+
35
+ ## 2. Constitution & Simplicity Check
36
+
37
+ - [x] **Simplicity Gate**: Minimal module footprint (3 new libraries in `src/lib/`, 1 new command in `src/commands/`, extension of `verify.js` and `cli.js`).
38
+ - [x] **Anti-Abstraction Gate**: Direct use of Node.js built-ins. No AST parsers, no external test frameworks.
39
+ - [x] **Test-First Imperative**: All 20 canonical P1 tests defined, mapped, and verified in TDD waves before implementation.
40
+ - [x] **Zero Silent Failures**: Explicit error codes, exact mathematical reconciliation equations, and strict exit code propagation.
41
+
42
+ ---
43
+
44
+ ## 3. Module Architecture & Boundaries
45
+
46
+ ```text
47
+ c:\CODES\Gemstack\
48
+ ├── src\
49
+ │ ├── lib\
50
+ │ │ ├── hasher.js <-- [REUSE] normalizeContent, hashFile, normalizePath
51
+ │ │ ├── contracts.js <-- [REUSE] column-0 fenced block parsing architecture
52
+ │ │ ├── findings.js <-- [REUSE] computeFindingFingerprint, createFinding, exceptions
53
+ │ │ ├── state.js <-- [REUSE] readState, writeStateAtomic, readSidecar, writeSidecarAtomic
54
+ │ │ ├── test-matrix.js <-- [NEW] Parser and validator for test matrix & acceptanceSignature
55
+ │ │ ├── closure-context.js <-- [NEW] RelevantClosureFiles, content digests, and closureContextHash
56
+ │ │ └── runner-adapters.js <-- [NEW] Native runner execution, TAP ingestion, and reconciliation
57
+ │ └── commands\
58
+ │ ├── collect.js <-- [NEW] Mutating evidence collector: runs tests and writes specs/<feature>/closure.json
59
+ │ └── verify.js <-- [EXTEND] Stage 5: Read-only closure evidence validation; Stage 6: Security and Test Runners
60
+ ├── specs\
61
+ │ └── 007-mechanical-test-matrix-closure-evidence\
62
+ │ ├── spec.md <-- [FROZEN]
63
+ │ ├── plan.md <-- [THIS DOCUMENT]
64
+ │ ├── tasks.md <-- [NEXT PHASE]
65
+ │ └── closure.json <-- [GENERATED SNAPSHOT ON COLLECT: specs/<feature>/closure.json]
66
+ └── tests\
67
+ ├── test-matrix.test.js <-- [NEW] P1 Categories A & B (Parsing, Duplicates, Signature Digest)
68
+ ├── reconciliation.test.js <-- [NEW] P1 Category C (Exact Math, Phantoms, Orphans, Missing Tests)
69
+ ├── runner-adapter.test.js <-- [NEW] P1 Category D (node:test execution, title token extraction)
70
+ ├── traceability.test.js <-- [NEW] P1 Category E (TASK -> TEST bindings, validation_required)
71
+ ├── closure-manifest.test.js <-- [NEW] P1 Category F (closure.json schema, context hashing, stale detection)
72
+ └── closure-gates.test.js <-- [NEW] P1 Categories G & H (Ship gate blocking, verified states, legacy mode)
73
+ ```
74
+
75
+ ### Module Specifications:
76
+
77
+ #### 3.1 `src/lib/test-matrix.js`
78
+ - **Purpose**: Extract, parse, and validate `gemstack-test-matrix` column-0 fenced blocks and compute the deterministic `acceptanceSignature`.
79
+ - **Public Functions**:
80
+ - `extractTestMatrixBlock(content: string): { matrix: object[]|null, isLegacy: boolean }`
81
+ - `validateTestMatrix(matrix: any): object[]` (returns validated array or throws specific errors)
82
+ - `computeAcceptanceSignature(matrix: object[]): string` (64-char lowercase SHA-256)
83
+ - **Errors**: `TEST_MATRIX_PARSE_ERROR`, `TEST_MATRIX_DUPLICATE_ID`, `TEST_MATRIX_INVALID_SHAPE`.
84
+ - **Dependencies**: `node:crypto`, `src/lib/hasher.js` (`normalizeContent`).
85
+ - **Side Effects**: None (pure functional).
86
+
87
+ #### 3.2 `src/lib/closure-context.js`
88
+ - **Purpose**: Compute deterministic repository context, identify relevant bound closure files without whole-repo scanning, and compute `closureContextHash`.
89
+ - **Public Functions**:
90
+ - `resolveRepositoryContext(rootPath: string): { type: 'git'|'non-git', commit: string|null, workingTreeClean: boolean|null }`
91
+ - `resolveRelevantFiles(featureDir: string, taskBindings: object, runnerFiles: string[]): string[]`
92
+ - `computeContentAggregateHash(rootPath: string, filePaths: string[]): string`
93
+ - `computeClosureContextHash(contextObj: object): string`
94
+ - **Dependencies**: `node:fs`, `node:path`, `node:crypto`, `node:child_process`, `src/lib/hasher.js`.
95
+ - **Side Effects**: Reads disk files and executes read-only git status queries if git is present.
96
+
97
+ #### 3.3 `src/lib/runner-adapters.js`
98
+ - **Purpose**: Execute native test runners safely without `shell: true`, parse machine-readable test outputs, and perform mathematical reconciliation.
99
+ - **Public Functions**:
100
+ - `executeRunner(command: string, args: string[], options: object): Promise<{ exitCode: number, stdout: string, stderr: string, durationMs: number }>`
101
+ - `parseNodeTestTap(tapOutput: string): { physicalTotal: number, passed: number, failed: number, skipped: number, tests: object[] }`
102
+ - `reconcileTestRun(canonicalMatrix: object[], runnerResults: object): { mathValid: boolean, phantoms: string[], orphans: string[], missing: string[], reconciliation: object }`
103
+ - `generateClosureManifest(params: object): object`
104
+ - **Dependencies**: `node:child_process`, `node:path`, `src/lib/hasher.js`.
105
+ - **Side Effects**: Spawns test runner subprocesses (during COLLECT only).
106
+
107
+ ---
108
+
109
+ ## 4. Test Matrix Parsing & Schema Validation
110
+
111
+ ### 4.1 Block Rules
112
+ - Only column-0 ````gemstack-test-matrix blocks are matched.
113
+ - 0 blocks: triggers `LEGACY` mode.
114
+ - 1 block: parses strict JSON.
115
+ - 2+ blocks: throws `TEST_MATRIX_PARSE_ERROR` (Upgrade A consistency model: exactly one structured block allowed).
116
+
117
+ ### 4.2 Strict Schema Validation
118
+ Each item in the array must be an object with exact fields:
119
+ - `id` (string): Must match `^TEST-[A-Z0-9]+-[A-Z0-9]+$`. Duplicate IDs trigger `TEST_MATRIX_DUPLICATE_ID`.
120
+ - `category` (string): Non-empty string.
121
+ - `layer` (enum): Must be one of `["UNIT", "INTEGRATION", "E2E", "CLI"]`.
122
+ - `description` (string): Non-empty string.
123
+ - `pass_criteria` (string): Non-empty string.
124
+ - `gate` (enum): Must be one of `["REQUIRED", "SUPPLEMENTAL"]`.
125
+ Any missing, unknown, or empty fields trigger `TEST_MATRIX_INVALID_SHAPE`.
126
+
127
+ ---
128
+
129
+ ## 5. Acceptance Signature (`acceptanceSignature`)
130
+
131
+ ### Canonical Serialization Algorithm:
132
+ 1. Sort canonical test objects by ASCII/code-unit ordering of `id`: `(a.id < b.id ? -1 : (a.id > b.id ? 1 : 0))`.
133
+ 2. Construct objects with ASCII-sorted keys: `["category", "description", "gate", "id", "layer", "pass_criteria"]`.
134
+ 3. Preserve semantic string values (`description`, `pass_criteria`, `category`) exactly without trimming or case-folding.
135
+ 4. Serialize to compact canonical JSON (no insignificant whitespace).
136
+ 5. Hash using SHA-256: 64-character lowercase hexadecimal digest.
137
+
138
+ ---
139
+
140
+ ## 6. Physical Test Bindings & Authoring Schema (`gemstack-test-bindings`)
141
+
142
+ PLAN defines deterministic physical execution mappings using a column-0 fenced block `gemstack-test-bindings`:
143
+
144
+ ```gemstack-test-bindings
145
+ [
146
+ {
147
+ "test_id": "TEST-CLOSURE-A01",
148
+ "runner": "node:test",
149
+ "file": "tests/test-matrix.test.js"
150
+ },
151
+ {
152
+ "test_id": "TEST-CLOSURE-A02",
153
+ "runner": "node:test",
154
+ "file": "tests/test-matrix.test.js"
155
+ },
156
+ {
157
+ "test_id": "TEST-CLOSURE-A03",
158
+ "runner": "node:test",
159
+ "file": "tests/test-matrix.test.js"
160
+ },
161
+ {
162
+ "test_id": "TEST-CLOSURE-B01",
163
+ "runner": "node:test",
164
+ "file": "tests/test-matrix.test.js"
165
+ },
166
+ {
167
+ "test_id": "TEST-CLOSURE-B02",
168
+ "runner": "node:test",
169
+ "file": "tests/test-matrix.test.js"
170
+ },
171
+ {
172
+ "test_id": "TEST-CLOSURE-C01",
173
+ "runner": "node:test",
174
+ "file": "tests/reconciliation.test.js"
175
+ },
176
+ {
177
+ "test_id": "TEST-CLOSURE-C02",
178
+ "runner": "node:test",
179
+ "file": "tests/reconciliation.test.js"
180
+ },
181
+ {
182
+ "test_id": "TEST-CLOSURE-C03",
183
+ "runner": "node:test",
184
+ "file": "tests/reconciliation.test.js"
185
+ },
186
+ {
187
+ "test_id": "TEST-CLOSURE-C04",
188
+ "runner": "node:test",
189
+ "file": "tests/reconciliation.test.js"
190
+ },
191
+ {
192
+ "test_id": "TEST-CLOSURE-D01",
193
+ "runner": "node:test",
194
+ "file": "tests/runner-adapter.test.js"
195
+ },
196
+ {
197
+ "test_id": "TEST-CLOSURE-D02",
198
+ "runner": "node:test",
199
+ "file": "tests/runner-adapter.test.js"
200
+ },
201
+ {
202
+ "test_id": "TEST-CLOSURE-D03",
203
+ "runner": "node:test",
204
+ "file": "tests/runner-adapter.test.js"
205
+ },
206
+ {
207
+ "test_id": "TEST-CLOSURE-E01",
208
+ "runner": "node:test",
209
+ "file": "tests/traceability.test.js"
210
+ },
211
+ {
212
+ "test_id": "TEST-CLOSURE-E02",
213
+ "runner": "node:test",
214
+ "file": "tests/traceability.test.js"
215
+ },
216
+ {
217
+ "test_id": "TEST-CLOSURE-F01",
218
+ "runner": "node:test",
219
+ "file": "tests/closure-manifest.test.js"
220
+ },
221
+ {
222
+ "test_id": "TEST-CLOSURE-F02",
223
+ "runner": "node:test",
224
+ "file": "tests/closure-manifest.test.js"
225
+ },
226
+ {
227
+ "test_id": "TEST-CLOSURE-F03",
228
+ "runner": "node:test",
229
+ "file": "tests/closure-manifest.test.js"
230
+ },
231
+ {
232
+ "test_id": "TEST-CLOSURE-G01",
233
+ "runner": "node:test",
234
+ "file": "tests/closure-gates.test.js"
235
+ },
236
+ {
237
+ "test_id": "TEST-CLOSURE-G02",
238
+ "runner": "node:test",
239
+ "file": "tests/closure-gates.test.js"
240
+ },
241
+ {
242
+ "test_id": "TEST-CLOSURE-H01",
243
+ "runner": "node:test",
244
+ "file": "tests/closure-gates.test.js"
245
+ }
246
+ ]
247
+ ```
248
+
249
+ ### Binding Validation Rules:
250
+ - Every `test_id` must exist in `gemstack-test-matrix`.
251
+ - Exactly one physical binding is allowed per canonical test ID (no duplicates).
252
+ - `runner` must be supported (`node:test`).
253
+ - `file` must be a repository-relative normalized POSIX path pointing inside the repository.
254
+
255
+ ### Physical Test Identification & Runner Execution:
256
+ - Title Token Extraction: Tests in bound runner files match `\b(TEST-[A-Z0-9]+-[A-Z0-9]+)\b` (e.g. `TEST-CLOSURE-A01: Description`).
257
+ - Any test claiming a canonical ID not in `gemstack-test-matrix`: emits **`ORPHAN_TEST`**.
258
+ - Any test executed without a canonical token: classified as **`SUPPORTING_TEST`**.
259
+
260
+ ---
261
+
262
+ ## 7. Project Closure Gates (`gemstack-closure-gates`)
263
+
264
+ Project closure gates are declared in `plan.md` using column-0 fenced block `gemstack-closure-gates`:
265
+
266
+ ```gemstack-closure-gates
267
+ [
268
+ {
269
+ "id": "project-tests",
270
+ "type": "PACKAGE_SCRIPT",
271
+ "script": "test",
272
+ "requirement": "REQUIRED",
273
+ "waivable": false
274
+ },
275
+ {
276
+ "id": "gate-ci-frontmatter",
277
+ "type": "PACKAGE_SCRIPT",
278
+ "script": "ci:frontmatter",
279
+ "requirement": "REQUIRED",
280
+ "waivable": false
281
+ },
282
+ {
283
+ "id": "gate-ci-mojibake",
284
+ "type": "PACKAGE_SCRIPT",
285
+ "script": "ci:mojibake",
286
+ "requirement": "REQUIRED",
287
+ "waivable": false
288
+ }
289
+ ]
290
+ ```
291
+
292
+ ### Safe Zero-Shell Package Script Execution:
293
+ - Direct Node execution: uses `process.execPath`.
294
+ - Package manager (npm): resolved platform-safely without `shell: true`.
295
+ - On Windows: locates `npm.cmd` via `path.join(path.dirname(process.execPath), 'npm.cmd')` or standard system PATH.
296
+ - On POSIX: locates `npm` binary.
297
+ - Invokes: `spawn(npmExec, ['run', script], { shell: false, stdio: 'pipe' })`.
298
+ - No shell string interpolation, no cmd/bash wrapper scripts.
299
+
300
+ ---
301
+
302
+ ## 8. Canonical Set Equality & Terminal State Arithmetic
303
+
304
+ ### 8.1 Authoritative Runner Terminal State Arithmetic
305
+ Native runners expose multiple terminal states. Reconciliation enforces exact equation:
306
+ $$\text{PASS} + \text{FAIL} + \text{SKIP} + \text{TODO} + \text{CANCELLED} == \text{TOTAL PHYSICAL EXECUTED}$$
307
+ - For required canonical tests: `TODO` and `CANCELLED` are normalized as `REQUIRED_TEST_SKIPPED` (unfulfilled requirement, blocker).
308
+ - Formula: $\text{Executed Canonical} + \text{Executed Supporting} == \text{Total Physical Executed}$.
309
+
310
+ ### 8.2 Set Semantics: Clean VERIFIED vs. VERIFIED_WITH_EXCEPTIONS
311
+ - **Clean VERIFIED**:
312
+ $$\text{EXPECTED\_REQUIRED\_CANONICAL\_IDS} == \text{PASSED\_REQUIRED\_CANONICAL\_IDS}$$
313
+ $$\text{FAILED\_REQUIRED} = \emptyset, \quad \text{SKIPPED\_REQUIRED} = \emptyset, \quad \text{NOT\_EXECUTED\_REQUIRED} = \emptyset, \quad \text{MISSING\_REQUIRED} = \emptyset$$
314
+ - **VERIFIED_WITH_EXCEPTIONS**:
315
+ $$\text{EXPECTED\_REQUIRED\_CANONICAL\_IDS} == \text{PASSED\_REQUIRED\_CANONICAL\_IDS} \cup \text{ACCEPTED\_REQUIRED\_DEVIATIONS}$$
316
+ The sets must be mutually disjoint. Deviations are never rewritten as PASS in the manifest; true outcomes (`SKIPPED`, `FAILED_GATE`) remain transparently recorded.
317
+
318
+ ### 8.3 Waiver & Non-Waivable Policy
319
+ - **`REQUIRED_TEST_FAILED`**: **STRICTLY NON-WAIVABLE**. A failing canonical test can never be waived into eligibility.
320
+ - **`REQUIRED_TEST_SKIPPED`**: Potentially waivable with explicit human exception bound to `closureContextHash`.
321
+ - **`REQUIRED_GATE_FAILED`**: Waivable ONLY if the gate declaration explicitly specifies `waivable: true`.
322
+
323
+ ---
324
+
325
+ ## 9. Physical Test Missing vs. Not Executed
326
+
327
+ Upgrade B avoids AST scanning by using the structured physical bindings:
328
+ 1. **`REQUIRED_TEST_MISSING`**: A required canonical ID has no valid binding entry in `gemstack-test-bindings`.
329
+ 2. **`REQUIRED_TEST_NOT_EXECUTED`**: A required canonical ID is bound in `gemstack-test-bindings`, but no matching execution event was emitted in the runner's TAP output during the test run.
330
+
331
+ ---
332
+
333
+ ## 10. Closure Context Object & Context Hash
334
+
335
+ Deterministic object structure:
336
+ ```json
337
+ {
338
+ "version": 1,
339
+ "repository": {
340
+ "type": "git",
341
+ "commit": "26db5c93c39aa2b5a1df97906eb46bc37894c214",
342
+ "working_tree_clean": true
343
+ },
344
+ "phase_hashes": {
345
+ "spec": "75f7b5aee9a7d45836128f619ba67d0870c9be3a2634d9152b01c6501faa513d",
346
+ "plan": "...",
347
+ "tasks": "..."
348
+ },
349
+ "acceptance_signature": "...",
350
+ "test_files_hash": "...",
351
+ "implementation_context_hash": "...",
352
+ "required_gate_definition_hash": "..."
353
+ }
354
+ ```
355
+ ### Relevant File Scope & Deterministic Invariant (`RelevantClosureFiles`):
356
+ `closureContextHash` never scans the entire repository. The relevant file scope is strictly derived as follows:
357
+ - **Phase Artifacts**: `spec.md`, `plan.md` (and `tasks.md` once created).
358
+ - **Test Matrix Acceptance Signature**: Deterministic SHA-256 digest of canonical test matrix in `spec.md`.
359
+ - **Physical Test Files**: Unique file paths extracted from `gemstack-test-bindings` in `plan.md`.
360
+ - **Implementation Files**: Unique file paths bound to implementation tasks in `tasks.md`.
361
+ - **Referenced Package Scripts Source**: `package.json` when a closure gate references a `PACKAGE_SCRIPT`.
362
+ - **Gate Definitions**: Bound via the `plan.md` phase hash (`gemstack-closure-gates`). Machine sidecar `specs/<feature>/.gemstack.json` is machine-owned audit/history only and is NEVER an author-owned gate configuration source.
363
+ All file paths are normalized to POSIX format, sorted by ASCII code units, and hashed deterministically.
364
+
365
+ ---
366
+
367
+ ## 11. Lifecycle Architecture: COLLECT → VERIFY → SHIP
368
+
369
+ 1. **`gemstack collect` (Mutating Evidence Collector)**:
370
+ - Command: `node src/cli.js collect` (or `gemstack collect`).
371
+ - Action: Resolves active feature directory (`specs/<active-feature>`), executes declared test runners and required project gates, parses TAP outputs, computes `closureContextHash`, evaluates gates, and atomically writes the feature-local manifest: `specs/<feature>/closure.json`. Never writes a global `.gemstack/closure.json`.
372
+ 2. **`gemstack verify` (Strictly Read-Only Validator)**:
373
+ - Command: `node src/cli.js verify`.
374
+ - Action: Read-only verification executing in 6 distinct stages:
375
+ 1. Stage 1/6: Structural Verification (Base files & Manifest)
376
+ 2. Stage 2/6: Memory & Handoff Integrity
377
+ 3. Stage 3/6: Local State Integrity (`.gemstack/state.json`)
378
+ 4. Stage 4/6: Architecture Consistency & Phase Hashes
379
+ 5. Stage 5/6: Mechanical Closure Evidence Validation (reads `specs/<feature>/closure.json`, validates gates, checks freshness against in-memory `closureContextHash`; strictly zero disk writes)
380
+ 6. Stage 6/6: Security and Test Runners (script audit, anti-silent-failure checks)
381
+ - Zero disk writes, zero evidence collection, zero mutation of `closure.json`.
382
+ 3. **`gemstack ship` (Gatekeeper)**:
383
+ - Command: `gemstack ship`.
384
+ - Action: Enforces that feature-local `specs/<feature>/closure.json` exists, is fresh, has status `VERIFIED` or policy-permitted `VERIFIED_WITH_EXCEPTIONS`, confirms architecture consistency passes, and awaits explicit human confirmation before lifecycle transition to `SHIPPED`.
385
+
386
+ ---
387
+
388
+ ## 12. Closure Manifest Schema (`closure.json`)
389
+
390
+ ```json
391
+ {
392
+ "schema": "gemstack-closure",
393
+ "version": 1,
394
+ "feature": "specs/007-mechanical-test-matrix-closure-evidence/",
395
+ "generated_at": "2026-09-11T12:00:00.000Z",
396
+ "status": "VERIFIED",
397
+ "closure_context": {
398
+ "closure_context_hash": "...",
399
+ "repository_type": "git",
400
+ "git_commit": "26db5c93c39aa2b5a1df97906eb46bc37894c214",
401
+ "working_tree_clean": true,
402
+ "relevant_files_digest": "..."
403
+ },
404
+ "acceptance_signature": "...",
405
+ "canonical_summary": { "required_total": 20, "required_passed": 20, "supplemental_total": 0, "supplemental_passed": 0 },
406
+ "physical_summary": { "supporting_total": 5, "supporting_passed": 5, "total_executed": 25, "total_passed": 25, "total_failed": 0, "total_skipped": 0 },
407
+ "reconciliation": { "math_valid": true, "phantoms_detected": 0, "orphans_detected": 0, "missing_canonical_ids": [] },
408
+ "task_traceability_summary": { "tasks_total": 17, "tasks_with_validation": 15, "tasks_documentation_only": 2, "unmapped_canonical_tests": [] },
409
+ "required_gates": {
410
+ "project-tests": "PASS",
411
+ "gate-ci-frontmatter": "PASS",
412
+ "gate-ci-mojibake": "PASS"
413
+ },
414
+ "supplemental_gates": {},
415
+ "exceptions": [],
416
+ "evidence_sources": [
417
+ {
418
+ "type": "PACKAGE_SCRIPT",
419
+ "script": "test",
420
+ "runner": "node:test",
421
+ "exit_code": 0,
422
+ "duration_ms": 420
423
+ }
424
+ ],
425
+ "blockers": [],
426
+ "warnings": []
427
+ }
428
+ ```
429
+
430
+ ---
431
+
432
+ ## 13. P1 Canonical Test Matrix Mapping (20 Tests)
433
+
434
+ | Canonical Test ID | Category | Layer | Target Test Suite | Exercised Modules | Expected Result / Finding |
435
+ | :--- | :--- | :--- | :--- | :--- | :--- |
436
+ | **TEST-CLOSURE-A01** | PARSING | UNIT | `tests/test-matrix.test.js` | `test-matrix.js` | Valid block returns CanonicalTest[] array |
437
+ | **TEST-CLOSURE-A02** | PARSING | UNIT | `tests/test-matrix.test.js` | `test-matrix.js` | Emits `TEST_MATRIX_PARSE_ERROR` / `TEST_MATRIX_DUPLICATE_ID` |
438
+ | **TEST-CLOSURE-A03** | PARSING | UNIT | `tests/test-matrix.test.js` | `test-matrix.js` | Cleanly ignores non-column-0 blocks without throwing |
439
+ | **TEST-CLOSURE-B01** | SIGNATURE | UNIT | `tests/test-matrix.test.js` | `test-matrix.js` | Deterministic SHA-256 invariant to JSON key order |
440
+ | **TEST-CLOSURE-B02** | SIGNATURE | UNIT | `tests/test-matrix.test.js` | `test-matrix.js` | Emits `ACCEPTANCE_SIGNATURE_MISMATCH` distinct from artifact change |
441
+ | **TEST-CLOSURE-C01** | RECONCILIATION | UNIT | `tests/reconciliation.test.js` | `runner-adapters.js` | Validates formula including all terminal states |
442
+ | **TEST-CLOSURE-C02** | RECONCILIATION | UNIT | `tests/reconciliation.test.js` | `runner-adapters.js` | Distinguishes `REQUIRED_TEST_MISSING` vs `REQUIRED_TEST_NOT_EXECUTED` |
443
+ | **TEST-CLOSURE-C03** | RECONCILIATION | UNIT | `tests/reconciliation.test.js` | `runner-adapters.js` | Emits non-waivable `PHANTOM_TEST` on runner mismatch |
444
+ | **TEST-CLOSURE-C04** | RECONCILIATION | UNIT | `tests/reconciliation.test.js` | `runner-adapters.js` | Emits `ORPHAN_TEST` for physical tests claiming unapproved IDs |
445
+ | **TEST-CLOSURE-D01** | RUNNER_ADAPTER | INTEGRATION | `tests/runner-adapter.test.js` | `runner-adapters.js` | Spawns `node:test` with `shell:false`, parses counts from TAP |
446
+ | **TEST-CLOSURE-D02** | RUNNER_ADAPTER | INTEGRATION | `tests/runner-adapter.test.js` | `runner-adapters.js` | Extracts `[TEST-CLOSURE-xxx]` from test titles accurately |
447
+ | **TEST-CLOSURE-D03** | RUNNER_ADAPTER | UNIT | `tests/runner-adapter.test.js` | `runner-adapters.js` | Handles non-zero exit codes gracefully without crashing |
448
+ | **TEST-CLOSURE-E01** | TRACEABILITY | UNIT | `tests/traceability.test.js` | `closure-context.js` | Validates `TASK -> TEST` direction; emits `TASK_VALIDATION_MISSING` |
449
+ | **TEST-CLOSURE-E02** | TRACEABILITY | UNIT | `tests/traceability.test.js` | `closure-context.js` | Emits `UNMAPPED_CANONICAL_TEST` if canonical test has no task |
450
+ | **TEST-CLOSURE-F01** | MANIFEST & CONTEXT | UNIT | `tests/closure-manifest.test.js`| `runner-adapters.js` | Serializes valid `closure.json` schema with nullable Git fields |
451
+ | **TEST-CLOSURE-F02** | MANIFEST & CONTEXT | UNIT | `tests/closure-manifest.test.js`| `closure-context.js` | Computes deterministic `closureContextHash` (clean, dirty, non-git) |
452
+ | **TEST-CLOSURE-F03** | MANIFEST & CONTEXT | UNIT | `tests/closure-manifest.test.js`| `closure-context.js` | Read-only detection of stale evidence (`CLOSURE_EVIDENCE_STALE`) |
453
+ | **TEST-CLOSURE-G01** | GATES | INTEGRATION | `tests/closure-gates.test.js` | `commands/verify.js` | Blocks closure when gate fails; `REQUIRED_TEST_FAILED` non-waivable |
454
+ | **TEST-CLOSURE-G02** | GATES | INTEGRATION | `tests/closure-gates.test.js` | `commands/verify.js` | Permits shipping when status is `VERIFIED` or `VERIFIED_WITH_EXCEPTIONS`|
455
+ | **TEST-CLOSURE-H01** | LEGACY | UNIT | `tests/closure-gates.test.js` | `commands/verify.js` | Specs without test matrix run in legacy mode with exit code 0 |
456
+
457
+ ---
458
+
459
+ ## 14. File Impact Map
460
+
461
+ ### New Files:
462
+ - `src/lib/test-matrix.js`: Matrix parser and signature calculator.
463
+ - `src/lib/closure-context.js`: Context resolution and file hashing.
464
+ - `src/lib/runner-adapters.js`: Subprocess runner and TAP reconciliation.
465
+ - `src/commands/collect.js`: Dedicated mutating evidence collection command.
466
+ - `tests/test-matrix.test.js`: P1 suites A & B.
467
+ - `tests/reconciliation.test.js`: P1 suite C.
468
+ - `tests/runner-adapter.test.js`: P1 suite D.
469
+ - `tests/traceability.test.js`: P1 suite E.
470
+ - `tests/closure-manifest.test.js`: P1 suite F.
471
+ - `tests/closure-gates.test.js`: P1 suites G & H.
472
+
473
+ ### Modified Files:
474
+ - `src/commands/verify.js`: Add Stage 5 of 6 (Mechanical Closure Evidence Validation, strictly read-only, reading `specs/<feature>/closure.json`) and advance Security & Test Runners to Stage 6 of 6. Strictly zero disk writes.
475
+ - `src/commands/ship.js`: Enforce that feature-local `specs/<feature>/closure.json` is fresh and `VERIFIED` before allowing lifecycle closure.
476
+ - `src/cli.js`: Register dedicated `collect` command.
477
+ - `package.json`: Update test script to include the 6 new test files while preserving all 5 existing test files.
478
+
479
+ ### Unchanged Critical Files:
480
+ - `src/lib/contracts.js`
481
+ - `src/lib/hasher.js`
482
+ - `src/lib/findings.js`
483
+ - `src/lib/state.js`
484
+ - Existing test suites: `tests/contracts.test.js`, `tests/hasher.test.js`, `tests/findings.test.js`, `tests/init.test.js`, `tests/verify.test.js`.