CLAUDE.md · diff

git:20260709.fc37d43 to git:20260713.90c0e23

39 added, 113 removed. Audit A to A.

- # CLAUDE.md
-
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-
- ## What this is
-
- `fslc` is the verifier for **FSL**, an AI-native formal specification language. A spec (`.fsl`) is
- parsed, compiled to Z3, and checked by **bounded model checking** (BMC) and **k-induction**. Every
- command emits **machine-readable JSON** on stdout — the tool is designed to sit inside an LLM
- write→verify→repair loop, so the JSON envelope and exit codes are a stable contract, not incidental
- output. The full language reference is `docs/LANGUAGE.md`; the doc map is `docs/README.md`.
-
- ## Commands
-
- Dev setup (this is a git worktree; system Python lacks `z3`/`lark`, so a venv is required to run the
- **working-tree** code — the globally installed `fslc` on `PATH` points at `~/.fsl`, not this tree):
-
- ```bash
- python3 -m venv .venv && source .venv/bin/activate
- pip install -e ".[dev]" # lark, z3-solver, pytest + editable fslc
- ```
-
- Run the CLI (after the editable install, `fslc …` and `python -m fslc …` both target the working tree):
-
- ```bash
- fslc check specs/cart_v1.fsl # parse + types only — the fast iteration loop
- fslc verify specs/cart_v1.fsl --depth 8 # BMC: verdict + shortest counterexample/witness
- fslc verify specs/cart_v1.fsl --engine induction # k-induction (infinite-depth proof)
- fslc refine impl.fsl abs.fsl mapping.fsl # does the detailed spec refine the abstract one
- fslc chain fsl-project.toml # run business → requirements → design → impl
- ```
-
- (Other subcommands: `scenarios`, `replay`, `testgen`, `mutate`, `explain`, `html`, `typestate` —
- see `cli.py` `_build_arg_parser` for the complete surface.)
-
- Testing:
-
- ```bash
- pytest -q # full suite — SLOW (several minutes); not the inner-loop signal
- pytest tests/test_v1.py -q # one file
- pytest tests/test_v1.py -k name # one test
- pip install pytest-xdist && pytest -n auto # optional parallelism (xdist is not a dev dependency)
- ```
-
- For fast iteration, gate on `fslc check`/`verify` of the specific spec and the one or two relevant
- test files; reserve the full `pytest` run for final confirmation. **When you touch the verifier
- semantics, dialects, or any `.fsl` under `specs/`/`examples/`, the corpus snapshot
- (`tests/test_corpus_snapshot.py`) will diff — never skip it.** Regenerate it only after an
- *intended* behavior change:
-
- ```bash
- FSLC_SNAPSHOT_UPDATE=1 pytest tests/test_corpus_snapshot.py -q
- ```
-
- ## Architecture
+ @AGENTS.md
- **The pipeline (one path, every command shares it):** `parser.parse_src` runs the Lark grammar
- (`grammar.py`, including the `Ast` transformer) to produce a tuple AST. The three *frontend dialects*
- — `compose`, `requirements`, `business` — are **desugared into the same kernel AST** here
- (`compose.py`, `dialects.py`) *before* anything downstream runs, so model/BMC only ever see kernel
- specs. `model.build_spec` validates that AST and builds the `spec` dict (Z3 sorts, constants, typed
- state/actions). An engine module then consumes the `spec` dict and returns a result `dict`. `cli.py`
- wraps every result in `_envelope` (adds `{"fsl": "1.0", …}` + faithfulness metadata), prints JSON,
- and maps the `result` field to an exit code via `exit_code()`.
+ # Claude Code instructions
- This means: **a kernel-AST change ripples through `grammar → model → bmc → runtime`, and a new
- surface syntax is usually a desugaring in `dialects.py`/`compose.py` that the kernel never knows
- about.** Prefer adding to the frontend over widening the kernel.
+ ## Sources of truth
- **Dual evaluator + independent oracle (the core correctness invariant).** There are two evaluators
- of FSL semantics that must agree:
+ - The working tree and verified repository artifacts are current state; conversation history is not.
+ - The native Rust workspace is authoritative. Treat `src/fslc/` as a frozen compatibility/LSP
+ surface unless the requested outcome explicitly crosses that boundary.
+ - Accepted decisions live in `docs/DESIGN-*.md`. Current task state lives in
+ `.claude/work/active.md` when that local file exists.
+ - Do not treat proposals, plans, auto memory, or an earlier session's claims as implemented behavior.
- - `bmc.py` (~4.7k lines, the heart) — symbolic: unrolls transitions into Z3 and solves.
- - `runtime.py` `Monitor` — a concrete, Z3-free interpreter (also powers `replay` and `testgen`).
+ ## Starting or resuming work
- `tests/test_evaluator_agreement.py` cross-checks them step-by-step on witness replay. Separately,
- `tests/oracle.py` is a **Z3-independent** BFS brute-forcer driving `Monitor` to catch *false
- negatives* (something truly violated being reported verified/proved/refines) — the failure mode Z3
- bugs hide. A change that makes BMC and Monitor disagree, or that the oracle catches, is a real
- regression, not a flaky test.
+ 1. Inspect `git status --short` and the relevant implementation before editing.
+ 2. If `.claude/work/active.md` exists, compare it with the working tree and report stale claims.
+ 3. For a substantial task, use `/task-start` to create or refresh the task packet before implementation.
+ 4. State the requested outcome, affected authority surface, invariant, and narrow verification plan.
+ 5. Do not read unrelated directories without a concrete reason.
- **Module map by responsibility** (`src/fslc/`):
+ ## Implementation policy
- | Concern | Module |
- |---|---|
- | Grammar + AST transformer | `grammar.py` |
- | Parse entry / refinement-file parse | `parser.py` |
- | `build_spec`, type→Z3 sort, const eval, `FslError` | `model.py`, `values.py` |
- | BMC `verify` / k-induction `prove` / `scenarios` / traces | `bmc.py` |
- | Concrete interpreter (replay, testgen backend) | `runtime.py` |
- | Refinement checking (`refine`, chains) | `refine.py` |
- | Spec composition (namespaces, synchronized actions) | `compose.py` |
- | Frontend dialects (requirements / business desugaring) | `dialects.py` |
- | `mutate`, `explain`, `typestate`, `testgen`, `html` reports | same-named modules |
- | Project manifest runner (`fslc chain`) | `chain.py` |
- | CLI dispatch, JSON envelope, exit codes | `cli.py` |
- | Acceptance / forbidden validation, faithfulness | `acceptance.py`, `diagnostics.py` |
+ - Prefer the smallest contract-preserving change. Do not add fallback behavior or compatibility work
+ unless the requirement or an established public contract calls for it.
+ - For new language behavior, change the Rust implementation first. Change the frozen Python reference
+ only when an accepted compatibility decision requires both implementations to move.
+ - Preserve the runtime/solver dependency boundary and symbolic/concrete/BFS agreement.
+ - Keep CLI stdout machine-readable where the command contract requires JSON; diagnostics and progress
+ belong on the established channel.
+ - Never suppress an error, weaken a spec, hand-edit a generated snapshot, or expand an allowlist merely
+ to make a check green.
- **Three-layer dialects.** Specs are written in consulting (business) / requirements / design layers,
- chained by refinement so requirement IDs propagate across diagnostics. The layers are one shared
- kernel plus dialect frontends — see `docs/DESIGN-layers.md` and `docs/DESIGN-dialects.md`.
+ ## Verification and review
- **JSON/exit-code contract** (`exit_code()` in `cli.py`): `0` = verified/proved/refines/conformant/
- generated/typestate/mutated/explained/ok; `1` = violated/reachable_failed/unknown_cti/nonconformant/
- refinement_failed; `2` = spec error (parse/name/type/semantics/io, and vacuity under `--vacuity
- error`); `3` = internal error. A few commands (`testgen`, `explain --readable`, `typestate --ts`,
- `html`/`testgen` without `-o`) write raw content to stdout instead of the JSON envelope.
+ 1. Run the narrowest relevant Rust test or native `fslc` command first.
+ 2. Inspect the diff and exercise the changed contract with positive, negative, and boundary evidence.
+ 3. Run broader Rust, compatibility, or browser gates in proportion to the affected surface.
+ 4. Use the specialized FSL reviewers after semantics, coupled language files, or specs change.
+ 5. Before ending or compacting substantial work, run `/checkpoint` and preserve exact test outcomes.
- ## Conventions specific to this repo
+ ## Context management
- - **New Python files need an SPDX header**: `# SPDX-License-Identifier: Apache-2.0` + `# Copyright <year> <name>`.
- - **A language feature must move all of its files together**: grammar/model/bmc (and `runtime.py` if
- it affects concrete semantics), plus `docs/LANGUAGE.md`, `skills/fsl/reference.md`, and a
- `docs/DESIGN-<feature>.md`. Add a regression test for any behavior change. (See `CONTRIBUTING.md`.)
- - **Changing `docs/LANGUAGE.md` or the CLI surface (`src/fslc/cli.py`) also moves the site**: run
- `python tools/build_site_reference.py` to regenerate `docs/intro/language.*.html` and
- `docs/intro/cli.*.html` (committed generated output — `tests/test_site_reference_snapshot.py`
- fails the build if you forget).
- - **Do not "hollow out" specs** to make them go green — weakening an invariant to dodge a
- counterexample defeats the point. When adding/changing a `.fsl`, confirm it stays non-vacuous
- (`fslc mutate` kill-rate, `--vacuity`).
- - **The `skills/` directory is canonical**; `.claude/skills/` are symlinks to it. Agent-facing
- language rules live in `skills/fsl/reference.md` and must track grammar changes.
- - Commit one topic per change and add the key points to the `[Unreleased]` section of `CHANGELOG.md`.
+ - Delegate broad exploration and verbose failure diagnosis to the dedicated read-only agents.
+ - Return compact evidence reports, not raw grep output or full logs, to the main conversation.
+ - Preserve exact paths, symbols, commands, exit codes, and failing test names in summaries.
+ - Use `/compact` at phase boundaries and `/clear` when switching to unrelated work. A fresh session must
+ be able to reconstruct the task from the task packet, Git diff, tests, and accepted design documents.
+ - Auto memory is a convenience cache only. Team rules, task state, and design decisions belong in the
+ repository locations above.