@haposoft/cafekit 0.16.0-rc.5 → 0.16.0-rc.7
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/README.md +21 -58
- package/bin/__tests__/change-firewall.semantic-firewall.test.js +4 -5
- package/bin/__tests__/codex-hooks.test.js +137 -0
- package/bin/__tests__/codex-native.test.js +130 -74
- package/bin/__tests__/develop-contract.test.js +155 -76
- package/bin/__tests__/installer-safety.test.js +19 -154
- package/bin/__tests__/package-inventory.test.js +10 -20
- package/bin/__tests__/release-preflight.semantic-firewall.test.js +13 -5
- package/bin/__tests__/validator-grounding.test.js +11 -11
- package/bin/install.js +2 -7
- package/bin/lib/context.js +0 -45
- package/bin/lib/copy-utils.js +1 -1
- package/bin/lib/i18n.js +0 -3
- package/bin/lib/manifest.js +1 -1
- package/bin/phases/claude-runtime.js +1 -3
- package/bin/phases/codex-runtime.js +4 -2
- package/bin/phases/copy-payload.js +3 -15
- package/bin/phases/post-install.js +5 -17
- package/bin/phases/root-config.js +2 -3
- package/bin/phases/select-platform.js +2 -5
- package/bin/phases/summary.js +0 -1
- package/package.json +3 -8
- package/src/claude/CLAUDE.md +19 -6
- package/src/claude/agents/project-manager.md +41 -9
- package/src/claude/agents/spec-maker.md +76 -155
- package/src/claude/gitignore +1 -1
- package/src/claude/hooks/__tests__/spec-gate.test.js +162 -0
- package/src/claude/hooks/lib/context.cjs +1 -1
- package/src/claude/hooks/lib/detect.cjs +1 -1
- package/src/claude/hooks/spec-gate.cjs +44 -9
- package/src/claude/hooks/spec-state.cjs +22 -11
- package/src/claude/hooks/task-scaffold-guard.cjs +1 -1
- package/src/claude/migration-manifest.json +17 -3
- package/src/claude/rules/manage-docs.md +43 -24
- package/src/claude/rules/state-sync.md +57 -62
- package/src/claude/rules/workflow.md +30 -13
- package/src/claude/scripts/change-firewall.cjs +2 -2
- package/src/claude/scripts/generate-skill-catalog.cjs +0 -1
- package/src/claude/scripts/spec-receipt.cjs +141 -1
- package/src/claude/scripts/spec-resolver.cjs +288 -0
- package/src/claude/skills/develop/SKILL.md +89 -125
- package/src/claude/skills/develop/references/parallel-waves.md +75 -162
- package/src/claude/skills/develop/references/quality-gate.md +61 -121
- package/src/claude/skills/develop/references/subagent-patterns.md +31 -38
- package/src/claude/skills/question/SKILL.md +1 -1
- package/src/claude/skills/specs/SKILL.md +142 -204
- package/src/claude/skills/specs/references/review.md +109 -243
- package/src/claude/skills/specs/references/templates.md +173 -0
- package/src/claude/skills/sync/SKILL.md +66 -90
- package/src/claude/skills/sync/references/sync-protocols.md +62 -83
- package/src/codex/AGENTS.md +20 -7
- package/src/codex/hooks/lib/spec-receipt.cjs +18 -0
- package/src/codex/hooks/lib/spec-utils.cjs +25 -0
- package/src/codex/hooks/spec-gate.cjs +46 -16
- package/src/codex/hooks/spec-state.cjs +22 -12
- package/src/codex/rules/state-sync.md +57 -61
- package/src/common/AGENTS.md +8 -1
- package/bin/__tests__/opencode-spec-gate.test.js +0 -799
- package/bin/lib/opencode-install.js +0 -899
- package/bin/phases/opencode-runtime.js +0 -38
- package/src/claude/skills/specs/references/archive-workflow.md +0 -74
- package/src/claude/skills/specs/references/ask-user-question-gates.md +0 -70
- package/src/claude/skills/specs/references/codebase-analysis.md +0 -102
- package/src/claude/skills/specs/references/cross-spec-dependency.md +0 -76
- package/src/claude/skills/specs/references/research-strategy.md +0 -107
- package/src/claude/skills/specs/references/scope-inquiry.md +0 -107
- package/src/claude/skills/specs/references/translation-mirror.md +0 -83
- package/src/claude/skills/specs/rules/design-discovery-full.md +0 -45
- package/src/claude/skills/specs/rules/design-discovery-light.md +0 -28
- package/src/claude/skills/specs/rules/design-principles.md +0 -138
- package/src/claude/skills/specs/rules/design-review.md +0 -55
- package/src/claude/skills/specs/rules/ears-format.md +0 -42
- package/src/claude/skills/specs/rules/phase-decision-matrix.md +0 -56
- package/src/claude/skills/specs/rules/task-scoring-rubric.md +0 -52
- package/src/claude/skills/specs/rules/tasks-generation.md +0 -189
- package/src/opencode/AGENTS.md +0 -41
- package/src/opencode/plugins/docs-sync.ts +0 -175
- package/src/opencode/plugins/inspect-block.ts +0 -164
- package/src/opencode/plugins/privacy-block.ts +0 -182
- package/src/opencode/plugins/rules.ts +0 -155
- package/src/opencode/plugins/session.ts +0 -179
- package/src/opencode/plugins/spec-gate.ts +0 -457
- package/src/opencode/plugins/spec-state.ts +0 -311
- package/src/opencode/plugins/state.ts +0 -253
- package/src/opencode/plugins/task-scaffold-guard.ts +0 -161
- package/src/opencode/plugins/usage.ts +0 -178
|
@@ -35,7 +35,6 @@ const FORBIDDEN_PAYLOAD = [
|
|
|
35
35
|
const RUNTIMES = {
|
|
36
36
|
claude: { root: '.claude', rules: '.claude/hooks/rules.cjs' },
|
|
37
37
|
codex: { root: '.codex', rules: '.codex/hooks/rules.cjs' },
|
|
38
|
-
opencode: { root: '.opencode', rules: '.opencode/plugins/rules.ts' },
|
|
39
38
|
};
|
|
40
39
|
|
|
41
40
|
function npmPack(args, cwd) {
|
|
@@ -303,29 +302,25 @@ function assertCombinedInstructionIsolation(root) {
|
|
|
303
302
|
const core = managedBlock(agents, '<!-- CAFEKIT CORE START -->', '<!-- CAFEKIT CORE END -->');
|
|
304
303
|
const claudeBlock = managedBlock(claude, '<!-- CAFEKIT CLAUDE START -->', '<!-- CAFEKIT CLAUDE END -->');
|
|
305
304
|
const codexBlock = managedBlock(agents, '<!-- CAFEKIT CODEX START -->', '<!-- CAFEKIT CODEX END -->');
|
|
306
|
-
const opencodeBlock = managedBlock(agents, '<!-- CAFEKIT OPENCODE START -->', '<!-- CAFEKIT OPENCODE END -->');
|
|
307
305
|
|
|
308
306
|
for (const [content, marker] of [
|
|
309
307
|
[agents, '<!-- CAFEKIT CORE START -->'],
|
|
310
308
|
[agents, '<!-- CAFEKIT CODEX START -->'],
|
|
311
|
-
[agents, '<!-- CAFEKIT OPENCODE START -->'],
|
|
312
309
|
[claude, '<!-- CAFEKIT CLAUDE START -->']
|
|
313
310
|
]) assert.equal((content.match(new RegExp(marker, 'g')) || []).length, 1);
|
|
314
311
|
|
|
315
|
-
assert.doesNotMatch(core, /Claude|Codex
|
|
316
|
-
assert.doesNotMatch(claudeBlock, /Codex
|
|
312
|
+
assert.doesNotMatch(core, /Claude|Codex|\.claude|\.codex|\/hapo:|\$hapo-/i);
|
|
313
|
+
assert.doesNotMatch(claudeBlock, /Codex|\.codex|\$hapo-/i);
|
|
317
314
|
assert.match(codexBlock, /native project instruction surface is root `AGENTS\.md`/);
|
|
318
|
-
assert.match(opencodeBlock, /native project instruction surface is root `AGENTS\.md`/);
|
|
319
315
|
assert.match(agents, /shared-root trade-off is intentional/);
|
|
320
316
|
// H5 ownership/ignore contract
|
|
321
317
|
assert.match(core, /runtime-neutral/i);
|
|
322
318
|
assert.match(core, /fail-safe/i);
|
|
323
319
|
assert.match(codexBlock, /owned by Codex/i);
|
|
324
|
-
assert.match(codexBlock, /ignore this entire Codex block/i);
|
|
325
|
-
assert.
|
|
326
|
-
assert.match(opencodeBlock, /ignore this entire OpenCode block/i);
|
|
320
|
+
assert.match(codexBlock, /If you are Claude Code or any other runtime, ignore this entire Codex block/i);
|
|
321
|
+
assert.doesNotMatch(codexBlock, /If you are Codex CLI or any other runtime, ignore this entire Codex block/i);
|
|
327
322
|
assert.doesNotMatch(core, /\$hapo-|hapo:/i);
|
|
328
|
-
assert.doesNotMatch(claudeBlock, /<!-- CAFEKIT
|
|
323
|
+
assert.doesNotMatch(claudeBlock, /<!-- CAFEKIT CODEX /);
|
|
329
324
|
}
|
|
330
325
|
function stableInstallSnapshot(root, platforms) {
|
|
331
326
|
const files = ['AGENTS.md', 'CLAUDE.md', '.gitignore'];
|
|
@@ -359,12 +354,6 @@ function assertHookLanguage(root, platform, lang, sessionId) {
|
|
|
359
354
|
assert.equal(runtimeJson.locale.responseLanguage, lang || null);
|
|
360
355
|
const rulesPath = path.join(root, runtime.rules);
|
|
361
356
|
assert.ok(fs.existsSync(rulesPath), `installed rules path missing: ${rulesPath}`);
|
|
362
|
-
if (platform === 'opencode') {
|
|
363
|
-
const plugin = fs.readFileSync(rulesPath, 'utf8');
|
|
364
|
-
assert.match(plugin, /if \(respondLang\)/);
|
|
365
|
-
assert.match(plugin, /Respond in \$\{respondLang\}/);
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
357
|
const result = spawnSync(process.execPath, [rulesPath], {
|
|
369
358
|
cwd: root,
|
|
370
359
|
input: JSON.stringify({ cwd: root, session_id: sessionId }),
|
|
@@ -857,9 +846,8 @@ test('packed tarball installer matrix proves locale, transforms, paths, and reru
|
|
|
857
846
|
const cases = [
|
|
858
847
|
{ name: 'claude', platforms: ['claude'] },
|
|
859
848
|
{ name: 'codex', platforms: ['codex'] },
|
|
860
|
-
{ name: '
|
|
861
|
-
{ name: 'combined', platforms: ['claude', 'codex',
|
|
862
|
-
{ name: 'combined-rerun', platforms: ['claude', 'codex', 'opencode'], rerun: true },
|
|
849
|
+
{ name: 'combined', platforms: ['claude', 'codex'] },
|
|
850
|
+
{ name: 'combined-rerun', platforms: ['claude', 'codex'], rerun: true },
|
|
863
851
|
];
|
|
864
852
|
for (const matrixCase of cases) {
|
|
865
853
|
for (const lang of [null, 'vi']) {
|
|
@@ -871,7 +859,9 @@ test('packed tarball installer matrix proves locale, transforms, paths, and reru
|
|
|
871
859
|
assertInstalledScripts(project, platform);
|
|
872
860
|
assertTransforms(project, platform);
|
|
873
861
|
}
|
|
874
|
-
if (matrixCase.platforms.
|
|
862
|
+
if (matrixCase.platforms.includes('claude') && matrixCase.platforms.includes('codex')) {
|
|
863
|
+
assertCombinedInstructionIsolation(project);
|
|
864
|
+
}
|
|
875
865
|
if (matrixCase.rerun) {
|
|
876
866
|
fs.appendFileSync(path.join(project, 'AGENTS.md'), '\n## User matrix note\nKeep this exact.\n');
|
|
877
867
|
fs.appendFileSync(path.join(project, 'CLAUDE.md'), '\n## User Claude matrix note\nKeep this exact.\n');
|
|
@@ -173,14 +173,14 @@ function buildEvolutionSpecFixture(root, name) {
|
|
|
173
173
|
// isolated root) -- reaches a real `released` generation 1 authority with no
|
|
174
174
|
// hand-crafted digests. Returns the bound instance and key commits/digests.
|
|
175
175
|
function publishOneReleasedGeneration(root) {
|
|
176
|
-
writeJson(path.join(root, 'specs/cafekit-semantic-eval-firewall/reports/bootstrap-legacy-bridge-review.json'), {
|
|
176
|
+
writeJson(path.join(root, 'specs/archive/cafekit-semantic-eval-firewall/reports/bootstrap-legacy-bridge-review.json'), {
|
|
177
177
|
schema_version: '1', verdict: 'PASS', blocking_count: 0, reviewed_criteria: ['R1.1'],
|
|
178
178
|
semantic_digest: cf.sha256Tag('bridge'), bootWindowBaseCommit: git(root, ['rev-parse', 'HEAD']).trim(),
|
|
179
179
|
written_at: new Date().toISOString(),
|
|
180
180
|
});
|
|
181
181
|
const baseCommit = git(root, ['rev-parse', 'HEAD']).trim();
|
|
182
182
|
const reviewReceiptDigest = cf.sha256Tag('seed-review-receipt');
|
|
183
|
-
writeJson(path.join(root, 'specs/cafekit-semantic-eval-firewall/spec.json'), {
|
|
183
|
+
writeJson(path.join(root, 'specs/archive/cafekit-semantic-eval-firewall/spec.json'), {
|
|
184
184
|
validation: {
|
|
185
185
|
semantic_review_history: {
|
|
186
186
|
entries: [{ sequence: 0, review_epoch: 0, verdict: 'PASS', review_receipt_digest: reviewReceiptDigest, semantic_digest: cf.sha256Tag('feature-digest') }],
|
|
@@ -553,7 +553,7 @@ test('sentinel: a corrupted freeze file surfaces as release-preflight exit 2, ne
|
|
|
553
553
|
// package.json wiring: version preserved, reserved paths excluded (real checkout, read-only)
|
|
554
554
|
// ---------------------------------------------------------------------------
|
|
555
555
|
|
|
556
|
-
test('package.json: version stays valid SemVer
|
|
556
|
+
test('package.json: version stays valid SemVer and the npm lifecycle stays unwired while the boot-window is open', () => {
|
|
557
557
|
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
|
558
558
|
// R1.6 requires the version field preserved, not pinned to one release literal
|
|
559
559
|
// this task happened to land against -- a hard-coded release string would
|
|
@@ -562,8 +562,16 @@ test('package.json: version stays valid SemVer (release-agnostic) and prepack/pr
|
|
|
562
562
|
// is a valid SemVer string (optional prerelease/build metadata included).
|
|
563
563
|
assert.equal(typeof pkg.version, 'string');
|
|
564
564
|
assert.match(pkg.version, /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);
|
|
565
|
-
|
|
566
|
-
|
|
565
|
+
// R0-01 wired prepack/prepublishOnly to release-preflight.mjs while stating
|
|
566
|
+
// it "does not invoke bootstrapBaseline, create a real freeze, or publish
|
|
567
|
+
// (D11/I9 boot-window)". R1-03 bootstrap-activation, which closes that
|
|
568
|
+
// window, never ran and this feature is paused -- so the wiring made every
|
|
569
|
+
// publish exit 2 on a freeze that nothing was allowed to create. The gate
|
|
570
|
+
// stays runnable on its own (`release:freeze`, and the script invoked
|
|
571
|
+
// directly by the tests above); it just no longer sits on npm's lifecycle.
|
|
572
|
+
// Rewire both when R1-03 lands.
|
|
573
|
+
assert.equal(pkg.scripts.prepack, undefined);
|
|
574
|
+
assert.equal(pkg.scripts.prepublishOnly, undefined);
|
|
567
575
|
assert.equal(pkg.scripts['release:freeze'], 'node scripts/benchmark-workflow.mjs freeze');
|
|
568
576
|
// pre-existing scripts untouched
|
|
569
577
|
assert.equal(pkg.scripts.test, 'node scripts/run-skill-self-tests.mjs');
|
|
@@ -525,9 +525,8 @@ The transition records success, failure, and recovery states.
|
|
|
525
525
|
test('Codex installer transform preserves all owned spec assets with idempotent runtime parity', () => {
|
|
526
526
|
for (const relative of [
|
|
527
527
|
'src/claude/skills/specs/SKILL.md',
|
|
528
|
-
'src/claude/skills/specs/rules/tasks-generation.md',
|
|
529
|
-
'src/claude/skills/specs/rules/design-principles.md',
|
|
530
528
|
'src/claude/skills/specs/references/review.md',
|
|
529
|
+
'src/claude/skills/specs/references/templates.md',
|
|
531
530
|
'src/claude/skills/specs/templates/design.md',
|
|
532
531
|
'src/claude/skills/specs/templates/task.md',
|
|
533
532
|
'src/claude/scripts/spec-scaffold.cjs',
|
|
@@ -548,14 +547,15 @@ test('Codex installer transform preserves all owned spec assets with idempotent
|
|
|
548
547
|
|
|
549
548
|
const skill = fs.readFileSync(path.join(ROOT, 'src/claude/skills/specs/SKILL.md'), 'utf8');
|
|
550
549
|
const review = fs.readFileSync(path.join(ROOT, 'src/claude/skills/specs/references/review.md'), 'utf8');
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
assert.match(skill,
|
|
554
|
-
assert.
|
|
555
|
-
assert.
|
|
556
|
-
assert.match(
|
|
557
|
-
assert.match(
|
|
558
|
-
assert.match(
|
|
550
|
+
const templates = fs.readFileSync(path.join(ROOT, 'src/claude/skills/specs/references/templates.md'), 'utf8');
|
|
551
|
+
assert.match(skill, /specs\/<feature>\/[\s\S]{0,160}plan\.md[\s\S]{0,160}task-01-<slug>\.md/);
|
|
552
|
+
assert.match(skill, /C1 — Scope[\s\S]*C2 — Findings[\s\S]*C3 — Done/);
|
|
553
|
+
assert.doesNotMatch(skill, /--(?:status|validate|archive)\b/);
|
|
554
|
+
assert.match(review, /path:line/);
|
|
555
|
+
assert.match(review, /Cap the presented[\s\S]{0,40}list at 15/);
|
|
556
|
+
assert.match(review, /Round three requires runtime evidence/);
|
|
557
|
+
assert.match(templates, /\| ID \| EARS criterion \| Proof \|/);
|
|
558
|
+
assert.match(templates, /## Canonical inline Receipt[\s\S]*Verification: PASS[\s\S]*Exit: 0[\s\S]*Base:[\s\S]*Head:/);
|
|
559
559
|
|
|
560
560
|
const parityRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-codex-validator-parity-'));
|
|
561
561
|
try {
|
|
@@ -3201,7 +3201,7 @@ test('D11/R1.14: the legacy-bridge precondition artifact is reachable and change
|
|
|
3201
3201
|
// resolves its own production root the same way, via git from its own file
|
|
3202
3202
|
// location) -- ROOT here is packages/spec, one level below that root.
|
|
3203
3203
|
const MONOREPO_ROOT = path.resolve(ROOT, '..', '..');
|
|
3204
|
-
const bridgePath = path.join(MONOREPO_ROOT, 'specs/cafekit-semantic-eval-firewall/reports/bootstrap-legacy-bridge-review.json');
|
|
3204
|
+
const bridgePath = path.join(MONOREPO_ROOT, 'specs/archive/cafekit-semantic-eval-firewall/reports/bootstrap-legacy-bridge-review.json');
|
|
3205
3205
|
assert.ok(fs.existsSync(bridgePath), 'the legacy-bridge artifact must exist before any R0-01 change-firewall assertion runs');
|
|
3206
3206
|
const parsed = CHANGE_FIREWALL.assertLegacyBridgeArtifact();
|
|
3207
3207
|
assert.equal(parsed.schema_version, '1');
|
package/bin/install.js
CHANGED
|
@@ -33,7 +33,6 @@ const {
|
|
|
33
33
|
removeObsoleteAgents
|
|
34
34
|
} = require('./phases/claude-runtime');
|
|
35
35
|
const { mergeClaudeSettings } = require('./phases/claude-settings');
|
|
36
|
-
const { installOpenCodeRuntime } = require('./phases/opencode-runtime');
|
|
37
36
|
const { installCodexRuntime } = require('./phases/codex-runtime');
|
|
38
37
|
const { writePlatformVersionMetadata } = require('./phases/write-metadata');
|
|
39
38
|
const { checkVersions } = require('./lib/version-check');
|
|
@@ -70,10 +69,6 @@ function installPlatform(ctx, platformKey) {
|
|
|
70
69
|
copyRulesDirectory(ctx, platformKey);
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
if (platformKey === 'opencode') {
|
|
74
|
-
installOpenCodeRuntime(ctx, platformKey);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
72
|
if (platformKey === 'codex') {
|
|
78
73
|
installCodexRuntime(ctx, platformKey);
|
|
79
74
|
}
|
|
@@ -95,11 +90,11 @@ function printHelp() {
|
|
|
95
90
|
Usage: npx @haposoft/cafekit [options]
|
|
96
91
|
|
|
97
92
|
Installs CafeKit skills, agents, rules and runtime into the current project
|
|
98
|
-
for Claude Code
|
|
93
|
+
for Claude Code and/or Codex CLI. Re-runs are selective: managed files are updated,
|
|
99
94
|
your edits are preserved.
|
|
100
95
|
|
|
101
96
|
Options:
|
|
102
|
-
--platform <id[,id]> Select claude
|
|
97
|
+
--platform <id[,id]> Select claude or codex explicitly
|
|
103
98
|
--dry-run Preview changes; write nothing
|
|
104
99
|
--force-overwrite Overwrite user-modified managed files (backup kept)
|
|
105
100
|
-u, --upgrade, -f, --force Alias of --force-overwrite
|
package/bin/lib/context.js
CHANGED
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
const fs = require('fs');
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const packageJson = require('../../package.json');
|
|
16
|
-
const { getOpenCodeCopyOptions } = require('./opencode-install');
|
|
17
16
|
const { isInteractive, createUI } = require('./ui');
|
|
18
17
|
const { resolveLang, createTranslator } = require('./i18n');
|
|
19
18
|
const { normalizeSourcePaths } = require('./copy-utils');
|
|
@@ -57,12 +56,10 @@ function loadClaudeMigrationManifest() {
|
|
|
57
56
|
const DEPENDENCY_TEMPLATES = {
|
|
58
57
|
commands: {
|
|
59
58
|
claude: {},
|
|
60
|
-
opencode: {},
|
|
61
59
|
codex: {}
|
|
62
60
|
},
|
|
63
61
|
agents: {
|
|
64
62
|
claude: {},
|
|
65
|
-
opencode: {},
|
|
66
63
|
codex: {}
|
|
67
64
|
}
|
|
68
65
|
};
|
|
@@ -93,29 +90,6 @@ const PLATFORMS = {
|
|
|
93
90
|
commands: true
|
|
94
91
|
}
|
|
95
92
|
},
|
|
96
|
-
opencode: {
|
|
97
|
-
id: 'opencode',
|
|
98
|
-
name: 'OpenCode',
|
|
99
|
-
description: 'OpenCode terminal AI coding agent',
|
|
100
|
-
folder: '.opencode',
|
|
101
|
-
detectFiles: ['.opencode', 'opencode.json', 'opencode.jsonc'],
|
|
102
|
-
commandsDir: '.opencode/commands',
|
|
103
|
-
skillsDir: '.opencode/skills',
|
|
104
|
-
agentsDir: '.opencode/agents',
|
|
105
|
-
skillsRef: '.opencode/skills',
|
|
106
|
-
commandPrefix: '/',
|
|
107
|
-
sourceDir: 'claude',
|
|
108
|
-
sourceSubdir: 'commands', // Dead config (manifest commands.core is []); kept for shape parity
|
|
109
|
-
backupTargets: ['.opencode', 'AGENTS.md', 'opencode.json', 'opencode.jsonc'],
|
|
110
|
-
capabilities: {
|
|
111
|
-
skills: true,
|
|
112
|
-
agents: true,
|
|
113
|
-
references: true,
|
|
114
|
-
scripts: true,
|
|
115
|
-
rules: true,
|
|
116
|
-
commands: false
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
93
|
codex: {
|
|
120
94
|
id: 'codex',
|
|
121
95
|
name: 'Codex CLI',
|
|
@@ -192,26 +166,8 @@ function getRuntimeSupportTargetDir(platformKey, subdir) {
|
|
|
192
166
|
return path.join(PLATFORMS[platformKey].folder, subdir);
|
|
193
167
|
}
|
|
194
168
|
|
|
195
|
-
// Warn opencode-only installs when a legacy .claude/ folder remains from
|
|
196
|
-
// pre-0.9.0 installs (when OpenCode shared the .claude/ runtime). We do not
|
|
197
|
-
// delete anything; user must remove it explicitly to opt in.
|
|
198
|
-
function warnLegacyClaudeFolder(platforms) {
|
|
199
|
-
if (!platforms.includes('opencode')) return;
|
|
200
|
-
if (platforms.includes('claude')) return;
|
|
201
|
-
if (!fs.existsSync('.claude')) return;
|
|
202
|
-
|
|
203
|
-
console.log('');
|
|
204
|
-
console.log('⚠ Legacy .claude/ folder detected.');
|
|
205
|
-
console.log(' As of CafeKit 0.9.0, OpenCode installs are self-contained under .opencode/.');
|
|
206
|
-
console.log(' The .claude/ folder from older installs is no longer used by OpenCode.');
|
|
207
|
-
console.log(' After this install finishes you can remove it: rm -rf .claude');
|
|
208
|
-
console.log('');
|
|
209
|
-
}
|
|
210
169
|
|
|
211
170
|
function getCopyOptions(platformKey, baseOptions = {}) {
|
|
212
|
-
if (platformKey === 'opencode') {
|
|
213
|
-
return getOpenCodeCopyOptions(baseOptions);
|
|
214
|
-
}
|
|
215
171
|
if (platformKey === 'codex') {
|
|
216
172
|
// Lazy require avoids coupling the shared registry to a runtime adapter
|
|
217
173
|
// during module initialization.
|
|
@@ -358,7 +314,6 @@ module.exports = {
|
|
|
358
314
|
getPlatformKeys,
|
|
359
315
|
hasPlatformCapability,
|
|
360
316
|
getRuntimeSupportTargetDir,
|
|
361
|
-
warnLegacyClaudeFolder,
|
|
362
317
|
getCopyOptions,
|
|
363
318
|
parseInstallerArgs,
|
|
364
319
|
createResults,
|
package/bin/lib/copy-utils.js
CHANGED
|
@@ -13,7 +13,7 @@ function isTextAsset(filePath) {
|
|
|
13
13
|
|
|
14
14
|
// Generated artifacts never ship as runtime payload: the coverage db, the
|
|
15
15
|
// Python bytecode cache directory, and bytecode files. Skipping them here keeps
|
|
16
|
-
// both copyRecursive and copyManagedTree
|
|
16
|
+
// both copyRecursive and copyManagedTree
|
|
17
17
|
// built on copyRecursive) from ever copying build residue.
|
|
18
18
|
function isGeneratedArtifact(name) {
|
|
19
19
|
const base = String(name || '');
|
package/bin/lib/i18n.js
CHANGED
|
@@ -107,7 +107,6 @@ const MESSAGES = {
|
|
|
107
107
|
// Next steps
|
|
108
108
|
nextStepsTitle: 'Next steps',
|
|
109
109
|
nsClaude: 'Start coding: /hapo:specs <feature>',
|
|
110
|
-
nsOpencode: 'Start coding: ask the agent to begin a new feature or brainstorm',
|
|
111
110
|
nsCodex: 'Start with `$hapo-specs <feature>`; review and trust project hooks with `/hooks`',
|
|
112
111
|
nsKeys: 'Some skills need API keys: {skills}\n Copy <skill>/.env.example → .env and fill in the values',
|
|
113
112
|
nsForce: 'To force-refresh managed files: npx @haposoft/cafekit --force-overwrite',
|
|
@@ -228,7 +227,6 @@ const MESSAGES = {
|
|
|
228
227
|
|
|
229
228
|
nextStepsTitle: '次のステップ',
|
|
230
229
|
nsClaude: 'コーディング開始: /hapo:specs <機能名>',
|
|
231
|
-
nsOpencode: 'コーディング開始: エージェントに新機能の開始またはブレストを依頼',
|
|
232
230
|
nsCodex: '`$hapo-specs <機能名>` で開始し、`/hooks` でプロジェクトフックを確認・信頼',
|
|
233
231
|
nsKeys: '一部のスキルに API キーが必要です: {skills}\n <skill>/.env.example を .env にコピーし、値を入力してください',
|
|
234
232
|
nsForce: '管理ファイルを強制更新: npx @haposoft/cafekit --force-overwrite',
|
|
@@ -348,7 +346,6 @@ const MESSAGES = {
|
|
|
348
346
|
|
|
349
347
|
nextStepsTitle: 'Bước tiếp theo',
|
|
350
348
|
nsClaude: 'Bắt đầu code: /hapo:specs <mô-tả-tính-năng>',
|
|
351
|
-
nsOpencode: 'Bắt đầu code: yêu cầu agent khởi động tính năng mới hoặc brainstorm',
|
|
352
349
|
nsCodex: 'Bắt đầu bằng `$hapo-specs <tính-năng>`; kiểm tra và trust project hooks bằng `/hooks`',
|
|
353
350
|
nsKeys: 'Một số skill cần API key: {skills}\n Sao chép <skill>/.env.example → .env rồi điền giá trị',
|
|
354
351
|
nsForce: 'Làm mới file được quản lý: npx @haposoft/cafekit --force-overwrite',
|
package/bin/lib/manifest.js
CHANGED
|
@@ -169,7 +169,7 @@ function createTracker(platformFolder, version, options = {}) {
|
|
|
169
169
|
* Classify a managed file against the recorded baseline + the incoming payload.
|
|
170
170
|
*
|
|
171
171
|
* @param {string} absPath destination path on disk
|
|
172
|
-
* @param {string} platformFolder root of the platform install (.claude
|
|
172
|
+
* @param {string} platformFolder root of the platform install (e.g. .claude)
|
|
173
173
|
* @param {object} manifest result of read(platformFolder)
|
|
174
174
|
* @param {string} payloadHash sha256 of the content about to be written
|
|
175
175
|
* @returns {{state: string, diskHash: string|null, recordedHash: string|null, changed: boolean}}
|
|
@@ -18,7 +18,6 @@ const {
|
|
|
18
18
|
const { sha256 } = require('../lib/manifest');
|
|
19
19
|
const { writeManagedFile, copyManagedTree } = require('../lib/managed-writer');
|
|
20
20
|
const { report, treeAction } = require('./report');
|
|
21
|
-
const { normalizeOpenCodeBody } = require('../lib/opencode-install');
|
|
22
21
|
const {
|
|
23
22
|
upsertManagedCoreBlock,
|
|
24
23
|
managedRange,
|
|
@@ -120,7 +119,7 @@ function copyClaudeAgentsMdFile(ctx, platformKey) {
|
|
|
120
119
|
ensureSharedAgentsMdCore(ctx);
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
/** Copy ROUTING.md (SKILLS_DIR substituted
|
|
122
|
+
/** Copy ROUTING.md (SKILLS_DIR substituted). */
|
|
124
123
|
function copyRoutingFile(ctx, platformKey) {
|
|
125
124
|
const platform = PLATFORMS[platformKey];
|
|
126
125
|
const src = path.join(SRC, platform.sourceDir, 'ROUTING.md');
|
|
@@ -129,7 +128,6 @@ function copyRoutingFile(ctx, platformKey) {
|
|
|
129
128
|
|
|
130
129
|
const transform = (content) => {
|
|
131
130
|
let c = content.replace(/\{\{SKILLS_DIR\}\}/g, platform.skillsRef);
|
|
132
|
-
if (platformKey === 'opencode') c = normalizeOpenCodeBody(c);
|
|
133
131
|
return c;
|
|
134
132
|
};
|
|
135
133
|
const { action } = writeManagedFile({
|
|
@@ -7,7 +7,6 @@ const { writeManagedFile, copyManagedTree } = require('../lib/managed-writer');
|
|
|
7
7
|
const { report, treeAction } = require('./report');
|
|
8
8
|
const { copyRulesDirectory } = require('./claude-runtime');
|
|
9
9
|
const {
|
|
10
|
-
normalizeCodexBody,
|
|
11
10
|
managedRange,
|
|
12
11
|
upsertManagedCodexBlock
|
|
13
12
|
} = require('../lib/codex-install');
|
|
@@ -138,7 +137,10 @@ function installManagedAgentsMd(ctx) {
|
|
|
138
137
|
ctx.results.errors++;
|
|
139
138
|
return;
|
|
140
139
|
}
|
|
141
|
-
|
|
140
|
+
// This template is already Codex-native. Running it through the generic
|
|
141
|
+
// Claude-to-Codex converter corrupts intentional cross-runtime wording such
|
|
142
|
+
// as the fail-safe instruction for Claude Code to ignore this block.
|
|
143
|
+
const block = fs.readFileSync(runtimeSource, 'utf8');
|
|
142
144
|
const next = upsertManagedCodexBlock(existing, block);
|
|
143
145
|
const action = !exists ? 'created' : next === existing ? 'unchanged' : 'updated';
|
|
144
146
|
|
|
@@ -17,10 +17,6 @@ const {
|
|
|
17
17
|
} = require('../lib/context');
|
|
18
18
|
const { writeManagedFile, copyManagedTree } = require('../lib/managed-writer');
|
|
19
19
|
const { report, treeAction } = require('./report');
|
|
20
|
-
const {
|
|
21
|
-
convertOpenCodeAgentContent,
|
|
22
|
-
convertOpenCodeCommandContent
|
|
23
|
-
} = require('../lib/opencode-install');
|
|
24
20
|
const {
|
|
25
21
|
codexAgentName,
|
|
26
22
|
convertCodexAgentContent
|
|
@@ -29,8 +25,7 @@ const {
|
|
|
29
25
|
const SRC = path.join(__dirname, '../../src');
|
|
30
26
|
|
|
31
27
|
/**
|
|
32
|
-
* Spec command files installed for a platform. Claude ships spec-* commands
|
|
33
|
-
* OpenCode generates its own command wrappers elsewhere.
|
|
28
|
+
* Spec command files installed for a platform. Claude ships spec-* commands.
|
|
34
29
|
*/
|
|
35
30
|
function getPlatformSpecFiles(platformKey, ctx) {
|
|
36
31
|
if (platformKey === 'claude') {
|
|
@@ -76,7 +71,6 @@ function copyPlatformFiles(ctx, platformKey) {
|
|
|
76
71
|
const tracker = ctx.trackers[platformKey];
|
|
77
72
|
const platformFolder = platform.folder;
|
|
78
73
|
|
|
79
|
-
// For OpenCode, text bodies are path-normalized (.claude → .opencode, etc.).
|
|
80
74
|
const bodyTransform = getCopyOptions(platformKey, {}).transform;
|
|
81
75
|
|
|
82
76
|
const skillsSourceDir = path.join(SRC, 'claude/skills');
|
|
@@ -162,9 +156,7 @@ function copyPlatformFiles(ctx, platformKey) {
|
|
|
162
156
|
'tester.md', 'code-reviewer.md', 'fullstack-developer.md', 'debugger.md'
|
|
163
157
|
];
|
|
164
158
|
let agentTransform;
|
|
165
|
-
if (platformKey === '
|
|
166
|
-
agentTransform = (content, src) => convertOpenCodeAgentContent(content, path.basename(src));
|
|
167
|
-
} else if (platformKey === 'codex') {
|
|
159
|
+
if (platformKey === 'codex') {
|
|
168
160
|
agentTransform = (content, src) => convertCodexAgentContent(content, path.basename(src));
|
|
169
161
|
}
|
|
170
162
|
|
|
@@ -222,11 +214,7 @@ function copyPlatformFiles(ctx, platformKey) {
|
|
|
222
214
|
return;
|
|
223
215
|
}
|
|
224
216
|
const cmdTransform = (content, s) => {
|
|
225
|
-
|
|
226
|
-
if (platformKey === 'opencode') {
|
|
227
|
-
c = convertOpenCodeCommandContent(c, path.basename(s));
|
|
228
|
-
}
|
|
229
|
-
return c;
|
|
217
|
+
return content.replace(/\{\{SKILLS_DIR\}\}/g, platform.skillsRef);
|
|
230
218
|
};
|
|
231
219
|
const { action } = writeManagedFile({
|
|
232
220
|
src, dest, platformFolder, ctx, tracker, transform: cmdTransform
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Phase: post-install runtime configuration.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Addressing config. Interactive prompts use ctx.ui (clack);
|
|
5
5
|
* in non-interactive/dry-run they are skipped via fallbacks so spawned/CI runs
|
|
6
6
|
* never hang.
|
|
7
7
|
*
|
|
@@ -15,14 +15,12 @@ const fs = require('fs');
|
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const { PLATFORMS } = require('../lib/context');
|
|
17
17
|
const { LANGUAGE_LABELS } = require('../lib/i18n');
|
|
18
|
-
const { setupOpenCodeModel, transformManagedOpenCodeContent } = require('../lib/opencode-install');
|
|
19
18
|
const { transformManagedCodexContent } = require('../lib/codex-install');
|
|
20
19
|
const { transformManagedClaudeContent } = require('./claude-runtime');
|
|
21
20
|
const { transformManagedCoreContent } = require('../lib/instruction-blocks');
|
|
22
21
|
const ASSISTANT_NAMES = {
|
|
23
22
|
claude: 'Claude Code',
|
|
24
|
-
codex: 'Codex CLI'
|
|
25
|
-
opencode: 'OpenCode'
|
|
23
|
+
codex: 'Codex CLI'
|
|
26
24
|
};
|
|
27
25
|
const LANGUAGE_LABEL_BY_CODE = {
|
|
28
26
|
vi: 'Vietnamese',
|
|
@@ -50,10 +48,6 @@ function instructionTargets(ctx) {
|
|
|
50
48
|
file: path.join(process.cwd(), 'AGENTS.md'),
|
|
51
49
|
transform: transformManagedCodexContent
|
|
52
50
|
},
|
|
53
|
-
opencode: {
|
|
54
|
-
file: path.join(process.cwd(), 'AGENTS.md'),
|
|
55
|
-
transform: transformManagedOpenCodeContent
|
|
56
|
-
}
|
|
57
51
|
};
|
|
58
52
|
return Object.keys(targets)
|
|
59
53
|
.filter((key) => ctx.platforms.includes(key))
|
|
@@ -161,7 +155,7 @@ function patchRuntimeLocale(ctx) {
|
|
|
161
155
|
|
|
162
156
|
/** Human assistant name for the active platform(s). */
|
|
163
157
|
function assistantName(platformKey) {
|
|
164
|
-
return ASSISTANT_NAMES[platformKey] ||
|
|
158
|
+
return ASSISTANT_NAMES[platformKey] || platformKey;
|
|
165
159
|
}
|
|
166
160
|
|
|
167
161
|
function configureAddressing(ctx, userAddress) {
|
|
@@ -188,7 +182,7 @@ ${name} always addresses the user as "${userAddress}" throughout the conversatio
|
|
|
188
182
|
}
|
|
189
183
|
|
|
190
184
|
async function setupAddressing(ctx) {
|
|
191
|
-
if (!ctx.platforms.some((key) => key === 'claude' || key === 'codex'
|
|
185
|
+
if (!ctx.platforms.some((key) => key === 'claude' || key === 'codex')) return;
|
|
192
186
|
|
|
193
187
|
let existingName = null;
|
|
194
188
|
for (const target of instructionTargets(ctx)) {
|
|
@@ -237,16 +231,10 @@ async function setupAddressing(ctx) {
|
|
|
237
231
|
/** Run the post-install configuration sequence. */
|
|
238
232
|
async function runPostInstall(ctx) {
|
|
239
233
|
if (ctx.dryRun) {
|
|
240
|
-
ctx.ui.info('[dry-run] Skipping
|
|
234
|
+
ctx.ui.info('[dry-run] Skipping addressing setup');
|
|
241
235
|
return ctx;
|
|
242
236
|
}
|
|
243
237
|
|
|
244
|
-
// OpenCode model uses its own readline; only run it when it won't block:
|
|
245
|
-
// interactive, or an env override is present (which it reads without prompting).
|
|
246
|
-
const hasModelEnv = Boolean(process.env.OPENCODE_MODEL || process.env.OPENCODE_DEFAULT_MODEL);
|
|
247
|
-
if (ctx.platforms.includes('opencode') && (ctx.interactive || hasModelEnv)) {
|
|
248
|
-
await setupOpenCodeModel(ctx.platforms, ctx.results);
|
|
249
|
-
}
|
|
250
238
|
|
|
251
239
|
await setupAddressing(ctx);
|
|
252
240
|
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* Ensures the root .gitignore carries CafeKit-managed patterns:
|
|
5
5
|
* - workflow dirs (plans, shared research)
|
|
6
6
|
* - install safety artifacts (backup, lock)
|
|
7
|
-
* - runtime folders (.claude/, .
|
|
7
|
+
* - runtime folders (.claude/, .codex/, .agents/) — reinstall via
|
|
8
8
|
* `npx @haposoft/cafekit`; do not commit the local payload
|
|
9
9
|
*
|
|
10
10
|
* Layered with the in-folder `.claude/.gitignore` /
|
|
11
|
-
* `.
|
|
11
|
+
* runtime `.gitignore` templates (secrets, skill deps, session
|
|
12
12
|
* state) so force-adds and partial un-ignores stay safe.
|
|
13
13
|
*
|
|
14
14
|
* Honors dry-run.
|
|
@@ -109,7 +109,6 @@ function ensureGitignore(ctx) {
|
|
|
109
109
|
'.cafekit.lock',
|
|
110
110
|
// Local runtime payload — reinstall with npx; keep out of git
|
|
111
111
|
'.claude/',
|
|
112
|
-
'.opencode/',
|
|
113
112
|
'.codex/',
|
|
114
113
|
'.agents/'
|
|
115
114
|
];
|
|
@@ -11,8 +11,7 @@ const path = require('path');
|
|
|
11
11
|
const {
|
|
12
12
|
PLATFORMS,
|
|
13
13
|
detectPlatforms,
|
|
14
|
-
getPlatformKeys
|
|
15
|
-
warnLegacyClaudeFolder
|
|
14
|
+
getPlatformKeys
|
|
16
15
|
} = require('../lib/context');
|
|
17
16
|
const { SUPPORTED, LANGUAGE_LABELS, OTHER_LABEL } = require('../lib/i18n');
|
|
18
17
|
|
|
@@ -162,7 +161,6 @@ async function resolvePlatforms(ctx) {
|
|
|
162
161
|
ctx.ui.info(ctx.t('installingFor', {
|
|
163
162
|
names: platformNames(requested)
|
|
164
163
|
}));
|
|
165
|
-
warnLegacyClaudeFolder(requested);
|
|
166
164
|
reportInstallMode(ctx);
|
|
167
165
|
return ctx;
|
|
168
166
|
}
|
|
@@ -216,7 +214,6 @@ async function resolvePlatforms(ctx) {
|
|
|
216
214
|
ctx.platforms = platforms;
|
|
217
215
|
|
|
218
216
|
ctx.ui.info(ctx.t('installingFor', { names: platformNames(platforms) }));
|
|
219
|
-
warnLegacyClaudeFolder(platforms);
|
|
220
217
|
|
|
221
218
|
reportInstallMode(ctx);
|
|
222
219
|
|
|
@@ -225,7 +222,7 @@ async function resolvePlatforms(ctx) {
|
|
|
225
222
|
|
|
226
223
|
/**
|
|
227
224
|
* Get the installed platform from cafekit.json files.
|
|
228
|
-
* Returns the platform key (e.g. 'claude', '
|
|
225
|
+
* Returns the platform key (e.g. 'claude', 'codex') or null.
|
|
229
226
|
*/
|
|
230
227
|
function getInstalledPlatforms() {
|
|
231
228
|
const installed = [];
|
package/bin/phases/summary.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haposoft/cafekit",
|
|
3
|
-
"version": "0.16.0-rc.
|
|
4
|
-
"description": "Native spec-driven workflows for Claude Code
|
|
3
|
+
"version": "0.16.0-rc.7",
|
|
4
|
+
"description": "Native spec-driven workflows for Claude Code and Codex CLI. Bundles CafeKit skills, runtime hooks, agents, and installer scaffolding.",
|
|
5
5
|
"author": "Haposoft <nghialt@haposoft.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"private": false,
|
|
@@ -13,9 +13,7 @@
|
|
|
13
13
|
"test:package": "node --test bin/__tests__/package-inventory.test.js",
|
|
14
14
|
"test:secrets": "node --test bin/__tests__/secret-scanner.test.js",
|
|
15
15
|
"test:policy": "node --test bin/__tests__/develop-contract.test.js",
|
|
16
|
-
"release:freeze": "node scripts/benchmark-workflow.mjs freeze"
|
|
17
|
-
"prepack": "node scripts/release-preflight.mjs",
|
|
18
|
-
"prepublishOnly": "node scripts/release-preflight.mjs"
|
|
16
|
+
"release:freeze": "node scripts/benchmark-workflow.mjs freeze"
|
|
19
17
|
},
|
|
20
18
|
"files": [
|
|
21
19
|
"bin",
|
|
@@ -58,9 +56,6 @@
|
|
|
58
56
|
"engines": {
|
|
59
57
|
"node": ">=18.0.0"
|
|
60
58
|
},
|
|
61
|
-
"devDependencies": {
|
|
62
|
-
"@opencode-ai/plugin": "1.15.11"
|
|
63
|
-
},
|
|
64
59
|
"dependencies": {
|
|
65
60
|
"@clack/prompts": "0.11.0",
|
|
66
61
|
"picocolors": "^1.1.1"
|
package/src/claude/CLAUDE.md
CHANGED
|
@@ -12,9 +12,22 @@
|
|
|
12
12
|
- macOS/Linux: `.claude/skills/.venv/bin/python3 scripts/<script>.py`
|
|
13
13
|
- Windows: `.claude\skills\.venv\Scripts\python.exe scripts\<script>.py`
|
|
14
14
|
- Consult `.claude/rules/state-sync.md`, `.claude/rules/hook-protocols.md`, and `.claude/rules/skill-workflow-routing.md` when their topics apply.
|
|
15
|
-
- Specs
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
- New Specs work uses the process-first flow. `/hapo:specs` opens C1, writes
|
|
16
|
+
`specs/<feature>/plan.md` with flat `task-NN-*.md` files beside it, then opens
|
|
17
|
+
C2 after adversarial review. It never starts implementation.
|
|
18
|
+
- Start implementation only through a new explicit `/hapo:develop` invocation.
|
|
19
|
+
Execute one unblocked task at a time; each task has exactly one `Status:`
|
|
20
|
+
field and the controller is its sole state-and-proof writer.
|
|
21
|
+
- Use `/hapo:sync` for surgical updates to observed file state. A done task
|
|
22
|
+
requires a canonical final inline `## Receipt` with the exact command,
|
|
23
|
+
`Exit: 0`, `Verification: PASS`, runtime-derived Base and Head values, and
|
|
24
|
+
non-empty fenced current output.
|
|
25
|
+
- At C3, show current receipts and unresolved limitations. The user decides
|
|
26
|
+
completion; no command, review, or host state may invent approval or proof.
|
|
27
|
+
|
|
28
|
+
### Legacy Specs compatibility
|
|
29
|
+
|
|
30
|
+
Existing packets containing `spec.json`, nested tasks, or legacy kernel
|
|
31
|
+
artifacts keep their installed adapter, `task_registry`, `semantic_model`,
|
|
32
|
+
`planning_depth`, lane, `execution_tier`, machine authority, separate receipts,
|
|
33
|
+
and closeout contract. Do not migrate them during unrelated process-first work.
|