@dunavnet/grantagent-migrate
v0.3.70
Published
Cross-workspace migration CLI for GrantAgent — detects legacy proposal/deliverable metadata, dry-runs the upgrade, and applies it with backups and rollback
Readme
@dunavnet/grantagent-migrate
Cross-workspace migration CLI for GrantAgent. Detects the schema version of an
existing proposal or deliverable workspace, produces a dry-run migration report,
and — when run without --dry-run — applies the upgrade atomically with backups
and a documented rollback.
Implements HE-600 — Cross-domain migration tooling.
Usage
npx @dunavnet/grantagent-migrate [options] <workspace-root>The <workspace-root> is the folder that contains (or should contain)
.proposalengine.json.
Options
| Flag | Description |
| --- | --- |
| --dry-run | Print the migration plan without writing anything. |
| --rollback | Restore the most recent migration from .bak.1 backups. |
| --force | Re-run even when the workspace already reports the target version. |
| --experimental-registers | Dangerous. Write the ADR-011 register files even though their output schema is not yet reconciled with the runtime readers in proposal-assistant. See "Schema-divergence gate" below. |
| --help, -h | Show help. |
What it migrates
Per ADR-011 (workspace registers) and ADR-010 (evaluation truth model):
| Legacy source | Target register | Status | Notes |
| --- | --- | --- | --- |
| reviews/*.json | metadata/reviews-v2/*.v2.json | Safe (always run) | Re-classified as evaluation.v2 legacy_unclassified (never guessed deterministic vs hosted). |
| metadata/workflow-state.json | (rewritten in place) | Safe (always run) | Legacy state/gate aliases normalised. |
| deliverables/**/metadata/*.{yaml,yml,json} | (rewritten in place) | Safe (always run) | HE-122 free-text nature / dissemination_level → closed enums (synonyms or Other + *_other). |
| metadata/project-deliverable-register.json | (rewritten in place) | Safe (always run) | Same enum normalisation on each commitment row. |
| .call/requirements.json | metadata/call-profile.json + metadata/requirement-traceability.json | Gated (needs --experimental-registers) | Output schema diverges from the runtime readers; suppressed by default. |
| metadata/intake-analysis.json | metadata/part-a-register.json | Gated (needs --experimental-registers) | Output schema diverges from the runtime readers; suppressed by default. |
| .proposalengine.json | (version bumped) | Conditionally advanced | Marker advances to 2.0 only when no register writes were gated; otherwise held back so a reconciled re-run picks them up. |
Raw originals are always preserved in their legacy locations; rewritten files are
backed up to .bak.1 (with a bounded .bak.N rotation). Every run appends an
entry to metadata/migration-log.jsonl (append-only).
Schema-divergence gate (solution-review T7-1)
The migrate transforms for the three ADR-011 registers (call-profile,
requirement-traceability, part-a-register) were written against a 2.0
schema that the runtime readers in mcp-servers/proposal-assistant/src/he-registers/
do not implement. The runtime reads '1.x' shapes with different record_kind
values and different field structures, and the readers cast
JSON.parse(...) as T without validation — so writing the migrate-output shape
silently corrupts the workspace. This is a class defect (the ADR-011
single-schema-source follow-up was never built) tracked as solution-review T7-1.
Default behaviour: the three register writes are diverted out of the plan
and surfaced as gatedOperations. The safe migrations (legacy reviews,
workflow-state aliases, deliverable enum normalisation) still run. The workspace
marker is not advanced, so a re-run after the schema is reconciled will pick
the gated registers up cleanly.
Opt-in: --experimental-registers removes the gate. Use it only on
throwaway workspaces for dev/dogfood; the runtime will misread the registers it
produces.
Exit code: when the gate suppresses all planned writes (i.e. the workspace had no safe operations to run), the CLI exits non-zero so CI/automation cannot treat the run as a silent no-op.
The class fix is the ADR-011 single zod schema source in
mcp-servers/shared, parsed at every register read.
Design
src/version.ts— detect the workspace schema version (marker → inferred → unknown).src/transforms.ts— pure transforms from legacy shapes to register shapes. No I/O, inputs not mutated; trivially testable.src/atomic-write.ts— atomic write +.bak.Nrotation + append-only migration log.src/planner.ts— scan a workspace into aMigrationPlan, then apply or roll back.src/index.ts— the CLI entry point (bundled with esbuild, mirroring@dunavnet/grantagent-setup).
