harness-everything 0.19.0 → 0.20.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 (69) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.releaserc.json +1 -1
  4. package/AGENTS.md +3 -2
  5. package/CHANGELOG.md +9 -0
  6. package/README.md +14 -14
  7. package/RELEASING.md +10 -2
  8. package/ci/invariant-routing.test.js +42 -19
  9. package/ci/mechanism-2af-worktree-isolation.test.js +3 -3
  10. package/ci/mechanism-2ag-router-worktree-contract.test.js +1 -0
  11. package/ci/mechanism-2an-workflow-obligations.test.js +1 -0
  12. package/ci/mechanism-2ap-sbom.test.js +92 -0
  13. package/ci/mechanism-33-issues-205-207.test.js +2 -2
  14. package/ci/openai-plugin-package.test.js +6 -6
  15. package/ci/router-workflow-plan.test.js +12 -9
  16. package/ci/workflow-execution-contract.test.js +2 -2
  17. package/docs/architecture.md +15 -15
  18. package/docs/enforcement-locks.md +17 -15
  19. package/docs/philosophy.md +43 -8
  20. package/docs/routing.md +23 -16
  21. package/docs/workflow-runtime.md +20 -19
  22. package/environment-detection/SKILL.md +3 -1
  23. package/evals/harness-everything/tasks/positive-tier1.yaml +1 -1
  24. package/evals/harness-everything/tasks/positive-tier2.yaml +1 -1
  25. package/evals/harness-everything/tasks/positive-tier3.yaml +1 -1
  26. package/evals/using-git-worktrees/tasks/positive-trigger.yaml +1 -1
  27. package/find-skills/SKILL.md +20 -29
  28. package/git-commit/SKILL.md +4 -4
  29. package/harness-everything/SKILL.md +16 -17
  30. package/harness-everything/references/triage-and-tiers.md +17 -16
  31. package/harness-everything/scripts/kernel-router-core.js +26 -24
  32. package/harness-everything/scripts/kernel-router.js +8 -8
  33. package/harness-everything/scripts/router-contract-core.js +7 -7
  34. package/harness-everything/scripts/router-contract.js +1 -1
  35. package/hooks/scripts/atomic-commit-check.js +1 -1
  36. package/hooks/scripts/boundary-guard.js +2 -2
  37. package/hooks/scripts/depth-guard.js +5 -6
  38. package/hooks/scripts/subagent-scope-guard.js +2 -2
  39. package/hooks/scripts/workflow-gate.js +1 -1
  40. package/install-cognitive-os/SKILL.md +3 -3
  41. package/opencode-plugin/plugin.json +1 -1
  42. package/package.json +5 -2
  43. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  44. package/plugins/harness-everything/hooks/scripts/atomic-commit-check.js +1 -1
  45. package/plugins/harness-everything/hooks/scripts/boundary-guard.js +2 -2
  46. package/plugins/harness-everything/hooks/scripts/depth-guard.js +5 -6
  47. package/plugins/harness-everything/hooks/scripts/subagent-scope-guard.js +2 -2
  48. package/plugins/harness-everything/hooks/scripts/workflow-gate.js +1 -1
  49. package/plugins/harness-everything/plugin.json +1 -1
  50. package/plugins/harness-everything/scripts/lib/advisory-text.js +19 -17
  51. package/plugins/harness-everything/skills/environment-detection/SKILL.md +3 -1
  52. package/plugins/harness-everything/skills/find-skills/SKILL.md +20 -29
  53. package/plugins/harness-everything/skills/git-commit/SKILL.md +4 -4
  54. package/plugins/harness-everything/skills/harness-everything/SKILL.md +16 -17
  55. package/plugins/harness-everything/skills/harness-everything/references/triage-and-tiers.md +17 -16
  56. package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router-core.js +26 -24
  57. package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router.js +8 -8
  58. package/plugins/harness-everything/skills/harness-everything/scripts/router-contract-core.js +7 -7
  59. package/plugins/harness-everything/skills/harness-everything/scripts/router-contract.js +1 -1
  60. package/plugins/harness-everything/skills/install-cognitive-os/SKILL.md +3 -3
  61. package/plugins/harness-everything/skills/rewrite-commits/SKILL.md +2 -2
  62. package/plugins/harness-everything/skills/to-tickets/SKILL.md +2 -2
  63. package/plugins/harness-everything/skills/using-git-worktrees/SKILL.md +6 -6
  64. package/rewrite-commits/SKILL.md +2 -2
  65. package/sbom.cdx.json +2110 -0
  66. package/scripts/generate-sbom.js +239 -0
  67. package/scripts/lib/advisory-text.js +19 -17
  68. package/to-tickets/SKILL.md +2 -2
  69. package/using-git-worktrees/SKILL.md +6 -6
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Generate a CycloneDX SBOM from package.json and package-lock.json.
6
+ *
7
+ * The file is intentionally generated during semantic-release rather than
8
+ * committed on every change. npm packs the generated root-level file, so the
9
+ * published package carries the exact release version and dependency graph.
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const crypto = require('crypto');
14
+ const path = require('path');
15
+
16
+ const ROOT = path.resolve(__dirname, '..');
17
+ const DEFAULT_OUTPUT = path.join(ROOT, 'sbom.cdx.json');
18
+ const PACKAGE_NAME = 'harness-everything';
19
+ const CYCLONEDX_VERSION = '1.7';
20
+ const CYCLONEDX_SCHEMA_URL = `https://cyclonedx.org/schema/bom-${CYCLONEDX_VERSION}.schema.json`;
21
+
22
+ function readJson(file) {
23
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
24
+ }
25
+
26
+ function npmPurl(name, version) {
27
+ const purlName = String(name).startsWith('@') ? `%40${String(name).slice(1)}` : String(name);
28
+ return `pkg:npm/${purlName}@${encodeURIComponent(version)}`;
29
+ }
30
+
31
+ function serialNumberFor(name, version) {
32
+ // UUID v5-shaped, deterministic serial number: release SBOMs remain
33
+ // comparable without introducing a random value into the generated file.
34
+ const namespace = Buffer.from('6ba7b8109dad11d180b400c04fd430c8', 'hex');
35
+ const digest = crypto.createHash('sha1').update(namespace).update(`${name}@${version}`).digest();
36
+ digest[6] = (digest[6] & 0x0f) | 0x50;
37
+ digest[8] = (digest[8] & 0x3f) | 0x80;
38
+ const hex = digest.toString('hex').slice(0, 32);
39
+ return `urn:uuid:${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
40
+ }
41
+
42
+ function integrityHash(integrity) {
43
+ if (typeof integrity !== 'string') return null;
44
+ const match = integrity.match(/^(sha\d+)-(.+)$/i);
45
+ if (!match) return null;
46
+ const algorithm = match[1].toUpperCase().replace(/^SHA(\d+)$/, 'SHA-$1');
47
+ return { alg: algorithm, content: match[2] };
48
+ }
49
+
50
+ function packageNameFromKey(key, record) {
51
+ if (record && record.name) return record.name;
52
+ const marker = '/node_modules/';
53
+ const last = key.lastIndexOf(marker);
54
+ const name = last === -1 ? key.replace(/^node_modules\//, '') : key.slice(last + marker.length);
55
+ if (name.startsWith('@')) return name;
56
+ return name.split('/node_modules/').pop();
57
+ }
58
+
59
+ function componentFor(key, record) {
60
+ const name = packageNameFromKey(key, record);
61
+ if (!record || !record.version || !name) return null;
62
+ const purl = npmPurl(name, record.version);
63
+ const component = {
64
+ type: 'library',
65
+ 'bom-ref': purl,
66
+ name,
67
+ version: record.version,
68
+ scope: record.dev ? 'excluded' : (record.optional ? 'optional' : 'required'),
69
+ purl
70
+ };
71
+ const hash = integrityHash(record.integrity);
72
+ if (hash) component.hashes = [hash];
73
+ return component;
74
+ }
75
+
76
+ function resolveDependencyKey(packages, parentKey, dependencyName) {
77
+ let current = parentKey;
78
+ while (true) {
79
+ const candidate = current
80
+ ? `${current}/node_modules/${dependencyName}`
81
+ : `node_modules/${dependencyName}`;
82
+ if (packages[candidate] && packages[candidate].version) return candidate;
83
+ const marker = '/node_modules/';
84
+ const markerIndex = current.lastIndexOf(marker);
85
+ if (markerIndex !== -1) current = current.slice(0, markerIndex);
86
+ else if (current.startsWith('node_modules/')) current = '';
87
+ else break;
88
+ }
89
+ return null;
90
+ }
91
+
92
+ function directDependencyNames(packageJson) {
93
+ return [...new Set([
94
+ ...Object.keys(packageJson.dependencies || {}),
95
+ ...Object.keys(packageJson.optionalDependencies || {}),
96
+ ...Object.keys(packageJson.devDependencies || {})
97
+ ])].sort();
98
+ }
99
+
100
+ function buildBom(root = ROOT, options = {}) {
101
+ const packageJson = readJson(path.join(root, 'package.json'));
102
+ const lock = readJson(path.join(root, 'package-lock.json'));
103
+ const version = options.version || packageJson.version;
104
+ const name = packageJson.name || PACKAGE_NAME;
105
+ const rootPurl = npmPurl(name, version);
106
+ const packages = lock.packages || {};
107
+ const components = [];
108
+ const componentByKey = new Map();
109
+
110
+ for (const [key, record] of Object.entries(packages)) {
111
+ if (!key) continue;
112
+ const component = componentFor(key, record);
113
+ if (!component) continue;
114
+ components.push(component);
115
+ componentByKey.set(key, component);
116
+ }
117
+
118
+ components.sort((left, right) => left.purl.localeCompare(right.purl));
119
+ const dependencyRecords = [];
120
+ const dependencySources = [['', packages[''] || {}]];
121
+ for (const [key, record] of Object.entries(packages)) {
122
+ if (key) dependencySources.push([key, record]);
123
+ }
124
+ for (const [key, record] of dependencySources) {
125
+ const sourceRef = key ? componentByKey.get(key)?.['bom-ref'] : rootPurl;
126
+ if (!sourceRef) continue;
127
+ const dependencyNames = key ? Object.keys(record.dependencies || {}) : directDependencyNames(packageJson);
128
+ const dependsOn = dependencyNames
129
+ .map(dependencyName => resolveDependencyKey(packages, key, dependencyName))
130
+ .map(dependencyKey => dependencyKey && componentByKey.get(dependencyKey)?.['bom-ref'])
131
+ .filter(Boolean)
132
+ .sort();
133
+ if (dependsOn.length) dependencyRecords.push({ ref: sourceRef, dependsOn: [...new Set(dependsOn)] });
134
+ }
135
+ dependencyRecords.sort((left, right) => left.ref.localeCompare(right.ref));
136
+
137
+ const bom = {
138
+ $schema: CYCLONEDX_SCHEMA_URL,
139
+ bomFormat: 'CycloneDX',
140
+ specVersion: CYCLONEDX_VERSION,
141
+ serialNumber: options.serialNumber || serialNumberFor(name, version),
142
+ version: 1,
143
+ metadata: {
144
+ timestamp: options.timestamp || new Date().toISOString(),
145
+ tools: [{ vendor: 'Harness Everything', name: 'generate-sbom.js', version }],
146
+ component: {
147
+ type: 'application',
148
+ 'bom-ref': rootPurl,
149
+ name,
150
+ version,
151
+ purl: rootPurl
152
+ }
153
+ },
154
+ components,
155
+ dependencies: dependencyRecords
156
+ };
157
+ return bom;
158
+ }
159
+
160
+ function validateBom(bom, expectedVersion) {
161
+ if (!bom || bom.bomFormat !== 'CycloneDX') throw new Error('SBOM must use the CycloneDX format');
162
+ if (bom.$schema !== CYCLONEDX_SCHEMA_URL) throw new Error(`SBOM schema must be ${CYCLONEDX_SCHEMA_URL}`);
163
+ if (bom.specVersion !== CYCLONEDX_VERSION) throw new Error(`SBOM specVersion must be ${CYCLONEDX_VERSION}, got ${bom.specVersion}`);
164
+ if (!Number.isInteger(bom.version) || bom.version < 1) throw new Error('SBOM document version is missing');
165
+ if (!/^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(String(bom.serialNumber || ''))) {
166
+ throw new Error('SBOM serialNumber must be a UUID URN');
167
+ }
168
+ const component = bom.metadata && bom.metadata.component;
169
+ if (!component || component.name !== PACKAGE_NAME) throw new Error('SBOM metadata component must be harness-everything');
170
+ if (!component.version || (expectedVersion && component.version !== expectedVersion)) {
171
+ throw new Error(`SBOM metadata version drift: expected ${expectedVersion || 'a version'}, got ${component.version || 'missing'}`);
172
+ }
173
+ if (!Array.isArray(bom.components)) throw new Error('SBOM components array is missing');
174
+ for (const entry of bom.components) {
175
+ if (!entry.name || !entry.version || !entry.purl || !entry['bom-ref']) throw new Error('SBOM component is incomplete');
176
+ }
177
+ if (!Array.isArray(bom.dependencies)) throw new Error('SBOM dependencies array is missing');
178
+ return true;
179
+ }
180
+
181
+ function writeBom(output, root = ROOT, options = {}) {
182
+ const bom = buildBom(root, options);
183
+ validateBom(bom, options.version);
184
+ fs.writeFileSync(output, `${JSON.stringify(bom, null, 2)}\n`, 'utf8');
185
+ return bom;
186
+ }
187
+
188
+ function parseArgs(argv) {
189
+ const options = { output: DEFAULT_OUTPUT, validate: null, version: null };
190
+ for (let index = 0; index < argv.length; index++) {
191
+ const arg = argv[index];
192
+ if (arg === '--output') options.output = path.resolve(argv[++index] || '');
193
+ else if (arg === '--validate') options.validate = path.resolve(argv[++index] || '');
194
+ else if (arg === '--version') options.version = argv[++index] || null;
195
+ else if (arg === '--help' || arg === '-h') options.help = true;
196
+ else throw new Error(`Unknown option: ${arg}`);
197
+ }
198
+ return options;
199
+ }
200
+
201
+ function main(argv = process.argv.slice(2)) {
202
+ const options = parseArgs(argv);
203
+ if (options.help) {
204
+ console.log('Usage: node scripts/generate-sbom.js [--output <file>] [--version <x.y.z>]');
205
+ console.log(' node scripts/generate-sbom.js --validate <file> [--version <x.y.z>]');
206
+ return 0;
207
+ }
208
+ if (options.validate) {
209
+ const bom = readJson(options.validate);
210
+ validateBom(bom, options.version);
211
+ console.log(`Validated CycloneDX SBOM ${options.validate} (${bom.components.length} components).`);
212
+ return 0;
213
+ }
214
+ const bom = writeBom(options.output, ROOT, { version: options.version });
215
+ console.log(`Generated CycloneDX SBOM ${options.output} (${bom.components.length} components).`);
216
+ return 0;
217
+ }
218
+
219
+ if (require.main === module) {
220
+ try {
221
+ process.exitCode = main();
222
+ } catch (error) {
223
+ console.error(`[Error] ${error.message}`);
224
+ process.exitCode = 1;
225
+ }
226
+ }
227
+
228
+ module.exports = {
229
+ buildBom,
230
+ componentFor,
231
+ CYCLONEDX_SCHEMA_URL,
232
+ CYCLONEDX_VERSION,
233
+ directDependencyNames,
234
+ integrityHash,
235
+ main,
236
+ npmPurl,
237
+ validateBom,
238
+ writeBom
239
+ };
@@ -7,14 +7,14 @@ const MARKER = "Harness OS Guidance (Advisory)";
7
7
 
8
8
  const advisoryInstructions = [
9
9
  `\n# ${MARKER}`,
10
- `Harness is an observer/router/reminder layer on this surface; do not treat its workflow guidance as a hard scheduler.`,
10
+ `Harness is an observer/router/reminder layer on this surface. Instruction/advisory delivery describes the mechanism, not semantic optionality; do not treat Harness as a hard scheduler.`,
11
11
  ``,
12
- `## 🚦 ENTRY TRIAGE & WORKFLOW GUIDANCE`,
12
+ `## 🚦 ENTRY TRIAGE & SEMANTIC CONTRACT`,
13
13
  `For software/project work, load the \`harness-everything\` skill and establish routing context before broad mutation:`,
14
- `1. Run \`npx github:dyphn1/Harness-everything next "<Brief summary of user's prompt>"\` when terminal execution is available.`,
15
- `2. For EVERY suggested skill, read its complete \`SKILL.md\` entry and evaluate applicability from \`USE FOR\`, \`DO NOT USE FOR\`, and its basic flow.`,
16
- `3. Treat the selected workflow topology and numeric limits as planning guidance, not a lock. Choose, combine, reorder, or skip steps when evidence supports it.`,
17
- `4. Prefer objective verification before declaring done and worktree isolation for broad/Tier-3 mutation.`,
14
+ `1. MUST route before broad mutation; run \`npx github:dyphn1/Harness-everything next "<Brief summary of user's prompt>"\` when terminal execution is available and no equivalent routing state was injected.`,
15
+ `2. MUST read/evaluate EVERY suggested skill. If applicable, MUST follow its core contract; if not-applicable, keep a flow-grounded reason.`,
16
+ `3. Selected-topology required obligations are MUSTs. Implementation tactics MAY adapt; numeric iteration/revision/replan/worker values MAY guide planning but never hard-block.`,
17
+ `4. MUST verify before claiming completion. Tier-3/Fable broad mutation MUST resolve isolation: linked worktree or explicit degraded fallback.`,
18
18
  `5. Use the routing checkpoint only as internal source state for the first Harness Status; do not render a competing progress block.`,
19
19
  ``,
20
20
  `## 📣 USER-VISIBLE STATUS (MUST)`,
@@ -31,10 +31,11 @@ const advisoryInstructions = [
31
31
  `- **No universal skill pipeline**: do not force every task through TODO/TDD/Fable.`,
32
32
  `- **Rule of 3**: after 3 same-signature failures, stop micro-retrying and use a fresh diagnosis / zoom-out. This reflection boundary may block mutation until completed.`,
33
33
  `- **Permission boundaries are separate**: explicit host/user approval for destructive or external actions remains authoritative.`,
34
- `- **Verification is evidence, not a cage**: missing evidence should trigger a reminder, not a persistent workflow lock.`,
35
- `- **Environment discovery**: discover OS, shell, package manager/runtime, and host capability before relying on them.`,
36
- `- **Memory / self-evolve**: persist only verified reusable lessons.`,
37
- `- **Prefer editing**: prefer targeted edits and logically complete commits over broad rewrites.`,
34
+ `- **Contract strength**: MUST = required semantic obligation; SHOULD = default with evidence-based exception; MAY = optional optimization. Reminder-only mechanics never downgrade MUST.`,
35
+ `- **Verification is evidence, not a cage**: MUST verify before completion claims; host hooks may remind rather than persistently block.`,
36
+ `- **Environment discovery**: MUST establish relevant OS/shell/toolchain/host facts before relying on environment-sensitive behavior.`,
37
+ `- **Memory / self-evolve**: durable writes MUST be authorized by the active workflow/session contract and backed by verified reusable evidence.`,
38
+ `- **Editing / commits**: targeted edits SHOULD be preferred; before committing, MUST inspect the staged diff and SHOULD split unrelated concerns.`,
38
39
  ``
39
40
  ].join('\n');
40
41
 
@@ -144,15 +145,16 @@ function buildCodexGlobalContent() {
144
145
  `# ${MARKER}`,
145
146
  `This instruction surface cannot be assumed to provide hard lifecycle blocking.`,
146
147
  ``,
147
- `- Discover the environment and host capabilities before relying on them.`,
148
- `- For software/project work, run \`npx github:dyphn1/Harness-everything next "<summary>"\` before mutation and use the routing checkpoint as source state for the first Harness Status.`,
148
+ `- MUST establish relevant environment/host facts before relying on environment-sensitive behavior.`,
149
+ `- For software/project work, MUST establish routing before broad mutation; use \`npx github:dyphn1/Harness-everything next "<summary>"\` when terminal execution is available and routing state was not already injected.`,
149
150
  `- For non-trivial work, MUST render one Markdown \`### 🚦 Harness Status\` with bullet-aligned bold \`Current\`, \`Read / Evidence\`, \`Next\`, and optional \`Risk / Blocked\`; repeat it at major phase/direction boundaries and before final completion.`,
150
- `- Read every suggested skill's complete \`SKILL.md\` entry before resolving applicability; do not omit it from metadata or confidence alone.`,
151
- `- Use the selected workflow topology as planning guidance; the model may adapt or skip steps with an explicit evidence-based reason.`,
151
+ `- MUST read/evaluate every suggested skill; applicable skill core contracts MUST be followed, while not-applicable needs a flow-grounded reason.`,
152
+ `- Selected-topology required obligations are MUSTs; implementation tactics MAY adapt and numeric planning values MAY guide but never hard-block.`,
152
153
  `- Do not impose a universal TODO/TDD/Fable sequence; use the smallest useful topology plus applicable skills.`,
153
- `- Prefer objective verification before declaring done; missing verification is a reminder, not a persistent lock.`,
154
- `- After 3 same-signature failures, stop retrying and use a fresh diagnosis / zoom-out.`,
155
- `- Prefer targeted edits and logically complete commits.`,
154
+ `- MUST verify before declaring done; a reminder-only host mechanism does not make verification optional.`,
155
+ `- Tier-3/Fable broad mutation MUST resolve isolation: verified linked worktree or explicit degraded fallback.`,
156
+ `- After 3 same-signature failures, MUST stop retrying and use a fresh diagnosis / zoom-out.`,
157
+ `- Targeted edits SHOULD be preferred; before committing, MUST inspect the staged diff and SHOULD split unrelated concerns.`,
156
158
  ``
157
159
  ].join('\n');
158
160
  }
@@ -4,7 +4,7 @@ description: Break a to-spec feature-spec, a plan, or a conversation into tracer
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.12.0
7
+ version: 0.19.1
8
8
  ---
9
9
 
10
10
  # To Tickets
@@ -33,7 +33,7 @@ Cuts a plan or spec into tracer-bullet vertical slices, each declaring its block
33
33
  ## Process
34
34
 
35
35
  1. Resolve path: script check (Exit 0 = configured location) → workspace convention dirs → platform fallback (`.claude`/`.github`/`.cursor` + `/harness-everything/tickets/`).
36
- 2. Gather context: prefer a referenced to-spec spec — design-audit verification MANDATORY first, else refuse; no spec is fine.
36
+ 2. Gather context: **SHOULD** use a referenced to-spec spec when available — design-audit verification is MANDATORY first, else refuse; no spec is fine.
37
37
  3. Explore codebase (optional): evidence-cited findings only; prefactor first.
38
38
  4. Draft vertical slices: complete path through every layer, demoable alone, one context window each; declare blocking edges.
39
39
  5. Quiz the user on granularity and edges until approved (MANDATORY gate).
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: using-git-worktrees
3
- description: Use when starting feature work needing isolation or before implementation plans - recommends an isolated workspace via native tools or git worktree fallback
3
+ description: Use when work needs isolation or before major implementation plans - resolves workspace isolation via native tools or git worktree fallback, with an explicit degraded fallback when isolation is unavailable
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.16.1
7
+ version: 0.19.1
8
8
  ---
9
9
 
10
10
  # Using Git Worktrees
@@ -13,9 +13,9 @@ Use native worktree support first; raw Git is the fallback.
13
13
 
14
14
  ## Contract
15
15
 
16
- - **Major workflow guidance** — Tier 3 / Fable-class engineering is safer in a linked worktree before broad source/artifact mutation. Reuse one or create one when practical.
17
- - **Ordinary mode** — isolation-needing feature work may honor an explicit user preference to stay in place.
18
- - Never nest a worktree inside an already isolated worktree.
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.
19
19
 
20
20
  ## Workflow
21
21
 
@@ -28,7 +28,7 @@ git rev-parse --show-superproject-working-tree
28
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
30
 
31
- **Step 1 — Create/enter**: prefer native `EnterWorktree`, `/worktree`, or `--worktree`. Otherwise use `git worktree add "$path" -b "$BRANCH_NAME"`. Prefer an already-ignored or external/sibling path. If creation/entry fails, report it and continue only with explicit awareness of the isolation risk.
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
32
 
33
33
  **Step 2 — Setup**: install project dependencies as needed.
34
34