Skip to content

feat(tg-7): == on braids decides braid-group equivalence (#50 ruling) - #87

Merged
hyperpolymath merged 2 commits into
mainfrom
feat/tg7-braid-equality
Jul 28, 2026
Merged

feat(tg-7): == on braids decides braid-group equivalence (#50 ruling)#87
hyperpolymath merged 2 commits into
mainfrom
feat/tg7-braid-equality

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Implements the owner ruling on #50 — option (a) true braid-group equivalence.

σ₁σ₂σ₁ == σ₂σ₁σ₂ is now true. Syntactic equality contradicted the language's own thesis (programs are topological objects; equivalence is isotopy), so == was the one place that quietly reverted to comparing representations.

Both engines, one change

The TG-3 differential and the Lean proof oracle straddle OCaml and Lean, so they move together.

OCamleval.ml Eq on VBraid routes through the existing, already-tested braid_equiv.ml (Dehornoy handle reduction). Identity is VBraid [], so identity comparisons flow through the same case. Added ast_of_gen(s), the missing inverse of the existing gen_of_ast.

Lean — new §BRAID-GROUP EQUIVALENCE in Tangle.lean ports the same procedure. Six Step rules use it: eqBraids, eqIdBraid, eqBraidId, and the three echoEq counterparts — echoEq must follow, because eval.ml's EchoEq explicitly reuses the Eq logic.

~ (isotopy) moved too — a completeness fix

~ used simplify_gens, which only cancels adjacent inverses and does not implement the braid relation. Leaving it while == became complete would allow a == b true with a ~ b false — incoherent, since for braids isotopy is braid-group equality. So this is a completeness fix, not a change of intended meaning.

Closed tangles are documented honestly: link isotopy is Markov equivalence (conjugation + stabilisation), which is NOT implemented. On tv_closed tangles this stays a sound-but-incomplete under-approximation — true is trustworthy, false only means "not equal as braids".

Metatheory: unchanged, and that is the point

Progress / Preservation / Determinism re-verified with 0 errors, no proof edits. All three need only that the right-hand side is a total function into Bool. Determinism is immediate: a function applied to fixed arguments yields a fixed result. TG-3's 496 kernel-checked obligations still pass — typing is untouched; only the runtime value changes.

⚠ Trusted, not proven

These are definitions, not axioms, so the sorry/axiom gate passes legitimately — but that does not make the claim proven. The metatheory holds relative to braidEquiv. That braidEquiv correctly decides braid-group equality is the mechanised Garside/Dehornoy proof, still research-grade (#51).

Recorded as new assumption A-TG-7.2 and stated plainly in PROOF-NARRATIVE.md, so a green gate cannot be mistaken for a proof. The Lean port is fuel-bounded rather than well-founded, because proving termination is that research obligation.

Tests that actually distinguish the semantics

The pre-existing braid-equality tests pass under either semantics, so they could not demonstrate this change. Eight new cases in test_eval.ml do:

Case Under list-eq Now
s1.s2.s1 == s2.s1.s2 (braid relation) false true
s1.s3 == s3.s1 (far commutation) false true
s1.s1^-1 == identity false true
identity == s1.s1^-1 (symmetry) false true
s1 == s1^-1 (guard: differing writhe) false false
s1.s2 == s2.s1 (guard: adjacent don't commute) false false
~ agrees with == on the braid relation false true
echoEq lowers to braid-group equality false true

Two are deliberate negatives, guarding against a decision procedure that just returns true. eval suite 104 → 112; whole suite 0 failures.

Two defects found while doing this

1. The OCaml compiler was never gated. dune build/dune test ran nowhere in CI — the lexer, parser, typechecker, interpreter and 4,000+ assertions (TG-3 1008, TG-5 189, TG-7 2220, TG-8 2311, plus the rest) were entirely ungated. A semantics change to eval.ml could have landed fully green with nothing having compiled it. Adds .github/workflows/ocaml-ci.yml, using apt rather than a setup action so no new third-party pin enters the estate (an unresolvable pin produces no check run at all).

2. The sorry/axiom gate's comment filter never worked. It anchored on ^\s*-- after grep -n had prepended <line>:, so the documented "outside comments" exemption could never match — only the unanchored /- alternative did anything. It went unnoticed purely because no comment had yet contained one of those words; the TG-7 trusted-base note does. Now anchored after the prefix: strictly tighter than the old catch-all, and verified to still catch a real sorry (negative control).

Verification

  • lean Tangle.lean0 errors
  • sorry/axiom/admit gate (fixed filter) → pass, and still catches an injected sorry
  • check-tg3-differential.sh496 obligations, 0 errors
  • dune build → clean; dune test --force0 failures across all suites

Closes the semantics half of #51.

🤖 Generated with Claude Code

hyperpolymath and others added 2 commits July 29, 2026 00:40
Implements the owner ruling on #50: option (a), true braid-group
equivalence. Syntactic equality contradicted the language's own thesis —
programs are topological objects and equivalence is isotopy — so `==` was
the one place that quietly reverted to comparing representations.
`σ₁σ₂σ₁ == σ₂σ₁σ₂` is now true.

Landed on BOTH engines in one change, because the TG-3 differential and the
Lean proof oracle straddle them:

* OCaml — `eval.ml` `Eq` on VBraid routes through `braid_equiv.ml`
  (Dehornoy handle reduction, already present and tested). `Identity` is
  `VBraid []`, so identity comparisons flow through the same case.
  Added `ast_of_gen(s)`, the missing inverse of the existing `gen_of_ast`
  (the runtime and AST generator records are structurally identical but
  nominally distinct).

* Lean — new §BRAID-GROUP EQUIVALENCE in `Tangle.lean` ports the same
  procedure (`unitsOf`, `inverseWord`, `freeReduce`, `scanHandle`,
  `rewriteInterior`, `reduceOne`, `reduceFuel`, `braidEquiv`). Six Step
  rules now use it: `eqBraids`, `eqIdBraid`, `eqBraidId` and the three
  `echoEq` counterparts — echoEq must follow because eval.ml's EchoEq
  explicitly reuses the Eq logic.

`~` (isotopy) routes there too. It used `simplify_gens`, which only cancels
adjacent inverses and does NOT implement the braid relation. Leaving that
while `==` became complete would have allowed `a == b` true with `a ~ b`
false, which is incoherent — for braids, isotopy IS braid-group equality.
This is a completeness fix, not a change of intended meaning. Closed
tangles are documented as a sound-but-incomplete under-approximation:
link isotopy is Markov equivalence, which is NOT implemented.

Progress / Preservation / Determinism re-verified UNCHANGED — all three need
only that the RHS is a total function into Bool. TG-3's 496 kernel-checked
obligations still pass (typing is untouched; only the runtime value changes).

TRUSTED, NOT PROVEN. These are definitions, not axioms, so the sorry/axiom
gate passes legitimately — but that does not make the claim proven. The
metatheory holds *relative to* `braidEquiv`; that `braidEquiv` correctly
DECIDES braid-group equality is the mechanised Garside/Dehornoy proof, which
remains research-grade (#51). Recorded as new assumption A-TG-7.2, and
stated plainly in PROOF-NARRATIVE.md so the green gate cannot be mistaken
for a proof. The Lean port is fuel-bounded rather than well-founded, since
proving termination IS that research obligation.

Tests: 8 new cases in test_eval.ml that DISTINGUISH the two semantics (the
pre-existing braid-equality tests pass either way, so they could not). They
cover the braid relation, far commutation, free cancellation, symmetry, and
two negatives guarding against a procedure that just says true. eval suite
104 -> 112, whole suite 0 failures.

Also in this change, both found while doing the above:

* `.github/workflows/ocaml-ci.yml` (NEW) — the OCaml compiler and its 4,000+
  assertions were NOT built or run by CI at all; only the Lean proofs were
  gated. A semantics change to eval.ml could have landed fully green with
  nothing having compiled it. Uses apt rather than a setup action so no new
  third-party pin enters the estate.

* `lean-proofs.yml` — the sorry/axiom gate's comment filter never worked:
  it anchored on `^\s*--` AFTER `grep -n` had prepended `<line>:`, so the
  documented "outside comments" exemption could never match. It went
  unnoticed only because no comment had yet contained one of these words;
  the TG-7 trusted-base note does. Anchored after the prefix — strictly
  tighter than the old unanchored `/-` catch-all, and verified to still
  catch a real `sorry`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The gate's first run went red on a test suite that reported ZERO failures.
The FAIL-marker check was case-insensitive and unanchored on the right, so
`^[[:space:]]*FAIL` matched the compositional suite's "Failed: 0" summary
line. A gate that cries wolf is worse than no gate — it trains maintainers
to ignore it.

Now requires a FAIL *marker*: "FAIL" followed by ':' or whitespace, matched
case-sensitively. Verified in both directions against the real test output:
no false positive on a passing run, still catches an injected
"  FAIL  Braid equality true", and "Failed: 0" is correctly ignored.
The tally check also now covers the "Failed: N" shape it previously missed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your trial's included automatic processing has been used for this period. Upgrade now, or comment "Gitar review" to run a review anytime.
Learn more

Code Review ⚠️ Changes requested 0 resolved / 2 findings

Implements true braid-group equivalence for == and ~ in OCaml and Lean, but the changes require fixes for OCaml CI grep false-positives and missing test coverage for the Lean braidEquiv port.

⚠️ Bug: New OCaml CI FAIL-grep false-positives on green runs

📄 .github/workflows/ocaml-ci.yml:78

The Test step greps the combined log with grep -qiE '^[[:space:]]*FAIL'. Because it is case-insensitive, it matches the summary line Failed: 0 that compiler/test/test_compositional.ml unconditionally prints (Printf.printf "Failed: %d ") even when zero tests failed. dune test surfaces that line, so this step will exit 1 and turn the gate red on a fully passing run — defeating the purpose of the newly-added gate (I verified the grep matches Failed: 0). Fix by dropping -i (all real per-test failures print uppercase FAIL, so case-sensitive matching still catches them while no longer colliding with the Failed: label).

Remove the -i flag so the summary label `Failed: 0` no longer matches; uppercase per-test `FAIL` lines are still caught.
if grep -qE '^[[:space:]]*FAIL' /tmp/dune-test.log; then
  echo "::error::A test reported FAIL — see the log above."
  exit 1
fi
💡 Quality: Lean braidEquiv port has no test coverage

📄 proofs/Tangle.lean:187-201

The new §BRAID-GROUP EQUIVALENCE definitions in Tangle.lean (braidEquiv/isTrivialBraid/reduceOne/scanHandle) are claimed to be a faithful port of braid_equiv.ml, but only the OCaml side is exercised by tests (test_eval.ml). A-TG-7.2 explicitly assumes the two engines agree, yet nothing would catch a divergence in the Lean port. Consider adding a few #eval/example checks in Tangle.lean mirroring the OCaml TG-7 cases (e.g. braidEquiv [σ1σ2σ1] [σ2σ1σ2] = true, [σ1] vs [σ1⁻¹] = false) so the port's behavior is at least smoke-tested where it is defined.

🤖 Prompt for agents
Code Review: Implements true braid-group equivalence for `==` and `~` in OCaml and Lean, but the changes require fixes for OCaml CI grep false-positives and missing test coverage for the Lean braidEquiv port.

1. ⚠️ Bug: New OCaml CI FAIL-grep false-positives on green runs
   Files: .github/workflows/ocaml-ci.yml:78

   The Test step greps the combined log with `grep -qiE '^[[:space:]]*FAIL'`. Because it is case-insensitive, it matches the summary line `Failed: 0` that `compiler/test/test_compositional.ml` unconditionally prints (`Printf.printf "Failed: %d
   "`) even when zero tests failed. `dune test` surfaces that line, so this step will `exit 1` and turn the gate red on a fully passing run — defeating the purpose of the newly-added gate (I verified the grep matches `Failed: 0`). Fix by dropping `-i` (all real per-test failures print uppercase `FAIL`, so case-sensitive matching still catches them while no longer colliding with the `Failed:` label).

   Fix (Remove the -i flag so the summary label `Failed: 0` no longer matches; uppercase per-test `FAIL` lines are still caught.):
   if grep -qE '^[[:space:]]*FAIL' /tmp/dune-test.log; then
     echo "::error::A test reported FAIL — see the log above."
     exit 1
   fi

2. 💡 Quality: Lean braidEquiv port has no test coverage
   Files: proofs/Tangle.lean:187-201

   The new §BRAID-GROUP EQUIVALENCE definitions in Tangle.lean (braidEquiv/isTrivialBraid/reduceOne/scanHandle) are claimed to be a faithful port of braid_equiv.ml, but only the OCaml side is exercised by tests (test_eval.ml). A-TG-7.2 explicitly assumes the two engines agree, yet nothing would catch a divergence in the Lean port. Consider adding a few `#eval`/`example` checks in Tangle.lean mirroring the OCaml TG-7 cases (e.g. `braidEquiv [σ1σ2σ1] [σ2σ1σ2] = true`, `[σ1] vs [σ1⁻¹] = false`) so the port's behavior is at least smoke-tested where it is defined.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@hyperpolymath
hyperpolymath merged commit f489211 into main Jul 28, 2026
25 checks passed
@hyperpolymath
hyperpolymath deleted the feat/tg7-braid-equality branch July 28, 2026 23:45
hyperpolymath added a commit that referenced this pull request Jul 29, 2026
`examples/` and `conformance/` existed but **were executed by nothing**
— not CI, not the Justfile.

## What that cost

`examples/isotopy.tangle` — the file whose entire purpose is to
demonstrate the language's central claim, *"two braids can be
structurally different but topologically equivalent"* — **was failing
its own assertions.**

The culprit is line 28:

```tangle
assert simplify(braid[s1, s3, s1^-1]) ~ braid[s3]
```

That needs **far commutation** (|1−3| ≥ 2), which the old free-reduction
`~` could not do. It began passing only when TG-7 (#87) made `~` decide
real braid-group equivalence.

Verified rather than assumed — built the pre-TG-7 commit and ran it:

```
=== isotopy.tangle under PRE-TG-7 build ===
Runtime error: Assertion failed
EXIT=1
```

So the showcase example had been broken for as long as `~` used free
reduction, and nothing noticed because nothing ran it. That is the case
for this gate, and incidentally a nice independent validation of TG-7.

## The gate is a ratchet

Five examples don't typecheck and five `conformance/valid/` programs
don't parse. Those are **real gaps** (filed as #88), and they can't be
fixed in the change that introduces the gate. So each is recorded
explicitly *with its cause*, and the gate enforces in **both**
directions:

- nothing currently working may break;
- nothing may join the known-bad lists;
- **anything that starts working must be removed from them** — a stale
entry fails the build.

That last rule is what stops an "expected failures" list decaying into a
permanent excuse.

**Verified adversarially** — I tried to break it three ways, and it
caught all three:

| Attack | Result |
|---|---|
| Break a must-run example (append a false assert) | `EVAL FAILED` →
exit 1 |
| Add an unlisted example | `UNLISTED` → exit 1 |
| Mark a *working* example as known-bad | `NOW WORKS` → exit 1 |

## Also: the conformance runner never worked

`run_conformance.sh` invoked `dune exec -- tangle --parse-only`. **Both
halves were wrong** — the executable is `main` (`compiler/bin/dune`),
not `tangle`, and there has never been a `--parse-only` flag. Every
invocation failed, so the suite reported **3/19** while proving nothing:
all three "passes" were `invalid/` cases, and an invalid case passes
*precisely when the command fails*. It failed for the wrong reason and
scored a point for it.

Fixed (prefers the pre-built binary, falls back to `dune exec`). The
real number is **14/19**.

## Current state locked in

| | |
|---|---|
| examples parse | **7/7** |
| examples typecheck + evaluate | 2/7 (`echo_pd`, `isotopy`) |
| conformance | **14/19** (was a fake 3/19) |
| `invalid/` correctly rejected | 3/3 |

Path filters were widened to `examples/**`, `conformance/**` and the
script itself — otherwise a change to the corpus would never trigger the
gate that checks it. Adds `just corpus` and `just check-all` for local
use.

Debt tracked in **#88**, with per-file causes and a suggested order.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jul 29, 2026
Closes both acceptance criteria of #56 — though the second was already
satisfied, and the first was mis-scoped.

## Criterion 1 — template residue

The issue recorded *"5 `{{PLACEHOLDER}}` files + 8 `rsr-template-repo`
references"*. Re-measured: **8** files matched `{{...}}` and **21**
lines matched the template name — but most were **meta-references**:
files *describing* the debt, not carrying it.

Three would have been damaged by a naive search-and-replace:

| File | Why it must not be touched |
|---|---|
| `src/rust/src/eval.rs` | `"...add{{}} block"` is a Rust `format!`
**escape** — `{{}}` renders as a literal `{}`, naming the `add{}`
language construct. Working code. |
| `methodology.a2ml` | The token appears inside `reject-if-contains` —
the rule that *forbids* it. |
| `AFFIRMATION.adoc`, `docs/identity-fabric/` | Prose recording the
debt. |

**The genuine residue, now cleared:** `{{DEPS}}`,
`{{BUILD_OUTPUT_PATH}}`, `{{PACKAGE_NAME}}`, `{{LANG_STACK}}`,
`{{BUILD_CMD}}`, `{{TEST_CMD}}`, `{{MUST_INVARIANTS}}`,
`{{PROJECT_UNIQUE_STRENGTH}}` — plus four contractiles and the citation
guide whose **subject was still the template**. `Intentfile` asserted
*"This repository is the canonical template for Rhodium Standard
Repository compliance"*; `CITATIONS.adoc` credited *"Polymath, Hyper"*
for *"RSR-template-repo"* in all five citation formats.

## A worse class, found while fixing it

All three QUICKSTARTs documented commands that **do not exist** — `just
build`, `just test`, `just lint`, `just panic-scan`, `just setup-dev`,
`just build-release`, `just install`, `just run`, `just heal`, `just
setup`, `just uninstall`, `just stapeln-run` — asserted an
`$XDG_CONFIG_HOME/tangle/config.toml` the compiler never reads, and
carried a *"Multi-Instance Deployment"* section for what is a single
stateless binary.

Documentation that fails when followed is worse than none. Every command
now shown is verified to exist, and the worked example shows the
**real** output (8 `assertion passed` lines, exit 0) rather than the
template's invented `Tangle started successfully.`

## Criterion 2 — already satisfied by #78

`README.adoc` carries the correction and `AFFIRMATION.adoc` has
*"Erratum — 2026-07-21 / Correction 1 — the stack does not exist"*.

Worth noting: the criterion as worded — *"does the simplified diagram
supersede, or re-state, the nuance?"* — presupposed the stack was real.
**Both options were wrong.** `KRL → TangleIR → …` describes a dependency
that never existed (`TangleIR` appears in no source file in either
repo). It was withdrawn, not reconciled.

## Making the rule real

`methodology.a2ml` has always declared `reject-if-contains = [...,
"rsr-template-repo"]` — and **nothing enforced it**, which is precisely
how the contractiles stayed mis-instantiated. Adds
`scripts/check-rsr-instantiation.sh` (plus `just rsr-check` and a CI
step), using an explicit allowlist so meta-references and the Rust
escape aren't false-positived.

**Verified adversarially:**

| Attack | Caught |
|---|---|
| Reintroduce a `{{PLACEHOLDER}}` | ✅ |
| Claim to be `rsr-template-repo` | ✅ |
| Document a phantom `just` recipe | ✅ |
| Rust `format!` escape `add{{}}` | ✅ correctly **ignored** |

It earned itself immediately: on first run it flagged
`QUICKSTART-USER.adoc`, a file I had not inspected, with six phantom
recipes.

## Also

`AFFIRMATION.adoc` gains a dated **Erratum — 2026-07-29** (Corrections
3–5) recording the residue clearance, that the OCaml compiler is no
longer unbuilt (#87/#89 — the previous erratum's closing line said it
was), and that `==` is no longer list equality (#50/#87).

All gates pass locally: build, corpus, rsr-check, `must-check`,
`trust-verify`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant