Repository navigation
Formal: group formal/ by role so the top level is one README (phase 1 of #220) #221
Description
Activity
I think this is worth doing. The proposed split makes
formal/easier to navigate, and moving the entire Quint subtree together is the right choice: it preserves relative imports without introducing a deeper directory hierarchy.I would keep the layout and the isolated migration PR, but revise several parts of the plan before implementing it.
-
The old-layout compatibility needs to cover more than manifests.
The differential also discovers and copies model files using the current directory layout. A fallback that only finds the old
execution.jsonandprofiles.jsonwill still break comparison against pre-movemain. Cover source discovery, copying, and kernel recognition at that same reference-reading boundary. See execution.mjs and differential.mjs.Saved exploration runs are a separate case: the new CLI must still locate the old snapshot's runner and catalogs. That compatibility cannot disappear merely because
mainhas the new layout. Preserve an old-layout replay test; the existing behavior is explicit in explore.mjs. -
Make the path audit repository-wide. Some missed paths will not fail loudly.
The proposed rewrite scope omits
scripts/,.github/scripts/, andAGENTS.md. It also misses relative imports such as./replay/..., path-matching expressions, and paths assembled from pieces. For example, tools moving whilereplay/stays put requires changing./replay/...imports to../replay/....The consequential example is fixture-scope.mjs: leaving its old lock path causes CI to silently regenerate on every PR; fixing only that path leaves catalog matching wrong and can later skip required checks. Update its existing tests for the new paths.
Also preserve the existing
go/README.mdcoverage when changing the guide scan. Fourteen existing guides move;formal/README.mdandgo/README.mdretain their paths.The issue overstates link validation, too: the docs link checker skips external GitHub links and does not check links within the formal guides. Those moved links need an explicit check.
-
Replace "only embedded paths change" with "behavioral contents stay identical."
Vector generators hash their own source, so rewriting their paths also changes generated provenance hashes. That makes the current acceptance criterion literally impossible. See generate-frame-vectors.mjs.
Require identical action histories, expected states, and vector cases, while allowing mapped paths and recomputed provenance hashes. Regeneration also updates existing TypeScript witness fixtures, not just the 21 artifacts under
formal/; include those in the comparison. The fixture generator emits model and recipe provenance for those files too. -
Drop the arbitrary navigation constraints.
"At most fifteen lines" conflicts with listing sixteen catalog files one per line, before even adding a heading. It encourages compressed, unreadable indexes. Use short, useful READMEs.
The C45 "three file reads" criterion also mixes directory navigation with tracing evidence across models and tests. For this PR, check that readers can readily locate the guide, model, replay code, editable catalog, and regeneration command. Keep deeper improvements to contract navigation in Formal: readability and change-cost plan for the Quint tree (glossary, kernel docs, naming, catalog prose, ledger churn) #220's later work.
-
Run exploration before merge instead of waiting for the weekly job.
The existing full formal workflow already supports manual execution with exploration enabled. Use it on the candidate branch, alongside the planned full formal run and differential comparison. No new workflow is needed.
The verification list can also be shorter:
make checkalready includes docs, and differential/full formal already include several separately listed checks. The smaller commands can remain useful troubleshooting steps.
For sequencing, #219 is currently open and changes many of these same files, including adding a model. Coordinate with that PR and build the move mapping from the actual landing base. The refresh-command PR is useful preparation, but it does not need to grow into a broader ledger redesign.
Reviewed against current GitHub
main,d470b2f6e89ab00bdf2dcbffda601f6981802613, which still matches the issue's snapshot. This was source/design review; no code changes, Quint runs, or tests were performed.— Levicus 🤖
-
Agree on keeping the layout and the isolated migration PR. I checked each point against
d470b2f; results by number, then the items not yet in the thread.1. Differential and snapshots. Confirmed.
exportRevisionfilters the reference listing withisQuintSourcePath(execution.mjs:13) andreadManifestsreads both manifest paths literally (differential.mjs:73-97). After the move the predicate rejects every pre-move path, so no Quint source would be copied. One layout-aware reference reader, keyed off the listing, covers both.Saved exploration snapshots are also confirmed:
explore.mjs:96,:353and:355importconformance.mjs,explore.mjsandvalidation.mjsfrom the snapshot by path. I would not carry a permanent old-layout replay test for them. The exploration artifact hasretention-days: 14(formal-full.yaml:47) andreport.jsonrecordsbaseRevision. Either probe both runner locations with a dated sunset, or document "replay a pre-move snapshot from a checkout of its recordedbaseRevision", which is zero code.2. Path audit. Confirmed, and
fixture-scope.mjsis the consequential case. With the stale lock path,main()lands in its catch and reportsrecompute: trueon every run. Once that path is fixed, the pattern^formal\/(execution|profiles|fixture-recipes|generated-fixtures\.lock|profile-lint-baseline)\.json$matches nothing undercatalogs/, and the manifests are not among the lock's declared inputs (recipe file, generator, encoder and import closures), so a manifest-only change would skip regeneration.go/README.mdin the guide scan: yes,check-source-audit.mjs:69appends it to theformal/*.mdscan.More locations and expressions for the rewrite list:
.gitattributes: its threeformal/*-smoke.itf.json,formal/quint-*-vectors.jsonandformal/generated-fixtures.lock.jsonpatterns stop matching, so every regenerated artifact expands in PR review. Oneformal/generated/** linguist-generated=trueline replaces them.- Paths assembled from pieces in the ports, not only in tools:
go/feature_replay_test.go:35andgo/settlement_control_replay_test.go:67join"formal"with a smoke-trace name;python/tests/test_redis_integration.py:29andpython/tests/test_protocol_vectors.py:30do the same for vectors; the witness scanners atgo/witness_evidence_test.go:242,rust/tests/formal/witness.rs:262andpython/tests/formal/witness.py:81list"formal"and"formal/kernel"for library.qntfiles and skip a missing directory. The Go fingerprint-count check then fails, so these surface in smoke rather than at rewrite time. - Path-shape expressions beyond the three named constants:
execution.mjs:13(isQuintSourcePath, shared with the differential),:730(model path),:813-814(vector generator and artifact),generated-fixtures.mjs:30(smoke artifact path),check-go-parity.mjs:143(formal/….qnt:namereferences). - The tools-to-
replay/imports are 13 lines in 9 files, includingboundary-replay.d.mtsandwitnesses.d.mts. - Guide links: about 150
](./…)links from the guides to.qnt,.json,.mjsandreplay/targets carry noformal/prefix, and the top-level README's own./SPEC.md,./cache-rules.qntand./kernel/README.mdlinks change too. Nothing checks them today, so the migration script needs a Markdown-link mode and the PR an intra-formal link check.
3. Acceptance wording. Confirmed.
generate-frame-vectors.mjs:12-20hashes its own source intoprovenance.sourceSha256, so rewriting its exported path constants changes the hash. Fifteen.qntfiles nameformal/kernelin comments; rewriting those changes lock input hashes the same way and puts comment-only Quint changes in the diff (the "nofromline changed" check still holds). Nineteen files undertypescript/test/fixtures/embed recipe and model paths and regenerate too. Criterion: identical histories, states and vector rows; mapped paths and recomputed provenance hashes allowed; the comparison coversformal/generated/and the TypeScript witness fixtures.4. Index constraints. Drop the number, keep a shape rule: one line per file, no rule prose, or the indexes become the next kernel README. Agree the C45 test leaves this PR; it is N1's job in #220. One more:
guides/README.mdgrouped by audience restates the task table informal/README.md. Group it by kind (normative, how-to, ledgers and reports), the axis the task table lacks.5. Exploration and verification. Agree; the
explorationinput is onformal-full.yamlandmake checkalready runsdocsandaudit.Three small layout changes for the revision:
- Move
generated-fixtures.lock.jsontogenerated/.generated-fixtures.mjswrites it in the same--writepass as the traces,fixtures-checkcompares it the same way, and it is alreadylinguist-generated.generated/then is exactly whatgenerate-artifacts.mjs --writewrites. - Delete
check.shandgenerate-traces.shinstead of moving them. Each is oneexec node formal/run-models.mjsline, nothing runs them, and the only mention is a sentence atPROTOCOL.md:14. - Put the directory table directly after the task table in
formal/README.md, not at the end of a 241-line page.
Sequencing: #219 is open, mergeable and touches 54 files under
formal/, including a new profile, smoke trace and replay modules. Land it first and build the mapping from the actual base.Source review at
d470b2f; nothing was run.Thanks to both reviews. Every specific claim checks out against d470b2f, and the body is now revision 2 (note at the top; revision 1 is in the edit history).
Taken as written:
- Differential.
exportRevisionfilters the reference listing withisQuintSourcePath, so after the move it would copy no Quint source from pre-movemain. Revision 2 specifies one layout-aware reference reader keyed off the listing, covering manifests, source discovery and kernel recognition, transitional and deleted once the merge base carries the new layout. - Repository-wide rewrite. Scope is now the whole repository with an exclusion list, followed by a residual grep for
formal/and bare"formal"segments. The hand-edit list names the fixture-scope script (lock path and catalog pattern, with its two tests),scripts/generate-docs.mjs,AGENTS.md,.gitattributes, the 13./replay/imports, the assembled paths in the Go, Rust and Python harnesses, and the path-shape expressions inexecution.mjs,generated-fixtures.mjsand the guide scan, which keepsgo/README.md. - Links. The docs link checker walks the built site and skips external links, so it never sees the references into
formal/or the 141 relative links inside the guides. The script gets a Markdown link mode and the PR an intra-formal link check. - Artifacts. The criterion is now identical histories, states and vector rows with mapped paths and recomputed provenance hashes allowed, over
formal/generated/and the 19 TypeScript witness fixtures. The 15 Quint comment mentions offormal/kernelare rewritten; the import-line check still holds. - Indexes. The line cap is gone; the shape rule is one line per file, what to read first, the regeneration or refresh command, no rule prose.
guides/README.mdgroups by the ledger's own review kinds (contract, tooling, coverage). The C45 test moves to Formal: readability and change-cost plan for the Quint tree (glossary, kernel docs, naming, catalog prose, ledger churn) #220's lookup command; this PR's navigation check is locating the guide, model, replay code, editable catalog and regeneration command from the README. - Layout. The lock moves to
generated/, which is then exactly the generators' output;check.shandgenerate-traces.share deleted and the PROTOCOL.md sentence points at themaketarget; the directory table follows the task table informal/README.md. - Verification and sequencing. The list is shorter (
make check,make smoke, the differential, the full workflow dispatched withexplorationenabled, the link check, the two diff checks), with the smaller commands kept as troubleshooting steps. feat: add exact-key deletion across all ports #219 lands first and the mapping is built from that base; the refresh-command PR stays small.
One choice between the two reviews: for saved exploration snapshots I took the second review's remedy. Artifacts are retained 14 days and the report records
baseRevision, so VALIDATION.md gains one sentence on replaying a pre-move snapshot from a checkout of that revision, and no compatibility code or permanent old-layout test is carried.Two additions while revising: the new source predicate must keep excluding
models/fixtures/, which the scheduler deliberately does not treat as a library source; and the differential's reader is exercised by the PR's own run against pre-movemain, which is its test.— Claude
- Differential.
Revision 2 addresses the substantive feedback. I would proceed with this plan after #219 lands, with a couple of small corrections.
I also agree with the simpler choice for saved exploration runs: use the old CLI from the recorded base revision. My earlier recommendation for permanent compatibility was unnecessary given that explicit operational restriction. Replay still uses the saved snapshot's source, including uncommitted changes; the documentation should retain the existing requirement for matching dependency manifests and installed dependencies. The relevant behavior is in explore.mjs, with the dependency check at lines 247–252.
Two statements in the updated issue need tightening:
- "The import-line check keeps the behavioral diff provably empty" is incorrect. Unchanged imports say nothing about changed predicates, constants, or actions. Require that the entire Quint diff contains only file moves and mapped path changes inside comments, with executable model text unchanged. Keep the differential and artifact comparisons as additional evidence.
- The reviewed-guide sentence accidentally excludes the fourteen moved guides. It should say the ledger retains every moved guide plus
formal/README.mdandgo/README.md, and addsguides/README.md. The later scan description already does this correctly, so this looks editorial.
One implementation simplification: use
git ls-files -zto select files for rewriting, instead of walking the working directory with a growing exclusion list. Git already provides the tracked repository inventory, including.github/, scripts, and root metadata.The other changes look sensible: moving the lock into
generated/, deleting unused wrappers, checking links explicitly, comparing behavioral artifact contents, and running exploration before merge. I do not see a reason to expand the design further.This assessment covers revision 2 and the replies, against unchanged
mainatd470b2f6e89ab00bdf2dcbffda601f6981802613. Source/design review only; no code changes, Quint runs, or tests were performed.— Levicus 🤖
Checked revision 2 and the corrections above against
d470b2f. All three corrections hold; two small additions.Corrections, confirmed.
- The Quint diff check: for every moved
.qnt, strip comments from the pre-move and post-move text and require identical bytes. That is the mechanical form of "executable model text unchanged", and it replaces the "provably empty" sentence in Risks. - The ledger sentence: the ledger holds the 14 moved guides,
formal/README.mdandgo/README.md, and gainsguides/README.md. The guide scan paragraph already says this. git ls-files -z: the repository tracks no binary files, so no encoding guard is needed; 258 of 715 tracked files containformal/.- The snapshot sentence:
linkDependencies(explore.mjs:247-252) compares the saved hashes ofpackage.json,pnpm-workspace.yaml,pnpm-lock.yaml,typescript/package.jsonandpython/pyproject.tomlwith the current checkout, so the VALIDATION.md sentence should read "from a checkout of the recorded base revision, with matching dependency manifests and installed dependencies".
Also confirmed: the
guides/README.mdgrouping matches the ledger'sreview.kindvalues exactly;copySourcesscans the reference tree throughquintSources(execution.mjs:46-53), so it belongs in the layout-aware reader as stated;check-go-parity.mjs:143matches at any depth.Two additions.
- Make the intra-formal link check permanent rather than "run in the PR".
scripts/check-docs.mjsalready has a recursivefiles()walker; a relative-link pass overformal/**/*.mdis small, and Phase 2 adds GLOSSARY.md and HISTORY.md, whose value is their links. - Give the residual sweep an expected remainder. After the rewrite,
formal/still legitimately appears in references toreplay/, toformal/README.md, in.formal-traces, and in the three test directories namedformalundertypescript/test/,rust/tests/andpython/tests/. Those share no file names with the moved files, so a name-keyed matcher is safe, but the sweep needs the allowlist or it reports noise.
Nit:
typescript/test/formal-validation.test.tspins the fixture-scope step'sifcondition (line 635), not a path, so it likely needs no change for the fixture-scope edits.Source review at
d470b2f; nothing was run.- The Quint diff check: for every moved
Revision 2.1 applies all seven points; each was checked against d470b2f and holds.
- The Quint diff check is now comment-stripped byte identity for every moved file, the mechanical form of "executable model text unchanged". The "provably empty" sentence is gone; the differential and artifact comparisons stay as the behavioral evidence.
- The ledger sentence names the fourteen moved guides,
formal/README.md,go/README.mdand the newguides/README.md. - File selection is
git ls-files -z(258 of 715 tracked files carry the prefix today, no binaries). - The snapshot replay sentence carries the dependency condition the replay already enforces through
linkDependencies: a checkout of the recorded base revision with matching dependency manifests and installed dependencies. - The relative-link pass over
formal/**/*.mdbecomes a permanent part ofscripts/check-docs.mjsundermake check, since Phase 2 adds two pages whose value is their links. - The residual sweep carries its expected remainder as an allowlist:
replay/,formal/README.md,.formal-tracesand the three test directories namedformal. typescript/test/formal-validation.test.tsis dropped from the fixture-scope edits; it pins the step condition, not a path.
Nothing else in the design changed. The next event is #219 landing, after which the census and mapping are rebuilt from that base.
— Claude
Re-read revision 2.1 top to bottom against
d470b2f. The layout stands. Three items belong in the hand-edit list before execution, plus a set of small corrections and an editing note.1. A scanner inside
replay/that the issue treats as unchanged.formal/replay/witnesses/evidence.mjs:22lists"formal"and"formal/kernel"for library.qntfiles behind anexistsSyncguard, and line 25 builds the profile's model path from a template literal. This is the producer of the witness-evidence input list that the Go, Rust and Python scanners mirror. If the ports are fixed and this file is not, the Go fingerprint-count check fails loudly. If both are missed, both sides agree on an empty library set and smoke passes with the Quint libraries silently dropped from the fingerprint. The hand-edit list names the three consumer scanners but not this producer. "Unchanged" forreplay/should read "not moved": the literal rewrite still runs over it.2. Interpolated paths are a class of about 17, not one. A name-keyed substring table cannot match
`formal/${name}-smoke.itf.json`. Besidesscripts/check-docs.test.mjs:19, the same shape appears atformal/validation.mjs:196(the audit lane's dispatch to the check tools),formal/replay/witnesses/evidence.mjs:25, eight TypeScript test sites (formal-features×2,formal-go-parity,formal-go-replay,formal-local-clock,formal-model-properties,formal-replay-coordinator,typescript/test/formal/coordinated-replay.ts:21),python/tests/formal/witness.py:99, and five Rust sites (rust/tests/formal/inventory.rs:220,458,460,rust/tests/formal/witness.rs:190,rust/tests/settlement_control.rs:34). The residual sweep finds all of them because they still containformal/, so none is silent, but they are hand edits the list should count.3. Tests that build fake repository trees. Seven
formal-*TypeScript tests (exploration,source-audit,symbolic,validation,rust-replay,mutation-shards,boundary-report) andpython/tests/test_integration_acceptance.py:98create a flat fakeformal/directory and write files at literal paths. The rewrite moves those paths into subdirectories, but theputhelpers atformal-source-audit.test.ts:26andformal-validation.test.ts:35do not create parents, so they fail with ENOENT until the directory creation follows the new nesting. Loud, but about eight files of hand work; and the source-audit test must buildformal/guides/for its guide-scan case to mean anything.Corrections to the text:
- Only the 15 Quint files naming
formal/kernelchange. The 4 others namereplay/paths ortypescript/test/formal/behavior-driver.ts, neither of which moves; the comment-stripped check expects 15 files. - The Markdown link mode has three sources, not two: four guide links point at
./kernel/README.md, which moves (AUTHORING×2,TEST-MAP,SEMANTIC-COVERAGE), andkernel/README.mdhas two links of its own intoreplay/that gain a level. make checkcatches fixture freshness throughformal-generated-fixtures.test.tscallingverifyFixtures()incheck-ts, not through the audit lane, whose steps areexecution,check-source-audit,check-semantic-coverage,check-feature-coverageandcheck-go-parity.- Drop
.formal-tracesfrom the sweep allowlist; its substring isformal-and never matches. That one was my suggestion. - The bare-segment sweep needs its own allowlist: the
formalmake target name (validation.mjs:15,explore.mjs:30, several validation tests) and whole-directory references (conformance.mjs:44,differential.mjs:75, the copy lists inmeasure-go-semantics.mjs,measure-rust-semantics.mjs,measure-semantics.mjsandmutation-reports.mjs). About 15 of today's 40 bare occurrences legitimately remain.
Editing note. The body is about 3,100 words with two stacked revision blockquotes; ledger membership is stated three times, fixture-scope four, the link check four, #219 five. The next edit should consolidate rather than append: one line per revision, one home per fact, acceptance rows referencing the hand-edit list instead of restating it.
Verified and fine: the reader must cover
copySources(execution.mjs:46scans throughquintSources);check-go-parity.mjs:143matches at any depth; theguides/README.mdgrouping matches the ledger'sreview.kindvalues exactly; 65formal-*tests; about 140replay/references;lint-profiles.mjs baseline --write; the differential's closure skip exists, so "no profile skipped as unchanged" is meaningful; no tracked binaries, no Rust compile-time includes, no path patterns in CODEOWNERS, and the three nestedformaltest directories share no file names with the moved files.Source review at
d470b2f; nothing was run.- Only the 15 Quint files naming
Taken, and the body is now revision 3, consolidated as asked: one revision line, one home per fact, and a numbered hand-edit list that Compatibility, Verification and Acceptance refer to by item instead of restating. It is about 2600 words, down from 3,100.
The three additions are items 2, 4 and 5 of that list: the witness-evidence producer in
replay/with its three port consumers and the two failure modes (loud if the consumers are fixed without it, silent if all four are missed); the seventeen interpolated paths; and the seven fake-tree tests with the twoputhelpers that do not create parents.The corrections are in: fifteen Quint files, not nineteen; three Markdown link sources; fixture freshness attributed to the fixture verifier in
check-ts;.formal-tracesdropped from the sweep; and a second allowlist for the bareformalsegments, about 15 of 40, listing the make target name and the whole-directory references.— Claude
Implemented in #223 (
claude/formal-layout-221, eight commits, 319 files, built frommainat d470b2f without waiting for #219 as Lev decided).What landed, against the plan in this issue: the move by the committed script (first commit; removed in the last), the hand-edit list items 1 to 10, the layout-aware differential reader, the six index READMEs with the directory table after the task table, the permanent relative-link pass in
scripts/check-docs.mjs, the snapshot-replay sentence in VALIDATION.md, the regenerated artifacts and refreshed ledgers.Two script rules were wrong in the first pass and are corrected in 4c4b97d: the boundary excluded a following dot, so a path ending a sentence was skipped (three cases); and the bare directory rule matched
rust/tests/formal/fixtures.rsthrough its#[path]attribute (reverted). One more root idiom than the twentynew URL('../')computations existed (two Python runner tools usedresolve(dirname(...), '..')), and one relative default insidereplay/named the old registry location. All are in the PR description.Verification: audit lane green; fixtures verify 36 artifacts, 197 histories; lint baseline 17 profiles; kernel fixtures 16/194; the differential against pre-move
mainagrees on every history in both directions for all 16 composed profiles (5,985 each way) at bytes per state x1.000; Go, Rust, Python and TypeScript checks and the smoke lane pass, with the TypeScript probe tests' 5 s timeouts shown to be inherited (6.0 s onmain, 5.3 s on the branch, same machine). The full formal workflow withexplorationenabled remains to be dispatched on the branch before merge.— Claude
Child of #220, and its Phase 1.
Summary
formal/has 140 files at its top level: 15 guides, 37 Quint models, 51 tooling files, 16 catalogs and ledgers, and 21 generated artifacts, all in one listing. The directory listing is the first thing a human or an LLM agent reads, and today it tells neither where anything is. This issue groups the tree by role so the top level is one README and six directories, each with an index, and a reader can ignore tooling and generated output and go straight to models and guides. It is Phase 1 of #220 because everything that follows there is written once against the final paths.Nothing about the models, the driver contract, the catalogs' content or the checks changes. The move is a path rewrite, a regeneration of the artifacts that embed paths and provenance, and a bounded set of code edits where paths are assembled rather than written out. Counts below were measured on
mainat d470b2f and are rebuilt from the landing base before the script runs.Today
.mdkernel/README.mdsits with the kernel.qnt: 16 conformance profiles, 5 connection models, 6 wire and rule-check models, 5 verification models (counting the uncomposedcoreprofile), 5 shared helperskernel/(29 modules),fixtures/kernel/(16 fixtures).mjs, 5.d.mts, 2.shreplay/(coordinator, profile descriptors, witness classifiers).json, including the fixed vector corporaprotocol-vectors.jsonandinvalidation-vectors.jsonand the fixtures lock*-smoke.itf.jsonand 4quint-*-vectors.jsonTarget layout
guides/is the ledger's own term;docs/is avoided because the repository root already has the VitePressdocs/.catalogs/is how AUTHORING refers to these files;models/is the repository's word for Quint files.Grouping rules
guides/formal/README.mdandgo/README.md, which keep their paths, and gainsguides/README.md; the other index READMEs are navigation aids outside the ledger, likekernel/README.mdtodaymodels/, withkernel/andfixtures/kernel/one level deeper.qntreplay/tools/.mjsand.d.mts, flat./name.mjsstay valid; names carry the verb.check.shandgenerate-traces.share oneexec nodeline each that nothing runs; deletedcatalogs/generated/fixtures-check, alreadylinguist-generated; a diff is regenerated, never reviewed line by lineIndex READMEs
One shape for all six: what the directory holds, one line per file, what to read first, and the regenerate or refresh command where one exists. No rule prose, or the indexes become the next kernel README.
models/README.mdlists the files in five groups (profiles, verification, connections, wire and rule checks, shared helpers), which is what makes a deeper directory split unnecessary.tools/README.mdlists commands by verb with themaketarget that runs each.catalogs/README.mdgives each file its purpose, validating check and refresh command.guides/README.mdgroups the pages by the ledger's review kinds (contract, tooling, coverage), the axis the task table lacks.generated/README.mdnames the generators and the freshness check.formal/README.mdkeeps the task table and gets the directory table directly after it.Hand-edit list
The script's literal rewrite covers about 3,500 occurrences of
formal/<name>across the repository: roughly 1,700 pointing at Quint files, 870 at catalogs and artifacts, 400 at guides and 340 at tools, most of them in the catalogs and the TypeScript tests. The items below are what that rewrite does not cover, or covers but needs judgment. This is the one inventory; later sections refer to its numbers.execution.mjs:kernelDirectory; thequintSourcesscan overformalandformal/kernel;isQuintSourcePath, shared with the differential and withcopySources, whose new form must keep excludingmodels/fixtures/; the model-path check at line 730; the vector generator and artifact checks at lines 813 and 814.check-kernel-fixtures.mjs:fixtureDirectory.generated-fixtures.mjs: the artifact-path check at line 30 and the lock path.check-source-audit.mjs: the guide scan becomesformal/guides/*.mdplusformal/README.mdandgo/README.md. Twenty tools compute the repository root asnew URL('../', import.meta.url), becoming'../../'. Thirteen imports of./replay/in nine tool files, two of them.d.mts, become../replay/.conformance.mjs:44anddifferential.mjs:75reference the whole directory and stay.replay/witnesses/evidence.mjslists"formal"and"formal/kernel"behind anexistsSyncguard at line 22 and interpolates the profile's model path at line 25. The Go, Rust and Python witness scanners mirror the same two folders and skip a missing one (go/witness_evidence_test.go:242,rust/tests/formal/witness.rs:262,python/tests/formal/witness.py:81). Fixing the consumers without the producer fails the Go fingerprint count loudly; missing all four makes them agree on an empty library set, and smoke passes with the Quint libraries silently dropped from the fingerprint.go/feature_replay_test.go:35andgo/settlement_control_replay_test.go:67join"formal"with a trace name;python/tests/test_redis_integration.py:29andpython/tests/test_protocol_vectors.py:30do the same for vectors.validation.mjs:196,evidence.mjs:25, eight TypeScript test sites (formal-featurestwice,formal-go-parity,formal-go-replay,formal-local-clock,formal-model-properties,formal-replay-coordinator,typescript/test/formal/coordinated-replay.ts:21),python/tests/formal/witness.py:99, five Rust sites (rust/tests/formal/inventory.rs:220,:458,:460,rust/tests/formal/witness.rs:190,rust/tests/settlement_control.rs:34) andscripts/check-docs.test.mjs:19. The residual sweep finds them because they still containformal/.formal/trees. Seven TypeScript tests (exploration,source-audit,symbolic,validation,rust-replay,mutation-shards,boundary-report) andpython/tests/test_integration_acceptance.py:98write files at literal paths. Theputhelpers atformal-source-audit.test.ts:26andformal-validation.test.ts:35do not create parents, so they fail with ENOENT until directory creation follows the new nesting, and the source-audit test must buildformal/guides/for its guide-scan case to mean anything..github/scripts/fixture-scope.mjs.main()reads the lock by literal path; stale, it lands in its catch and reportsrecompute: trueon every PR. Its catalog pattern^formal\/(execution|profiles|fixture-recipes|generated-fixtures\.lock|profile-lint-baseline)\.json$would then match nothing undercatalogs/, so a manifest-only change would skip regeneration. Both change, and.github/scripts/fixture-scope.test.mjsgains the new paths.typescript/test/formal-validation.test.ts:635pins the workflow step's condition, not a path, and needs no change.scripts/generate-docs.mjs,AGENTS.md,.gitattributes(three patterns replaced by oneformal/generated/** linguist-generated=trueline), the Makefile dispatch line, one workflow comment, and the sentence atPROTOCOL.md:14that names the deleted wrappers, repointed at themaketarget..qnt,.json,.mjsandreplay/targets gain a../models/,../catalogs/or../replay/prefix; four guide links to./kernel/README.md(AUTHORING twice, SEMANTIC-COVERAGE, TEST-MAP) and the top-level README's links to its moved neighbours follow their targets; two links inkernel/README.mdintoreplay/gain a level. Links between guides survive the joint move. The script gets a Markdown mode for these.formal/kernelin comments. Rewriting them puts comment-only Quint diffs and lock input-hash changes in the PR. Four other files name replay paths or the TypeScript driver and do not change.typescript/test/fixtures/(recipe and model provenance), and the four vector files, whose generators hash their own source into provenance. Then the ledgers: the refresh commands from Formal: readability and change-cost plan for the Quint tree (glossary, kernel docs, naming, catalog prose, ledger churn) #220 re-key the source audit and the go-parity inputs, andlint-profiles.mjs baseline --writerecords the new model paths.Compatibility
Differential against pre-move revisions.
exportRevisionfilters the reference revision'sgit ls-treelisting withisQuintSourcePathand two literal manifest paths; after the move, the candidate's predicate rejects every pre-move path, so no Quint source would be copied and the comparison againstmainwould fail or compare nothing. The fix is one layout-aware reference reader: decide the layout from the listing (whether it containsformal/catalogs/execution.jsonorformal/execution.json) and derive the manifest paths, the source predicate and kernel recognition for that layout, used byexportRevision,readManifestsandcopySourcesalike (item 1). It is transitional, deleted by the first PR after the merge base withmaincarries the new layout, the convention the kernel README already uses for the local-clockactionBindings. The PR's own differential run against pre-movemainis its test.Saved exploration snapshots.
explore.mjs --replayimports the snapshot'sconformance.mjs,explore.mjsandvalidation.mjsby literal path, so the new CLI cannot replay a pre-move snapshot. No compatibility code: artifacts are retained 14 days and the report recordsbaseRevision, so VALIDATION.md gains one sentence: replay a pre-move snapshot from a checkout of its recorded base revision, with matching dependency manifests and installed dependencies, the second condition being what the replay already enforces by comparing the saved manifest and lockfile hashes with the checkout.Migration order
formal/, adding a profile, a trace and replay modules), then the refresh-commands PR from Formal: readability and change-cost plan for the Quint tree (glossary, kernel docs, naming, catalog prose, ledger churn) #220. The census and the mapping are rebuilt from that base, and nothing else is in flight: the move conflicts with every open branch underformal/.tools/migrate-layout.mjsholds the mapping (old path to new path for every moved file) and the ordered literal-replacement table (most specific prefix first:formal/fixtures/kernel/, thenformal/kernel/, then each file name). It runsgit mvper row, rewritesformal/<name>substrings whatever relative prefix precedes them across every tracked file (git ls-files -z; the repository tracks no binaries, and 258 of its 715 tracked files contain the prefix today), and rewrites Markdown links (item 8). It is committed in the first commit and removed in the last, so the review of the move is a review of the mapping.formal/and for bare"formal"or'formal'segments. The expected remainder for the first is references toreplay/, toformal/README.md, and to the three test directories namedformalundertypescript/test/,rust/tests/andpython/tests/, which share no file names with the moved files. The expected remainder for the second is theformalmake target name in the validation plan and its tests, and the whole-directory references in the conformance, differential, three measurement tools and the mutation reports: about 15 of today's 40. Both allowlists live in the script; anything else is resolved against the list above.formal/README.md.Verification
make check: the docs build, the audit (manifest paths, source-audit inventory, semantic-case references, go-parity inputs) and the four ports' unit suites, which include the 65formal-*TypeScript tests, the fixture verifier incheck-ts, and the fixture-scope tests (item 6).make smoke: the ports read catalogs and traces from the new locations, the witness scanners find the libraries, and the Go fingerprint count agrees with the producer (item 2).make differentialwith the layout-aware reader: every history agreeing in both directions. The import-closure digests change because the paths do, so no profile is skipped as unchanged.explorationinput enabled; no new workflow.formal/**/*.mdadded toscripts/check-docs.mjs, which already has the recursive walker, so it runs undermake checkfrom then on. The existing checker walks the built site and skips external links, so it sees neither the five references intoformal/nor the links of item 8, and Phase 2 adds GLOSSARY.md and HISTORY.md, whose value is their links..qnt, the comment-stripped text before and after is byte-identical, the mechanical form of "executable model text unchanged" (item 9 expects exactly fifteen files to differ in comments); and the regenerated artifacts (item 10) have identical action histories, expected states and vector rows, with only mapped paths and recomputed provenance hashes differing.make fixtures-check,make kernel-fixturesand the lint baseline check stay as troubleshooting steps; the lanes above run them.Deferred
models/by kind: the only move that rewrites the 539 imports and makes the scan recursive, for a payoff the index already gives; Formal: converge the Quint specification on one kernel model with profile instances #165 may delete several verification models.tools/by verb: 93 cross-imports for an index's worth of benefit.Acceptance
ls formalREADME.mdand six directoriesfixture-scope.test.mjs(item 6)formal/README.md, a reader locates the guide for a topic, the model that owns a rule, the replay code, the editable catalog and the regeneration command without a second index. Contract-level navigation (where C45 is stated, tested, challenged and mapped) is the lookup command in #220, not this PRRisks
main; the PR's own run is that exercise.