Skip to content

finixpos: production payment-alignment case study (task, corpora, reference models, 7-model study) - #4

Open
jdubray wants to merge 54 commits into
mainfrom
js-sam-tla-phase3
Open

finixpos: production payment-alignment case study (task, corpora, reference models, 7-model study)#4
jdubray wants to merge 54 commits into
mainfrom
js-sam-tla-phase3

Conversation

@jdubray

@jdubray jdubray commented Jul 6, 2026

Copy link
Copy Markdown
Owner

What this adds

The finixpos task and study: a SysMoBench case study on a production restaurant POS ↔ PAX A920 Pro ↔ Finix payment state-alignment protocol, written in SAM semantics. This branch is the artifact set referenced by the companion arXiv article ("Artifacts and reference models at https://github.com/jdubray/SysMoBench-1").

Task + corpora

  • tla_eval/tasks/finixpos/ — task.yaml, pinned 7-variable observable-state contract, prompts for three arms (JS-SAM / lean next() / constrained TLA+), verbatim source packet with provenance (EXTRACTION.md).
  • data/sys_traces/finixpos* — four real-execution trace corpora (75/101-window pre-fix, 24-scenario post-fix v1/v2), generated by an instrumented copy of the production workflow against the repo's own terminal emulator + failure-injection proxy (data/patches/finixpos_trace.patch).
  • trace_loader.py extended to the harness's {pre, action, data, post} event form (+ test).

Reference models + correctness deliverable

  • Hand-written TLC-checked models (reference/FinixPOS*.tla): base + environment, crash/staleness/two-attempt/refund extensions, patched-semantics variants; annotated counterexamples for every confirmed gap; mutation + positive controls (CONTROLS.md).
  • Real Finix sandbox reproduction evidence (reference/sandbox_repro/) — including the demonstrated code/emulator-vs-real-rails 422-shape divergence.
  • CONTAMINATION.md, EXTENDED_FAILURE_MODEL.md, corpus_coverage.md.

Study

  • Drivers: scripts/finixpos_tv.py (mechanical three-arm replay), finixpos_phase3_study.py (resumable, prompt-cached), rescore/analysis/post-fix scripts.
  • Results of record: 7 models × 3 arms × N=5 (Fable 5, Opus 4.8, Sonnet 4.6, Haiku 4.5, mistral-large-2512, devstral-2512, labs-leanstral-1-5), both corpora, all generated specs + per-window statuses committed (output/finixpos_*).
  • Headline: the lean next() contract is best-or-tied and never unscoreable for all seven models across two vendors; TLA+ deficits are dominated by checkability + the guard idiom; semantic error is language-independent. The cross-vendor replication is of ordering and taxonomy, not level.
  • Full write-up: docs/finixpos_study_results.md (design: docs/finixpos_study_plan.md).

Not included

The article itself (published independently on arXiv) and the production-code fix patches (maintained in the POS repo). .gitignore updated accordingly.

Validation

  • Reference specs 75/75 and 101/101 in all three languages; rule-drop mutations fail identical window sets cross-language.
  • make test additions: trace-loader event-form test passes (one pre-existing unrelated failure on clean tree, documented in results doc).

🤖 Generated with Claude Code

https://claude.ai/code/session_011bXyeoAhL6iz3CibTtU2Qv

b-erdem and others added 30 commits June 22, 2026 21:49
Phase-4 builds its per-invariant cfg by appending the expert invariant to
the model's phase-2 base cfg (forwarded via --config-file). If that base
cfg carries the model's own INVARIANT/PROPERTY/CONSTRAINT/ACTION_CONSTRAINT/
POSTCONDITION sections, the expert check is contaminated: TLC can halt on
the model's assertion before the expert one gets a clean verdict (false
FAIL), or a model CONSTRAINT/ACTION_CONSTRAINT shrinks the state space so a
violating path is never reached (false PASS). This affects every model
whose phase-2 cfg flows into phase 3, not just BYO methods that emit a cfg.

Strip those sections from the base cfg before composing the per-invariant
cfg sent to TLC. The keyword set follows TLC's ModelConfig and includes the
singular and plural forms. Classification is comment-aware (handles \* and
(* *) comments, including multi-line) and treats a keyword line as a
section header only when it is not an assignment, so a constant whose name
collides with a keyword is not dropped. Comments inside a dropped section
are dropped with it, and CRLF is normalized.

Add regression tests: unit coverage for the keyword and comment forms, the
keyword-as-constant case, and the safety/liveness composition paths, plus
TLC-gated behavioral tests that show the verdict actually changes for
INVARIANT (plain and comment-prefixed) and CONSTRAINT contamination.
* ci: run pytest on push and PR

Add a GitHub Actions workflow that installs the package, sets up Java, fetches
tla2tools.jar (pinned to the same version as scripts/setup_tools.py), and runs
the test suite. The TLA+ tests, including the TLC-gated behavioral ones, run
in CI rather than skipping.

Scoped to tests/test_languages for now. tests/test_models is left out:
test_model_connection.py makes live provider API calls that need secrets, and
test_litellm_adapter.py has pre-existing failures unrelated to this workflow.

* ci: add python matrix, concurrency cancel, and pyproject cache key

* ci: narrow matrix to 3.10/3.11 and set requires-python >=3.10 (mcp needs 3.10)

---------

Co-authored-by: Qian-Cheng-nju <Qian-Cheng-nju@users.noreply.github.com>
Co-authored-by: Qian-Cheng-nju <Qian-Cheng-nju@users.noreply.github.com>
JavaScript + SAM pattern (sam.js.org) via @cognitive-fab/sam-pattern and
@cognitive-fab/sam-fsm: the bench's first non-formal-language backend, and
the first to use the direct Phase 3 transition-validation path.

- tla_eval/languages/js_sam.py: LanguageBackend subclass; shells out to a
  Node helper (tools/js-sam/cli.mjs, JSON-in/JSON-out) like Alloy/PAT.
  Phase 2/4 use the sam-pattern bundled behavior explorer (depthMax 6,
  bench-wide constant). Agent translators remap to a direct API call,
  matching Alloy/PAT precedent.
- tools/js-sam: helper CLI (ping/validate/check/transitions/invariants);
  captures sam-pattern acceptor throws (async-wrapped) and treats gated
  'unexpected action' rejections as legal model behavior.
- Direct Phase 3 path: new language-neutral NDJSON trace loader
  (trace_loader.py) + implementation of the stubbed direct branch in
  transition_validation.py per add_new_spec_language.md 3.3.
- spin task: js-sam prompts (Rocket Launcher in-context example, mandatory
  module contract) and 3 starter invariant templates.
- scripts/run_benchmark.py: TLA+ tool gate now applies only when the TLA+
  backend is selected; other languages use backend.check_available().
- Tests: 29 new (backend integration via real Node, trace loader,
  direct-path evaluator) plus known-good/known-bad spec fixtures and
  synthetic traces. docs/js_sam_backend_spec.md records the design and
  the implementation map.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
From-scratch explainer for reviewers new to the integration: what
SysMoBench is, what the JS-SAM backend adds, the module contract, the
reference and adversarial fixtures, every test that was run and what it
demonstrates, and the honest list of what is not yet demonstrated
(no live LLM runs, real spin traces not in the repo).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
claude-sonnet-4-20250514 generated a JS-SAM spin spec from the Asterinas
source and passed Phase 1 (first attempt, no correction loop), Phase 2
(clean depth-6 exploration), and Phase 4 (3/3 expert invariants via the
live translation path). Phase 3 remains blocked on captured traces.

The run surfaced a config bug: the claude entry requested
max_tokens 200000, which the Anthropic API rejects for claude-sonnet-4
(output cap 64000). Lowered with a dated probe note; this entry also
backs the Alloy/PAT invariant-translator remaps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_run_helper assumed a POSIX host: it called os.getuid()/os.getgid()
(absent on Windows) and mounted the helper and spec at their host paths,
which are invalid container destinations for Windows C:\ paths.

Decouple host paths from fixed in-container mount points (/opt/js-sam,
/work), normalize -v sources to forward slashes, rewrite specPath to the
container path, and pick a non-root --user via a hasattr(os, "getuid")
guard (host uid:gid on POSIX, fixed 1000:1000 on Windows). Behavior is
unchanged on Linux/macOS; the JS-SAM backend now runs on a Windows host
against Docker Desktop. Verified by the existing container integration
tests plus a real Phase 1/2 sandbox run.
claude-sonnet-4-20250514 retired 2026-06-15, so the `claude` entry
returned not_found. Repoint it to claude-opus-4-8.

Opus 4.7+, Fable 5, and Mythos 5 removed temperature/top_p/top_k and
return a 400 if any are sent. Dropping them from models.yaml is not
enough — the adapter falls back to GenerationConfig defaults and sent
temperature unconditionally, and litellm's drop_params does not yet
cover these newer models. Add _should_omit_sampling_params() (mirroring
_should_omit_top_p) and guard both temperature and top_p with it.

Verified end to end: get_configured_model("claude").generate_direct(...)
succeeds, and a full spin/JS-SAM run passes Phases 1, 2, and 4.
_display_evaluation_results prints ✓/✗ status glyphs, which raise
UnicodeEncodeError on consoles whose default encoding can't represent
them (e.g. Windows cp1252) — the evaluation succeeds, then the final
summary print crashes. Reconfigure sys.stdout/sys.stderr to UTF-8 at
startup; a no-op where they already speak it.
"States explored" was hardcoded to 0 for any backend that isn't TLA+:
runtime_check only parsed it from TLC output, and the invariant
evaluator never set the top-level field at all (so even TLA+ Phase 4
showed 0). The JS-SAM helper already returns stepsExplored — it just
wasn't threaded through.

Add states_explored to ModelCheckOutcome and populate it from the JS-SAM
check response; seed runtime_check's states from the backend outcome
(TLA+ still overrides via its own parse); and set the invariant
evaluator's top-level states_explored to the sum across cases. Also fix
the JS-SAM Phase-4 metadata key (steps_explored -> states_explored) so
the evaluator actually reads it.

Verified on spin/JS-SAM: Phase 2 reports 326592 steps (depthMax 6),
Phase 4 reports 979776 (326592 x 3 invariants).
Adds the trace-capture harness for the spin task so JS-SAM Phase 3
(transition validation) runs against real Asterinas spinlock behavior
rather than being blocked on missing traces.

- scripts/harness/spin/run.sh: clone Asterinas v0.16.0 (LF), apply the
  reference instrumentation + the 2-thread ktest patch, build+run under
  QEMU-TCG on an ext4 docker volume, parse serial JSON into NDJSON.
- scripts/harness/spin/build_and_test.sh: in-container build+run.
- scripts/harness/spin/parse_traces.py: folds kernel events
  (TryAcquireBlocking/AcquireSuccess/AcquireFail/Release) into
  (pre, post) windows keyed on the objective {lockHeld, lockHolder}
  state; JS-SAM's projection rule ignores model-internal bookkeeping.
- data/patches/spin_2thread_ktest.patch: adds test_spin_2thread, a
  2-actor contention scenario (try_lock avoids single-CPU deadlock);
  the reference ktests are single-actor.
- data/sys_traces/spin/spin_2thread.ndjson: the captured 8-window trace
  set (force-added; traces are normally generated, not checked in).

The v0.16.0 pin (patch doesn't apply to main), LF normalization of the
clone and patches, and the ext4 build volume (host FS lacks fallocate)
are what make this Linux-oriented flow run under Docker Desktop on
Windows. Verified: Phase 3 scores 87.5% (7/8) on the Opus 4.8 spin model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Documents the first four-phase JS-SAM run on the spin task (Opus 4.8):
method, the Phase 3 kernel trace-capture pipeline, results (Phase 3
87.5%, with the try_lock-from-free modeling discrepancy), the
cross-platform fixes, limitations, and reproduction steps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Broadens Phase-3 coverage from 8 to 28 windows. test_spin_seq exercises
alternating holders, try_lock successes, and repeated failed contention
(still single-CPU-safe). build_and_test.sh now takes the ktest name as an
argument and run.sh loops over both scenarios into per-scenario NDJSON.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generalizes the agent-based invariant translator so backends other than
TLA+ can use it instead of remapping to a single direct LLM call
(maintainer Open Question 4).

- New evaluation/semantics/agent_translation.py: language-neutral core
  (CLI selection, workspace setup, subprocess exec, output read). Caller
  supplies the workspace files, agent instructions, and output parser.
- TLA+ AgentInvariantTranslator now delegates to it (behavior preserved;
  its _select_agent_cli/_execute_agent_cli move into the shared module).
- JS-SAM translate_invariants: translator=claude-code/codex now drives
  the agent CLI (JS predicates against the generated spec, reusing the
  existing JSON parser); "claude"/explicit model still uses the direct
  call. End-to-end agent runs require the claude-code/codex CLI.
- Tests mock the CLI boundary: shared-core success/missing-output/failure
  /codex paths, JS-SAM agent routing, and TLA+ delegation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds sonnet (Sonnet 4.6) and haiku (Haiku 4.5) model entries and records
the three-model comparison across all four phases on the 28-window trace
corpus. Transition validation (Phase 3) is the discriminating metric:
Opus 89.3% > Sonnet 50.0% > Haiku 21.4%, while all three pass phases
1/2/4 — the clearest evidence that JS-SAM's signal is in reproducing real
system behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fable 5 (Anthropic's most capable model) scores only 50.0% (14/28) on
Phase 3 — below Opus 4.8 (89.3%) and tied with Sonnet — despite passing
phases 1/2/4. Cause is systematic: its model no-ops every ReleaseLock
(0/11), leaving the lock held after release. Demonstrates that general
capability rankings do not automatically transfer to formal-modeling
accuracy, and that transition validation catches whole-action defects
the other phases miss.

Fable 5 model entry sends no sampling params or thinking config (both
removed on that model; adapter's _should_omit_sampling_params covers it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Feeds each model its own failing Phase-3 transitions (real pre/action/
post vs what its model produced) and asks it to fix the spec, then
re-scores. One round, same 28-window corpus.

Result — self-correction tracks capability, more sharply than one-shot:
  Opus 4.8   89.3% -> 100%   (full repair)
  Fable 5    50.0% -> 100%   (fully fixes its ReleaseLock defect, 0->100%)
  Sonnet 4.6 50.0% -> 60.7%  (fixes AcquireLock; ReleaseLock still 0%)
  Haiku 4.5  21.4% -> 21.4%  (no improvement)

Fable's baseline defect turns out to be a recoverable slip, not a
capability ceiling. Adds scripts/experiments/repair_phase3.py (driver)
and docs/js_sam_repair_experiment.md (write-up + caveats).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Phase-3 repair driver was placed under scripts/experiments/, which is
gitignored, so the prior commit shipped the write-up without the script it
references. Move it to scripts/repair_phase3.py and fix the doc reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Head-to-head on spin (same 4 models). Both languages: 4/4 pass syntax
(P1). They diverge at model-checkability (P2): JS-SAM 4/4, TLA+ 1/4.

Attributed precisely:
- Opus & Sonnet: unbounded `CHOOSE v : v \notin Threads` for "no owner"
  — SANY accepts, TLC rejects. A TLA+ semantic trap absent in JS (native
  null). Caught 2/4 models incl. the strongest.
- Haiku: harness config generation fell back -> no usable .cfg (0 states)
  — a config surface JS-SAM's self-contained module avoids entirely.
- Fable: clean (574 states, 7 invariants).

Direct evidence for the JS-SAM hypothesis, with honest caveats (the TLA+
gap mixes a real pitfall with a config-gen shortfall; single task; direct
translator; Phase 3 not compared).

Also fixes scripts/setup_tools.py: shutil.move ran inside the open
NamedTemporaryFile block, truncating tla2tools.jar / CommunityModules on
Windows (broken zips -> ClassNotFoundException: tla2sany.SANY). Flush and
close before moving, and verify the byte count against Content-Length.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…kers)

Removes the agent confound from the JS-SAM-vs-TLA+ Phase-3 comparison by
equalizing the observable-state contract and replaying transitions
mechanically in TLC.

- tasks/spin/prompts/direct_call_constrained.txt: constrains the TLA+ spec
  to VARIABLES lockHeld, lockHolder + AcquireLock(thread,callType) /
  ReleaseLock(thread), matching the trace schema, with a TLC-safe
  NONE == "none" (no unbounded CHOOSE — the trap that broke free-form TLA+).
- scripts/tla_direct_tv.py: direct TLC trace-validation. Per window, a TV
  module pins the pre-state, takes one action step, and asserts the
  post-state unreachable (INVARIANT NoPost); a violation => window passes.
  Classifies pass/fail/unscoreable and reports conditional (over scoreable)
  and unconditional (unscoreable=fail) numbers.
- docs/js_sam_tla_phase3_plan.md: pre-registered design + interpretations.

Validated: constrained Opus scores 28/28 (100%); breaking ReleaseLock to a
no-op fails all 11 release windows (60.7%) — the replay discriminates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Runs the three-arm paired study (deployed-JS, constrained-JS, constrained-
TLA), N=5 per model, 28-window corpus, scored per-window.

Key result (docs/js_sam_tla_phase3_results.md): most of the raw TLA+ ≫
JS-SAM reversal was PROMPT PRESCRIPTIVENESS — adding the single-step
semantics to the JS prompt closed most of the gap (Fable 58->96%, Sonnet
50->100%). A real, one-directional residual survives: with identical
semantics, TLA+(constrained) hits 100% for every model/generation while
JS(constrained) does not for 3/4 (Opus 89%, Fable 96%, Haiku 41%; Sonnet
ties at 100%); b=0 in every McNemar (JS never beats TLA on any window).
Reads as a partial "formal directness forces precision" effect that strong
models close. Checkability fully solved by the constraint (0 unscoreable).

Adds the constrained JS-SAM prompt (deployed + the same semantics block),
adds {source_code} to the constrained TLA prompt for fairness, and the
paired driver with per-model McNemar. Raw per-window data checked in.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cript

Adds a fourth arm: a bare next(state, action, data) -> {lockHeld, lockHolder}
pure function (no SAM library, two keys) — structurally isomorphic to the
TLA+ relation — run in the same locked-down Docker sandbox.

Result (docs/js_sam_tla_phase3_results.md): plain-JS ties TLA+ at 100% for
every model and generation (McNemar b=0 c=0 for all four). Haiku goes
40.7% (SAM) -> 100% (plain-JS). So the JS-SAM Phase-3 gap is the SAM
pattern's wiring (proposals/acceptors/intent + mandatory auxiliary state),
NOT JavaScript and NOT "formal vs informal": the dividing line is a minimal
declarative transition shape (TLA+ relation == plain next()), which SAM-JS
lacks. Design implication: a leaner JS-SAM spec contract recovers fidelity.

Adds tools/plain-js/tv.mjs (sandboxed runner), scripts/plain_js_tv.py,
the plain-JS prompt, and the pjs arm + plainJS-vs-TLA McNemar in the driver.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follows from the plain-JS finding (the Phase-3 gap is SAM's machinery, not
JavaScript). Prototypes a lean spec contract — a pure next(state, action,
data) over the two observable keys, the JS analogue of the TLA+ relation —
and shows it supports all of Phases 2/3/4 without the SAM library:

- tools/plain-js/explore.mjs: generic bounded explorer over {init, next}
  (Phase 2 runtime: crashes/determinism/serializable; Phase 4: invariants).
- tools/plain-js/reference_spin.js: reference lean spec.
- scripts/plain_js_tv.py: adds plain_js_explore(); generalizes the sandbox runner.
- scripts/lean_demo.py: runs Phases 2/3/4 on a lean spec in the sandbox.
  Reference spec: Phase 2 PASS (3 states), Phase 4 3/3, Phase 3 28/28.
- docs/js_sam_lean_contract.md: design, evidence, integration options
  (lean backend / hybrid SAM-scaffolding / status-quo), tradeoffs.

Note: paper/methodology.md updated locally to the four-arm design (gitignored).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reverses the earlier decision to gitignore paper/. The methodology note and
manuscript cite the raw per-window results in output/tla_phase3_study.json
(all four arms, N=5, committed at a347471) and the lean-contract prototype;
tracking them together keeps the paper reproducible from the repo alone.

- paper/methodology.md   — four-arm design + §7 result summary
- paper/js_sam_sysmobench.tex/.pdf — manuscript
- .gitignore: track paper source, ignore only LaTeX build byproducts

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merges the two half-results into one coherent claim. The prototype validated
Phases 2/4 on a reference spec; the paired study validated Phase 3 on
model-generated specs but discarded the spec text. This regenerates N lean
specs per model, SAVES each, and runs Phases 2/3/4 on the same saved spec.

Result: 20/20 model-generated lean specs pass every phase (5/5 each for Opus
4.8, Fable 5, Sonnet 4.6, Haiku 4.5) — loadable, Phase 2 clean (3 states),
Phase 3 28/28, Phase 4 3/3.

- scripts/lean_full_pipeline_study.py: generate -> save -> all-phase eval, resumable.
- output/lean_specs/<model>_<gen>.js: the 20 saved generations (the artifacts
  the claim rests on).
- output/lean_full_pipeline.json: per-spec per-phase raw results.
- docs/js_sam_lean_contract.md: path 1 upgraded from plausible to demonstrated;
  adds the N=5x4-model table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kizo-core and others added 24 commits July 2, 2026 14:08
The spin lean result carried two caveats: a tiny observable state (3 reachable
states, two scalars) and heavy solution convergence. A structurally different
second task settles both. locksvc is PGo's centralized lock service; its
observable state is {holder, waiters} — a lock holder plus a FIFO wait queue
(an unbounded list) — with four actions and FCFS grant semantics.

Trace capture (real Go execution, distsys trace recorder):
- scripts/harness/locksvc/run.sh already existed; parse_traces.py fixed for
  Windows UTF-8; build_windows.py (new) folds PGo events into {holder, waiters}
  observable (pre,action,post) windows, repairing empty-vclock ordering
  artifacts (a CriticalSection logged before its own Grant) causally.
- data/sys_traces/locksvc/: 60 windows across 5 runs (15 per action).

Study (task-agnostic now):
- scripts/lean_task_config.py: per-task action domain + invariants + reference.
- lean_demo.py / lean_full_pipeline_study.py take --task.
- tools/plain-js/tv.mjs generalized to the projection rule (compare every key
  in the trace post-state; extra model keys ignored) — was hardcoded to spin.
- tools/plain-js/reference_locksvc.js + plain-js prompt.

Result: 20/20 model-generated lean specs pass every phase on locksvc (5/5 each
for Opus 4.8, Fable 5, Sonnet 4.6, Haiku 4.5). Bounded exploration reaches 31
states (vs spin's 3); solutions de-converge to 11 distinct specs of 20 (spin:
9/20), 36-48 lines with per-model clustering. Artifacts:
output/lean_specs_locksvc/, output/lean_full_pipeline_locksvc.json.

docs/js_sam_lean_contract.md: second-task section + caveats updated.
(paper/ tex edits left to the author; PDF rebuild needs pdflatex, absent here.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…symmetry review

Review: the JS and TLA+ Phase-3 replays did not check the same relation, and the
asymmetry favored TLA+. JS is functional (next(pre) must equal post); the TLC
replay was existential (post merely reachable). An over-permissive TLA+ action
passes existentially while admitting wrong post-states; the no-op negative
control only proved failure on unreachable posts, not on permissive specs.

Fix + evidence:
- tla_direct_tv.py: add branching_factor() + functional_replay_window() +
  --functional. A window passes only if the action's one-step image from the
  pinned pre is exactly {post} (branching factor 1) — as strict as next(pre)==post.
- tools/tla/permissive_spin.tla (negative control): holder := any thread on
  acquire. Passes the EXISTENTIAL replay 28/28 (100%) but the functional check
  flags 17 windows over-permissive (bf=2) -> 39.3%. tools/tla/reference_spin.tla
  passes both (28/28, bf=1). (Fully mixed int/string permissiveness is caught as
  unscoreable — TLC throws comparing "none" to an int thread id.)
- tla_functional_audit.py: regenerate + SAVE 20 constrained TLA+ specs (5x4
  models) and re-score under the functional check. Result: existential =
  functional = 140/140 per model, 0 over-permissive windows, max bf = 1. Every
  generated spec is deterministic — the TLA+ 100% is earned under the strict
  relation, not an existential artifact.
- tla_phase3_study.py: score_tla now uses the functional check (over_permissive
  counts as fail), so the paired comparison applies one relation to both languages.
- output/tla_specs/*.tla + tla_functional_audit.json: the audited artifacts.
- docs/js_sam_tla_phase3_results.md: "Replay-check symmetry" section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review: the identical 326,592 "states" across all four models was a red flag —
the metric counts explored action-sequences, not states, making Phase 2's
number model-independent and the paper's "structurally equivalent state
spaces" claim wrong.

Audit confirmed, and worse: steps = (d+1)*|intents|^d = 7*6^6 = 326,592 —
safety-callback invocations over the intent-permutation tree, a pure function
of the contract-pinned intent domain and depthMax (verified empirically at
depths 1/2/3: 12/108/864). Identical for ANY conforming spec by construction.
Counting distinct semantic states (unique model snapshots) separates them:
Opus/Fable/Sonnet 7, Haiku 1 — Haiku's intent actions drop their arguments so
every proposal is rejected; its exploration never leaves init. Haiku's Phase-2
PASS (and vacuously its Phase-4 3/3) certified a spec that cannot move. Two
Claude generations also differ (June spec: 5; July spec: 7) at identical steps.
Nuance on the review's premise: Fable's spec DOES release in exploration
(7 states) — its Phase-3 Release 0% is the pinned-observable-state replay
hitting its auxiliary threadStatus guard, not an unreachable release.

Fixes (TDD: fixture + failing tests first):
- tools/js-sam/cli.mjs: explore() tracks a seen-set of serialized snapshots ->
  distinctStates, surfaced by check + invariants; determinism digest includes it.
- js_sam.py: states_explored = distinctStates (semantic); the combinatorial
  step count stays available as steps_explored/raw_output.
- runtime_check.py parse_tlc_output: states_explored = TLC "distinct states
  found" (was "states generated", a work counter with duplicate visits).
  Fable's TLA spec: 574 generated / 159 distinct.
- tests: spin-neverrelease.js fixture (release no-op) + regression asserting
  identical steps but differing distinct states; TLC parsing unit tests;
  repaired test_missing_traces_reported_cleanly (its premise — spin traces not
  checked in — went stale when the corpus was committed).

Docs/paper: comparison tables now show distinct states (7/7/7/1; 159 for the
TLA arm); "structurally equivalent state spaces" retracted and replaced by the
audit; the three incommensurable counts (326,592 steps / 574 generated / 3
observable) reconciled explicitly in docs/js_sam_vs_tla_comparison.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
…ers revised

Review: repaired specs were only re-checked on Phase 1 + Phase 3, on the same
28 windows quoted in the repair prompt; nothing prevented memorizing the
failing windows, making "Fable repairs to 100%" possibly a prompt-following
result, and Experiment 2's "three tiers" unverified as modeling.

scripts/repair_generalization.py re-runs the repair loop with artifacts SAVED
(output/repair_specs/) and scores every baseline and repaired spec on:
P3-seen (28 windows = 8 distinct combos), P3-heldout (the 10 combos of the
observable domain the corpus never shows), P2 (distinct-semantic-state
metric), and P4 (three observable invariants).

Results:
- Opus/Fable/Sonnet repaired: 28/28 seen, 10/10 held-out, 7 distinct states,
  3/3 invariants. Haiku: unchanged (6/28, 1 state — still inert).
- No memorization: zero state-equality tables/window literals in any repaired
  spec. Fable's and Sonnet's repairs are the same root-cause semantic fix —
  release ownership moved from the auxiliary threadStatus guard (unsatisfiable
  under a pinned observable pre-state) to authoritative lockHeld/lockHolder;
  Sonnet's repair documents the diagnosis in a comment.
- Honest structural limit, stated in script+doc+paper: the kernel emits
  acquire events at acquisition success, so every held-out combo is an
  observable no-op — held-out alone refutes only unsafe-default memorizers;
  inspection and P2/P4 carry the conclusion. A state-changing held-out set
  (locksvc) is flagged as the stronger follow-up vehicle.
- Replication surprise: Sonnet repaired to 28/28 here vs 60.7% originally.
  The "three clean tiers (full/partial/none)" claim is revised everywhere
  (doc + paper abstract/intro/section): stable boundary is repairers vs
  Haiku; mid-tier single-round outcomes are sampling-dependent at N=1. The
  original "Sonnet cannot fix its release path" contrast is retracted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
…eanalysis

Review: pooling windows across 5 generations treats 140 outcomes as
independent when generations collapse to a handful of unique solutions — the
same bug counted five times, chi-square inflated (c=59, c=83). Correct unit
is the unique solution or the generation; report uniqueness for every arm.

Audit confirms, worse than estimated: behavioral fingerprints (per-window
outcome vectors) collapse to 1-2 unique per (model, arm) for ALL 16 cells —
e.g. Haiku's c=83 was one bug counted four times plus one partial.

scripts/tla_phase3_analysis.py (new) reanalyzes the committed raw JSON:
1. Uniqueness for EVERY arm (fingerprints; plus text-level uniqueness of the
   saved regenerated sets as corroboration: pjs 9/20, locksvc 11/20, tla 10/20
   unique texts, each collapsing to one behavior per arm).
2. Exact generation-level permutation test (unit=generation, diff in mean
   pass rate, all C(10,5)=252 relabelings, two-sided; floor p=2/252≈0.0079).
3. Unique-solution view (n=1-2/cell: effect sizes only, no tests).

Results at the defensible unit:
- deployed-JS vs TLA: significant for ALL four models (p=.0079, at floor) —
  the contract-tax effect is not a pooling artifact.
- constrained-JS vs TLA: significant for Opus and Haiku only. FABLE'S STARRED
  DEFICIT IS WITHDRAWN (chi2=4.2* pooled -> p=0.44 at generation level).
- plain-JS vs TLA: identical in every generation (delta=0; no test needed).

All pooled chi-square values retracted in docs + paper + methodology note;
tla_phase3_study.py now prints b/c as descriptive only and points to the
analysis script; the plan doc records the post-hoc analysis revision; the
paper's McNemar table is replaced by uniqueness + permutation results and the
"significant for Opus, Fable, and Haiku" finding corrected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
…ss checks

Review: Phase 4 cannot catch the paper's own headline bug — a never-releasing
lock satisfies mutual exclusion BECAUSE it is broken; the three JS-SAM
invariants are safety-only and demonstrably too weak; the interesting lock
properties are liveness, and neither the SAM checker nor the lean explorer
checks any temporal property. Not a "template count" caveat: Phase 4 as
implemented could not fail Fable's spec in principle.

Verified, precisely:
- The shipped NoDeadlock is "some thread is not 'trying'" — satisfied by the
  stuck HOLDER being 'locked'. Empirical demo: a release-is-a-no-op mutant of
  the known-good spec gets the IDENTICAL 3/3 Phase-4 verdict as the correct
  spec. Pinned as a characterization test
  (test_safety_only_phase4_passes_a_never_releasing_lock) that fails loudly if
  Phase 4 ever gains the capability.
- Fable's defect is doubly invisible: it manifests only under a pinned
  observable pre-state, so even a liveness-capable from-init checker would
  pass its spec — only trace replay catches it. Both layers stated.
- The TLA+ template set declares two liveness properties (PROPERTY-routed), so
  the P4 gap is qualitative, not just numeric — noted.

Mitigation (TDD): the lean explorer gains bounded PROGRESS checks —
EF-reachability over the explored graph ("from every reachable state
satisfying `from`, a `goal` state is reachable within the bound"). Explicitly
NOT liveness (no fairness, bounded horizon) and labeled as such everywhere.
- tools/plain-js/explore.mjs: state graph + progressViolations.
- lean_task_config.py: Acquire/Release progress for spin; Grant/Release for
  locksvc (state-changing — also strengthens future held-out replay).
- Results: never-releasing lean mutant fails ReleaseProgress (safety P4
  passes it); the inert-spec class fails AcquireProgress; reference specs and
  ALL 40 saved model-generated lean specs pass on both tasks — the
  strengthened Phase 4 hardens, not overturns, the 20/20 results.

Docs/paper: "template count differs" limitation replaced by the precise
in-principle statement + empirical demo + mitigation in
docs/js_sam_vs_tla_comparison.md, docs/js_sam_lean_contract.md, and the
paper's Experiment-3 setup + limitations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
… unconditionally

Review: the 2x2 in (contract, prompt) had an empty cell — the lean arm always
carried the semantics block, so "contract minimality buys transcription
fidelity" was conditional on spec-level semantics in the prompt, and the
prompt>contract>language ranking rested on an incomplete design with ceiling
effects.

Added the missing cell (pjd): the identical lean contract with the entire
single-step semantics block removed — the model derives the transition
semantics from the Rust source alone
(tla_eval/tasks/spin/prompts/plain-js/direct_call_nosemantics.txt).

Result: lean-without-semantics = 100% for EVERY generation of EVERY model —
including Haiku, at 28.6% under SAM with the same absent semantics. 20/20
generations, 5/5 unique spec texts per model (saved: output/study_specs/),
all behaviorally identical and correct. The suspected conditionality is
refuted: the contract effect holds without prompt semantics.

Completed factorial contrasts (generation-level exact permutation):
- contract effect, no semantics (js vs pjd): significant for ALL FOUR models
  (delta .186/.421/.500/.714, each p=.0079, the floor).
- prompt effect within SAM (js vs jsc): significant for only Fable (.379,
  p=.024) and Sonnet (.500, p=.0079); Opus and Haiku p=1.0.
- prompt effect within lean (pjd vs pjs): delta=0 — unidentifiable at ceiling.
This INVERTS the earlier headline: "prompt explains most of the reversal" was
an artifact of examining only the SAM column. Revised everywhere to:
contract first (uniform), prompt second (model-dependent), language nowhere.
Ceiling honesty stated: ordering among the 100% cells and any
prompt x contract interaction are not identifiable at the top; identifiable
claims are contract >= prompt per model and lean-sufficiency without
semantics vs SAM-insufficiency with it.

- tla_phase3_study.py: pjd arm; saves spec text for every new cell.
- tla_phase3_analysis.py: 5 arms, four factorial contrasts + 2x2 table.
- results doc: completed-2x2 section; finding 4 revised.
- paper: abstract, intro bullet, missing-cell paragraph, tab:crosslang
  column, findings rewrite, discussion + conclusion ranking corrected
  (contract, then prompt, then language), derivation open-question updated.
- methodology note aligned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
…he base rate

Review: of the 17 acquire windows, contention windows have post = pre — a
no-op acquire passes them free; report no-change counts and per-class rates.

Verified: 6 of 28 windows (all contention acquires) are no-change =>
identity base rate 21.4% overall, 6/17 = 35.3% on acquires. Experiment 1's
Haiku row is EXACTLY that base rate: Acquire 6/17 is precisely the six
freebies, Release 0/11, zero state-changing windows passed. Conditional on
the 22 change windows the Experiment-1 spread is 0% (Haiku) / 36.4%
(Fable, Sonnet) / 86.4% (Opus) — not 21.4–89.3%.

- tla_phase3_analysis.py section 0: corpus composition, identity base rate,
  per-(model, arm) change/no-change split, freebie share of passes. Study
  arms: every arm passes all 6 freebies; Haiku SAM+none is 9.1%
  conditional-on-change (75% of its headline passes were freebies),
  SAM+sem 24.5% (52.6%). Lean/TLA cells unaffected (100% everywhere).
- docs/js_sam_model_comparison.md: base-rate section; finding restated on
  the conditional metric (0–86.4%). Replication note: re-scoring saved
  Haiku specs shows a ±2-window sensitivity in sequential replay
  (async-error attribution timing); either value sits at/near the base rate.
- docs/js_sam_tla_phase3_results.md: conditional-on-change table for all
  five arms; correction largest where scores are lowest; no factorial
  conclusion flips.
- paper: abstract, intro bullet, Experiment-1 finding + tab:models caption,
  conclusion — spreads restated as 0–86.4% on state-changing windows with
  the base rate named.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
…order unobservable)

Found by the point-7 base-rate audit: 10 of the 60 locksvc windows were
no-op grants/releases — grants to a non-HEAD client under the fold's FCFS
assumption. Root cause: the fold ordered `waiters` by client-SEND order, but
the real server grants in ARRIVAL order, which the network reorders (one
capture logged sends 1,3,2 while the server queued <<3,2,1>> — exactly the
reordering upstream locksvc.tla's NoPriorityInversion comment warns about).
The old fold silently converted real state changes into no-op windows —
wrong ground truth.

Arrival order is not a function of the client-side single steps the trace
exposes, so at this projection waiters is a SET (canonically sorted array):
grant valid for any waiter of a free lock; FCFS is unobservable here.

- build_windows.py: set-semantics fold + documentation of the bug.
- reference_locksvc.js + plain-js prompt: set semantics.
- Corpus re-folded: 60 windows, 15 no-change (all legitimately the CS
  no-ops; identity base rate 25%), zero bogus no-op grants.
- Study invalidated and REGENERATED from scratch (old specs implemented
  head-grant FCFS): 20/20 fresh lean specs pass every phase again —
  including the bounded progress checks — over a 20-state space, 14/20
  unique texts (4/4/4/2 per model), 45/45 on state-changing windows.
- docs/js_sam_lean_contract.md + paper locksvc section: correction
  documented in-line; 31-state/11-20 numbers replaced.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
… fidelity"

Review: two hand-authored deterministic scenarios; no blocking-waiter wakeup
(the actual spin); locksvc's exactly-15-per-action suggests a deterministic
schedule; the vector-clock repair reorders ground truth per an asserted
assumption; and the conclusion's "interchangeable on fidelity" doesn't carry
the qualifier the Limitations admit.

Audited (scripts/trace_coverage_audit.py), with each sub-claim resolved:
- spin: confirmed — 2 deterministic ktest scenarios; 8/18 observable combos
  covered; contention is try-only (2 combos); ZERO blocking-lock-on-held
  windows. Sharper than the review: the instrumentation emits acquire at
  acquisition SUCCESS (pre=free), so the spin phase is invisible in this
  projection BY CONSTRUCTION — a woken blocking waiter is indistinguishable
  from an uncontended acquire.
- locksvc: half-confirmed — the exactly-15/action is protocol completion
  (one fixed terminating workload: 3 one-shot clients = 12 windows/run), not
  schedule determinism: the 5 runs are 5/5 distinct action sequences with 4
  distinct grant orders (including the network reordering that exposed the
  fold bug). No re-requests, no deeper contention, no failure paths.
- reorder premise: now DEMONSTRATED, not asserted — every CriticalSection
  event reads GrantMsg=3 from its mailbox (message-passing happens-before),
  verified per event; build_windows gains validate_cs_consumed_grant() and
  refuses traces where the premise fails.

Docs/paper: "Trace coverage" section in the results doc; the paper's
28-window limitation bullet replaced with the full audit; the conclusion's
"yes, interchangeably on fidelity" now reads "interchangeable on fidelity ON
WHAT WE MEASURED — a two-variable projection of deterministic, easy slices —
untested on uncovered combos, projection-invisible behavior, richer
workloads, or adversarial interleavings."

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
Review: derivation (both languages), as-deployed agent path, multi-round
repair, other vendors, locksvc factorial — acknowledged but load-bearing;
plus independent replication given experimenter-authored materials on both
sides.

RUN — TLA+ derivation arm (tld: constrained TLA contract, semantics block
removed, mirroring the lean derivation arm). The factorial is now a full
3x2 (contract SAM/lean/TLA x prompt none/sem), and derivation splits the
languages for the first time:
- lean+none beats TLA+none for Opus and Sonnet (100% vs 78.6%, delta=.214,
  p=.0079 each), directionally for Haiku (87.1%, p=.17), tie for Fable.
- Mechanism precise, and NOT wrong posts: every TLA shortfall window is
  unscoreable. Without instruction, Opus/Sonnet (5/5 gens) and Haiku (3/5)
  write the classic TLA idiom — AcquireLock GUARDED on the lock being free —
  making a failed attempt a disabled action, not an observable no-op step;
  pinned contention windows then have no successor (TLC deadlock). Fable
  writes no-op-tolerant actions unprompted.
- Readings: the lean contract's TOTALITY (next() must return) structurally
  forbids the partial-action idiom; the semantics block was doing real,
  previously invisible work for TLA+ specifically (tld vs tla p=.0079 for
  the affected models); under pre-registered conditional scoring the
  guard-idiom specs are 100% on scoreable windows — both numbers reported.
  "Contract first, prompt second" refines to: the prompt matters exactly
  where the contract under-constrains.

RUN — multi-round repair (scripts/repair_multiround.py): Haiku does NOT
converge — 6/28 -> 6/28 -> 6/28 across three rounds of full-failure
feedback, exploration still 1-state; every round's spec saved and audited
(held-out + P2). Sonnet full-repairs in round 1 again (2nd full repair in 3
runs — more evidence the original "partial" tier was sampling noise).

NOT RUN, with stated reasons: as-deployed agent path (reintroduces the
confound the direct replay removed); other vendors (registry entries exist,
credentials don't — user-level unblock); locksvc factorial (lean arms done;
SAM/TLA arms need locksvc prompts + a locksvc TLC replay module).

REPLICATION.md: rerun every analysis from committed raw data with no API
access; regenerate every experiment from pinned prompts/models; inventory
of every experimenter-authored material (scenarios, semantics block,
derivation prompts, repair prompt, invariant sets, projections) as an
independent variable to audit or vary. Paper's positionality bullet now
names the experimenter-authored-materials problem and calls independent
replication (ideally by the SysMoBench maintainers) a prerequisite for
treating the cross-language conclusions as more than a single-author case
study. Missing-arms inventory bullet added with per-arm status.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
Camera copy incorporating the full nine-point review response: functional
(branching-factor) Phase-3 replay with permissive negative control; Phase-2
distinct-state metric audit (vacuous-pass finding); repair generalization
audit (held-out + inspection) and multi-round repair; generation-level
statistics replacing the pseudo-replicated pooled McNemar; safety-only
Phase-4 precision + bounded progress checks; the completed 3x2
contract-by-prompt factorial including both derivation arms (lean totality
vs TLA+ guard idiom); corpus base-rate analysis; trace-coverage audit with
the qualifier carried into the conclusion; locksvc corpus correction;
missing-arms inventory, replication guide, and the positionality/independent-
replication statement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PX1erummaoUTV7NKhQAdr5
…ask, corpora, reference models)

New SysMoBench task extracted from a production restaurant POS (baanbaan
Merchant/v2): pinned 7-variable observable-state contract, 9-action dispatch
alphabet, prompts for the SAM / lean-JS / constrained-TLA+ arms, verbatim
source packet with provenance (EXTRACTION.md).

- Real-execution trace corpora (emulator + chaos proxy): v1 75 windows /
  17 scenarios, v2 101 windows (3 windows per acceptor-rewrite rule),
  post-fix corpora for patch validation; instrumentation diff in
  data/patches/finixpos_trace.patch.
- Hand-written reference models: FinixPOS.tla + environment (TLC-checked),
  crash/staleness/two-attempt/refund extensions, patched-semantics variants
  (FinixPOSFixed*, FixedV2), annotated counterexamples for every confirmed
  gap, sandbox reproduction evidence (real Finix 422 shape), CONTROLS.md
  (positive + mutation controls), CONTAMINATION.md, EXTENDED_FAILURE_MODEL.md.
- trace_loader: accept the harness's {pre, action, data, post} event form
  (+ test).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011bXyeoAhL6iz3CibTtU2Qv
- Drivers: finixpos_tv.py (mechanical replay, all three arms; validated by
  positive + mutation controls), finixpos_phase3_study.py (two-phase
  generate-then-score with Anthropic prompt caching), v2 rescore, post-fix
  study (best-model+arm revalidation), final analysis (permutation tests,
  uniqueness counts).
- Results of record: 7 models (Fable 5, Opus 4.8, Sonnet 4.6, Haiku 4.5,
  mistral-large-2512, devstral-2512, labs-leanstral-1-5) x SAM / lean /
  TLA+ x N=5, scored on both corpora; all 105 + 10 generated specs and
  per-window statuses committed for the audit trail.
- Headline: lean next() contract best-or-tied and never unscoreable for all
  seven models across two vendors; TLA+ deficits dominated by checkability
  and the guard idiom; SAM ceremony kills specs in model-specific ways;
  semantic error is language-independent. Replication is of ordering and
  taxonomy, not level - only Claude frontier models reached deployment-grade
  reliability.
- config: mistral-large / devstral / leanstral model entries.
- docs: study plan (pre-registered design) and full results incl. the
  correctness deliverable (7 confirmed gaps + review-response revisions,
  patch v1/v2 validation, accepted-operational-risk scoping).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011bXyeoAhL6iz3CibTtU2Qv
…prompt

Ground Phase 3 for the etcd task. Upstream etcd-io/raft (pinned 26647d5)
ships complete trace instrumentation behind the with_tla build tag; the
harness adds only an NDJSON TraceLogger sink and five deterministic
3-node RawNode scenarios (election, proposals, heartbeats, stale-campaign
vote rejection, higher-term re-election), then folds the event streams
into whole-cluster observable windows {nodes: {id: {role, term, vote,
commit, log}}}.

Corpus: 97 windows across the five canonical target actions, zero
self-consistency violations, byte-identical regeneration, provenance
pinned per run. A hand-written reference lean spec replays 97/97 through
the benchmark's own sandboxed path (positive control); two projection
approximations (index-only vote up-to-dateness, heartbeat commit clamp)
are documented in the README and exact on these corpora by construction.

The plain-js prompt is the lean derivation form: contract pinned (state
shape, action/data schemas, {init, next} module), no semantics block —
transition semantics must be derived from raft.go.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…round repair

Study (scripts/lean_full_pipeline_study.py --task etcd --n 5): all 20
specs load and explore cleanly (~130k distinct states at depth 8), none
passes every phase one-shot. Fable reaches 97/97 conformance on 4/5
generations but violates CommitWithinLog under open-world exploration
(unclamped heartbeat commit); the other models fall short on Phase 3,
dominated by HandleVoteRequest (step-down/vote-reset/up-to-dateness).
Per-window failure detail in output/lean_etcd_p3_failures.json.

Repair round (scripts/repair_lean_etcd.py): each failing spec gets its
own module, every failing window with expected vs actual, and invariant
counterexamples; one rewrite; full re-score. Result: fable 4/5 all-pass,
claude 2/5, sonnet 0/5 (3/5 cells regress below baseline), haiku 0/5
(all cells plateau at 93/97 on the same four vote windows). tv.mjs now
returns the module's produced state on failed windows to support the
feedback loop (backward-compatible).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paper sources are working drafts, not benchmark artifacts; keep them out
of the repository.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paper sources are working drafts, not benchmark artifacts; keep them out
of the repository.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… semantics)

The strict-profile breaking change (frozen pre-state, next-draft writes,
mandatory unchanged() framing) does not affect this helper or generated
specs, which run in default mode: full pytest suite 23/23 green against
2.1.2 in Docker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfSbyWEBtEwBsxB2CuSYYv
Add an `opus5` model entry (claude-opus-5) and extend the sampling-omit
list to the Claude 5 family. `claude-sonnet-5` had only ever been patched
in the sysmobench-etcd worktree, so the main repo would have 400'd on it.

Smoke test (spin / TLA+ / direct_call, N=5), written up in
docs/opus5_smoke_test_results.md:

  P1 5/5. P2 first shot 1/5 -- four specs die on
  `NULL == CHOOSE v : v \notin Threads`, an unbounded CHOOSE that SANY
  accepts and TLC refuses, so they explore 0 states. One repair round
  takes P2 to 5/5 with zero regressions. P4 is 35/35.

  The deficit is idiom selection, not modeling ability. Single-shot P2
  understates it by 5x; the repair round is where the signal is.

Three harness bugs, two of which corrupt scores independent of model
quality:

1. Phase 4 appends `<Name> == ...` to the generated module, colliding
   with the spec's own operator when the model names its invariants
   after the expert templates. SANY rejects the module and the invariant
   is scored FAIL without TLC running. Perfect correlation across 5 runs
   x 7 invariants. scripts/rescore_p4_name_collision.py re-scores
   mechanically; all 8 collided invariants then pass.

2. extract_tla in repair_tla_spin.py takes the FIRST ```tla fence, so a
   reply that quotes the offending line before emitting the rewrite
   loses its module. repair_tla_spin.py is committed AS IT RAN, unfixed,
   so the recorded Experiment-3 numbers stay reproducible -- but those
   numbers were produced with this extractor and any parse-failure
   repair there should be re-checked. The corrected extractor lives in
   repair_tla_spin_opus5.py.

3. transition_validation is unusable on Windows: transition_validation.py
   passes Windows paths to bash, which eats the backslashes (exit 127).
   Phase 3 never ran, so whether these specs replay real traces is still
   open.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkfxRoMS4KxrSyxLB5PpmY
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.

4 participants