harness-everything 0.25.0 → 0.26.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 (36) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +36 -0
  4. package/ci/mechanism-35-verification-contract.test.js +416 -0
  5. package/ci/mutation-check.js +2 -0
  6. package/ci/mutation-loader.js +4 -0
  7. package/docs/audit.md +2 -0
  8. package/harness-everything/SKILL.md +2 -2
  9. package/harness-everything/scripts/verify-gate.js +223 -75
  10. package/hooks/scripts/lib/workflow-isolation.js +3 -4
  11. package/hooks/scripts/state-persist.js +1 -1
  12. package/opencode-plugin/plugin.json +1 -1
  13. package/package.json +1 -1
  14. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  15. package/plugins/harness-everything/hooks/scripts/lib/workflow-isolation.js +3 -4
  16. package/plugins/harness-everything/hooks/scripts/state-persist.js +1 -1
  17. package/plugins/harness-everything/plugin.json +1 -1
  18. package/plugins/harness-everything/scripts/lib/verification-contract.js +357 -0
  19. package/plugins/harness-everything/skills/harness-everything/SKILL.md +2 -2
  20. package/plugins/harness-everything/skills/harness-everything/scripts/verify-gate.js +223 -75
  21. package/plugins/harness-everything/skills/using-git-worktrees/SKILL.md +14 -13
  22. package/plugins/harness-everything/skills/using-git-worktrees/references/submodules-in-worktrees.md +135 -0
  23. package/plugins/harness-everything/skills/verification-loop/SKILL.md +15 -23
  24. package/plugins/harness-everything/skills/verification-loop/references/project-verification-contract.md +112 -0
  25. package/plugins/harness-everything/skills/verification-loop/references/verification-phases.md +4 -3
  26. package/plugins/harness-everything/skills/verification-loop/references/verify-contract.schema.json +32 -0
  27. package/plugins/harness-everything/skills/verification-loop/references/verify-result.schema.json +65 -0
  28. package/sbom.cdx.json +7 -7
  29. package/scripts/lib/verification-contract.js +357 -0
  30. package/using-git-worktrees/SKILL.md +14 -13
  31. package/using-git-worktrees/references/submodules-in-worktrees.md +135 -0
  32. package/verification-loop/SKILL.md +15 -23
  33. package/verification-loop/references/project-verification-contract.md +112 -0
  34. package/verification-loop/references/verification-phases.md +4 -3
  35. package/verification-loop/references/verify-contract.schema.json +32 -0
  36. package/verification-loop/references/verify-result.schema.json +65 -0
@@ -1,100 +1,248 @@
1
1
  #!/usr/bin/env node
2
+ 'use strict';
3
+
2
4
  /**
3
- * Verification Gatekeeper
4
- * Runs the target project's own mechanical checks (lint/test scripts from the
5
- * nearest package.json) and refuses completion (Exit 1) when any of them fail.
5
+ * Harness project verification gate.
6
6
  *
7
- * Escape hatches, both deliberate:
8
- * - `.verify-fail.tmp` in cwd forces a failure - the hermetic self-regression
9
- * suite uses it to prove the blocking mechanism without needing a real
10
- * broken project.
11
- * - HARNESS_SKIP_PROJECT_CHECKS=1 skips the project-script phase (the tmp-file
12
- * check above still applies). The gate sets it for its own children so a
13
- * project whose test suite itself drives the Harness runtime (like this repo) cannot
14
- * recurse back into the gate.
7
+ * Contract: .harness/verify.json
8
+ * Discovery: read-only candidates; legacy package lint/test remains auto-run.
9
+ * UNCHECKED_* is non-blocking control flow and is never passing-test evidence.
15
10
  */
16
- const { execSync } = require('child_process');
17
11
  const fs = require('fs');
18
12
  const path = require('path');
13
+ const { spawnSync } = require('child_process');
19
14
 
20
- console.log("=== đŸ›Ąī¸ Harness Verification Gate ===");
21
-
22
- // Phase 1: explicit failure injection (test/demo hook).
23
- if (fs.existsSync('.verify-fail.tmp')) {
24
- console.error("❌ Verification FAILED: .verify-fail.tmp present (injected failure).");
25
- console.error("Please fix the code and run this gate again.");
26
- process.exit(1);
15
+ function loadVerificationModel() {
16
+ for (const candidate of [
17
+ path.resolve(__dirname, '../../scripts/lib/verification-contract.js'),
18
+ path.resolve(__dirname, '../../../scripts/lib/verification-contract.js'),
19
+ ]) {
20
+ if (fs.existsSync(candidate)) return require(candidate);
21
+ }
22
+ throw new Error('verification-contract runtime is missing from this Harness distribution');
27
23
  }
28
24
 
29
- if (process.env.HARNESS_SKIP_PROJECT_CHECKS === '1') {
30
- console.log("â„šī¸ HARNESS_SKIP_PROJECT_CHECKS=1 - project scripts skipped (recursion guard / hermetic test mode).");
31
- console.log("✅ Verification SUCCESS (injection check only).");
32
- process.exit(0);
25
+ const {
26
+ detectRoot,
27
+ enumerateSubmoduleRoots,
28
+ findGitRoot,
29
+ readContract,
30
+ } = loadVerificationModel();
31
+
32
+ const jsonMode = process.argv.includes('--json');
33
+ const cwd = path.resolve(process.cwd());
34
+ const repoRoot = findGitRoot(cwd);
35
+
36
+ function emptyReport(status, reason) {
37
+ return {
38
+ status,
39
+ reason,
40
+ repoRoot,
41
+ roots: [],
42
+ checks: [],
43
+ candidates: [],
44
+ };
33
45
  }
34
46
 
35
- // Phase 2: locate the nearest package.json, walking up from cwd but never
36
- // above the enclosing git repository (same scoping rule as tier-router.js).
37
- function findPackageDir() {
38
- let dir = path.resolve(process.cwd());
39
- while (true) {
40
- if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
41
- if (fs.existsSync(path.join(dir, '.git'))) return null; // repo root reached, none found
42
- const parent = path.dirname(dir);
43
- if (parent === dir) return null;
44
- dir = parent;
47
+ function emit(report) {
48
+ if (jsonMode) {
49
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
50
+ return;
51
+ }
52
+
53
+ console.log('=== đŸ›Ąī¸ Harness Verification Gate ===');
54
+ console.log(`Status: ${report.status}`);
55
+ console.log(`Repository: ${report.repoRoot}`);
56
+ if (report.reason) console.log(`Reason: ${report.reason}`);
57
+
58
+ for (const root of report.roots || []) {
59
+ console.log(`\n[${root.status}] ${root.path}`);
60
+ if (root.reason) console.log(` ${root.reason}`);
61
+ for (const check of root.checks || []) {
62
+ const exit = check.exitCode === null ? 'timeout/error' : check.exitCode;
63
+ console.log(` check ${check.id}: ${check.run} (exit ${exit}, ${check.durationMs}ms)`);
64
+ if (check.stdout) process.stdout.write(check.stdout.endsWith('\n') ? check.stdout : check.stdout + '\n');
65
+ if (check.stderr) process.stderr.write(check.stderr.endsWith('\n') ? check.stderr : check.stderr + '\n');
66
+ }
67
+ for (const candidate of root.candidates || []) {
68
+ console.log(` candidate [${candidate.source}]: ${candidate.run}`);
69
+ }
45
70
  }
71
+
72
+ if (report.status === 'UNCHECKED_NO_CHECKS' || report.status === 'UNCHECKED_DISCOVERED') {
73
+ console.log('\nExit code is 0 (non-blocking), but NO COMPLETE AUTOMATED EVIDENCE was produced.');
74
+ console.log('PROHIBITED: Do NOT cite this run as proof that tests pass. Perform the listed or project-declared verification first.');
75
+ } else if (report.status === 'PASSED') {
76
+ console.log('\n✅ Verification PASSED. Objective project checks completed successfully.');
77
+ } else if (report.status === 'FAILED') {
78
+ console.error('\n❌ Verification FAILED. Fix the reported check/contract error and run this gate again.');
79
+ } else if (report.status === 'SKIPPED') {
80
+ console.log('â„šī¸ Project checks skipped by HARNESS_SKIP_PROJECT_CHECKS=1.');
81
+ }
82
+ }
83
+
84
+ function finish(report) {
85
+ emit(report);
86
+ process.exit(report.status === 'FAILED' ? 1 : 0);
87
+ }
88
+
89
+ function statusRank(status) {
90
+ return {
91
+ PASSED: 0,
92
+ UNCHECKED_NO_CHECKS: 1,
93
+ UNCHECKED_DISCOVERED: 2,
94
+ FAILED: 3,
95
+ }[status] ?? 0;
96
+ }
97
+
98
+ function aggregateStatus(roots) {
99
+ if (!roots.length) return 'UNCHECKED_NO_CHECKS';
100
+ return roots.reduce((worst, root) => statusRank(root.status) > statusRank(worst) ? root.status : worst, 'PASSED');
46
101
  }
47
102
 
48
- function detectRunner(dir) {
49
- if (fs.existsSync(path.join(dir, 'pnpm-lock.yaml'))) return 'pnpm';
50
- if (fs.existsSync(path.join(dir, 'yarn.lock'))) return 'yarn';
51
- if (fs.existsSync(path.join(dir, 'bun.lockb')) || fs.existsSync(path.join(dir, 'bun.lock'))) return 'bun';
52
- return 'npm';
103
+ function executeCheck(check) {
104
+ const started = Date.now();
105
+ const result = spawnSync(check.run, {
106
+ cwd: check.cwd,
107
+ shell: true,
108
+ encoding: 'utf8',
109
+ windowsHide: true,
110
+ timeout: check.timeoutSec * 1000,
111
+ env: { ...process.env, HARNESS_SKIP_PROJECT_CHECKS: '1' },
112
+ maxBuffer: 16 * 1024 * 1024,
113
+ });
114
+ const durationMs = Date.now() - started;
115
+ const timedOut = !!(result.error && result.error.code === 'ETIMEDOUT');
116
+ const exitCode = typeof result.status === 'number' ? result.status : null;
117
+ return {
118
+ id: check.id,
119
+ run: check.run,
120
+ cwd: check.cwd,
121
+ source: check.source,
122
+ exitCode,
123
+ durationMs,
124
+ timedOut,
125
+ signal: result.signal || null,
126
+ stdout: result.stdout || '',
127
+ stderr: result.stderr || '',
128
+ passed: !timedOut && exitCode === 0,
129
+ };
53
130
  }
54
131
 
55
- const pkgDir = findPackageDir();
56
- let scripts = {};
57
- if (pkgDir) {
58
- try {
59
- scripts = JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf8')).scripts || {};
60
- } catch (err) {
61
- console.error(`❌ Verification FAILED: package.json at ${pkgDir} is unreadable/invalid JSON (${err.message}).`);
62
- process.exit(1);
132
+ function publicCheck(check) {
133
+ return {
134
+ id: check.id,
135
+ run: check.run,
136
+ cwd: check.cwd,
137
+ source: check.source,
138
+ exitCode: check.exitCode,
139
+ durationMs: check.durationMs,
140
+ ...(check.timedOut ? { timedOut: true } : {}),
141
+ ...(check.signal ? { signal: check.signal } : {}),
142
+ ...(!jsonMode && check.stdout ? { stdout: check.stdout } : {}),
143
+ ...(!jsonMode && check.stderr ? { stderr: check.stderr } : {}),
144
+ };
145
+ }
146
+
147
+ function candidateView(candidate) {
148
+ return {
149
+ source: candidate.source,
150
+ run: candidate.run,
151
+ cwd: candidate.cwd,
152
+ };
153
+ }
154
+
155
+ function rootReason(status, checks, candidates) {
156
+ if (status === 'FAILED') {
157
+ const failed = checks.find(check => !check.passed);
158
+ if (!failed) return 'verification failed';
159
+ return failed.timedOut
160
+ ? `check ${failed.id} timed out after ${failed.durationMs}ms`
161
+ : `check ${failed.id} exited ${failed.exitCode}`;
63
162
  }
163
+ if (status === 'UNCHECKED_DISCOVERED') return `${candidates.length} verification candidate(s) detected but not executed`;
164
+ if (status === 'UNCHECKED_NO_CHECKS') return 'no project verification checks were declared or detected';
165
+ return `${checks.length} check(s) passed`;
64
166
  }
65
167
 
66
- // npm-init's placeholder ("no test specified" + exit 1) is absence of a test
67
- // suite, not a failing one - running it would block every fresh project.
68
- const runnable = ['lint', 'test'].filter(name =>
69
- typeof scripts[name] === 'string' &&
70
- scripts[name].trim() !== '' &&
71
- !/no test specified/i.test(scripts[name])
72
- );
73
-
74
- if (!pkgDir || runnable.length === 0) {
75
- console.log("âš ī¸ Verification UNCHECKED: No automated lint/test scripts detected in package.json.");
76
- console.log(" Exit code is 0 (non-blocking), but NO AUTOMATED EVIDENCE was produced.");
77
- console.log(" PROHIBITED: Do NOT cite this run as proof that tests pass. Perform manual or alternative verification first.");
78
- process.exit(0);
168
+ function contractPlan(contract) {
169
+ const rootResults = contract.roots.map(root => ({
170
+ path: root.path,
171
+ status: 'UNCHECKED_NO_CHECKS',
172
+ reason: '',
173
+ checks: [],
174
+ candidates: [],
175
+ }));
176
+ const byPath = new Map(rootResults.map(root => [path.resolve(root.path), root]));
177
+
178
+ for (const check of contract.checks) {
179
+ const executed = executeCheck(check);
180
+ const root = byPath.get(path.resolve(check.root));
181
+ root.checks.push(executed);
182
+ }
183
+
184
+ for (const root of rootResults) {
185
+ root.status = root.checks.length === 0
186
+ ? 'UNCHECKED_NO_CHECKS'
187
+ : root.checks.some(check => !check.passed) ? 'FAILED' : 'PASSED';
188
+ root.reason = rootReason(root.status, root.checks, root.candidates);
189
+ }
190
+ return rootResults;
79
191
  }
80
192
 
81
- const runner = detectRunner(pkgDir);
82
- for (const name of runnable) {
83
- console.log(`\nâ–ļ Running "${runner} run ${name}" in ${pkgDir} ...`);
84
- try {
85
- execSync(`${runner} run ${name}`, {
86
- cwd: pkgDir,
87
- stdio: 'inherit',
88
- timeout: 10 * 60 * 1000,
89
- env: { ...process.env, HARNESS_SKIP_PROJECT_CHECKS: '1' }
193
+ function discoveredPlan() {
194
+ const roots = [];
195
+ for (const rootPath of enumerateSubmoduleRoots(repoRoot)) {
196
+ const detected = detectRoot(rootPath);
197
+ const executed = detected.checks.map(executeCheck);
198
+ const candidates = detected.candidates;
199
+ const status = executed.some(check => !check.passed)
200
+ ? 'FAILED'
201
+ : candidates.length > 0 ? 'UNCHECKED_DISCOVERED'
202
+ : executed.length > 0 ? 'PASSED' : 'UNCHECKED_NO_CHECKS';
203
+ roots.push({
204
+ path: rootPath,
205
+ status,
206
+ reason: rootReason(status, executed, candidates),
207
+ checks: executed,
208
+ candidates,
90
209
  });
91
- console.log(`✅ ${name} passed.`);
92
- } catch (err) {
93
- console.error(`\n❌ Verification FAILED: "${runner} run ${name}" exited non-zero${err.signal ? ` (signal ${err.signal})` : ''}.`);
94
- console.error("Fix the failures above and run this gate again.");
95
- process.exit(1);
96
210
  }
211
+ return roots;
97
212
  }
98
213
 
99
- console.log("\n✅ Verification SUCCESS. You may now complete the task.");
100
- process.exit(0);
214
+ function finalize(roots) {
215
+ const status = aggregateStatus(roots);
216
+ const failingRoot = roots.find(root => root.status === status);
217
+ const report = {
218
+ status,
219
+ reason: failingRoot ? failingRoot.reason : '',
220
+ repoRoot,
221
+ roots: roots.map(root => ({
222
+ path: root.path,
223
+ status: root.status,
224
+ reason: root.reason,
225
+ checks: root.checks.map(publicCheck),
226
+ candidates: root.candidates.map(candidateView),
227
+ })),
228
+ checks: roots.flatMap(root => root.checks.map(publicCheck)),
229
+ candidates: roots.flatMap(root => root.candidates.map(candidateView)),
230
+ };
231
+ finish(report);
232
+ }
233
+
234
+ try {
235
+ // Injection remains first and cannot be bypassed by the recursion guard.
236
+ if (fs.existsSync(path.join(cwd, '.verify-fail.tmp'))) {
237
+ finish(emptyReport('FAILED', '.verify-fail.tmp present (injected failure)'));
238
+ }
239
+
240
+ if (process.env.HARNESS_SKIP_PROJECT_CHECKS === '1') {
241
+ finish(emptyReport('SKIPPED', 'HARNESS_SKIP_PROJECT_CHECKS=1 recursion guard'));
242
+ }
243
+
244
+ const contract = readContract(repoRoot);
245
+ finalize(contract ? contractPlan(contract) : discoveredPlan());
246
+ } catch (error) {
247
+ finish(emptyReport('FAILED', error && error.message ? error.message : String(error)));
248
+ }
@@ -4,44 +4,45 @@ description: Use when work needs isolation or before major implementation plans
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.19.1
7
+ version: 0.25.1
8
8
  ---
9
9
 
10
10
  # Using Git Worktrees
11
11
 
12
- Use native worktree support first; raw Git is the fallback.
12
+ Use native worktree support first; raw Git is fallback.
13
13
 
14
14
  ## Contract
15
15
 
16
- - **Tier 3 / Fable (MUST resolve)** — before broad source/artifact mutation, reuse/create a verified linked worktree. If isolation is unavailable or the user explicitly chooses to stay in place, MUST report an explicit degraded fallback/risk before continuing.
17
- - **Ordinary mode (SHOULD)** — use isolation when it materially reduces collision/risk; explicit user preference may keep work in place.
18
- - **MUST NOT** nest a worktree inside an already isolated worktree.
16
+ - **Tier 3 / Fable (MUST resolve)** — isolate before broad mutation. If unavailable or explicitly declined, MUST report an explicit degraded fallback/risk.
17
+ - **Ordinary mode SHOULD** isolate when it materially reduces collision/risk.
18
+ - **MUST NOT** nest a worktree inside an isolated worktree.
19
19
 
20
20
  ## Workflow
21
21
 
22
- **Step 0 — Detect isolation** (adapt per `environment-detection`):
22
+ **Step 0 — Detect**:
23
23
  ```bash
24
24
  git rev-parse --git-dir
25
25
  git rev-parse --git-common-dir
26
26
  git branch --show-current
27
27
  git rev-parse --show-superproject-working-tree
28
+ git submodule status --recursive
28
29
  ```
29
- Different git/common dirs and no superproject => already isolated; continue to Step 2. A submodule is a normal repo for this check.
30
+ Different git/common dirs and no superproject => already isolated. If submodules exist, **MUST** read `<this-skill-dir>/references/submodules-in-worktrees.md` before committing inside them.
30
31
 
31
- **Step 1 — Create/enter**: use native `EnterWorktree`, `/worktree`, or `--worktree` first; otherwise use `git worktree add "$path" -b "$BRANCH_NAME"`. Use an already-ignored or external/sibling path when possible. If creation/entry fails, MUST surface the degraded isolation disposition before continuing.
32
+ **Step 1 — Create/enter**: use native `EnterWorktree`, `/worktree`, or `--worktree`; otherwise `git worktree add "$path" -b "$BRANCH_NAME"`. On failure, MUST surface the degraded disposition.
32
33
 
33
- **Step 2 — Setup**: install project dependencies as needed.
34
+ **Step 2 — Setup**: install dependencies as needed.
34
35
 
35
- **Step 3 — Baseline**: run relevant tests/build before implementation when useful; surface existing failures before relying on the baseline.
36
+ **Step 3 — Baseline**: run relevant tests/build when useful; surface existing failures.
36
37
 
37
38
  Deep dive: <this-skill-dir>/references/workflow-details.md
38
39
 
39
40
  ## USE FOR:
40
- - Tier 3 / Fable-class mutable engineering
41
+ - Tier 3 / Fable mutable engineering
41
42
  - feature work needing isolation
42
43
  - pre-implementation-plan setup
43
44
 
44
45
  ## DO NOT USE FOR:
45
46
  - nesting inside an existing worktree
46
- - read-only analysis with no mutation
47
- - branch/merge workflows unrelated to workspace isolation
47
+ - read-only analysis
48
+ - unrelated branch/merge workflows
@@ -0,0 +1,135 @@
1
+ # Submodules inside linked worktrees
2
+
3
+ A linked worktree of a superproject does **not** share the primary checkout's submodule object store. After `git submodule update --init`, each submodule normally uses a worktree-specific git dir such as:
4
+
5
+ ```text
6
+ <super>/.git/worktrees/<worktree-id>/modules/<submodule>
7
+ ```
8
+
9
+ The primary checkout uses:
10
+
11
+ ```text
12
+ <super>/.git/modules/<submodule>
13
+ ```
14
+
15
+ A superproject gitlink records only a commit SHA. It does not publish or copy that commit into the primary checkout. Therefore a submodule commit made only on detached HEAD in a linked worktree can become unreachable when that worktree is removed.
16
+
17
+ ## Required rules
18
+
19
+ 1. **MUST use a named branch before committing in the submodule.** `git submodule update` normally leaves the submodule detached.
20
+ 2. **MUST make the new submodule commit reachable outside the linked worktree before treating the superproject gitlink bump as deliverable.** Publish it to the submodule remote, or fetch it into the primary checkout's submodule repository.
21
+ 3. **MUST verify external reachability before removing the linked worktree.** A branch that exists only inside the linked worktree is not sufficient publication evidence.
22
+
23
+ ## Detect the situation
24
+
25
+ From the superproject worktree:
26
+
27
+ ```bash
28
+ git rev-parse --git-dir
29
+ git rev-parse --git-common-dir
30
+ git submodule status --recursive
31
+ git -C <submodule> rev-parse --absolute-git-dir
32
+ git -C <submodule> branch --show-current
33
+ ```
34
+
35
+ A linked superproject has different git/common dirs. A blank submodule branch means detached HEAD.
36
+
37
+ PowerShell uses the same Git commands; quote paths containing spaces:
38
+
39
+ ```powershell
40
+ git -C "<submodule>" rev-parse --absolute-git-dir
41
+ git -C "<submodule>" branch --show-current
42
+ ```
43
+
44
+ ## Prevent the problem
45
+
46
+ Before editing or committing in the submodule:
47
+
48
+ ```bash
49
+ git -C <submodule> switch -c <task-branch>
50
+ # edit + verify
51
+ git -C <submodule> add <paths>
52
+ git -C <submodule> commit -m "<message>"
53
+ SUB_SHA=$(git -C <submodule> rev-parse HEAD)
54
+ ```
55
+
56
+ PowerShell:
57
+
58
+ ```powershell
59
+ git -C "<submodule>" switch -c "<task-branch>"
60
+ # edit + verify
61
+ git -C "<submodule>" add <paths>
62
+ git -C "<submodule>" commit -m "<message>"
63
+ $SubSha = git -C "<submodule>" rev-parse HEAD
64
+ ```
65
+
66
+ ## Publish before the gitlink is considered deliverable
67
+
68
+ Preferred path when the submodule has a remote:
69
+
70
+ ```bash
71
+ git -C <submodule> push -u origin <task-branch>
72
+ git -C <submodule> branch -r --contains "$SUB_SHA"
73
+ ```
74
+
75
+ ```powershell
76
+ git -C "<submodule>" push -u origin "<task-branch>"
77
+ git -C "<submodule>" branch -r --contains $SubSha
78
+ ```
79
+
80
+ If pushing is not appropriate, copy the commit into the primary checkout's submodule repository. From the linked worktree first give a detached commit a name if necessary:
81
+
82
+ ```bash
83
+ git -C <worktree>/<submodule> branch -f <task-branch> <sha>
84
+ git -C <primary>/<submodule> fetch <worktree>/<submodule> <task-branch>:<task-branch>
85
+ git -C <primary>/<submodule> cat-file -e <sha>^{commit}
86
+ ```
87
+
88
+ ```powershell
89
+ git -C "<worktree>/<submodule>" branch -f "<task-branch>" "<sha>"
90
+ git -C "<primary>/<submodule>" fetch "<worktree>/<submodule>" "<task-branch>:<task-branch>"
91
+ git -C "<primary>/<submodule>" cat-file -e "<sha>^{commit}"
92
+ ```
93
+
94
+ After the commit is externally reachable, stage the gitlink in the superproject:
95
+
96
+ ```bash
97
+ git add <submodule>
98
+ git diff --cached --submodule=log
99
+ ```
100
+
101
+ ## Recovery after an unpublished gitlink was already committed
102
+
103
+ Do **not** remove the linked worktree. Recover the referenced SHA while its submodule object store still exists:
104
+
105
+ ```bash
106
+ git -C <worktree>/<submodule> branch -f <task-branch> <sha>
107
+ git -C <primary>/<submodule> fetch <worktree>/<submodule> <task-branch>:<task-branch>
108
+ git -C <primary>/<submodule> cat-file -e <sha>^{commit}
109
+ git -C <primary> submodule update
110
+ ```
111
+
112
+ The PowerShell form is identical except paths/refs should be quoted as shown above.
113
+
114
+ ## Pre-removal check
115
+
116
+ For every changed gitlink SHA, prove at least one external home exists:
117
+
118
+ ```bash
119
+ git -C <submodule> branch -r --contains <sha>
120
+ # OR, from the primary checkout:
121
+ git -C <primary>/<submodule> cat-file -e <sha>^{commit}
122
+ ```
123
+
124
+ If neither succeeds, **MUST NOT** remove the linked worktree yet.
125
+
126
+ ## Handoff checklist
127
+
128
+ For each changed submodule record:
129
+
130
+ - submodule path
131
+ - commit SHA
132
+ - branch name
133
+ - publication location: remote-tracking ref or primary checkout object store
134
+ - superproject gitlink staged/committed state
135
+ - reachability check result before worktree removal
@@ -4,44 +4,36 @@ description: "Run objective verification gates (build, types, lint, tests, secur
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.15.0
7
+ version: 0.26.0
8
8
  ---
9
9
 
10
10
  # Verification Loop
11
11
 
12
- Run objective quality gates before claiming a change is ready.
12
+ Run objective gates before claiming delivery readiness.
13
13
 
14
14
  ## Skill Contract
15
15
 
16
16
  | Component | Specification |
17
17
  | :--- | :--- |
18
- | **Trigger / Input** | Completed implementation or change awaiting delivery. |
19
- | **Expected Output** | Evidence-backed verification report and delivery decision. |
20
- | **State Mutations** | Fills a report from `<this-skill-dir>/templates/verification-report.template.md`. |
21
- | **Enforcement Gate** | Build, type, lint, test, security, and diff gates all pass. |
18
+ | **Trigger / Input** | Completed change awaiting delivery. |
19
+ | **Expected Output** | Evidence-backed verification report. |
20
+ | **State Mutations** | Fills `<this-skill-dir>/templates/verification-report.template.md`. |
21
+ | **Enforcement Gate** | Every applicable gate passes. |
22
22
 
23
23
  ## USE FOR:
24
-
25
- - Verify a feature or change before delivery
26
- - Run quality gates before a PR
24
+ - Verify a completed change
25
+ - Run quality gates before delivery/PR
27
26
 
28
27
  ## DO NOT USE FOR:
29
-
30
- - Fixing failures without re-running the loop
31
- - Writing new features or tests (use `tdd`)
28
+ - Fixing failures without re-running verification
29
+ - New feature/test implementation (use `tdd`)
32
30
 
33
31
  ## Workflow
34
32
 
35
- Never claim completion until every applicable gate passes. On failure, fix the cause
36
- and rerun from the first affected gate.
37
-
38
- 1. Build: `npm run build`.
39
- 2. Types: `npx tsc --noEmit` or `pyright .`.
40
- 3. Lint: `npm run lint` or `ruff check .`.
41
- 4. Tests and coverage: `npm run test -- --coverage`.
42
- 5. Security scan: secrets and stray logs.
43
- 6. Diff review: `git diff --stat` and unintended-change checks.
44
- 7. Contract reconciliation when the work cites `REQ-*` / ADR/spec/ticket lineage or a contract-integrity trace exists: run the strict `<this-skill-dir>/scripts/contract-integrity-audit.js` gate with current #58 evidence and `--workspace <workspace>` so traced source/test/implementation artifacts are fingerprinted. `AUDIT`, `NOT_EVALUATED`, stale source binding, or drift is **not ready** for delivery.
45
- 8. Fill `<this-skill-dir>/templates/verification-report.template.md` with evidence.
33
+ 1. Run applicable build, type, lint, test/coverage, security, and diff gates.
34
+ 2. Reconcile cited REQ/ADR/spec lineage with `<this-skill-dir>/scripts/contract-integrity-audit.js`; stale/failed/audit-only evidence is not ready.
35
+ 3. For project-declared/discovered mechanical gates, follow `<this-skill-dir>/references/project-verification-contract.md`.
36
+ 4. On failure, fix and rerun from the first affected gate.
37
+ 5. Fill `<this-skill-dir>/templates/verification-report.template.md` with actual evidence.
46
38
 
47
39
  Deep dive: `<this-skill-dir>/references/verification-phases.md`