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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +36 -0
- package/ci/mechanism-35-verification-contract.test.js +416 -0
- package/ci/mutation-check.js +2 -0
- package/ci/mutation-loader.js +4 -0
- package/docs/audit.md +2 -0
- package/harness-everything/SKILL.md +2 -2
- package/harness-everything/scripts/verify-gate.js +223 -75
- package/hooks/scripts/lib/workflow-isolation.js +3 -4
- package/hooks/scripts/state-persist.js +1 -1
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/workflow-isolation.js +3 -4
- package/plugins/harness-everything/hooks/scripts/state-persist.js +1 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/scripts/lib/verification-contract.js +357 -0
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +2 -2
- package/plugins/harness-everything/skills/harness-everything/scripts/verify-gate.js +223 -75
- package/plugins/harness-everything/skills/using-git-worktrees/SKILL.md +14 -13
- package/plugins/harness-everything/skills/using-git-worktrees/references/submodules-in-worktrees.md +135 -0
- package/plugins/harness-everything/skills/verification-loop/SKILL.md +15 -23
- package/plugins/harness-everything/skills/verification-loop/references/project-verification-contract.md +112 -0
- package/plugins/harness-everything/skills/verification-loop/references/verification-phases.md +4 -3
- package/plugins/harness-everything/skills/verification-loop/references/verify-contract.schema.json +32 -0
- package/plugins/harness-everything/skills/verification-loop/references/verify-result.schema.json +65 -0
- package/sbom.cdx.json +7 -7
- package/scripts/lib/verification-contract.js +357 -0
- package/using-git-worktrees/SKILL.md +14 -13
- package/using-git-worktrees/references/submodules-in-worktrees.md +135 -0
- package/verification-loop/SKILL.md +15 -23
- package/verification-loop/references/project-verification-contract.md +112 -0
- package/verification-loop/references/verification-phases.md +4 -3
- package/verification-loop/references/verify-contract.schema.json +32 -0
- 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
|
-
*
|
|
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
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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.
|
|
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
|
|
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
|
|
17
|
-
- **Ordinary mode
|
|
18
|
-
- **MUST NOT** nest a worktree inside an
|
|
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
|
|
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
|
|
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
|
|
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
|
|
34
|
+
**Step 2 â Setup**: install dependencies as needed.
|
|
34
35
|
|
|
35
|
-
**Step 3 â Baseline**: run relevant tests/build
|
|
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
|
|
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
|
|
47
|
-
- branch/merge workflows
|
|
47
|
+
- read-only analysis
|
|
48
|
+
- unrelated branch/merge workflows
|
package/plugins/harness-everything/skills/using-git-worktrees/references/submodules-in-worktrees.md
ADDED
|
@@ -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.
|
|
7
|
+
version: 0.26.0
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Verification Loop
|
|
11
11
|
|
|
12
|
-
Run objective
|
|
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
|
|
19
|
-
| **Expected Output** | Evidence-backed verification report
|
|
20
|
-
| **State Mutations** | Fills
|
|
21
|
-
| **Enforcement Gate** |
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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`
|