Skip to content

feat(jeg): close all 17 deferred rules; report the one remaining hole - #104

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/jeg-full-coverage
Jul 29, 2026
Merged

feat(jeg): close all 17 deferred rules; report the one remaining hole#104
hyperpolymath merged 1 commit into
mainfrom
feat/jeg-full-coverage

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

The problem

Seventeen rules matched a -> () arm in check. That is not a partial check — it is an accept. A forged node naming T-Close, T-App or T-Weave could conclude any type at all and the graph still checked green.

The JEG's entire claim is "evidence, not a log" — that a derivation can be validated without trusting whoever produced it. Those seventeen arms were the part that made the claim false.

Why three of them were genuinely underivable

T-App, T-Crossing and T-Weave could not be re-derived from the recorded data, because the judgement didn't carry what the rule reads. So the judgement was widened:

  • j_ctx now holds env_entry, not ty — a T-App node cannot be checked without the callee's signature.
  • j_sigma (new) holds the strand contextT-Crossing/T-Weave read Σ rather than premise types. They were previously bare leaves: nodes asserting a type with nothing licensing it.

The other fourteen were derivable all along and simply hadn't been written.

Shared rule functions — the JEG now cannot drift

infer_binop was already type-in/type-out. The remaining rules were inline in infer_expr, so the JEG would have had to re-implement them — and a JEG that re-implements the rules can drift from the typechecker, at which point it certifies a rule the compiler doesn't apply and the evidence is worthless.

Extracted 16 (infer_close, infer_mirror, infer_echo_eq, join_arm_ty, …) into one definition each, called by both. Drift is impossible by construction rather than by discipline. The extraction is behaviour-preserving — every pre-existing test passes unchanged.

Two rules with real substance

  • T-Let checks the conclusion is the body's type and that the body was checked under the binding the let actually makes. Type-only checking would accept a derivation whose body silently assumed x had a more convenient type — there's a test for exactly that forgery.
  • T-Weave re-runs the strand linearity check from feat(typecheck): QTT quantity semiring — strands are linear in weave #103, so a graph cannot launder a weave that duplicates or drops a strand.

The remaining hole is reported, not hidden

T-Add-Block's island has its own judgement (⊢_hd). Rather than a silent accept it's recorded — new unchecked : derivation -> (string * judgement) list. So "check succeeded" and "check succeeded and re-derived every node" are now distinguishable results, and --derive says which:

== c ==  (3 nodes, depth 3, every node re-derived)
[T-Close] |- close(mirror(w)) : Tangle[I, I]
  [T-Mirror] |- mirror(w) : Word[3]
    [T-Var] w:Word[3] |- w : Word[3]

Verification

  • JEG suite 19 → 51 tests, structured as an honest/forged pair per rule closed — so each test's forged half is a case that would have passed before
  • 709 compiler tests pass (dune runtest --force)
  • conformance 23/23; corpus + RSR gates green

Follows #103 (merged). feat/jeg-complete on origin is a superseded pre-rebase copy of this branch — safe to delete.

🤖 Generated with Claude Code

Seventeen rules matched a `-> ()` arm in `check`. That is not a partial
check — it is an ACCEPT. A forged node naming T-Close, T-App or T-Weave could
conclude any type at all and the graph still checked green. Since the JEG's
whole claim is "evidence, not a log", those arms were the part that made the
claim false.

## What made them underivable, and the fix

Three of them (T-App, T-Crossing, T-Weave) genuinely could not be re-derived
from the recorded data, because the judgement did not carry what the rule
reads. So the judgement was widened:

  * `j_ctx` now holds `env_entry`, not `ty` — a T-App node cannot be checked
    without the callee's SIGNATURE.
  * `j_sigma` (new) holds the strand context — T-Crossing and T-Weave read
    Sigma rather than premise types. They were previously bare LEAVES:
    nodes asserting a type with nothing licensing it at all.

The rest were derivable all along and simply had not been written.

## Shared rule functions, so the JEG cannot drift

`infer_binop` was already a type-in/type-out function. The remaining rules
were inline in `infer_expr`, so the JEG would have had to RE-IMPLEMENT them —
and a JEG that re-implements the rules can drift from the typechecker, at
which point it certifies a rule the compiler does not apply and the evidence
is worthless.

Extracted 16 of them (`infer_close`, `infer_mirror`, `infer_echo_eq`,
`join_arm_ty`, ...) into one definition each, called by BOTH. Drift is now
impossible by construction rather than by discipline. The extraction is
behaviour-preserving: all pre-existing tests pass unchanged.

## Two rules with real substance

  * T-Let checks the conclusion is the BODY's type AND that the body was
    checked under the binding the let actually makes. Checking only the type
    would accept a derivation whose body silently assumed `x` had some more
    convenient type — there is a test for exactly that forgery.
  * T-Weave re-runs the strand LINEARITY check, so a graph cannot launder a
    weave that duplicates or drops a strand.

## The remaining hole is reported, not hidden

T-Add-Block's island has its own judgement (|-_hd). Rather than a silent
accept, it is recorded: new `unchecked : derivation -> (string * judgement)
list`. "check succeeded" and "check succeeded and re-derived every node" are
now distinguishable, and `--derive` prints which it was:

    == c ==  (3 nodes, depth 3, every node re-derived)

## Verification

  * JEG suite 19 -> 51 tests; an honest/forged PAIR for each rule closed, so
    each test's forged half is a case that would have PASSED before
  * 709 compiler tests pass (`dune runtest --force`)
  * conformance 23/23, corpus + RSR gates green

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

gitar-bot Bot commented Jul 29, 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 ✅ Approved

Closes all 17 deferred rules in the JEG to eliminate partial acceptance holes, widening judgements and extracting shared rule functions to prevent typechecker drift. No issues found.

Auto-approved and auto-merge armed: No blocking issues found.
Please see Auto-approve Docs for details on setting custom approval criteria. — merges when pipeline and required approvals pass.

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

@gitar-bot

gitar-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

⚠️ Gitar auto-approved this PR but could not enable auto-merge: auto-merge is disabled for this repository — enable "Allow auto-merge" in the repository settings.

@gitar-bot gitar-bot Bot added the gitar-approved Added by Gitar label Jul 29, 2026

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR and enabled auto-merge (configure)

@hyperpolymath
hyperpolymath marked this pull request as ready for review July 29, 2026 07:48
@hyperpolymath
hyperpolymath merged commit 9144818 into main Jul 29, 2026
25 checks passed
@hyperpolymath
hyperpolymath deleted the feat/jeg-full-coverage branch July 29, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gitar-approved Added by Gitar

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant