This field log is now “Opening the Door,” at fmpl-revival-log.html.
+Redirecting…
+diff --git a/DEV.md b/DEV.md index e76e345..9e7d1ad 100644 --- a/DEV.md +++ b/DEV.md @@ -23,7 +23,7 @@ and [`project.md`](project.md). ## Workspace layout -Six workspace crates (`benches/` exists but is excluded from the workspace — +Seven workspace crates (`benches/` exists but is excluded from the workspace — it depends on the external `execution_tape` crate; see the root `Cargo.toml` comments): @@ -40,6 +40,7 @@ comments): - `fmpl-web/` — Axum server with HTMX frontend, per-user sessions, approval queue, storylet system - `fmpl-tui/` — Ratatui TUI with DAG-based conversation management (Ctrl+L for chat mode) - `fmpl-bootstrap/` — Minimal interpreter for build-time parser generation (avoids circular deps) +- `fmpl-wasm/` — wasm-bindgen bindings for the browser REPL **FMPL standard library** (`lib/`): @@ -85,8 +86,8 @@ comments): ## Documentation map -- `docs/known-gaps.md` — current limitations, grouped by root cause (the ~185 - `#[ignore]`d tests each carry a reason pointing here) +- `docs/known-gaps.md` — current limitations, grouped by root cause (74 + `#[ignore]`d tests; the integration-suite ones carry a reason pointing here) - `docs/design-principles.md` — durable invariants (DESIGN-001…005) - `docs/STANDARDS.md` — documentation standards for design docs, plans, specs - `specs/README.md` — spec index and crate overview diff --git a/README.md b/README.md index cb34792..2e5e133 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,10 @@ that grammar (see [`fmpl.ebnf`](fmpl.ebnf)) seeded this project. The syntax is only lightly similar to the 1992 language, and everything beyond the grammar — the streaming model, first-class PEG grammars, the indexed-RPN VM, capability security, the metacircular bootstrap — is new design. The MUD lineage is -first-hand: Nunley also co-wrote cool++, a C++ rewrite of Stephen White's -CoolMUD (White created MOO, from which LambdaMOO was forked). See -[`project.md`](project.md) for the full north star. +first-hand: Nunley was one of the developers on cool++/coldmud; ColdStore — a +C++ persistent object store and VM toolkit from that era — is the better +reference for what he was doing then. See [`project.md`](project.md) for the +full north star. ## A taste @@ -110,7 +111,8 @@ Source → Lexer (logos) → Parser → AST → Compiler → Indexed RPN bytecod - [Engineering tour](https://mparrett.github.io/fmpl/fmpl-tour.html) — one-page overview: architecture, what works, what doesn't - [Browser REPL](https://mparrett.github.io/fmpl/repl.html) — fmpl-core as WebAssembly, live on GitHub Pages -- Field logs — engineering retrospectives: [revitalizing the agent-written codebase](https://mparrett.github.io/fmpl/fmpl-rehab-log.html) and [closing the metacircular-parser gap](https://mparrett.github.io/fmpl/fmpl-field-log.html) +- Field logs — engineering retrospectives: [Opening the Door](https://mparrett.github.io/fmpl/fmpl-revival-log.html) (revitalizing the agent-written codebase) and [Closing the Loop](https://mparrett.github.io/fmpl/fmpl-field-log.html) (closing the metacircular-parser gap) +- [The Layer Underneath](https://mparrett.github.io/fmpl/fmpl-process.html) — the agent process that wrote FMPL: requirement lattice, hook-enforced state machine, adversarial review, read out of `archive/agent-harness` - [`project.md`](project.md) — north star, principles, design lineage - [`docs/design-principles.md`](docs/design-principles.md) — durable design invariants - [`AGENTS.md`](AGENTS.md) — workflow rules and gotchas for agents and humans diff --git a/docs/fmpl-field-log.html b/docs/fmpl-field-log.html index ec2c267..c061fc7 100644 --- a/docs/fmpl-field-log.html +++ b/docs/fmpl-field-log.html @@ -381,7 +381,7 @@
After the public-release rehabilitation (the 24-commit sweep reviewed in PR #1 and chronicled in its own field log), the repo had a working build, CI, and an honest ledger of what didn’t work: docs/known-gaps.md, dominated by one bucket of ~120 ignored tests labeled metacircular parser not yet complete. FMPL’s flagship commitment (DESIGN-001) is that the canonical parser is written in FMPL and the Rust parser is stage-0 scaffolding — so this bucket wasn’t a feature gap, it was the language not yet being what it claims to be.
After the public-release revitalization (the 24-commit sweep reviewed in PR #1 and chronicled in Opening the Door), the repo had a working build, CI, and an honest ledger of what didn’t work: docs/known-gaps.md, dominated by one bucket of ~120 ignored tests labeled metacircular parser not yet complete. FMPL’s flagship commitment (DESIGN-001) is that the canonical parser is written in FMPL and the Rust parser is stage-0 scaffolding — so this bucket wasn’t a feature gap, it was the language not yet being what it claims to be.
The plan ordered three workstreams deliberately. First a documentation test harness, because the parser work would change language behavior and we wanted the docs to break loudly when it did. Then a WebAssembly build — independent of parser internals, safe to do while thinking. Then the critical path. The sequencing mattered: by the time the grammar changed, every code block in the tutorial was executing in CI and would have caught a regression the unit suite missed.
Process archaeology · github.com/mparrett/fmpl
+FMPL’s autonomous loop carried a requirements lattice, a hook-enforced state machine, adversarial review rounds, and an iteration log that recorded its own failures. None of it was legible from the surface of the repo. Here it is.
+ + +Diagram from PROMPT.md on the archive branch; block messages from ralph-gate.py at 2a6db48, abridged. A companion to the field logs Opening the Door and Closing the Loop.
Most published writing about autonomous coding agents describes either the prompt or the output. This is the middle: what a loop looks like when someone has been running it long enough to know where it lies to itself. Norman Nunley ran one against FMPL for five months, and the arrangement he assembled to keep it honest is more interesting than most of what gets written about the practice.
+It reached us as thirty thousand lines to triage before we had read enough to understand them, because that is how a process layer arrives when you fork someone’s solo project — legible to its participants, opaque from outside. Sidelining it to an archive branch was the right call for a repo with different tooling, and it made the layer invisible rather than merely unfamiliar. This page opens it back up.
+Everything here is quoted from primary sources on archive/agent-harness: the prompt the loop ran under, the hooks that gated it, the requirement documents, the iteration log, the memory store. All of it predates the cleanup this fork performed — the state of the project as its author left it, not as we rearranged it.
All of it sits on one branch, archive/agent-harness, at the commit this fork found on arrival. Nothing was deleted to make the main branch legible; the harness was moved intact in a single commit, and the branch is the “before” snapshot. Four bodies of material matter:
PreToolUse gate that blocks tool calls not permitted in the current state, and a PostToolUse transition hook that advances it.Some of this is off-the-shelf and some is written for FMPL. The memory system arrived as an installed stack (agentic_stack_version 0.15.0, 2026-05-09, with adapters for both Claude Code and Codex); the loop driver lives in a ralph-loop/ directory shared across the author’s projects; docs/superpowers/ takes its name from an existing methodology. PROMPT.md, the roadmap, the requirement documents, and the iteration log are this project’s own. Nunley assembled all of it and aimed it at a language implementation for five months, which is the part that produced the artifact.
Do not expect to open the archive branch and read the hooks. At the tip, ralph.sh and every file under .claude/hooks/ is a symlink into /Users/ndn/development/ralph-loop/ — a path that existed only on the author’s machine. The loop driver was shared across projects and was never repo-resident.
The real sources survive earlier in history, before the symlink conversion. git show 2a6db48:.claude/hooks/ralph-gate.py is the full gate; git show 38b551c:.claude/settings.json is the wiring. Every hook quotation on this page comes from those commits.
The planning layer is a five-level structure, and each level is machine-checkable against the one above it:
+EPIC-010 Bootstrap Parity 31 epic documents
+ └─ STORY-0043 as a language implementor… 100 stories
+ └─ AC-1 … AC-5 impact:correctness 331 acceptance criteria
+ seam:integration
+ └─ SCENARIO-0030 proof seam, 88 scenarios
+ preconditions, observables,
+ execution command
+ └─ ITER-0001 delivered 22 logged iterations
+
+ Two properties make this more than paperwork. First, every acceptance criterion carries an impact and a seam — where the claim must be proven. The distribution is a fair summary of the project’s testing philosophy: 246 integration, 61 unit, 12 app-level, 7 process-level, 5 end-to-end. This is a codebase that decided early that its interesting failures live between components, and staffed its acceptance criteria accordingly.
Second, scenarios carry an execution command. A scenario begins life with Automation status: pending and Execution command: TBD, and is “promoted to sentinel” when a real command exists that proves it:
SCENARIO-0030: promoted to sentinel,
+ command = `cargo test -p fmpl-core --test ast_to_ir_parity parity_integer`
+SCENARIO-0031 through SCENARIO-0037: marked BLOCKED:ITER-0002
+
+ A blocked scenario names the iteration that will unblock it. The coverage ledger closes the loop in the other direction, classifying each source spec by how much of it has been lifted into stories and scenarios — specs/grammar-system.md at 18 stories and 14 scenarios, specs/vm.md at 15 and 9, each marked covered. Requirements trace down to commands; commands trace back up to specs.
The idiom outlived the harness. docs/behavior-scenarios.md on the main branch is a direct descendant, and it is a literal build input: fmpl-core’s build script generates a test suite from it, so moving the file breaks the build rather than silently orphaning the corpus.
An LLM told to run tests before committing will usually run tests before committing. Usually is the problem. The loop was configured so that the instruction was not advice: a PreToolUse hook read the current state from .ralph-state.json and returned exit code 2 — a hard block, with the reason fed back to the agent — for any tool call the state did not permit.
cargo test
+ The iteration starts by proving the tree is green. Every other tool is blocked until it is.jj issue ready, then jj issue show. No reading, no editing, no exploring.jj issue close is blocked, forcing the agent to commit to a task.Write is blocked until docs/codebase/ has been read. Unfiltered cargo output is blocked — commands must pipe through grep/head, which is context budgeting enforced at the tool boundary. Re-picking a task is blocked: “you are committed to the current task.”tests_passed, clippy_passed, and health_check_passed. Passing the gates once buys nothing after a change.jj describe is blocked while health_check_passed is false, with an explicit reason: the VERIFY step may have run a filtered subset, so run the full suite now.A second arc — RESEARCH → DOCUMENT → COMMIT — handles work that needs discovery first, and gates differently: RESEARCH allows reads and dispatched subagents but blocks Bash outside the issue tracker, and DOCUMENT permits writes only to docs/codebase/ and AGENTS.md. Attempting to write code from either state silently transitions to IMPLEMENT rather than failing, so the machine bends where the work genuinely changed shape and holds where it did not.
The details are where the operating experience shows. The gate refuses to read ~/.cargo/registry and points at a documentation tool instead. It refuses cargo doc --open, which does nothing headless. It refuses rustc on a file in /tmp, because a throwaway compile is how a loop convinces itself something works without adding a test. It honors a list of protected files, so edits a human made outside the loop cannot be reverted by it. And it checks whether the process that wrote the state file is still alive — if the driver died, the stale state is ignored rather than blocking an interactive session forever. Each of those is a scar.
The prompt carries the same texture: research budget under 40% of context and implementation under 60%; “the issue description IS your research”; a three-strike rule that converts a repeated error into a written spec and a stop; and a flat prohibition on the agent’s own to-do tool, because the issue tracker is the task list and two task lists means neither is true. Each iteration ends by printing exactly one line — COMPLETED:<id> or BLOCKED:<id> — which is what makes the loop a loop.
PAR — parallel adversarial review — is the load-bearing reviewer in this system. Rounds run against a plan or a diff and return a verdict with numbered findings. It is the answer to the question the first field log failed to ask: who reviews a solo agent’s work?
+ITER-0004c shows it working. Three PAR rounds ran against the iteration’s scope before implementation began and surfaced 26 actionable findings. What they changed:
+Lambda, Let, Match, Call, List, Map, or Block. Verified against the source and corrected before anything was built against it.Then the harder result. The iteration’s plan called for a code transformer written in FMPL. It was built — grammar plus a Rust driver — and a PAR spec-compliance review failed it: three of six rules were implementable cleanly, and the other three needed left-hand-side context tracking the grammar engine cannot express. The transformer was abandoned, the change dropped, and the work continued by hand. Round one had already flagged hand-migration as viable, so the fallback was on record before the attempt.
+An adversarial review that only ever approves is a formality. This one killed a piece of finished work on spec-compliance grounds and sent the iteration down its own second-choice path.
+The iteration log runs to 22 entries across six days, 2026-05-08 to 2026-05-13. Most entries are what you would expect: stories delivered, tasks executed, scenarios promoted, test counts. The interesting one is ITER-0004b, whose heading carries (PARTIAL) and whose dates read completed 2026-05-08; reconciled 2026-05-09.
+The iteration’s goal was a single canonical representation for structured values. A commit landed claiming “Final step of ITER-0004b.” A day later the reconciliation says why that was false, under its own heading — Why the gap wasn’t caught at commit time:
+++The Phase C commit message claims “Final step of ITER-0004b — single canonical representation,” and that claim is true at the Rust runtime level but false at the FMPL stdlib and AST/parser-surface level. The 16
+#[ignore]’d tests inoptimizer_integration.rswere the canary that should have failed loudly to catch the gap; instead they sit silent. Workspace tests pass because nothing currently exercises a path that would surface the parser-shape mismatch.
The entry then enumerates exactly what was skipped — a transformer never built, five standard-library files still in the old syntax with per-file counts, an optimizer wiring step passed over, parser surfaces still present — and reschedules all of it into a named successor iteration under a rule the process calls deferring-work-must-reschedule. Three lessons are recorded, including one that reads as a direct rebuke of the commit that caused it: a commit message that says “final step of X” should be checked against X’s stated acceptance criteria.
+A green test suite said the iteration was done. The requirements lattice said otherwise, someone checked, and the disagreement went into the permanent log rather than getting resolved by moving the goalposts. That is the whole argument for keeping requirements separate from tests: the tests can only tell you about the code you wrote.
+ +The third lesson from that entry is the one worth stealing: #[ignore]’d tests are a deferred-acceptance contract. The 16 silent optimizer tests were the iteration’s own acceptance criteria, written down and then not run. They should be un-ignored by whichever iteration delivers their preconditions, or tracked as scheduled work with an owner.
This fork arrived at 185 ignored tests and turned them into docs/known-gaps.md — a ledger grouped by root cause, with each ignore carrying a machine-readable reason. Same idea, reached from the outside.
The most ambitious piece of the harness is the memory system, installed as a versioned stack on 2026-05-09, two days into the documented sprint. Hooks capture episodic entries as the agent works. A “dream cycle” runs unattended: cluster the episodes, extract structured patterns, stage them as candidates with lifecycle metadata, apply a heuristic prefilter that drops obvious junk, decay old entries, and write a review queue for the next session. Its docstring is explicit about the boundaries it will not cross:
+++Never: subjective validation (host agent reviews via CLI tools) · promotion to
+LESSONS.md(graduate.pydoes that) · git commit (unattended repo writes are dangerous on a host hook).
A mechanical process stages; a reasoning process judges; neither writes to the repository unattended. That separation is the right one, and it is drawn in code rather than in a README.
+The yield is smaller than the machinery, though the clock is a fair defense: the stack had four days between installation and the archive tip. In that window it accumulated 1,648 episodic entries, 23 candidates awaiting review, 2 graduated, 9 rejected, and 4 lessons in the semantic store. Read the queue and the reason is plain — the top-priority candidates are mostly failure telemetry (“this skill has failed 10 times in 14 days, flag for rewrite”), which is useful operational signal and not the durable engineering lesson the pipeline was built to distill. One graduated lesson is genuinely good, about enumerating rename targets after a compiler-checked deletion rather than before. The ratio of infrastructure to insight is roughly 107 files to 4 lessons.
+Two things follow. The first is a caution for anyone reading this as a template: the review queue graduates lessons, not code. It is not a code review mechanism, and reading it as one would put the reviewer in the wrong place entirely — PAR is where code gets adjudicated. The second is chronological. The pre-agentic archive inside .agent/ is dated January 2026 and records a 100-iteration, three-hour loop run against the terminal UI, tasks checked off and 222 tests passing. The commit history agrees: 131 commits in January against 48 in May. The memory stack is a late addition to a loop that had already been running for months.
This fork sidelined the harness for a reason that has nothing to do with its quality: we drive the repo with different agents on a different loop, and a process encoded for someone else’s tooling sitting where a visitor looks first is misleading in both directions. That decision stands. It also had a cost, and the cost is specific.
+Invariants the loop enforced in code — full suite green before commit, design docs read before the first write, filtered output — became prose in AGENTS.md that an agent is trusted to remember. Trust is weaker than a hook returning exit code 2. specs/portable-invariant-enforcement.md audits that gap and concludes it is narrower than it first appears: CI and the build script already enforce most of it, and the house idiom for the rest is a repo-resident test that asserts the invariant. Git hooks are rejected as a tier for the same reason they would fail here — this repo is driven with jj, which skips them.
What survived the move without argument, because it was portable to begin with: the specs directory and its curated index pairing each spec with the code implementing it; the behavior corpus, now a build input; the habit of an ignored test carrying its reason; and the discipline of a ledger that makes “burn down the bucket” a measurable goal rather than a sentiment.
+Engineering field log · github.com/mparrett/fmpl
-Revitalizing an agent-written codebase: 24 commits from a repo that wouldn’t build on a fresh clone to a published fork with CI, executable claims, and an honest ledger of its own gaps.
- - -This log is the prequel to Closing the Loop and the making-of behind the engineering tour. FMPL itself is Norman Nunley’s Rust-built descendant of a 1992 MUD language; this is the log of reviving his revival.
-The repo we inherited was written almost entirely by an autonomous agent loop — “ralph,” set up and steered by the project’s creator, Norman Nunley, iterating against a roadmap for months. What it produced is genuinely impressive: a working language with a bytecode VM, a PEG grammar engine, an object system, and a parser that generates itself. What it also produced is the sediment of its own process: a 107-file .agent/ memory system, 41 files of “superpowers” process documentation, ralph driver scripts, run logs, a bespoke .git-issue/ tracker, and prompt files — roughly 30,000 lines of machinery about building the thing, interleaved with the thing.
And the repo did not build. Not subtly: cargo build failed at manifest resolution, before compiling a line, because one workspace member had a path dependency on execution_tape — a “verified bytecode VM” crate that existed on exactly one machine, and this wasn’t it. Cargo resolves every member’s manifest before building any of them, so the missing path failed everything, including cargo build -p fmpl-cli. The agent never noticed because the agent never had to do a fresh clone.
The first day was recon before surgery: a static read plus live runs of the REPL, TUI, and web app, written up as findings before any code changed. That ordering was deliberate. With an inherited codebase — especially an agent-inherited one — the temptation is to start fixing what you see; the discipline is to first record what is actually true, because you will not get a second chance to observe the artifact undisturbed.
-The central judgment call of the whole rehabilitation: what to do with the agent’s machinery. Deleting it would have been clean and irreversible — and wrong twice over. The harness is part of the project’s story (the tour says plainly that the implementation is agent-written), and it may be useful again if an autonomous loop resumes. Keeping it in place was also wrong: a public repo where the first thing a visitor meets is the scaffolding, not the building.
-The answer was a third category. Everything the agent needed and a human doesn’t was moved — intact, in one commit — to an archive/agent-harness branch. Nothing was deleted; the main branch simply stopped carrying it. The same commit unified the agent’s CLAUDE.md into a single human-and-agent AGENTS.md, later split again into lean workflow rules plus a DEV.md inventory. The test of the triage: months later, nobody has needed anything back from the archive — but it costs nothing to keep, and it makes the history honest.
“Sideline, don’t delete” became the arc’s standing rule, and it echoes forward: the pre-rehab branch freezes the untouched inherited tip as PR #1’s base, the advisory-PR convention pins every later phase boundary the same way, and the dormant cross_compile feature keeps the execution_tape integration described in comments rather than erased. A revitalization you can diff against its “before” is one you can trust.
The work was planned as tiers with a deliberate order — each one meaningless without the one below it — and built as a stack of branches, each on the previous, so the whole thing could be reviewed bottom-up and landed as one fast-forward:
-execution_tape dependency (the cross_compile feature goes dormant, documented in Cargo.toml comments), untrack build artifacts, add README, LICENSE, a justfile, and a CI workflow. Everything else assumes this floor exists.archive/agent-harness (§2). Done early so every later diff is about the language, not the scaffolding.if as a guard alias for when (the docs’ own idiom didn’t parse), universal type predicates on every value, a targeted hint when @{…} misroutes, and a DEMO.md sweep against a live REPL.The ordering encodes a belief about inherited code: credibility is restored outside-in. A stranger’s first five minutes are clone, build, run an example from the README, hit an edge. Ergonomic polish on a repo that doesn’t build is decoration; deep test work before the harness is sidelined gets tangled in machinery that’s about to move.
-The repo had never had CI — the agent ran its own checks, on its own machine, in its own environment. The very first CI run failed twice, and both failures were the same lesson wearing different clothes: properties nobody asserts are properties nobody has.
-The first failure was the determinism check: regenerate the parser twice, byte-compare. The outputs differed every run — grammar rules travel through a HashMap, so each generator process emitted the parser’s functions in a different random order. Rust doesn’t care about definition order, so ~1,390 tests had been green for months over a nondeterministic build. The fix was one sort; the point is that the assertion had existed the whole time as an ignored test nobody ran.
The second was humbler: CI installs the latest stable toolchain, and its clippy was newer than the local one, with new lints. A locally-clean build is only evidence about the local toolchain. That became a standing rule in AGENTS.md — keep local Rust current, because a clean local clippy is not proof of a green CI.
A third repair completed the set: the checked-arithmetic work from Tier 4 had moved a panic rather than removing it — the generated parser’s digit-fold actions were emitted with .unwrap() on the newly-fallible ops. Threading Result through generated grammar actions (and rejecting the tempting catch_unwind shortcut, which would have poisoned VM mutexes) is the change that started the parser-generator epoch history at 6 — the same counter the next arc drove to 9.
The suite we inherited had ~185 ignored tests with no stated reasons — indistinguishable from neglect, and unusable as a map. The Tier 4 pass read every one, grouped them by root cause, wrote a machine-readable reason into each #[ignore], and distilled the result into docs/known-gaps.md: metacircular parser (~120), pattern matching (~50), pending design decisions, odds and ends.
This was the quiet keystone of the whole revitalization. An agent-written test suite encodes months of intent — tests written ahead of features, aspirational behavior pinned before it exists — but intent you can’t read is indistinguishable from cruft. Turning the ignore pile into a ledger did two things: it made the repo honest to visitors (the count reads as roadmap, not rot), and it made progress measurable. The payoff came fast: the next arc picked the ~120 bucket off the top of the ledger, burned it to zero in two days, and could prove it — because the ledger defined what zero meant.
-Publishing had its own fork points. The upstream repo wasn’t pushable (403 — no access), which settled the topology: a fork carries the work, upstream remains the lineage. The rehabilitated history needed to be reviewable without being mergeable — the fork’s main already contained everything — which produced the advisory-PR convention: freeze a pre-rehab branch at the inherited tip, open a PR from main against it, mark it review-only, close it when read. The frozen base is the “before” snapshot; merging would destroy it. That convention outlived the rehab — every phase since gets an advisory PR based on the previous phase’s frozen tip.
The last mile was the engineering tour: a single page that says what the thing is, what provably works, and what honestly doesn’t — every claim in it verified against a fresh REPL, like the TUTORIAL sweep before it. GitHub Pages hosting came with one classic paper cut: Jekyll choked on the design docs, so .nojekyll ships in docs/. (Pages later moved to Actions-based deploys in the wasm arc; the file remains as a fossil of this one.)
execution_tape could have been vendored to keep cross_compile alive. Dormancy won: a fresh clone must build with zero external state, and the feature’s seam is documented for whenever the crate returns.Revitalizing an agent-written codebase turned out to differ from ordinary legacy work in one repeated way: the artifact is strong where humans are weak, and weak where humans are strong. The agent never got tired, so the language core is deep and the test suite vast. The agent never changed machines, so the build was broken for everyone else on earth. It never had a reviewer, so nothing distinguished roadmap from rot. Every tier addressed some version of that asymmetry — and the fixes were mostly social artifacts, not code: a README, a ledger, a review convention, an archive, CI as an outside observer.
-What the later arcs inherited from this one: a build a stranger can run, a ledger that made “burn down the bucket” a measurable goal, an advisory-PR convention still in use at #5 and #6, the epoch discipline for generated-parser changes, and the habit — docs verified against a live REPL — that grew into the doctest harness in the very next phase.
-archive/agent-harness, none deletedknown-gaps.mdThis field log is now “Opening the Door,” at fmpl-revival-log.html.
+Redirecting…
+