@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.
- package/INSTALL-BUNDLE.json +1 -1
- package/RELEASE-SPAN-UPDATE-CONTRACTS.json +64 -0
- package/node_modules/@gdh/adapters/dist/authoring-hook-render.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/authoring-hook-render.js +2 -1
- package/node_modules/@gdh/adapters/dist/authoring-hook-render.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.js +2 -1
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js +2 -1
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js +2 -1
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/deferred-actions-advisory.d.ts +71 -0
- package/node_modules/@gdh/adapters/dist/deferred-actions-advisory.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/deferred-actions-advisory.js +89 -0
- package/node_modules/@gdh/adapters/dist/deferred-actions-advisory.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/durable-backup.d.ts +209 -0
- package/node_modules/@gdh/adapters/dist/durable-backup.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/durable-backup.js +346 -0
- package/node_modules/@gdh/adapters/dist/durable-backup.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/index.d.ts +10 -1
- package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/index.js +24 -2
- package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/inventory-sweep.d.ts +53 -0
- package/node_modules/@gdh/adapters/dist/inventory-sweep.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/inventory-sweep.js +98 -0
- package/node_modules/@gdh/adapters/dist/inventory-sweep.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/process-orchestration.d.ts +223 -0
- package/node_modules/@gdh/adapters/dist/process-orchestration.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/process-orchestration.js +368 -0
- package/node_modules/@gdh/adapters/dist/process-orchestration.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts +157 -14
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.js +570 -89
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/skill-rendering.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/skill-rendering.js +25 -0
- package/node_modules/@gdh/adapters/dist/skill-rendering.js.map +1 -1
- package/node_modules/@gdh/adapters/package.json +8 -8
- package/node_modules/@gdh/authoring/package.json +2 -2
- package/node_modules/@gdh/cli/dist/index.d.ts +9 -0
- package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/index.js +244 -2
- package/node_modules/@gdh/cli/dist/index.js.map +1 -1
- package/node_modules/@gdh/cli/dist/migrate.d.ts +152 -1
- package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/migrate.js +290 -6
- package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
- package/node_modules/@gdh/cli/dist/self-update.d.ts +14 -0
- package/node_modules/@gdh/cli/dist/self-update.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/self-update.js +197 -15
- package/node_modules/@gdh/cli/dist/self-update.js.map +1 -1
- package/node_modules/@gdh/cli/package.json +10 -10
- package/node_modules/@gdh/core/dist/index.d.ts +97 -5
- package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/index.js +23 -5
- package/node_modules/@gdh/core/dist/index.js.map +1 -1
- package/node_modules/@gdh/core/dist/migrations/entries/s2c2_to_s2c3_rules_schema_v2_to_v3.d.ts +3 -0
- package/node_modules/@gdh/core/dist/migrations/entries/s2c2_to_s2c3_rules_schema_v2_to_v3.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/entries/s2c2_to_s2c3_rules_schema_v2_to_v3.js +247 -0
- package/node_modules/@gdh/core/dist/migrations/entries/s2c2_to_s2c3_rules_schema_v2_to_v3.js.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/entries/s3c8_to_s3c9_register_runtime_bridge_autoload.d.ts +3 -0
- package/node_modules/@gdh/core/dist/migrations/entries/s3c8_to_s3c9_register_runtime_bridge_autoload.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/entries/s3c8_to_s3c9_register_runtime_bridge_autoload.js +152 -0
- package/node_modules/@gdh/core/dist/migrations/entries/s3c8_to_s3c9_register_runtime_bridge_autoload.js.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/envelope-output-validator.d.ts +3 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/envelope-output-validator.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/envelope-output-validator.js +67 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/envelope-output-validator.js.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/index.d.ts +37 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/index.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/index.js +60 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/index.js.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/types.d.ts +121 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/types.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/types.js +2 -0
- package/node_modules/@gdh/core/dist/migrations/envelopes/types.js.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/golden-harness.d.ts +40 -0
- package/node_modules/@gdh/core/dist/migrations/golden-harness.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/golden-harness.js +71 -0
- package/node_modules/@gdh/core/dist/migrations/golden-harness.js.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts +322 -0
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js +384 -0
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/probes.d.ts +58 -0
- package/node_modules/@gdh/core/dist/migrations/probes.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/probes.js +112 -0
- package/node_modules/@gdh/core/dist/migrations/probes.js.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/registry.d.ts +205 -0
- package/node_modules/@gdh/core/dist/migrations/registry.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/migrations/registry.js +214 -0
- package/node_modules/@gdh/core/dist/migrations/registry.js.map +1 -0
- package/node_modules/@gdh/core/dist/state/atomic-write.d.ts +19 -0
- package/node_modules/@gdh/core/dist/state/atomic-write.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/state/atomic-write.js +34 -0
- package/node_modules/@gdh/core/dist/state/atomic-write.js.map +1 -0
- package/node_modules/@gdh/core/dist/state/migration-state.d.ts +135 -0
- package/node_modules/@gdh/core/dist/state/migration-state.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/state/migration-state.js +186 -0
- package/node_modules/@gdh/core/dist/state/migration-state.js.map +1 -0
- package/node_modules/@gdh/core/dist/state/processes-snapshot.d.ts +72 -0
- package/node_modules/@gdh/core/dist/state/processes-snapshot.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/state/processes-snapshot.js +113 -0
- package/node_modules/@gdh/core/dist/state/processes-snapshot.js.map +1 -0
- package/node_modules/@gdh/core/dist/state/render-inventory.d.ts +54 -0
- package/node_modules/@gdh/core/dist/state/render-inventory.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/state/render-inventory.js +77 -0
- package/node_modules/@gdh/core/dist/state/render-inventory.js.map +1 -0
- package/node_modules/@gdh/core/package.json +1 -1
- package/node_modules/@gdh/docs/dist/agent-contract.d.ts.map +1 -1
- package/node_modules/@gdh/docs/dist/agent-contract.js +2 -1
- package/node_modules/@gdh/docs/dist/agent-contract.js.map +1 -1
- package/node_modules/@gdh/docs/dist/guidance.d.ts.map +1 -1
- package/node_modules/@gdh/docs/dist/guidance.js +3 -1
- package/node_modules/@gdh/docs/dist/guidance.js.map +1 -1
- package/node_modules/@gdh/docs/package.json +2 -2
- package/node_modules/@gdh/mcp/package.json +8 -8
- package/node_modules/@gdh/observability/package.json +2 -2
- package/node_modules/@gdh/runtime/dist/bridge-surface.js +63 -2
- package/node_modules/@gdh/runtime/dist/bridge-surface.js.map +1 -1
- package/node_modules/@gdh/runtime/package.json +2 -2
- package/node_modules/@gdh/scan/package.json +3 -3
- package/node_modules/@gdh/verify/package.json +7 -7
- 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":"
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|