Skip to content

feat(migrate): widen conversion and add manifest, replacement, and structural checks - #69

Merged
eiserv merged 11 commits into
mainfrom
feat/migrate-m7
Nov 1, 2026
Merged

eiserv merged 11 commits into
mainfrom
feat/migrate-m7

Conversation

@eiserv

@eiserv eiserv commented Nov 1, 2026

Copy link
Copy Markdown
Owner

Summary

First M7 steps: migrate now converts what the analysis rates Astro-convertible, every migration writes a provenance record, and an existing or interrupted output can be handled safely.

  • Conversion. migrate::convert hands the component function to convert_component instead of reading the body itself. That adds arrow functions, a function or const named by export default <name>, expression statements before the final return, and any object pattern of a component's props, such as default values. Pages that read params and anonymous component functions stay preserved with their reason.

  • Manifest (D-105). .frameshift/manifest.json holds the plan hash, the plan's binding, and the origin and digest of every other output file. It is an operation of the plan, but it stays outside the plan document and hash, because it names that hash. verify, which will also read it, remains planned.

  • Replacing output (D-106). --on-existing replace-managed replaces an existing output only if its file set and digests match the manifest exactly. The old tree moves to .<name>.frameshift-previous and is never deleted, and it moves back if the commit fails.

  • Interrupted runs. --clean-staging removes a staging directory only when it is a real directory with the Frameshift marker.

  • --strict and --show-files. --strict writes nothing and exits with 1 when a unit is rated unknown or unsupported; --show-files lists every operation of a real run.

  • Structural postcondition (D-107). Every generated script and Astro frontmatter must parse again, runtime imports must name planned files or packages other than Next.js, and no React directive may remain. A failing file is preserved with its reason, so a rule defect cannot reach the output. The Astro template is not re-parsed yet.

Validation

  • The existing goldens did not change with the conversion refactor, so previously converted files keep their exact output.

  • static-site gained an arrow page behind export default <name>, a page with an expression statement, and an arrow component with a default prop value.

  • Its output was built with the pinned astro7-preact10 profile outside the repository: 8 pages, no script tag in any page. The arrow component rendered its default and explicit prop values, and the expression statement ran before the markup.

  • A real-world check over the 211 App Router package directories of the approved corpus (read-only, no project code run): every plan exited with 0, two plans of a package were byte-identical, every migrate --plan succeeded, all 12,954 written files matched their manifest digests, and the sampled sources stayed unchanged, also with a second --on-existing replace-managed run per package. The structural postcondition preserved 5 files that would have kept a React directive and refused nothing else. IMPLEMENTATION.md section 52 records what stays preserved and why.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --offline --locked -- -D warnings
  • cargo test --workspace --offline --locked
  • pwsh -NoProfile -File .github/scripts/check.ps1
  • Astro build of the expected output

🤖 Generated with Claude Code

eiserv and others added 11 commits November 1, 2026 21:16
Migrate now accepts what the analysis rates Astro-convertible: arrow functions,
a declaration named by export default, expression statements, and any object
pattern of a component's props. Pages that read params stay preserved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every migration now writes .frameshift/manifest.json with the plan hash, the
binding, and the origin and digest of every other output file (D-105). It is the
base for replace-managed output and verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only a real directory that holds the staging marker is removed, so the option
cannot delete a directory that a person created.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An existing output is replaced only if its manifest records it exactly, so no
manual edit can be lost. The old tree moves next to the output and is never
deleted, and it moves back if the commit fails (D-106).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--strict writes nothing and exits with 1 when a unit is rated unknown or
unsupported, and --show-files lists every operation of a real run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A generated script or Astro frontmatter must parse again, its runtime imports must
name planned files or packages other than Next.js, and no React directive may
remain. A file that fails is preserved with its reason, so a rule defect cannot
reach the output (D-107).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A comment between the parentheses and the markup of a returned root lay inside
the root's span, so the conversion treated it as checked, but the markup
conversion only reads what the parentheses enclose, and the comment was lost.
The fuzzer's astro target found it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@eiserv
eiserv merged commit fb2ef37 into main Nov 1, 2026
21 checks passed
@eiserv
eiserv deleted the feat/migrate-m7 branch November 1, 2026 20:37
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