@skillcap/gdh 0.23.0 → 0.24.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 (128) hide show
  1. package/INSTALL-BUNDLE.json +1 -1
  2. package/RELEASE-SPAN-UPDATE-CONTRACTS.json +64 -0
  3. package/node_modules/@gdh/adapters/dist/authoring-hook-render.d.ts.map +1 -1
  4. package/node_modules/@gdh/adapters/dist/authoring-hook-render.js +2 -1
  5. package/node_modules/@gdh/adapters/dist/authoring-hook-render.js.map +1 -1
  6. package/node_modules/@gdh/adapters/dist/claude-statusline-render.d.ts.map +1 -1
  7. package/node_modules/@gdh/adapters/dist/claude-statusline-render.js +2 -1
  8. package/node_modules/@gdh/adapters/dist/claude-statusline-render.js.map +1 -1
  9. package/node_modules/@gdh/adapters/dist/claude-update-hook-render.d.ts.map +1 -1
  10. package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js +2 -1
  11. package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js.map +1 -1
  12. package/node_modules/@gdh/adapters/dist/claude-update-worker-render.d.ts.map +1 -1
  13. package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js +2 -1
  14. package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js.map +1 -1
  15. package/node_modules/@gdh/adapters/dist/deferred-actions-advisory.d.ts +71 -0
  16. package/node_modules/@gdh/adapters/dist/deferred-actions-advisory.d.ts.map +1 -0
  17. package/node_modules/@gdh/adapters/dist/deferred-actions-advisory.js +89 -0
  18. package/node_modules/@gdh/adapters/dist/deferred-actions-advisory.js.map +1 -0
  19. package/node_modules/@gdh/adapters/dist/durable-backup.d.ts +209 -0
  20. package/node_modules/@gdh/adapters/dist/durable-backup.d.ts.map +1 -0
  21. package/node_modules/@gdh/adapters/dist/durable-backup.js +346 -0
  22. package/node_modules/@gdh/adapters/dist/durable-backup.js.map +1 -0
  23. package/node_modules/@gdh/adapters/dist/index.d.ts +10 -1
  24. package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
  25. package/node_modules/@gdh/adapters/dist/index.js +24 -2
  26. package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
  27. package/node_modules/@gdh/adapters/dist/inventory-sweep.d.ts +53 -0
  28. package/node_modules/@gdh/adapters/dist/inventory-sweep.d.ts.map +1 -0
  29. package/node_modules/@gdh/adapters/dist/inventory-sweep.js +98 -0
  30. package/node_modules/@gdh/adapters/dist/inventory-sweep.js.map +1 -0
  31. package/node_modules/@gdh/adapters/dist/process-orchestration.d.ts +223 -0
  32. package/node_modules/@gdh/adapters/dist/process-orchestration.d.ts.map +1 -0
  33. package/node_modules/@gdh/adapters/dist/process-orchestration.js +368 -0
  34. package/node_modules/@gdh/adapters/dist/process-orchestration.js.map +1 -0
  35. package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts +157 -14
  36. package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts.map +1 -1
  37. package/node_modules/@gdh/adapters/dist/self-update-mechanics.js +570 -89
  38. package/node_modules/@gdh/adapters/dist/self-update-mechanics.js.map +1 -1
  39. package/node_modules/@gdh/adapters/dist/skill-rendering.d.ts.map +1 -1
  40. package/node_modules/@gdh/adapters/dist/skill-rendering.js +25 -0
  41. package/node_modules/@gdh/adapters/dist/skill-rendering.js.map +1 -1
  42. package/node_modules/@gdh/adapters/package.json +8 -8
  43. package/node_modules/@gdh/authoring/package.json +2 -2
  44. package/node_modules/@gdh/cli/dist/index.d.ts +9 -0
  45. package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
  46. package/node_modules/@gdh/cli/dist/index.js +244 -2
  47. package/node_modules/@gdh/cli/dist/index.js.map +1 -1
  48. package/node_modules/@gdh/cli/dist/migrate.d.ts +152 -1
  49. package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
  50. package/node_modules/@gdh/cli/dist/migrate.js +290 -6
  51. package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
  52. package/node_modules/@gdh/cli/dist/self-update.d.ts +14 -0
  53. package/node_modules/@gdh/cli/dist/self-update.d.ts.map +1 -1
  54. package/node_modules/@gdh/cli/dist/self-update.js +197 -15
  55. package/node_modules/@gdh/cli/dist/self-update.js.map +1 -1
  56. package/node_modules/@gdh/cli/package.json +10 -10
  57. package/node_modules/@gdh/core/dist/index.d.ts +97 -5
  58. package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
  59. package/node_modules/@gdh/core/dist/index.js +23 -5
  60. package/node_modules/@gdh/core/dist/index.js.map +1 -1
  61. package/node_modules/@gdh/core/dist/migrations/entries/s2c2_to_s2c3_rules_schema_v2_to_v3.d.ts +3 -0
  62. package/node_modules/@gdh/core/dist/migrations/entries/s2c2_to_s2c3_rules_schema_v2_to_v3.d.ts.map +1 -0
  63. package/node_modules/@gdh/core/dist/migrations/entries/s2c2_to_s2c3_rules_schema_v2_to_v3.js +247 -0
  64. package/node_modules/@gdh/core/dist/migrations/entries/s2c2_to_s2c3_rules_schema_v2_to_v3.js.map +1 -0
  65. package/node_modules/@gdh/core/dist/migrations/entries/s3c8_to_s3c9_register_runtime_bridge_autoload.d.ts +3 -0
  66. package/node_modules/@gdh/core/dist/migrations/entries/s3c8_to_s3c9_register_runtime_bridge_autoload.d.ts.map +1 -0
  67. package/node_modules/@gdh/core/dist/migrations/entries/s3c8_to_s3c9_register_runtime_bridge_autoload.js +152 -0
  68. package/node_modules/@gdh/core/dist/migrations/entries/s3c8_to_s3c9_register_runtime_bridge_autoload.js.map +1 -0
  69. package/node_modules/@gdh/core/dist/migrations/envelopes/envelope-output-validator.d.ts +3 -0
  70. package/node_modules/@gdh/core/dist/migrations/envelopes/envelope-output-validator.d.ts.map +1 -0
  71. package/node_modules/@gdh/core/dist/migrations/envelopes/envelope-output-validator.js +67 -0
  72. package/node_modules/@gdh/core/dist/migrations/envelopes/envelope-output-validator.js.map +1 -0
  73. package/node_modules/@gdh/core/dist/migrations/envelopes/index.d.ts +37 -0
  74. package/node_modules/@gdh/core/dist/migrations/envelopes/index.d.ts.map +1 -0
  75. package/node_modules/@gdh/core/dist/migrations/envelopes/index.js +60 -0
  76. package/node_modules/@gdh/core/dist/migrations/envelopes/index.js.map +1 -0
  77. package/node_modules/@gdh/core/dist/migrations/envelopes/types.d.ts +121 -0
  78. package/node_modules/@gdh/core/dist/migrations/envelopes/types.d.ts.map +1 -0
  79. package/node_modules/@gdh/core/dist/migrations/envelopes/types.js +2 -0
  80. package/node_modules/@gdh/core/dist/migrations/envelopes/types.js.map +1 -0
  81. package/node_modules/@gdh/core/dist/migrations/golden-harness.d.ts +40 -0
  82. package/node_modules/@gdh/core/dist/migrations/golden-harness.d.ts.map +1 -0
  83. package/node_modules/@gdh/core/dist/migrations/golden-harness.js +71 -0
  84. package/node_modules/@gdh/core/dist/migrations/golden-harness.js.map +1 -0
  85. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts +322 -0
  86. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts.map +1 -0
  87. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js +384 -0
  88. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js.map +1 -0
  89. package/node_modules/@gdh/core/dist/migrations/probes.d.ts +58 -0
  90. package/node_modules/@gdh/core/dist/migrations/probes.d.ts.map +1 -0
  91. package/node_modules/@gdh/core/dist/migrations/probes.js +112 -0
  92. package/node_modules/@gdh/core/dist/migrations/probes.js.map +1 -0
  93. package/node_modules/@gdh/core/dist/migrations/registry.d.ts +205 -0
  94. package/node_modules/@gdh/core/dist/migrations/registry.d.ts.map +1 -0
  95. package/node_modules/@gdh/core/dist/migrations/registry.js +214 -0
  96. package/node_modules/@gdh/core/dist/migrations/registry.js.map +1 -0
  97. package/node_modules/@gdh/core/dist/state/atomic-write.d.ts +19 -0
  98. package/node_modules/@gdh/core/dist/state/atomic-write.d.ts.map +1 -0
  99. package/node_modules/@gdh/core/dist/state/atomic-write.js +34 -0
  100. package/node_modules/@gdh/core/dist/state/atomic-write.js.map +1 -0
  101. package/node_modules/@gdh/core/dist/state/migration-state.d.ts +135 -0
  102. package/node_modules/@gdh/core/dist/state/migration-state.d.ts.map +1 -0
  103. package/node_modules/@gdh/core/dist/state/migration-state.js +186 -0
  104. package/node_modules/@gdh/core/dist/state/migration-state.js.map +1 -0
  105. package/node_modules/@gdh/core/dist/state/processes-snapshot.d.ts +72 -0
  106. package/node_modules/@gdh/core/dist/state/processes-snapshot.d.ts.map +1 -0
  107. package/node_modules/@gdh/core/dist/state/processes-snapshot.js +113 -0
  108. package/node_modules/@gdh/core/dist/state/processes-snapshot.js.map +1 -0
  109. package/node_modules/@gdh/core/dist/state/render-inventory.d.ts +54 -0
  110. package/node_modules/@gdh/core/dist/state/render-inventory.d.ts.map +1 -0
  111. package/node_modules/@gdh/core/dist/state/render-inventory.js +77 -0
  112. package/node_modules/@gdh/core/dist/state/render-inventory.js.map +1 -0
  113. package/node_modules/@gdh/core/package.json +1 -1
  114. package/node_modules/@gdh/docs/dist/agent-contract.d.ts.map +1 -1
  115. package/node_modules/@gdh/docs/dist/agent-contract.js +2 -1
  116. package/node_modules/@gdh/docs/dist/agent-contract.js.map +1 -1
  117. package/node_modules/@gdh/docs/dist/guidance.d.ts.map +1 -1
  118. package/node_modules/@gdh/docs/dist/guidance.js +3 -1
  119. package/node_modules/@gdh/docs/dist/guidance.js.map +1 -1
  120. package/node_modules/@gdh/docs/package.json +2 -2
  121. package/node_modules/@gdh/mcp/package.json +8 -8
  122. package/node_modules/@gdh/observability/package.json +2 -2
  123. package/node_modules/@gdh/runtime/dist/bridge-surface.js +63 -2
  124. package/node_modules/@gdh/runtime/dist/bridge-surface.js.map +1 -1
  125. package/node_modules/@gdh/runtime/package.json +2 -2
  126. package/node_modules/@gdh/scan/package.json +3 -3
  127. package/node_modules/@gdh/verify/package.json +7 -7
  128. package/package.json +11 -11
@@ -1,4 +1,4 @@
1
- import type { GdhLifecycleTerminalResult, GdhManagedProjectSurfaceId, GdhProjectLifecycleCompatibilityResult } from "@gdh/core";
1
+ import type { GdhLifecycleTerminalResult, GdhManagedProjectSurfaceId, GdhProjectLifecycleCompatibilityResult, GdhHostHarnessIntrospect, GdhProcessRestartProbe, GdhMigrationEnvelopeOutput } from "@gdh/core";
2
2
  export declare const GDH_PROJECT_SURFACE_MIGRATION_ID = "project_surface_lifecycle_v1";
3
3
  type MigrationActionKind = "rewrite_file" | "rename_directory" | "delete_file" | "validate_surface" | "run_command" | "manual_review";
4
4
  type MigrationActionState = "planned" | "applied" | "unchanged" | "blocked";
@@ -62,5 +62,156 @@ export declare function deriveLifecycleTerminalResult(input: {
62
62
  readonly state: MigrationRunState;
63
63
  }): GdhLifecycleTerminalResult;
64
64
  export declare function rewriteProjectConfig(content: string): string;
65
+ /**
66
+ * Discriminated result of {@link clearMigrationBackups} (D-17 / STA-04).
67
+ *
68
+ * - `cleared`: every probe was clean, the staging dir was absent, and
69
+ * `.gdh-state/backup/` was removed from disk. Idempotent retry returns `noop`.
70
+ * - `noop`: nothing to do — either no migration state and no backup
71
+ * (`no_migration_state`), or a migration state exists but no backup is
72
+ * present (`no_backup_present`). Both safe to ignore.
73
+ * - `blocked_by_pending_probes`: cleanup refused. Either at least one probed
74
+ * deferred action returned `pending` or `stale` (`deferred_actions_pending`,
75
+ * `pending` carries the action ids), or `.gdh-state/backup.new/` exists
76
+ * indicating an interrupted migration (`migration_in_progress`, `pending`
77
+ * is empty).
78
+ */
79
+ export type GdhClearBackupsResult = {
80
+ readonly state: "cleared";
81
+ readonly cleared_backup_path: string;
82
+ } | {
83
+ readonly state: "noop";
84
+ readonly reason: "no_backup_present" | "no_migration_state";
85
+ } | {
86
+ readonly state: "blocked_by_pending_probes";
87
+ readonly pending: readonly string[];
88
+ readonly reason: "deferred_actions_pending" | "migration_in_progress";
89
+ };
90
+ export interface GdhClearMigrationBackupsInput {
91
+ readonly targetPath: string;
92
+ readonly adapters?: {
93
+ readonly hostHarnessIntrospect?: GdhHostHarnessIntrospect;
94
+ readonly processProbe?: GdhProcessRestartProbe;
95
+ };
96
+ }
97
+ /**
98
+ * Idempotent backup-cleanup command (D-17 / STA-04).
99
+ *
100
+ * Three-gate truth table:
101
+ * 1. Refuse if `.gdh-state/backup.new/` exists (interrupted migration:
102
+ * `migration_in_progress`).
103
+ * 2. Noop if neither backup nor migration state exist (`no_migration_state`),
104
+ * or if no backup but migration state exists (`no_backup_present`).
105
+ * 3. For each deferred action, run the probe; if any probe is not `clean`,
106
+ * refuse with `deferred_actions_pending` and the pending action ids.
107
+ * 4. Otherwise remove `.gdh-state/backup/` recursively and return `cleared`.
108
+ *
109
+ * The migration.json file is NOT modified. Deferred actions are independent of
110
+ * the backup directory; they may have been satisfied previously.
111
+ *
112
+ * Threat mitigations:
113
+ * - T-72-05-03 (TOCTOU): re-reads migration.json AND backup state freshly per
114
+ * call; no cached probe results consumed across calls. Same handler used by
115
+ * the advisory gate in `buildGdhStatusResult` (Plan 05 Task 03).
116
+ * - T-72-05-05 (silent deletion under pending probes): probe-gate is mandatory
117
+ * and `state: "blocked_by_pending_probes"` is exit code 1 with structured
118
+ * `pending` ids; idempotent retry is safe.
119
+ * - T-72-05-07 (clear during interrupted migration): staging-dir check at
120
+ * step 1 returns `migration_in_progress` and refuses cleanup.
121
+ *
122
+ * Defaults: `adapters.hostHarnessIntrospect = null` and `adapters.processProbe
123
+ * = null` so probes degrade to `pending` when the host cannot evaluate the
124
+ * deferred conditions; cleanup then refuses safely instead of spuriously
125
+ * clearing.
126
+ */
127
+ export declare function clearMigrationBackups(input: GdhClearMigrationBackupsInput): Promise<GdhClearBackupsResult>;
128
+ /**
129
+ * Discriminated result of {@link recordEnvelopeResult}.
130
+ *
131
+ * - `recorded`: payload validated, marker triple matched, slot written
132
+ * atomically. `pending_envelope_resume` is preserved by D-13 — the calling
133
+ * agent re-runs `gdh self-update` to advance the chain past the envelope.
134
+ * - `blocked`: validation gate failed. Each `reason` corresponds to a typed
135
+ * failure mode the dispatcher surfaces with exit code 1 (Pitfall 4 — never
136
+ * let JSON.parse / readFile / schema-mismatch escape as raw exceptions).
137
+ *
138
+ * The seven blocked reasons enumerated cover every gate executed by
139
+ * `recordEnvelopeResult`. `input_required` is reserved for the dispatcher's
140
+ * usage-error surface (mutually-exclusive --result / --result-file gate).
141
+ */
142
+ export type GdhRecordEnvelopeResultResult = {
143
+ readonly state: "recorded";
144
+ readonly envelope_ref: string;
145
+ readonly recorded_at: string;
146
+ readonly from_pin: string;
147
+ readonly to_pin: string;
148
+ readonly output_contract_state: GdhMigrationEnvelopeOutput["state"];
149
+ } | {
150
+ readonly state: "blocked";
151
+ readonly reason: "envelope_not_found" | "no_pending_chain" | "envelope_ref_mismatch" | "parse_failed" | "schema_violation" | "result_file_unreadable" | "input_required";
152
+ readonly details: string;
153
+ };
154
+ export interface GdhRecordEnvelopeResultInput {
155
+ readonly targetPath: string;
156
+ readonly envelopeRef: string;
157
+ readonly resultSource: {
158
+ readonly kind: "inline";
159
+ readonly json: string;
160
+ } | {
161
+ readonly kind: "file";
162
+ readonly absolutePath: string;
163
+ };
164
+ /** Deterministic clock injection for tests. */
165
+ readonly nowIso?: () => string;
166
+ }
167
+ /**
168
+ * Record an envelope output_contract block to `.gdh-state/migration.json` per
169
+ * D-07 / D-08 / D-13.
170
+ *
171
+ * Gate sequence (fail-closed, no exception propagates to caller):
172
+ * 1. Slug shape (path-traversal defense via {@link isEnvelopeSlug}).
173
+ * 2. Read --result-file (or use --result inline).
174
+ * 3. JSON.parse (Pitfall 4 — caught and surfaced as `parse_failed`).
175
+ * 4. Schema validation against {@link isEnvelopeOutput} typed union.
176
+ * 5. Read migration.json; require `pending_envelope_resume` marker
177
+ * (D-09 / D-11 / D-13 — chain must be paused before record).
178
+ * 6. Triple match: marker.envelope_ref === input.envelopeRef.
179
+ * 7. Append prior slot to `attempts[]` (Pitfall 2 — outer fields become a
180
+ * new attempt; existing attempts[] history accumulates).
181
+ * 8. Atomic write through {@link writeMigrationState}; D-13 preserves
182
+ * `pending_envelope_resume` (record does NOT clear it).
183
+ *
184
+ * The function NEVER calls `bumpAndRebakePin` and NEVER advances the chain
185
+ * (D-13 lock). The agent re-runs `gdh self-update` to resume.
186
+ */
187
+ export declare function recordEnvelopeResult(input: GdhRecordEnvelopeResultInput): Promise<GdhRecordEnvelopeResultResult>;
188
+ /**
189
+ * Discriminated result of {@link clearEnvelope}.
190
+ *
191
+ * - `cleared`: the per-slug slot was removed from `migration.json.envelopes`.
192
+ * `pending_envelope_resume` is NOT touched (mirrors D-13 isolation).
193
+ * - `noop` with `slot_not_present`: state exists but no slot for this slug.
194
+ * - `noop` with `no_migration_state`: no migration.json on disk.
195
+ *
196
+ * Idempotent — re-running on a missing slot returns `noop`, not `blocked`.
197
+ */
198
+ export type GdhClearEnvelopeResult = {
199
+ readonly state: "cleared";
200
+ readonly envelope_ref: string;
201
+ } | {
202
+ readonly state: "noop";
203
+ readonly reason: "slot_not_present" | "no_migration_state";
204
+ };
205
+ export interface GdhClearEnvelopeInput {
206
+ readonly targetPath: string;
207
+ readonly envelopeRef: string;
208
+ }
209
+ /**
210
+ * Remove the per-slug slot from `migration.json.envelopes`. Testing/debug
211
+ * companion to {@link recordEnvelopeResult}; does NOT touch
212
+ * `pending_envelope_resume` (deliberate isolation — clearing the slot does
213
+ * not unpause a chain).
214
+ */
215
+ export declare function clearEnvelope(input: GdhClearEnvelopeInput): Promise<GdhClearEnvelopeResult>;
65
216
  export {};
66
217
  //# sourceMappingURL=migrate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,9 @@ import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import * as gdhCore from "@gdh/core";
4
4
  import { readProjectConfig, resolveProjectRoot } from "@gdh/authoring";
5
- import { inspectProjectLifecycleCompatibility, installSupportedAgentAdapters, } from "@gdh/adapters";
5
+ import { BACKUP_RELATIVE_PATH, BACKUP_STAGING_RELATIVE_PATH, inspectProjectLifecycleCompatibility, installSupportedAgentAdapters, readBackupManifest, } from "@gdh/adapters";
6
6
  import { planAgentContractWrite, planGuidanceArtifactWrites, planRulesArtifactWrite } from "@gdh/docs";
7
+ import { isEnvelopeOutput, isEnvelopeSlug, readMigrationState, runDeferredActionProbe, writeMigrationState, } from "@gdh/core";
7
8
  import { inspectRuntimeBridgeSurface, repairRuntimeBridgeSurface, } from "@gdh/runtime";
8
9
  import { applyRepairableOnboardingWrites } from "@gdh/scan";
9
10
  export const GDH_PROJECT_SURFACE_MIGRATION_ID = "project_surface_lifecycle_v1";
@@ -117,11 +118,19 @@ export async function migrateProjectLifecycleSurface(input) {
117
118
  if (launcherAction !== null) {
118
119
  actions.push(launcherAction);
119
120
  }
120
- const runtimeTerminology = await migrateRuntimeTerminologySurface({
121
- targetPath,
122
- dryRun: input.dryRun,
123
- });
124
- actions.push(...translateRuntimeTerminologyActions(runtimeTerminology.actions));
121
+ // Phase 72 Plan 02: the schema/contract orchestration that
122
+ // migrateRuntimeTerminologySurface drove (recipes→run-configurations
123
+ // rename, scenarios→verification-scenarios rename, project.yaml
124
+ // schema-2→schema-3 rewrite, legacy project_entries.gd copy) is dead
125
+ // code in the v0.18+ supported range. Per the audit at
126
+ // .planning/phases/72-migration-workflow-internals-registry-inventory-durable-back/72-02-AUDIT.md,
127
+ // the only real class-2/3 migration in the supported range is the
128
+ // s2c2→s2c3 rules-schema bump, now owned by the registry entry
129
+ // entry_s2c2_to_s2c3_rules_schema in @gdh/core. Plan 03's inventory
130
+ // sweep deletes the unreachable sub-functions and their pre-v0.18
131
+ // regression tests atomically. WFL-03 (D-20/D-21) makes pre-v0.18
132
+ // pins unreachable for self-update consumers; the schema/contract
133
+ // rewrite paths are no longer driven by any post-v0.18 invocation.
125
134
  let projectConfig = await readProjectConfig(targetPath);
126
135
  if (projectConfig !== null) {
127
136
  const repairableWrites = await planRepairableProjectSurfaceWrites(targetPath, projectConfig, compatibility);
@@ -594,6 +603,28 @@ async function migrateLegacyLauncher(input) {
594
603
  manualSteps: [],
595
604
  };
596
605
  }
606
+ // TODO(72-Plan-03): unreachable from migrateProjectLifecycleSurface as of
607
+ // Phase 72 Plan 02. Preconditions (.gdh/recipes/, .gdh/scenarios/, legacy
608
+ // project_entries.gd) never match the v0.18+ supported range. Plan 03's
609
+ // inventory sweep deletes this function and its descendants
610
+ // (migrateLegacyProjectBridgeEntry,
611
+ // rewriteRunConfiguration, rewriteVerificationScenario, rewriteVersionHeader,
612
+ // extractPrimaryGodotProjectPath, ensureProjectAllowedProviders,
613
+ // isDockerWideningEligibleRunConfiguration, ensureYamlStringListValues,
614
+ // ensureRuntimeBridgeProjectEntryPath, ensureRuntimeKnowledgePath) plus the
615
+ // pre-v0.18 regression test groups in migrate.test.ts atomically. See
616
+ // .planning/phases/72-migration-workflow-internals-registry-inventory-durable-back/72-02-AUDIT.md
617
+ // "Disposition Table" for the full deletion set.
618
+ //
619
+ // WR-09: `rewriteProjectConfig` is RETAINED — do NOT delete in the Plan-03
620
+ // sweep even though it appears in the descendant chain above. It has a live
621
+ // caller at `preHealMissingGdhVersionField` (this file) AND is exported and
622
+ // imported directly by `migrate.test.ts`. The audit's deletion set was
623
+ // computed from the dead-code reachability graph rooted in
624
+ // `migrateRuntimeTerminologySurface`; the live preheal caller arrived later
625
+ // (D-06 minimal pre-heal) and was not re-checked. See the "RETAINED" guard
626
+ // comment immediately above `export function rewriteProjectConfig` for the
627
+ // in-place marker.
597
628
  async function migrateRuntimeTerminologySurface(input) {
598
629
  const targetPath = path.resolve(input.targetPath);
599
630
  const configPath = path.join(targetPath, PROJECT_CONFIG_PATH);
@@ -908,6 +939,18 @@ async function applyMinimalDryRunPreHeal(targetPath) {
908
939
  await fs.writeFile(configPath, originalContent, "utf8");
909
940
  };
910
941
  }
942
+ // WR-09: RETAINED — do NOT delete in the Plan-03 dead-code sweep. Despite
943
+ // appearing in the TODO(72-Plan-03) deletion set at line ~923 (which
944
+ // references the dead-code chain rooted in migrateRuntimeTerminologySurface),
945
+ // this function has TWO live consumers in the v0.18+ supported range:
946
+ // 1. `preHealMissingGdhVersionField` at line ~1266 (D-06 auto-heal of
947
+ // missing `gdh_version:` field on unmigrated projects).
948
+ // 2. Direct import in `packages/cli/src/migrate.test.ts` for the
949
+ // pre-v0.18 regression test groups (those groups themselves remain
950
+ // live regression coverage for the auto-heal branch).
951
+ // The Plan-03 sweep MUST distinguish this function from the genuinely dead
952
+ // helpers in the same chain. See the WR-09 note above the
953
+ // migrateRuntimeTerminologySurface TODO block.
911
954
  export function rewriteProjectConfig(content) {
912
955
  let next = rewriteVersionHeader(content, 3);
913
956
  next = next.replace(/^default_recipe:/m, "default_run_configuration:");
@@ -1098,4 +1141,245 @@ async function directoryExists(directoryPath) {
1098
1141
  return false;
1099
1142
  }
1100
1143
  }
1144
+ /**
1145
+ * Idempotent backup-cleanup command (D-17 / STA-04).
1146
+ *
1147
+ * Three-gate truth table:
1148
+ * 1. Refuse if `.gdh-state/backup.new/` exists (interrupted migration:
1149
+ * `migration_in_progress`).
1150
+ * 2. Noop if neither backup nor migration state exist (`no_migration_state`),
1151
+ * or if no backup but migration state exists (`no_backup_present`).
1152
+ * 3. For each deferred action, run the probe; if any probe is not `clean`,
1153
+ * refuse with `deferred_actions_pending` and the pending action ids.
1154
+ * 4. Otherwise remove `.gdh-state/backup/` recursively and return `cleared`.
1155
+ *
1156
+ * The migration.json file is NOT modified. Deferred actions are independent of
1157
+ * the backup directory; they may have been satisfied previously.
1158
+ *
1159
+ * Threat mitigations:
1160
+ * - T-72-05-03 (TOCTOU): re-reads migration.json AND backup state freshly per
1161
+ * call; no cached probe results consumed across calls. Same handler used by
1162
+ * the advisory gate in `buildGdhStatusResult` (Plan 05 Task 03).
1163
+ * - T-72-05-05 (silent deletion under pending probes): probe-gate is mandatory
1164
+ * and `state: "blocked_by_pending_probes"` is exit code 1 with structured
1165
+ * `pending` ids; idempotent retry is safe.
1166
+ * - T-72-05-07 (clear during interrupted migration): staging-dir check at
1167
+ * step 1 returns `migration_in_progress` and refuses cleanup.
1168
+ *
1169
+ * Defaults: `adapters.hostHarnessIntrospect = null` and `adapters.processProbe
1170
+ * = null` so probes degrade to `pending` when the host cannot evaluate the
1171
+ * deferred conditions; cleanup then refuses safely instead of spuriously
1172
+ * clearing.
1173
+ */
1174
+ export async function clearMigrationBackups(input) {
1175
+ const backupAbs = path.join(input.targetPath, BACKUP_RELATIVE_PATH);
1176
+ const stagingAbs = path.join(input.targetPath, BACKUP_STAGING_RELATIVE_PATH);
1177
+ // Gate 1: refuse during in-progress migration (D-17, T-72-05-07)
1178
+ const stagingExists = await fs
1179
+ .stat(stagingAbs)
1180
+ .then(() => true)
1181
+ .catch(() => false);
1182
+ if (stagingExists) {
1183
+ return {
1184
+ state: "blocked_by_pending_probes",
1185
+ pending: [],
1186
+ reason: "migration_in_progress",
1187
+ };
1188
+ }
1189
+ const manifest = await readBackupManifest(input.targetPath);
1190
+ const backupExists = manifest !== null;
1191
+ const migrationState = await readMigrationState(input.targetPath);
1192
+ // Gate 2: noop discriminator
1193
+ if (!backupExists && migrationState === null) {
1194
+ return { state: "noop", reason: "no_migration_state" };
1195
+ }
1196
+ if (!backupExists) {
1197
+ return { state: "noop", reason: "no_backup_present" };
1198
+ }
1199
+ // Gate 3: probe-gate (T-72-05-05)
1200
+ const pending = [];
1201
+ for (const action of migrationState?.deferred_actions ?? []) {
1202
+ const result = await runDeferredActionProbe(action, {
1203
+ targetPath: input.targetPath,
1204
+ hostHarnessIntrospect: input.adapters?.hostHarnessIntrospect ?? null,
1205
+ processProbe: input.adapters?.processProbe ?? null,
1206
+ });
1207
+ if (result.status !== "clean") {
1208
+ pending.push(action.id);
1209
+ }
1210
+ }
1211
+ if (pending.length > 0) {
1212
+ return {
1213
+ state: "blocked_by_pending_probes",
1214
+ pending,
1215
+ reason: "deferred_actions_pending",
1216
+ };
1217
+ }
1218
+ // Gate 4: clear (does NOT modify migration.json)
1219
+ await fs.rm(backupAbs, { recursive: true, force: true });
1220
+ return { state: "cleared", cleared_backup_path: backupAbs };
1221
+ }
1222
+ /**
1223
+ * Record an envelope output_contract block to `.gdh-state/migration.json` per
1224
+ * D-07 / D-08 / D-13.
1225
+ *
1226
+ * Gate sequence (fail-closed, no exception propagates to caller):
1227
+ * 1. Slug shape (path-traversal defense via {@link isEnvelopeSlug}).
1228
+ * 2. Read --result-file (or use --result inline).
1229
+ * 3. JSON.parse (Pitfall 4 — caught and surfaced as `parse_failed`).
1230
+ * 4. Schema validation against {@link isEnvelopeOutput} typed union.
1231
+ * 5. Read migration.json; require `pending_envelope_resume` marker
1232
+ * (D-09 / D-11 / D-13 — chain must be paused before record).
1233
+ * 6. Triple match: marker.envelope_ref === input.envelopeRef.
1234
+ * 7. Append prior slot to `attempts[]` (Pitfall 2 — outer fields become a
1235
+ * new attempt; existing attempts[] history accumulates).
1236
+ * 8. Atomic write through {@link writeMigrationState}; D-13 preserves
1237
+ * `pending_envelope_resume` (record does NOT clear it).
1238
+ *
1239
+ * The function NEVER calls `bumpAndRebakePin` and NEVER advances the chain
1240
+ * (D-13 lock). The agent re-runs `gdh self-update` to resume.
1241
+ */
1242
+ export async function recordEnvelopeResult(input) {
1243
+ // Gate 1: slug shape (path-traversal defense). isEnvelopeSlug rejects
1244
+ // non-string, uppercase, dashes, and traversal segments.
1245
+ if (!isEnvelopeSlug(input.envelopeRef)) {
1246
+ return {
1247
+ state: "blocked",
1248
+ reason: "envelope_not_found",
1249
+ details: `invalid slug shape: ${input.envelopeRef}`,
1250
+ };
1251
+ }
1252
+ // Gate 2: read --result-file or use --result inline.
1253
+ let rawJson;
1254
+ if (input.resultSource.kind === "file") {
1255
+ const read = await fs
1256
+ .readFile(input.resultSource.absolutePath, "utf8")
1257
+ .catch(() => null);
1258
+ if (read === null) {
1259
+ return {
1260
+ state: "blocked",
1261
+ reason: "result_file_unreadable",
1262
+ details: input.resultSource.absolutePath,
1263
+ };
1264
+ }
1265
+ rawJson = read;
1266
+ }
1267
+ else {
1268
+ rawJson = input.resultSource.json;
1269
+ }
1270
+ // Gate 3: JSON parse (Pitfall 4 — never let JSON.parse throw out of the CLI).
1271
+ let parsed;
1272
+ try {
1273
+ parsed = JSON.parse(rawJson);
1274
+ }
1275
+ catch (error) {
1276
+ return {
1277
+ state: "blocked",
1278
+ reason: "parse_failed",
1279
+ details: error instanceof Error ? error.message : String(error),
1280
+ };
1281
+ }
1282
+ // Gate 4: schema validation against typed union.
1283
+ if (!isEnvelopeOutput(parsed)) {
1284
+ const preview = JSON.stringify(parsed).slice(0, 120);
1285
+ return {
1286
+ state: "blocked",
1287
+ reason: "schema_violation",
1288
+ details: `expected GdhMigrationEnvelopeOutput, got ${preview}`,
1289
+ };
1290
+ }
1291
+ // Gate 5: read migration.json; require pending_envelope_resume marker.
1292
+ const state = await readMigrationState(input.targetPath);
1293
+ const marker = state?.pending_envelope_resume ?? null;
1294
+ if (marker === null) {
1295
+ return {
1296
+ state: "blocked",
1297
+ reason: "no_pending_chain",
1298
+ details: "no pending_envelope_resume marker in migration.json",
1299
+ };
1300
+ }
1301
+ if (marker.envelope_ref !== input.envelopeRef) {
1302
+ return {
1303
+ state: "blocked",
1304
+ // WR-02: this is a slug/marker mismatch, NOT a pin pair mismatch. The
1305
+ // pin-staleness check (D-10) lives in applyMigrationChain via
1306
+ // isEnvelopeRecordValid. Use a discriminant that names what actually
1307
+ // failed so dispatcher messages and dogfooding log scrapers don't
1308
+ // mislead operators.
1309
+ reason: "envelope_ref_mismatch",
1310
+ details: `pending_envelope_resume.envelope_ref is "${marker.envelope_ref}", not "${input.envelopeRef}"`,
1311
+ };
1312
+ }
1313
+ // Gate 6: append prior slot to attempts[] (Pitfall 2). The prior slot's OWN
1314
+ // outer fields become a new attempt; we ALSO carry forward the prior slot's
1315
+ // existing attempts[] so history accumulates across re-records.
1316
+ const priorSlot = state?.envelopes?.[input.envelopeRef] ?? null;
1317
+ const nowIso = (input.nowIso ?? (() => new Date().toISOString()))();
1318
+ const newAttempts = priorSlot
1319
+ ? [
1320
+ ...priorSlot.attempts,
1321
+ {
1322
+ recorded_at: priorSlot.recorded_at,
1323
+ from_pin: priorSlot.from_pin,
1324
+ to_pin: priorSlot.to_pin,
1325
+ output_contract: priorSlot.output_contract,
1326
+ },
1327
+ ]
1328
+ : [];
1329
+ const newSlot = {
1330
+ recorded_at: nowIso,
1331
+ from_pin: marker.from_pin,
1332
+ to_pin: marker.to_pin,
1333
+ output_contract: parsed,
1334
+ attempts: newAttempts,
1335
+ };
1336
+ // Gate 7: atomic write through writeMigrationState (Pitfall 6).
1337
+ // D-13: do NOT clear pending_envelope_resume here. The next bumpAndRebakePin
1338
+ // run reads the recorded slot, advances the chain, and clears the marker
1339
+ // on success.
1340
+ //
1341
+ // WR-05: `state` is guaranteed non-null past Gate 5 (line 1869-1875 returns
1342
+ // `blocked: no_pending_chain` when `state?.pending_envelope_resume` is null,
1343
+ // which transitively requires `state` to be non-null). The previous fallback
1344
+ // (`state ?? {... last_applied_at: { schema: 0, agentContract: 0 } ...}`)
1345
+ // was unreachable AND would have written invalid (0, 0) bumps to disk if a
1346
+ // refactor ever weakened Gate 5. We assert non-null here so a future weaken
1347
+ // of Gate 5 fails type-check rather than corrupting last_applied_at.
1348
+ const baseState = state;
1349
+ await writeMigrationState(input.targetPath, {
1350
+ ...baseState,
1351
+ envelopes: { ...(baseState.envelopes ?? {}), [input.envelopeRef]: newSlot },
1352
+ pending_envelope_resume: marker, // preserve — D-13
1353
+ });
1354
+ return {
1355
+ state: "recorded",
1356
+ envelope_ref: input.envelopeRef,
1357
+ recorded_at: nowIso,
1358
+ from_pin: marker.from_pin,
1359
+ to_pin: marker.to_pin,
1360
+ output_contract_state: parsed.state,
1361
+ };
1362
+ }
1363
+ /**
1364
+ * Remove the per-slug slot from `migration.json.envelopes`. Testing/debug
1365
+ * companion to {@link recordEnvelopeResult}; does NOT touch
1366
+ * `pending_envelope_resume` (deliberate isolation — clearing the slot does
1367
+ * not unpause a chain).
1368
+ */
1369
+ export async function clearEnvelope(input) {
1370
+ const state = await readMigrationState(input.targetPath);
1371
+ if (state === null) {
1372
+ return { state: "noop", reason: "no_migration_state" };
1373
+ }
1374
+ if (!state.envelopes || !(input.envelopeRef in state.envelopes)) {
1375
+ return { state: "noop", reason: "slot_not_present" };
1376
+ }
1377
+ const { [input.envelopeRef]: _drop, ...rest } = state.envelopes;
1378
+ void _drop;
1379
+ await writeMigrationState(input.targetPath, {
1380
+ ...state,
1381
+ envelopes: rest,
1382
+ });
1383
+ return { state: "cleared", envelope_ref: input.envelopeRef };
1384
+ }
1101
1385
  //# sourceMappingURL=migrate.js.map