@opengsd/gsd-core 1.7.0-rc.6 → 1.7.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 (76) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-executor.md +2 -1
  4. package/agents/gsd-security-auditor.md +13 -15
  5. package/bin/install.js +5 -4
  6. package/commands/gsd/mempalace-capture.md +27 -1
  7. package/commands/gsd/surface.md +6 -6
  8. package/gsd-core/bin/gsd-tools.cjs +63 -2
  9. package/gsd-core/bin/lib/api-coverage.cjs +3 -4
  10. package/gsd-core/bin/lib/audit.cjs +7 -6
  11. package/gsd-core/bin/lib/capability-registry.cjs +57 -57
  12. package/gsd-core/bin/lib/check-command-router.cjs +1 -1
  13. package/gsd-core/bin/lib/clock.cjs +19 -0
  14. package/gsd-core/bin/lib/commands.cjs +48 -9
  15. package/gsd-core/bin/lib/config-loader.cjs +6 -2
  16. package/gsd-core/bin/lib/config.cjs +12 -0
  17. package/gsd-core/bin/lib/core-utils.cjs +8 -2
  18. package/gsd-core/bin/lib/drift.cjs +4 -4
  19. package/gsd-core/bin/lib/frontmatter.cjs +22 -0
  20. package/gsd-core/bin/lib/gsd2-import.cjs +2 -1
  21. package/gsd-core/bin/lib/init.cjs +27 -13
  22. package/gsd-core/bin/lib/install-engine.cjs +3 -2
  23. package/gsd-core/bin/lib/installer-migration-authoring.cjs +2 -1
  24. package/gsd-core/bin/lib/installer-migrations.cjs +1 -1
  25. package/gsd-core/bin/lib/markdown-sectionizer.cjs +342 -0
  26. package/gsd-core/bin/lib/markdown-table.cjs +698 -0
  27. package/gsd-core/bin/lib/milestone.cjs +217 -31
  28. package/gsd-core/bin/lib/phase-command-router.cjs +50 -2
  29. package/gsd-core/bin/lib/phase-lifecycle.cjs +62 -36
  30. package/gsd-core/bin/lib/phase-locator.cjs +23 -2
  31. package/gsd-core/bin/lib/phase.cjs +436 -61
  32. package/gsd-core/bin/lib/plan-scan.cjs +3 -0
  33. package/gsd-core/bin/lib/roadmap-parser.cjs +218 -13
  34. package/gsd-core/bin/lib/roadmap.cjs +100 -49
  35. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +7 -6
  36. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +3 -2
  37. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +2 -1
  38. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +25 -17
  39. package/gsd-core/bin/lib/schema-detect.cjs +2 -1
  40. package/gsd-core/bin/lib/security.cjs +1 -1
  41. package/gsd-core/bin/lib/shell-command-projection.cjs +61 -25
  42. package/gsd-core/bin/lib/smart-entry.cjs +4 -3
  43. package/gsd-core/bin/lib/state-transition.cjs +100 -45
  44. package/gsd-core/bin/lib/state.cjs +391 -126
  45. package/gsd-core/bin/lib/surface.cjs +2 -2
  46. package/gsd-core/bin/lib/template.cjs +2 -1
  47. package/gsd-core/bin/lib/uat.cjs +54 -8
  48. package/gsd-core/bin/lib/ui-safety-gate.cjs +23 -1
  49. package/gsd-core/bin/lib/verify.cjs +4 -3
  50. package/gsd-core/bin/lib/workstream.cjs +3 -2
  51. package/gsd-core/bin/lib/worktree-safety.cjs +1 -1
  52. package/gsd-core/bin/lib/write-set.cjs +38 -0
  53. package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
  54. package/gsd-core/references/checkpoints.md +12 -0
  55. package/gsd-core/workflows/add-tests.md +1 -1
  56. package/gsd-core/workflows/debug.md +2 -0
  57. package/gsd-core/workflows/execute-phase.md +5 -3
  58. package/gsd-core/workflows/fast.md +8 -22
  59. package/gsd-core/workflows/progress.md +2 -2
  60. package/gsd-core/workflows/quick.md +2 -0
  61. package/gsd-core/workflows/review.md +42 -3
  62. package/gsd-core/workflows/secure-phase.md +1 -1
  63. package/gsd-core/workflows/ship.md +8 -2
  64. package/gsd-core/workflows/spec-phase.md +1 -1
  65. package/gsd-core/workflows/transition.md +1 -1
  66. package/hooks/dist/gsd-statusline.js +164 -14
  67. package/hooks/gsd-statusline.js +164 -14
  68. package/package.json +4 -2
  69. package/scripts/ci-test-scope.cjs +39 -1
  70. package/scripts/gen-golden-install-parity-zcode.cjs +35 -45
  71. package/scripts/gen-install-tree-fixtures.cjs +75 -0
  72. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -1
  73. package/scripts/lint-table-schema-drift.cjs +157 -0
  74. package/skills/gsd-mempalace-capture/SKILL.md +27 -1
  75. package/skills/gsd-surface/SKILL.md +6 -6
  76. package/vscode/package.json +1 -1
@@ -3,60 +3,29 @@
3
3
  /**
4
4
  * Standalone golden-fixture generator for tests/golden-install-parity.
5
5
  *
6
- * This is a BUILD-TIME generation script — NOT a test run. It replicates the
7
- * buildParityManifest logic from tests/golden-install-parity.test.cjs and
8
- * captures the zcode fixture so the parity test (which the gsd-test gate runs)
9
- * has a committed artifact to compare against. The authoritative test gate
10
- * remains `gsd-test run`, never a local `node --test`.
6
+ * This is a BUILD-TIME generation script — NOT a test run. It imports the
7
+ * canonical buildParityManifest builder from tests/helpers/install-shared.cjs
8
+ * (issue #2266 — single source of truth shared with
9
+ * tests/golden-install-parity.test.cjs) and captures the zcode fixture so the
10
+ * parity test (which the gsd-test gate runs) has a committed artifact to
11
+ * compare against. The authoritative test gate remains `gsd-test run`, never
12
+ * a local `node --test`.
11
13
  *
12
14
  * Usage: node scripts/gen-golden-install-parity-zcode.cjs
13
15
  */
14
16
  const fs = require('node:fs');
15
17
  const path = require('node:path');
16
- const crypto = require('node:crypto');
18
+ const { execFileSync } = require('node:child_process');
17
19
 
18
20
  const ROOT = path.resolve(__dirname, '..');
19
- const { walk, runMinimalInstall, RUNTIME_META } = require(path.join(ROOT, 'tests', 'helpers', 'install-shared.cjs'));
20
- const PKG_VERSION = require(path.join(ROOT, 'package.json')).version;
21
+ // buildParityManifest (and its exclusion constants) is the canonical single
22
+ // source of truth in tests/helpers/install-shared.cjs (issue #2266) — the
23
+ // generator no longer keeps its own inline copy, which had drifted from the
24
+ // test harness's copy (missing the realpath/`<HOME>` normalization) and
25
+ // mis-generated the claude-local fixture (#2100).
26
+ const { runMinimalInstall, RUNTIME_META, buildParityManifest, BUILD_SCRIPT } = require(path.join(ROOT, 'tests', 'helpers', 'install-shared.cjs'));
21
27
  const FIXTURE_DIR = path.join(ROOT, 'tests', 'fixtures', 'golden-install-parity');
22
28
 
23
- const VOLATILE_FILES = new Set([
24
- 'gsd-file-manifest.json',
25
- 'gsd-install-state.json',
26
- '.gsd-source',
27
- 'gsd-core/CHANGELOG.md',
28
- ]);
29
- // Must match tests/golden-install-parity.test.cjs exactly — settings.local.json
30
- // (Claude LOCAL hook surface, #338/#2086) embeds the same platform-varying
31
- // node-runner command and is excluded there; omitting it here mis-generated the
32
- // claude-local fixture (#2100).
33
- const HOOK_CONFIG_FILES = new Set(['settings.json', 'settings.local.json', 'hooks.json']);
34
- // Kimi's native config.toml (#2095) — see tests/golden-install-parity.test.cjs'
35
- // HOOK_CONFIG_RELATIVE_PATHS comment for why this is an exact relative-path
36
- // exclusion rather than a HOOK_CONFIG_FILES basename entry (a basename entry
37
- // would also blind Codex's stable, platform-independent config.toml fixture).
38
- const HOOK_CONFIG_RELATIVE_PATHS = new Set(['.kimi/config.toml']);
39
- const EXCLUDED_PREFIXES = ['gsd-core/bin/lib/'];
40
-
41
- function buildParityManifest(configDir, root) {
42
- const allFiles = walk(configDir);
43
- const unsorted = {};
44
- for (const full of allFiles) {
45
- const rel = path.relative(configDir, full).split(path.sep).join('/');
46
- if (VOLATILE_FILES.has(rel)) continue;
47
- if (HOOK_CONFIG_FILES.has(path.basename(rel))) continue;
48
- if (HOOK_CONFIG_RELATIVE_PATHS.has(rel)) continue;
49
- if (EXCLUDED_PREFIXES.some((p) => rel.startsWith(p))) continue;
50
- const content = fs.readFileSync(full);
51
- const normalized = content.toString('utf8').split(root).join('<HOME>').split(PKG_VERSION).join('<VERSION>');
52
- const hash = crypto.createHash('sha256').update(normalized).digest('hex').slice(0, 16);
53
- unsorted[rel] = hash;
54
- }
55
- const sorted = {};
56
- for (const key of Object.keys(unsorted).sort()) sorted[key] = unsorted[key];
57
- return sorted;
58
- }
59
-
60
29
  function cleanup(root) {
61
30
  try { fs.rmSync(root, { recursive: true, force: true }); } catch { /* best effort */ }
62
31
  }
@@ -69,6 +38,13 @@ function cleanup(root) {
69
38
  // With no args, regenerates ALL runtimes. With args, only the named runtimes.
70
39
  const targets = process.argv.slice(2).length > 0 ? process.argv.slice(2) : Object.keys(RUNTIME_META);
71
40
  fs.mkdirSync(FIXTURE_DIR, { recursive: true });
41
+
42
+ // Build hooks/dist before capturing so fixtures are complete even in a clean
43
+ // checkout (hooks/dist is gitignored + built; DEFECT.HOOKS-DIST-SCOPED-CI). The
44
+ // test harness's before() hook does the same; without it a fresh checkout omits
45
+ // every hooks/* path and this generator silently writes short fixtures.
46
+ execFileSync(process.execPath, [BUILD_SCRIPT], { stdio: 'pipe' });
47
+
72
48
  for (const runtime of targets) {
73
49
  if (!Object.prototype.hasOwnProperty.call(RUNTIME_META, runtime)) {
74
50
  process.stderr.write(`[gen] unknown runtime '${runtime}' (not in RUNTIME_META) — skipping\n`);
@@ -85,3 +61,17 @@ for (const runtime of targets) {
85
61
  fs.writeFileSync(fixturePath, JSON.stringify(actual, null, 2) + '\n', 'utf8');
86
62
  process.stdout.write(`[gen] ${runtime}: wrote ${Object.keys(actual).length} file hashes -> ${fixturePath}\n`);
87
63
  }
64
+
65
+ // Also regenerate the claude LOCAL legacy-layout fixture (claude-local.json).
66
+ // This layout is distinct from the global install (commands/gsd-*.md +
67
+ // agents/gsd-*.md) and has its own parity assertion in the test harness.
68
+ const { configDir: localConfigDir, root: localRoot } = runMinimalInstall({ runtime: 'claude', scope: 'local' });
69
+ let localActual;
70
+ try {
71
+ localActual = buildParityManifest(localConfigDir, localRoot);
72
+ } finally {
73
+ cleanup(localRoot);
74
+ }
75
+ const localFixturePath = path.join(FIXTURE_DIR, 'claude-local.json');
76
+ fs.writeFileSync(localFixturePath, JSON.stringify(localActual, null, 2) + '\n', 'utf8');
77
+ process.stdout.write(`[gen] claude-local: wrote ${Object.keys(localActual).length} file hashes -> ${localFixturePath}\n`);
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * Standalone golden-fixture generator for tests/golden-install-tree (#2267 Phase 2).
5
+ *
6
+ * This is a BUILD-TIME generation script — NOT a test run. It imports the
7
+ * canonical buildInstallTree builder from tests/helpers/install-shared.cjs,
8
+ * which in turn reuses buildParityManifest's exact exclusion set (issue
9
+ * #2266) so the file-set fixtures here and the golden-install-parity content
10
+ * fixtures never diverge on which files they cover. The authoritative test
11
+ * gate remains `gsd-test run`, never a local `node --test`.
12
+ *
13
+ * Usage: node scripts/gen-install-tree-fixtures.cjs [runtime ...]
14
+ */
15
+ const fs = require('node:fs');
16
+ const path = require('node:path');
17
+ const { execFileSync } = require('node:child_process');
18
+
19
+ const ROOT = path.resolve(__dirname, '..');
20
+ // buildInstallTree (and the exclusion constants it reuses from
21
+ // buildParityManifest) is the canonical single source of truth in
22
+ // tests/helpers/install-shared.cjs (issue #2266/#2267) — this generator does
23
+ // not keep its own inline copy of the walk/exclusion logic.
24
+ const { runMinimalInstall, RUNTIME_META, buildInstallTree, BUILD_SCRIPT } = require(path.join(ROOT, 'tests', 'helpers', 'install-shared.cjs'));
25
+ const FIXTURE_DIR = path.join(ROOT, 'tests', 'fixtures', 'install-tree');
26
+
27
+ function cleanup(root) {
28
+ try { fs.rmSync(root, { recursive: true, force: true }); } catch { /* best effort */ }
29
+ }
30
+
31
+ // Regenerate the fixture for every runtime in RUNTIME_META. Needed when a
32
+ // SHARED gsd-core payload file (e.g. model-catalog.json, capability-registry)
33
+ // changes content — its path appears in every runtime's manifest, so all
34
+ // fixtures must be recaptured together. Usage:
35
+ // node scripts/gen-install-tree-fixtures.cjs [runtime ...]
36
+ // With no args, regenerates ALL runtimes. With args, only the named runtimes.
37
+ const targets = process.argv.slice(2).length > 0 ? process.argv.slice(2) : Object.keys(RUNTIME_META);
38
+ fs.mkdirSync(FIXTURE_DIR, { recursive: true });
39
+
40
+ // Build hooks/dist before capturing so fixtures are complete even in a clean
41
+ // checkout (hooks/dist is gitignored + built; DEFECT.HOOKS-DIST-SCOPED-CI). The
42
+ // test harness's before() hook does the same; without it a fresh checkout omits
43
+ // every hooks/* path and this generator silently writes short fixtures.
44
+ execFileSync(process.execPath, [BUILD_SCRIPT], { stdio: 'pipe' });
45
+
46
+ for (const runtime of targets) {
47
+ if (!Object.prototype.hasOwnProperty.call(RUNTIME_META, runtime)) {
48
+ process.stderr.write(`[gen] unknown runtime '${runtime}' (not in RUNTIME_META) — skipping\n`);
49
+ continue;
50
+ }
51
+ const { configDir, root } = runMinimalInstall({ runtime, scope: 'global' });
52
+ let actual;
53
+ try {
54
+ actual = buildInstallTree(configDir, root);
55
+ } finally {
56
+ cleanup(root);
57
+ }
58
+ const fixturePath = path.join(FIXTURE_DIR, `${runtime}.json`);
59
+ fs.writeFileSync(fixturePath, JSON.stringify(actual, null, 2) + '\n', 'utf8');
60
+ process.stdout.write(`[gen] ${runtime}: wrote ${actual.length} paths -> ${fixturePath}\n`);
61
+ }
62
+
63
+ // Also regenerate the claude LOCAL legacy-layout fixture (claude-local.json).
64
+ // This layout is distinct from the global install (commands/gsd-*.md +
65
+ // agents/gsd-*.md) and has its own snapshot assertion in the test harness.
66
+ const { configDir: localConfigDir, root: localRoot } = runMinimalInstall({ runtime: 'claude', scope: 'local' });
67
+ let localActual;
68
+ try {
69
+ localActual = buildInstallTree(localConfigDir, localRoot);
70
+ } finally {
71
+ cleanup(localRoot);
72
+ }
73
+ const localFixturePath = path.join(FIXTURE_DIR, 'claude-local.json');
74
+ fs.writeFileSync(localFixturePath, JSON.stringify(localActual, null, 2) + '\n', 'utf8');
75
+ process.stdout.write(`[gen] claude-local: wrote ${localActual.length} paths -> ${localFixturePath}\n`);
@@ -99,7 +99,6 @@
99
99
  "tests/mcp-tool-inheritance.test.cjs :: source-text-is-the-product",
100
100
  "tests/milestone-summary.test.cjs :: source-text-is-the-product",
101
101
  "tests/milestone.test.cjs :: source-text-is-the-product",
102
- "tests/milestone.test.cjs :: structural-regression-guard",
103
102
  "tests/model-catalog-runtime-defaults.test.cjs :: source-text-is-the-product",
104
103
  "tests/next-safety-gates.test.cjs :: source-text-is-the-product",
105
104
  "tests/next-up-clear-order.test.cjs :: source-text-is-the-product",
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Table-schema drift lint (ADR-2143 §3 Generative-Fix-Divergence guard, §7
6
+ * "prohibition with teeth", epic #2143).
7
+ *
8
+ * `TABLE_SCHEMAS` (`src/markdown-table.cts`, compiled to
9
+ * `gsd-core/bin/lib/markdown-table.cjs`) is the single-source registry naming
10
+ * every canonical GSD pipe-table's column-header shape. Each registered
11
+ * variant's exact `| col | col |` header string MUST appear verbatim in the
12
+ * ONE template/workflow file that emits that table — otherwise the writer and
13
+ * the registry have silently drifted apart, the exact bug class this ADR
14
+ * closes (#2137 — reader hard-codes a shape the writer doesn't emit; #2133 —
15
+ * the `quick.md` writer and its shell guard disagree on column count; #2119 —
16
+ * dual SECURITY.md writers with conflicting shapes).
17
+ *
18
+ * Modeled on scripts/lint-phase-id-drift.cjs / scripts/lint-package-identity-drift.cjs:
19
+ * a standalone node script (not a node:test), wired into `lint:ci`, exit 0 clean /
20
+ * exit 1 + message on drift.
21
+ */
22
+
23
+ const fs = require('node:fs');
24
+ const path = require('node:path');
25
+
26
+ // Schema id -> the ONE canonical template/workflow file that must emit every
27
+ // variant's header verbatim (ADR-2143 §3).
28
+ const SCHEMA_SOURCE_FILES = {
29
+ RoadmapProgress: path.join('gsd-core', 'templates', 'roadmap.md'),
30
+ RequirementsTraceability: path.join('gsd-core', 'templates', 'requirements.md'),
31
+ QuickTasks: path.join('gsd-core', 'workflows', 'quick.md'),
32
+ Security: path.join('gsd-core', 'templates', 'SECURITY.md'),
33
+ };
34
+
35
+ /** Build the exact `| a | b | c |` header line for one schema variant. */
36
+ function buildHeader(variant) {
37
+ return `| ${variant.columns.join(' | ')} |`;
38
+ }
39
+
40
+ /** Normalize whitespace around pipes so template formatting quirks (extra
41
+ * spaces added by an editor, etc.) don't produce a false drift report. */
42
+ function normalize(line) {
43
+ return line.replace(/[ \t]*\|[ \t]*/g, '|').trim();
44
+ }
45
+
46
+ /**
47
+ * Pure: given `schemas` (a `TABLE_SCHEMAS`-shaped object) and a
48
+ * `readFile(relPath) -> string|null` accessor, find every variant whose exact
49
+ * header does not appear verbatim (pipe-whitespace normalized) in its
50
+ * schema's registered source file. Returns
51
+ * `[{ schemaId, label, header, file, reason }]`; empty when clean.
52
+ */
53
+ function findTableSchemaDrift(schemas, readFile, sourceFiles = SCHEMA_SOURCE_FILES) {
54
+ const violations = [];
55
+ for (const [schemaId, variants] of Object.entries(schemas)) {
56
+ const relPath = sourceFiles[schemaId];
57
+ if (!relPath) {
58
+ violations.push({
59
+ schemaId,
60
+ label: null,
61
+ header: null,
62
+ file: null,
63
+ reason: 'no canonical source file registered for this schema id in SCHEMA_SOURCE_FILES',
64
+ });
65
+ continue;
66
+ }
67
+
68
+ const content = readFile(relPath);
69
+ if (content == null) {
70
+ for (const variant of variants) {
71
+ violations.push({
72
+ schemaId,
73
+ label: variant.label,
74
+ header: buildHeader(variant),
75
+ file: relPath,
76
+ reason: 'source file not found or unreadable',
77
+ });
78
+ }
79
+ continue;
80
+ }
81
+
82
+ const normalizedLines = content.split(/\r?\n/).map(normalize);
83
+ for (const variant of variants) {
84
+ const expected = normalize(buildHeader(variant));
85
+ if (!normalizedLines.includes(expected)) {
86
+ violations.push({
87
+ schemaId,
88
+ label: variant.label,
89
+ header: buildHeader(variant),
90
+ file: relPath,
91
+ reason: 'header not found verbatim in source file',
92
+ });
93
+ }
94
+ }
95
+ }
96
+ return violations;
97
+ }
98
+
99
+ /**
100
+ * Load the built seam and scan the real repo tree. Returns the same shape as
101
+ * `findTableSchemaDrift`. If the seam hasn't been built yet (`npm run
102
+ * build:lib`), reports a single actionable violation rather than throwing.
103
+ */
104
+ function scanRepo(root) {
105
+ const seamPath = path.join(root, 'gsd-core', 'bin', 'lib', 'markdown-table.cjs');
106
+ let seam;
107
+ try {
108
+ seam = require(seamPath);
109
+ } catch (e) {
110
+ return [{
111
+ schemaId: null,
112
+ label: null,
113
+ header: null,
114
+ file: null,
115
+ reason: `cannot load the markdown-table seam at ${path.relative(root, seamPath)} — run 'npm run build:lib' first (${e.message})`,
116
+ }];
117
+ }
118
+
119
+ const readFile = (relPath) => {
120
+ try {
121
+ return fs.readFileSync(path.join(root, relPath), 'utf8');
122
+ } catch {
123
+ return null;
124
+ }
125
+ };
126
+
127
+ return findTableSchemaDrift(seam.TABLE_SCHEMAS, readFile);
128
+ }
129
+
130
+ function main() {
131
+ const root = path.join(__dirname, '..');
132
+ const violations = scanRepo(root);
133
+ if (violations.length === 0) {
134
+ process.stdout.write(
135
+ 'ok table-schema-drift: every TABLE_SCHEMAS variant header appears verbatim in its canonical template/workflow\n',
136
+ );
137
+ return;
138
+ }
139
+ process.stderr.write(
140
+ 'table-schema-drift: TABLE_SCHEMAS variant(s) whose header is absent from their canonical source file (ADR-2143 §3).\n',
141
+ );
142
+ process.stderr.write(
143
+ 'Either update the template/workflow to emit the registered header verbatim, or update\n' +
144
+ 'TABLE_SCHEMAS in src/markdown-table.cts to match — the two must never drift.\n',
145
+ );
146
+ for (const v of violations) {
147
+ const id = v.label ? `${v.schemaId}.${v.label}` : (v.schemaId ?? '?');
148
+ const loc = v.file ? ` (${v.file})` : '';
149
+ const expected = v.header ? ` — expected ${JSON.stringify(v.header)}` : '';
150
+ process.stderr.write(` ${id}${loc}: ${v.reason}${expected}\n`);
151
+ }
152
+ process.exitCode = 1;
153
+ }
154
+
155
+ if (require.main === module) main();
156
+
157
+ module.exports = { findTableSchemaDrift, scanRepo, buildHeader, normalize, SCHEMA_SOURCE_FILES };
@@ -55,7 +55,33 @@ This step is `onError: skip` at `discuss:post` / `plan:post` / `verify:post` --
55
55
  On any error or timeout, stop and let the phase continue -- capture is best-effort.
56
56
 
57
57
  1. **Dedup first.** Interactive: `mempalace_check_duplicate` on the artifact's deterministic drawer id. Headless: rely on `mempalace mine`'s content-hash idempotency.
58
- 2. **Add the drawer (verbatim).** File the exact artifact text into `room: <room>` of `wing: <wing>` with provenance (`source_file`, phase id). Interactive: `mempalace_add_drawer`. Headless: `mempalace mine <path> --wing <wing> --room <room>`.
58
+ 2. **Add the drawer (verbatim).** File the exact artifact text into `room: <room>` of `wing: <wing>` with provenance (`source_file`, phase id). Interactive: `mempalace_add_drawer`. Headless: see below.
59
+
60
+ **`mempalace mine` has no `--room` flag** — only `search` accepts `--room` ([CLI reference: https://mempalaceofficial.com/reference/cli.html](https://mempalaceofficial.com/reference/cli.html)). Room assignment is driven by `detect_room()` matching folder-path segments against the `rooms:` list in `mempalace.yaml` ([mining guide: https://mempalaceofficial.com/guide/mining.html](https://mempalaceofficial.com/guide/mining.html) — "Rooms are auto-detected from your folder structure"; [config reference: https://mempalaceofficial.com/guide/configuration.html](https://mempalaceofficial.com/guide/configuration.html)). Stage the artifact under a room-named folder so `detect_room()` assigns it correctly:
61
+
62
+ ```bash
63
+ STAGE=".planning/.mempalace-stage"
64
+ # One-time: declare the GSD room taxonomy so detect_room() recognizes these folders
65
+ mkdir -p "$STAGE"
66
+ [ -f "$STAGE/mempalace.yaml" ] || cat > "$STAGE/mempalace.yaml" <<'YAML'
67
+ rooms:
68
+ - decisions
69
+ - planning
70
+ - milestones
71
+ - problems
72
+ - general
73
+ YAML
74
+ # Suppress MemPalace cache artifacts written into the scanned tree
75
+ [ -f "$STAGE/.gitignore" ] || echo "mempalace_embedder.json" > "$STAGE/.gitignore"
76
+ # Stage under <room>/<phase-id>/<basename> — stable path so mine's content-hash
77
+ # idempotency (file_already_mined keys on absolute source_file + mtime) deduplicates
78
+ # instead of creating duplicate drawers on re-runs.
79
+ ROOM_DIR="$STAGE/<room>/<phase-id>"
80
+ mkdir -p "$ROOM_DIR"
81
+ cp "<artifact-path>" "$ROOM_DIR/<basename>"
82
+ # Mine with --wing only — no --room flag; detect_room() assigns from the folder path
83
+ mempalace mine "$STAGE" --wing <wing>
84
+ ```
59
85
  3. **Mirror KG facts** when `config.mempalace.mirror_kg` is true: extract decision/delivery facts and `mempalace_kg_add` them with `valid_from` = the phase date (e.g. `(<project>, decided, <decision>)` from CONTEXT; `(<phase>, delivered, <capability>)` from SUMMARY). Under `augment` these are an *additive* mirror of GSD's native `.planning/graphs/`. Under `kg_backend`/`replace` the palace KG is the *authoritative* fact store — GSD still produces `.planning/graphs/` through its normal graphify, so an unreachable palace never loses a fact.
60
86
  4. Re-running a phase MUST NOT create duplicate drawers (deterministic ids + `check_duplicate`).
61
87
 
@@ -37,9 +37,9 @@ Parse the first token of $ARGUMENTS:
37
37
  ## list / status
38
38
 
39
39
  Load the capability registry and call `listSurface(runtimeConfigDir, manifest, CLUSTERS, registry)` from
40
- `gsd-core/bin/lib/surface.cjs`. The registry is loaded via:
40
+ the engine module at `${runtimeConfigDir}/gsd-core/bin/lib/surface.cjs`. The registry is loaded via:
41
41
  ```js
42
- const registry = require('gsd-core/bin/lib/capability-registry.cjs');
42
+ const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs');
43
43
  ```
44
44
  Display:
45
45
 
@@ -71,7 +71,7 @@ Install profile: standard (from .gsd-profile)
71
71
  3. `writeSurface(runtimeConfigDir, surfaceState)`.
72
72
  4. Resolve and re-apply:
73
73
  ```js
74
- const registry = require('gsd-core/bin/lib/capability-registry.cjs');
74
+ const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs');
75
75
  const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope);
76
76
  applySurface(runtimeConfigDir, layout, manifest, CLUSTERS, registry);
77
77
  ```
@@ -89,7 +89,7 @@ Valid cluster names: `core_loop`, `audit_review`, `milestone`, `research_ideate`
89
89
  3. Add cluster to `surfaceState.disabledClusters` (deduplicate).
90
90
  4. `writeSurface` → resolve layout → `applySurface`:
91
91
  ```js
92
- const registry = require('gsd-core/bin/lib/capability-registry.cjs');
92
+ const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs');
93
93
  const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope);
94
94
  applySurface(runtimeConfigDir, layout, manifest, CLUSTERS, registry);
95
95
  ```
@@ -103,7 +103,7 @@ Valid cluster names: `core_loop`, `audit_review`, `milestone`, `research_ideate`
103
103
  2. Remove cluster from `surfaceState.disabledClusters`.
104
104
  3. `writeSurface` → resolve layout → `applySurface`:
105
105
  ```js
106
- const registry = require('gsd-core/bin/lib/capability-registry.cjs');
106
+ const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs');
107
107
  const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope);
108
108
  applySurface(runtimeConfigDir, layout, manifest, CLUSTERS, registry);
109
109
  ```
@@ -151,7 +151,7 @@ All paths can be overridden by reading the `CLAUDE_CONFIG_DIR` env var if set.
151
151
 
152
152
  - Unknown cluster name → list valid cluster names, exit without writing.
153
153
  - Unknown profile name → list known profiles (`core`, `standard`, `full`), exit.
154
- - Missing `surface.cjs` → prompt: "Run `npm i -g gsd-core` to reinstall GSD."
154
+ - Missing `surface.cjs` → prompt: "Run `npm i -g @opengsd/gsd-core` to reinstall GSD."
155
155
 
156
156
  <execution_context>
157
157
  Surface state file: `~/.claude/.gsd-surface.json`
@@ -2,7 +2,7 @@
2
2
  "name": "gsd-core-vscode",
3
3
  "displayName": "GSD Core",
4
4
  "description": "GSD orchestration engine embedded in VS Code (ADR-1239 IDE profile).",
5
- "version": "1.7.0-rc.6",
5
+ "version": "1.7.0",
6
6
  "publisher": "opengsd",
7
7
  "engines": {
8
8
  "vscode": "^1.105.0"