Skip to content

Add explicit runtime migration tooling#37

Merged
TraderSamwise merged 2 commits into
masterfrom
phase/migration-compatibility-hard-cut
May 26, 2026
Merged

Add explicit runtime migration tooling#37
TraderSamwise merged 2 commits into
masterfrom
phase/migration-compatibility-hard-cut

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented May 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • add explicit migration audit/import/rollback CLI for runtime-core hard cut
  • remove silent startup migration of old global agent-facing context/history/status
  • report corrupt legacy files, block imports over non-empty runtime exchange, and record rollback manifests

Verification

  • yarn verify
  • yarn build
  • built CLI smoke: migration audit with isolated AIMUX_HOME

Summary by CodeRabbit

  • New Features

    • Added aimux migration commands: audit (read-only inspection), import (imports legacy runtime artifacts into active lane, copies legacy artifacts into repo-local state, and records a timestamped rollback manifest), and rollback (restores prior state using a manifest).
  • Documentation

    • Expanded migration docs with explicit audit/import/rollback procedures and checklists.
  • Refactor

    • Removed automatic migration from path initialization; migrations are explicit.
  • Tests

    • Added end-to-end tests covering audit, import, rollback, and blocked/import conditions.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 23ece948-7792-46cb-91c9-59cfba81b5b6

📥 Commits

Reviewing files that changed from the base of the PR and between f2f3294 and 6d549cb.

📒 Files selected for processing (1)
  • src/main.ts

📝 Walkthrough

Walkthrough

Adds explicit, lane-aware migration tooling: reporting, import with backups/manifest and rollback, CLI commands (audit/import/rollback), tests, docs, extends legacy importer to accept extra legacy paths, and removes implicit init-time copying of agent-facing state.

Changes

Runtime Migration Feature

Layer / File(s) Summary
Migration types and infrastructure
src/runtime-migration.ts
Defines migration types/interfaces and internal utilities for directory listing, JSON/JSONL validation, runtime YAML validation, backups, and manifest-based rollback.
Migration report building
src/runtime-migration.ts
Implements buildRuntimeMigrationReport to scan legacy artifacts, validate files, emit diagnostics, and compute migration status (blocked/needs_import/clean).
Legacy importer additional paths
src/runtime-core/exchange-import.ts
Extends importRuntimeExchangeFromLegacyFiles to accept optional additional*Paths arrays and merge them into discovered history/context/recording/status file lists.
Migration import, rollback, and rendering
src/runtime-migration.ts
Implements importRuntimeMigration to import legacy exchange, create timestamped backups and manifest.json, copy legacy context/history/status into .aimux, write runtime-exchange.yaml; implements rollbackRuntimeMigration and JSON renderers.
Migration test suite
src/runtime-migration.test.ts
Adds Vitest suite with isolated temp repo/home setups verifying initPaths no longer migrates agent-facing state, blocked diagnostics on corrupted legacy files, import/write/manifest/backups behavior, and rollback semantics.
CLI migration commands
src/main.ts
Adds migration CLI group with audit (renders report), import (initializes paths then imports and renders), and rollback (resolves manifest and renders); adjusts preAction to skip normal init for migration audit.
Remove implicit migration from initialization
src/paths.ts
Removes unused cpSync/readdirSync imports; deletes migrateAgentFacingStateToLocal and migrateDirIfNeeded helpers; removes call from initPaths so init no longer copies global agent-facing dirs into repo-local .aimux.
Documentation updates
docs/dev-runtime.md, docs/runtime-authority-dead-paths.md
Documents lane-aware migration commands and specifies audit/import/rollback behaviors, backup/manifest recording, and checklist items for removing silent dual-writes and adding tests.

Sequence Diagram

sequenceDiagram
  participant CLI as aimux CLI
  participant Migr as RuntimeMigration
  participant Store as RuntimeExchangeStore
  participant FS as Filesystem

  CLI->>Migr: audit(projectRoot) -> buildRuntimeMigrationReport()
  Migr->>FS: scan legacy dirs & validate files
  Migr-->>CLI: report JSON

  CLI->>Migr: import(projectRoot)
  Migr->>FS: read legacy artifacts (history/context/status)
  Migr->>Store: importRuntimeExchangeFromLegacyFiles(...)
  Migr->>FS: write migration-backups/<timestamp>/runtime-exchange.yaml (backup)
  Migr->>FS: copy legacy dirs -> .aimux/
  Migr->>FS: write manifest.json
  Migr->>Store: write runtime-exchange.yaml
  Migr-->>CLI: import result JSON

  CLI->>Migr: rollback(manifestPath)
  Migr->>FS: restore backups, remove written files/dirs
  Migr-->>CLI: rollback result JSON
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • TraderSamwise/aimux#30: Introduces the legacy runtime exchange importer that this change extends with optional additional legacy path arrays.
  • TraderSamwise/aimux#29: Earlier documentation around runtime authority/dead paths that this PR expands with explicit migration command guidance.

Poem

🐰 A rabbit hops through legacy files with care,

audits, imports, then rolls back what's there.
Backups and manifests tucked in a row,
no silent moves — explicit steps on show.
Hooray for clear lanes and a safe restore!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add explicit runtime migration tooling' directly and clearly summarizes the main change: introducing explicit CLI commands for runtime migrations (audit, import, rollback) to replace silent startup migration behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch phase/migration-compatibility-hard-cut

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
docs/dev-runtime.md (1)

50-56: ⚡ Quick win

Add a rollback command example next to audit/import.

Lines 50-53 show the forward flow only; adding rollback usage here makes recovery workflow immediately discoverable.

📘 Suggested doc update
 ```sh
 aimux-dev migration audit --project /path/to/scratch-project
 aimux-dev migration import --project /path/to/scratch-project
+aimux-dev migration rollback <manifest-path>
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/dev-runtime.md around lines 50 - 56, Add a rollback example next to the
existing migration audit/import snippets by inserting the command usage
"aimux-dev migration rollback " immediately after the import line
and include a short one-line note that this command restores state from the
recorded rollback manifest (created under migration-backups/) so readers can
discover the recovery workflow alongside audit/import; reference the command
name "aimux-dev migration rollback" and the manifest concept so the addition is
easy to locate and verify.


</details>

</blockquote></details>
<details>
<summary>src/runtime-core/exchange-import.ts (1)</summary><blockquote>

`353-365`: _⚡ Quick win_

**Deduplicate and validate additional paths before building continuity refs.**

Line 353 onward appends raw `additional*Paths`; this can introduce duplicate/blank/non-existent paths, which then produce duplicate or malformed continuity refs.

 

<details>
<summary>Proposed hardening (dedupe + trim + existence + extension checks)</summary>

```diff
+function mergeContinuityPaths(
+  discovered: string[],
+  additional: string[] | undefined,
+  isAllowed: (path: string) => boolean,
+): string[] {
+  return unique([...discovered, ...(additional ?? [])]).filter((path) => existsSync(path) && isAllowed(path));
+}
+
 export function importRuntimeExchangeFromLegacyFiles(
   input: {
@@
   return buildRuntimeExchangeFromLegacySnapshot({
@@
-    historyPaths: [
-      ...listFiles(getHistoryDir(), (name) => name.endsWith(".jsonl")),
-      ...(input.additionalHistoryPaths ?? []),
-    ],
-    contextPaths: [
-      ...listNestedFiles(getContextDir(), (name) => name.endsWith(".md") || name.endsWith(".jsonl")),
-      ...(input.additionalContextPaths ?? []),
-    ],
-    recordingPaths: [
-      ...listFiles(getRecordingsDir(), (name) => name.endsWith(".txt") || name.endsWith(".log")),
-      ...(input.additionalRecordingPaths ?? []),
-    ],
-    statusPaths: [...listFiles(getStatusDir(), (name) => name.endsWith(".md")), ...(input.additionalStatusPaths ?? [])],
+    historyPaths: mergeContinuityPaths(
+      listFiles(getHistoryDir(), (name) => name.endsWith(".jsonl")),
+      input.additionalHistoryPaths,
+      (path) => path.endsWith(".jsonl"),
+    ),
+    contextPaths: mergeContinuityPaths(
+      listNestedFiles(getContextDir(), (name) => name.endsWith(".md") || name.endsWith(".jsonl")),
+      input.additionalContextPaths,
+      (path) => path.endsWith(".md") || path.endsWith(".jsonl"),
+    ),
+    recordingPaths: mergeContinuityPaths(
+      listFiles(getRecordingsDir(), (name) => name.endsWith(".txt") || name.endsWith(".log")),
+      input.additionalRecordingPaths,
+      (path) => path.endsWith(".txt") || path.endsWith(".log"),
+    ),
+    statusPaths: mergeContinuityPaths(
+      listFiles(getStatusDir(), (name) => name.endsWith(".md")),
+      input.additionalStatusPaths,
+      (path) => path.endsWith(".md"),
+    ),
     attachments: readLegacyAttachments(),
   });
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime-core/exchange-import.ts` around lines 353 - 365, The code appends
raw additional*Paths into historyPaths/contextPaths/recordingPaths/statusPaths
which can introduce duplicates, blank entries or non-existent/malformed paths;
update the construction of historyPaths, contextPaths, recordingPaths and
statusPaths (the lines that use listFiles/listNestedFiles and
input.additional*Paths) to first normalize each additional path by trimming
whitespace, filter out empty strings, validate file existence (e.g.
fs.existsSync) and enforce allowed extensions (.jsonl for history, .md|.jsonl
for context, .txt|.log for recordings, .md for status), and deduplicate (e.g.
via a Set) before spreading them into the arrays so only valid, unique paths are
added.
src/runtime-migration.ts (1)

458-460: 💤 Low value

Duplicate manifest write is redundant.

The manifest is written twice (lines 458 and 460) with identical content. The second write doesn't capture any state change since manifest isn't modified between the two calls. If line 458 is meant as a checkpoint before the exchange write, it's misleading because it already records the exchange in manifest.wrote before the actual write occurs.

Consider removing line 458 and writing the manifest only after the exchange is successfully persisted.

♻️ Suggested fix
     manifest.wrote.push({ kind: "runtime-exchange", path: report.authority.runtimeExchangePath });
-    writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
     const writtenExchange = new RuntimeExchangeStore(report.authority.runtimeExchangePath).write(exchange);
     writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime-migration.ts` around lines 458 - 460, The manifest is being
written twice with no intervening changes; remove the first writeFileSync call
and instead call writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) +
"\n") only after the exchange has been successfully persisted by
RuntimeExchangeStore(report.authority.runtimeExchangePath).write(exchange) (the
writtenExchange result), so update the code around manifestPath/manifest and
RuntimeExchangeStore.write to persist the exchange first and then write the
manifest once.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main.ts`:
- Around line 3001-3007: The audit command is supposed to be read-only but
global preAction always calls initPaths(projectRoot) which mutates state; update
the preAction hook to skip calling initPaths(projectRoot) when the invoked
command is "audit" (or when opts indicate read-only), or add a readOnly flag
that prevents mutation and check it before calling initPaths; ensure the audit
action still calls resolveProjectRoot/pathResolve and then directly runs
buildRuntimeMigrationReport/renderRuntimeMigrationReport without triggering
initPaths so migration audit remains non-mutating.

---

Nitpick comments:
In `@docs/dev-runtime.md`:
- Around line 50-56: Add a rollback example next to the existing migration
audit/import snippets by inserting the command usage "aimux-dev migration
rollback <manifest-path>" immediately after the import line and include a short
one-line note that this command restores state from the recorded rollback
manifest (created under migration-backups/) so readers can discover the recovery
workflow alongside audit/import; reference the command name "aimux-dev migration
rollback" and the manifest concept so the addition is easy to locate and verify.

In `@src/runtime-core/exchange-import.ts`:
- Around line 353-365: The code appends raw additional*Paths into
historyPaths/contextPaths/recordingPaths/statusPaths which can introduce
duplicates, blank entries or non-existent/malformed paths; update the
construction of historyPaths, contextPaths, recordingPaths and statusPaths (the
lines that use listFiles/listNestedFiles and input.additional*Paths) to first
normalize each additional path by trimming whitespace, filter out empty strings,
validate file existence (e.g. fs.existsSync) and enforce allowed extensions
(.jsonl for history, .md|.jsonl for context, .txt|.log for recordings, .md for
status), and deduplicate (e.g. via a Set) before spreading them into the arrays
so only valid, unique paths are added.

In `@src/runtime-migration.ts`:
- Around line 458-460: The manifest is being written twice with no intervening
changes; remove the first writeFileSync call and instead call
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n") only after
the exchange has been successfully persisted by
RuntimeExchangeStore(report.authority.runtimeExchangePath).write(exchange) (the
writtenExchange result), so update the code around manifestPath/manifest and
RuntimeExchangeStore.write to persist the exchange first and then write the
manifest once.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 264a621d-4344-4448-a5ba-07434af9cf23

📥 Commits

Reviewing files that changed from the base of the PR and between 3c003fd and f2f3294.

📒 Files selected for processing (7)
  • docs/dev-runtime.md
  • docs/runtime-authority-dead-paths.md
  • src/main.ts
  • src/paths.ts
  • src/runtime-core/exchange-import.ts
  • src/runtime-migration.test.ts
  • src/runtime-migration.ts

Comment thread src/main.ts
@TraderSamwise
TraderSamwise merged commit 3b6cab3 into master May 26, 2026
1 check passed
@TraderSamwise
TraderSamwise deleted the phase/migration-compatibility-hard-cut branch May 26, 2026 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant