Skip to content

parse-reader: splice multi-line def := continuations (eigentrust pitfall #15) - #12

Merged
hierophantos merged 1 commit into
mainfrom
claude/fix-eigentrust-pitfall-15-mldef
Apr 30, 2026
Merged

parse-reader: splice multi-line def := continuations (eigentrust pitfall #15)#12
hierophantos merged 1 commit into
mainfrom
claude/fix-eigentrust-pitfall-15-mldef

Conversation

@kumavis

@kumavis kumavis commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Fixes pitfall #15 from docs/tracking/2026-04-23_eigentrust_pitfalls.md.

Summary

def c-asym-3 : [List [List Rat]]
  := '['[rz 1/2 1/2] '[rz rz ro] '[ro rz rz]]

silently suppressed evaluation of downstream top-level expressions. The reducer never fired, PHASE-TIMINGS reported reduce_ms = 0, and the computed value was never printed. The same def collapsed to one line worked normally (reduce_ms = 38_855 for a 3-iter benchmark).

Load-bearing for benchmark validity — silent suppression was the worst of both worlds (no error, but no work).

Reproducer (before/after)

def d : [List Rat]
  := '[1/2 1/2 1/2]
d
Form 1 (def) Form 2 (d)
Before Unbound variable: $list-literal Unbound variable: d (reduce_ms=0)
After d : [List Rat] defined '[1/2 1/2 1/2] : [List Rat]

Identical to the one-line form.

Fix

WS reader splices the := BODY continuation into the def's parent token stream. Narrower than the spec splice rule from PR #4: only continuations whose first token is := are spliced. Bare bodies, + 1 2 applications, bracketed groups stay wrapped, preserving today's def c\n + 1 2(def c (+ 1 2)) semantics.

Implementation: added def-form-node?, flatten-with-boundaries/def, continuation-starts-with-assign? to parse-reader.rkt, dispatched in tree-node->stx-elements.

Coexistence note with PR #4 (pitfall #6)

PR #4 (pitfall #6) is not yet merged to main, but uses the same dispatch site in tree-node->stx-elements. This PR introduces the flatten-with-boundaries/spec triple from PR #4's commit 6c42aa9 alongside the new def helpers because both fixes share the dispatch site. Whichever lands second will need a small dedup of flatten-with-boundaries/spec, spec-form-node?, and continuation-starts-with-keyword?.

Test plan

  • New tests/test-def-multiline-ws.rkt — 12 cases: datum-level equivalence between one-line and two-line forms, end-to-end evaluation regression, := on its own line, multi-line BODY
  • Adjacent regression sample (337 tests across 11 files: process-ws-01, parse-reader, let-arrow-syntax, spec*, defmacro, cond-01, bound-args-01, int-patterns-01, first-rest-01, hashable-01) all pass
  • Lib examples re-run cleanly: lattices.prologos, foreign.prologos, 2026-04-02-ppn-track3.prologos
  • CI fix included (benchmarks/micro/info.rkt)

Commits

  1. e7dbc0f — primary fix in parse-reader.rkt + new test file
  2. 2ebdb15 — CI fix (skip stale bench file)

https://claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x


Generated by Claude Code

@kumavis
kumavis force-pushed the claude/fix-eigentrust-pitfall-15-mldef branch from 2ebdb15 to 5331e70 Compare April 26, 2026 01:03
kumavis added a commit that referenced this pull request Apr 26, 2026
Adds pvec-nth-int / pvec-length-int / pvec-take-int / pvec-drop-int to
prologos::core::pvec, mirroring the existing nth-int / length-int /
take-int / drop-int quartet on List. These let an algorithm that already
carries Int counters (e.g. for an int-le budget termination check)
index into a PVec without maintaining a parallel Nat counter.

Implementation routes through pvec-to-list and the existing List Int
helpers (and back via pvec-from-list for take/drop), avoiding a
freshly-introduced Int->Nat conversion primitive. An earlier draft
defined a private int-to-nat-clamp helper and called pvec-slice
directly, but pvec-slice's whnf rule needs nat-value to succeed on its
lo/hi args — and a defn-defined int-to-nat-clamp does not reduce far
enough inside the slice's whnf to satisfy nat-value, leaving the slice
stuck. Routing through List sidesteps this entirely and matches the
List versions' semantics exactly: negative indices are out-of-range,
out-of-bounds nth returns none, take/drop clamp to [0, length].

Cost: O(n) for nth-int/take-int/drop-int (matching the List
counterparts). pvec-length-int is O(log32 n).

Did NOT add a top-level Int -> Nat helper. The pitfall noted that
from-int : Int -> Nat does not exist, but `from-int` is already a
parser keyword (Int -> Rat), and the List helpers themselves avoid the
conversion via pure recursion — so a public Int -> Nat would just be a
foot-gun that doesn't unblock anything the new helpers don't already
address.

Tests: tests/test-pvec-int-helpers.rkt covers length on empty/1/2,
nth at 0/1/last/negative/out-of-bounds/empty, take and drop at
1/0/negative/larger-than-length, and a take+drop length round-trip.
A standalone smoke test (run separately) exercises all 16 cases via
process-string + pvec-to-list/pvec-length-int and confirms PASS on
both Racket 8.10 (with current-parallel-executor #f) and Racket 9.1.
The test file uses the standard test-support.rkt + rackunit pattern;
the pre-existing (thread #:pool 'own) blocker on Racket 8.10 prevents
running it via `raco test` here, but it is ready for the standard
test runner.

https: //claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x
Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>
@kumavis
kumavis force-pushed the claude/fix-eigentrust-pitfall-15-mldef branch from 5331e70 to 1f67a47 Compare April 26, 2026 03:06
kumavis added a commit that referenced this pull request Apr 26, 2026
Adds pvec-nth-int / pvec-length-int / pvec-take-int / pvec-drop-int to
prologos::core::pvec, mirroring the existing nth-int / length-int /
take-int / drop-int quartet on List. These let an algorithm that already
carries Int counters (e.g. for an int-le budget termination check)
index into a PVec without maintaining a parallel Nat counter.

Implementation routes through pvec-to-list and the existing List Int
helpers (and back via pvec-from-list for take/drop), avoiding a
freshly-introduced Int->Nat conversion primitive. An earlier draft
defined a private int-to-nat-clamp helper and called pvec-slice
directly, but pvec-slice's whnf rule needs nat-value to succeed on its
lo/hi args — and a defn-defined int-to-nat-clamp does not reduce far
enough inside the slice's whnf to satisfy nat-value, leaving the slice
stuck. Routing through List sidesteps this entirely and matches the
List versions' semantics exactly: negative indices are out-of-range,
out-of-bounds nth returns none, take/drop clamp to [0, length].

Cost: O(n) for nth-int/take-int/drop-int (matching the List
counterparts). pvec-length-int is O(log32 n).

Did NOT add a top-level Int -> Nat helper. The pitfall noted that
from-int : Int -> Nat does not exist, but `from-int` is already a
parser keyword (Int -> Rat), and the List helpers themselves avoid the
conversion via pure recursion — so a public Int -> Nat would just be a
foot-gun that doesn't unblock anything the new helpers don't already
address.

Tests: tests/test-pvec-int-helpers.rkt covers length on empty/1/2,
nth at 0/1/last/negative/out-of-bounds/empty, take and drop at
1/0/negative/larger-than-length, and a take+drop length round-trip.
A standalone smoke test (run separately) exercises all 16 cases via
process-string + pvec-to-list/pvec-length-int and confirms PASS on
both Racket 8.10 (with current-parallel-executor #f) and Racket 9.1.
The test file uses the standard test-support.rkt + rackunit pattern;
the pre-existing (thread #:pool 'own) blocker on Racket 8.10 prevents
running it via `raco test` here, but it is ready for the standard
test runner.

https: //claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x
Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>
kumavis added a commit that referenced this pull request Apr 26, 2026
Adds pvec-nth-int / pvec-length-int / pvec-take-int / pvec-drop-int to
prologos::core::pvec, mirroring the existing nth-int / length-int /
take-int / drop-int quartet on List. These let an algorithm that already
carries Int counters (e.g. for an int-le budget termination check)
index into a PVec without maintaining a parallel Nat counter.

Implementation routes through pvec-to-list and the existing List Int
helpers (and back via pvec-from-list for take/drop), avoiding a
freshly-introduced Int->Nat conversion primitive. An earlier draft
defined a private int-to-nat-clamp helper and called pvec-slice
directly, but pvec-slice's whnf rule needs nat-value to succeed on its
lo/hi args — and a defn-defined int-to-nat-clamp does not reduce far
enough inside the slice's whnf to satisfy nat-value, leaving the slice
stuck. Routing through List sidesteps this entirely and matches the
List versions' semantics exactly: negative indices are out-of-range,
out-of-bounds nth returns none, take/drop clamp to [0, length].

Cost: O(n) for nth-int/take-int/drop-int (matching the List
counterparts). pvec-length-int is O(log32 n).

Did NOT add a top-level Int -> Nat helper. The pitfall noted that
from-int : Int -> Nat does not exist, but `from-int` is already a
parser keyword (Int -> Rat), and the List helpers themselves avoid the
conversion via pure recursion — so a public Int -> Nat would just be a
foot-gun that doesn't unblock anything the new helpers don't already
address.

Tests: tests/test-pvec-int-helpers.rkt covers length on empty/1/2,
nth at 0/1/last/negative/out-of-bounds/empty, take and drop at
1/0/negative/larger-than-length, and a take+drop length round-trip.
A standalone smoke test (run separately) exercises all 16 cases via
process-string + pvec-to-list/pvec-length-int and confirms PASS on
both Racket 8.10 (with current-parallel-executor #f) and Racket 9.1.
The test file uses the standard test-support.rkt + rackunit pattern;
the pre-existing (thread #:pool 'own) blocker on Racket 8.10 prevents
running it via `raco test` here, but it is ready for the standard
test runner.

https: //claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x
Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>

@hierophantos hierophantos left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. Surgical scope mirrors #4 (preparser splice based on first-token check), with a deliberately narrower rule: only :=-headed continuations splice; bare bodies stay wrapped — preserving def c\n + 1 2(def c (+ 1 2)) semantics for the existing bare-body idiom. End-to-end test coverage (using run-ns-ws-last) pins the silent-suppression case, which is the worst-quality bug shape (no error, just no work — was reporting reduce_ms = 0 in benchmarks).

Audit-passed against our codebase: 354 single-line def := body exist, all unaffected; zero multi-line def usage today, so the change is purely additive.

Now that #4 has landed (3d7ccb19), this PR is showing CONFLICTING — per your own note in the PR body, needs a rebase to drop the duplicate spec-form-node? / flatten-with-boundaries/spec / continuation-starts-with-keyword? (now on main) and add the def-form-node? branch on top of the existing spec-form-node? cond clause.

Approving — ready to merge once the rebase lands.

@kumavis
kumavis force-pushed the claude/fix-eigentrust-pitfall-15-mldef branch from 1f67a47 to b51900d Compare April 27, 2026 06:32
@kumavis
kumavis force-pushed the claude/fix-eigentrust-pitfall-15-mldef branch from b51900d to d1c92a5 Compare April 27, 2026 08:06
kumavis pushed a commit that referenced this pull request Apr 27, 2026
Wires the OCapN port to a real Racket toolchain and fixes the issues
the test run surfaced.

Library fixes
  - vat.prologos: rename `spawn` -> `vat-spawn` (collision with the
    reserved surface form recognised in macros.rkt:`'spawn`),
    `spawn-actor` -> `vat-spawn-actor`. Same in core.prologos and the
    acceptance file.
  - vat.prologos: drop the `Sigma Vat Nat` return shape for spawn /
    fresh-promise / send. Replace with a named `Allocated` struct +
    `alloc-vat` / `alloc-id` accessors. The Sigma form ran into
    "could not infer" elaborator errors when the body destructured
    via `match | pair a b -> ...` and then re-constructed a Sigma;
    `[fst p]` / `[snd p]` reused on the same `p` tripped QTT
    multiplicity. The named struct sidesteps both.
  - vat.prologos: reorder `resolve-promise` / `break-promise` BEFORE
    `apply-effect` (forward-reference rule — module elaboration is
    single-pass top-to-bottom). Also reorder `step-after-act` before
    `deliver-msg` and `list-length-helper` before `queue-length`.
  - vat.prologos: drop the queued-pipeline-flush in resolve-promise /
    break-promise. PromiseState's queue is `List SyrupValue` (wire
    repr); the vat queue is `List VatMsg` (decoded); flushing across
    the boundary would need re-encoding. Phase 1.

Test-fixture fix (load-bearing)
  - All 8 OCapN test files were updated to capture and restore
    `current-ctor-registry` and `current-type-meta` across the setup-
    -> run boundary. The standard fixture pattern from
    `test-hashable-01.rkt` does NOT preserve these — fine for tests
    that only declare traits, but breaks once a preamble's imports
    declare new `data` types (every `data` in our 8 modules). Without
    it, the reducer sees a stale ctor-registry and refuses to fire
    pattern arms over user constructors; results print as un-reduced
    `[reduce ... | vat x y z a -> x] : Nat` strings.
    Documented as goblin-pitfall #12; the canonical fixture in
    test-support.rkt should grow this for every future test.

Compat fence
  - driver.rkt: guard
    `(current-parallel-executor (make-parallel-thread-fire-all))` with
    a feature-detection try/catch on `thread #:pool 'own`. Racket 9
    ships parallel threads; Racket 8 does not. Fence preserves the
    Racket-9 fast path and falls back to sequential firing on 8.

Acceptance
  - examples/2026-04-27-ocapn-acceptance.prologos updated to match
    the new vat-spawn/Allocated API and verified to run clean via
    process-file.

Pitfalls catalogue (docs/tracking/2026-04-27_GOBLIN_PITFALLS.md)
  - #0 (sandbox/no-Racket): closed.
  - +#11 — Racket-8 vs Racket-9 `thread #:pool` compat
  - +#12 — test fixture loses ctor-registry/type-meta across calls
           [highest-impact; canonical fixture pattern needs update]
  - +#13 — `spawn` is a reserved surface keyword; collides silently
  - +#14 — `match | pair a b ->` on Sigma + Sigma reconstruction =>
           "could not infer"
  - +#15 — QTT multiplicity on `[fst p]`/`[snd p]` reused thrice
  - +#16 — single-pass module elaboration: forward references error
  - +#17 — promise-queue (Syrup) vs vat-queue (VatMsg) type clash
           on flush — design pitfall, scope cut

Test results
  refr      6/6   syrup    22/22  promise   16/16  message  19/19
  behavior 13/13  vat     21/21   pipeline   5/5   captp     7/7
  e2e       8/8                                  total  117/117 PASS
kumavis pushed a commit that referenced this pull request Apr 27, 2026
Per user direction:
- Replace the body of every DELETED entry with a single-sentence
  explanation. Numbers reserved per prior instruction.
- Delete #15 (QTT multiplicity on fst/snd thrice). I re-tested
  with a real Racket — `pair [snd p] [fst p]` then a third use of
  `fst p` works fine; no multiplicity error. The failure I had
  conflated this with was actually #14's "match-and-reconstruct
  Sigma" issue.

Result: pitfalls doc shrinks from 765 to 534 lines. Remaining
real claims: #1, #4, #5, #11, #12, #13, #14, #16, #17, #18, #19,
#20 (the user has reviewed only #0-10 so far; #11-20 still
pending their review).
…alls #15)

A two-line def with type annotation —

    def c : [List Int]
      := '[1 2 3]

— silently suppressed evaluation: PHASE-TIMINGS reported reduce_ms=0
and the bound name elaborated but never reduced. The same def collapsed
to one line worked normally. The silent-no-work shape was particularly
bad for benchmarks (reported zero reduce time regardless of workload).

Add a def-form-node? branch to tree-node->stx-elements and a
flatten-with-boundaries/def variant that splices ONLY continuations
whose first token is :=. All other continuations (bare body, bracketed
application chain, etc.) stay wrapped, preserving today's

    def c
      + 1 2

→ (def c (+ 1 2)) semantics for the bare-body idiom.

Narrower than the spec splice rule (#4 / #6) on purpose: spec needs to
expose -> from anywhere in a multi-line type signature, def only needs
to expose := which is always the head of a natural line break.

Test coverage in test-def-multiline-ws.rkt covers:
  - Datum equivalence between one-line and two-line shapes (12 cases).
  - Bare-body wrapping is preserved.
  - End-to-end via run-ns-ws-last (silent-suppression regression pin).
  - Multi-line BODY after :=, := alone on a line, and definitions
    spanning lines as bracketed groups.

Three-level WS validation:
  - Level 1 (sexp): the test file uses run-ns-last where applicable.
  - Level 2 (WS string): ws-read / run-ns-ws-last cover the dispatch
    and elaboration paths.
  - Level 3 (WS file): process-file on a .prologos with the two-line
    form reports reduce_ms=5 / reduce_steps=39, matching the one-line
    shape exactly (was reduce_ms=0 / reduce_steps=0 before).

https://claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x

Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>
@kumavis
kumavis force-pushed the claude/fix-eigentrust-pitfall-15-mldef branch from d1c92a5 to 274cd2a Compare April 28, 2026 21:19
@kumavis
kumavis requested a review from hierophantos April 30, 2026 20:04
kumavis added a commit that referenced this pull request Apr 30, 2026
Adds pvec-nth-int / pvec-length-int / pvec-take-int / pvec-drop-int to
prologos::core::pvec, mirroring the existing nth-int / length-int /
take-int / drop-int quartet on List. These let an algorithm that already
carries Int counters (e.g. for an int-le budget termination check)
index into a PVec without maintaining a parallel Nat counter.

Implementation routes through pvec-to-list and the existing List Int
helpers (and back via pvec-from-list for take/drop), avoiding a
freshly-introduced Int->Nat conversion primitive. An earlier draft
defined a private int-to-nat-clamp helper and called pvec-slice
directly, but pvec-slice's whnf rule needs nat-value to succeed on its
lo/hi args — and a defn-defined int-to-nat-clamp does not reduce far
enough inside the slice's whnf to satisfy nat-value, leaving the slice
stuck. Routing through List sidesteps this entirely and matches the
List versions' semantics exactly: negative indices are out-of-range,
out-of-bounds nth returns none, take/drop clamp to [0, length].

Cost: O(n) for nth-int/take-int/drop-int (matching the List
counterparts). pvec-length-int is O(log32 n).

Did NOT add a top-level Int -> Nat helper. The pitfall noted that
from-int : Int -> Nat does not exist, but `from-int` is already a
parser keyword (Int -> Rat), and the List helpers themselves avoid the
conversion via pure recursion — so a public Int -> Nat would just be a
foot-gun that doesn't unblock anything the new helpers don't already
address.

Tests: tests/test-pvec-int-helpers.rkt covers length on empty/1/2,
nth at 0/1/last/negative/out-of-bounds/empty, take and drop at
1/0/negative/larger-than-length, and a take+drop length round-trip.
A standalone smoke test (run separately) exercises all 16 cases via
process-string + pvec-to-list/pvec-length-int and confirms PASS on
both Racket 8.10 (with current-parallel-executor #f) and Racket 9.1.
The test file uses the standard test-support.rkt + rackunit pattern;
the pre-existing (thread #:pool 'own) blocker on Racket 8.10 prevents
running it via `raco test` here, but it is ready for the standard
test runner.

https: //claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x
Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>
@hierophantos
hierophantos merged commit a443b64 into main Apr 30, 2026
1 check passed
@hierophantos
hierophantos deleted the claude/fix-eigentrust-pitfall-15-mldef branch April 30, 2026 21:42
kumavis pushed a commit that referenced this pull request May 4, 2026
Wires the OCapN port to a real Racket toolchain and fixes the issues
the test run surfaced.

Library fixes
  - vat.prologos: rename `spawn` -> `vat-spawn` (collision with the
    reserved surface form recognised in macros.rkt:`'spawn`),
    `spawn-actor` -> `vat-spawn-actor`. Same in core.prologos and the
    acceptance file.
  - vat.prologos: drop the `Sigma Vat Nat` return shape for spawn /
    fresh-promise / send. Replace with a named `Allocated` struct +
    `alloc-vat` / `alloc-id` accessors. The Sigma form ran into
    "could not infer" elaborator errors when the body destructured
    via `match | pair a b -> ...` and then re-constructed a Sigma;
    `[fst p]` / `[snd p]` reused on the same `p` tripped QTT
    multiplicity. The named struct sidesteps both.
  - vat.prologos: reorder `resolve-promise` / `break-promise` BEFORE
    `apply-effect` (forward-reference rule — module elaboration is
    single-pass top-to-bottom). Also reorder `step-after-act` before
    `deliver-msg` and `list-length-helper` before `queue-length`.
  - vat.prologos: drop the queued-pipeline-flush in resolve-promise /
    break-promise. PromiseState's queue is `List SyrupValue` (wire
    repr); the vat queue is `List VatMsg` (decoded); flushing across
    the boundary would need re-encoding. Phase 1.

Test-fixture fix (load-bearing)
  - All 8 OCapN test files were updated to capture and restore
    `current-ctor-registry` and `current-type-meta` across the setup-
    -> run boundary. The standard fixture pattern from
    `test-hashable-01.rkt` does NOT preserve these — fine for tests
    that only declare traits, but breaks once a preamble's imports
    declare new `data` types (every `data` in our 8 modules). Without
    it, the reducer sees a stale ctor-registry and refuses to fire
    pattern arms over user constructors; results print as un-reduced
    `[reduce ... | vat x y z a -> x] : Nat` strings.
    Documented as goblin-pitfall #12; the canonical fixture in
    test-support.rkt should grow this for every future test.

Compat fence
  - driver.rkt: guard
    `(current-parallel-executor (make-parallel-thread-fire-all))` with
    a feature-detection try/catch on `thread #:pool 'own`. Racket 9
    ships parallel threads; Racket 8 does not. Fence preserves the
    Racket-9 fast path and falls back to sequential firing on 8.

Acceptance
  - examples/2026-04-27-ocapn-acceptance.prologos updated to match
    the new vat-spawn/Allocated API and verified to run clean via
    process-file.

Pitfalls catalogue (docs/tracking/2026-04-27_GOBLIN_PITFALLS.md)
  - #0 (sandbox/no-Racket): closed.
  - +#11 — Racket-8 vs Racket-9 `thread #:pool` compat
  - +#12 — test fixture loses ctor-registry/type-meta across calls
           [highest-impact; canonical fixture pattern needs update]
  - +#13 — `spawn` is a reserved surface keyword; collides silently
  - +#14 — `match | pair a b ->` on Sigma + Sigma reconstruction =>
           "could not infer"
  - +#15 — QTT multiplicity on `[fst p]`/`[snd p]` reused thrice
  - +#16 — single-pass module elaboration: forward references error
  - +#17 — promise-queue (Syrup) vs vat-queue (VatMsg) type clash
           on flush — design pitfall, scope cut

Test results
  refr      6/6   syrup    22/22  promise   16/16  message  19/19
  behavior 13/13  vat     21/21   pipeline   5/5   captp     7/7
  e2e       8/8                                  total  117/117 PASS
kumavis pushed a commit that referenced this pull request May 4, 2026
Per user direction:
- Replace the body of every DELETED entry with a single-sentence
  explanation. Numbers reserved per prior instruction.
- Delete #15 (QTT multiplicity on fst/snd thrice). I re-tested
  with a real Racket — `pair [snd p] [fst p]` then a third use of
  `fst p` works fine; no multiplicity error. The failure I had
  conflated this with was actually #14's "match-and-reconstruct
  Sigma" issue.

Result: pitfalls doc shrinks from 765 to 534 lines. Remaining
real claims: #1, #4, #5, #11, #12, #13, #14, #16, #17, #18, #19,
#20 (the user has reviewed only #0-10 so far; #11-20 still
pending their review).
hierophantos added a commit that referenced this pull request May 5, 2026
…2-pv12

pvec: add Int-indexed helpers (eigentrust pitfall #12)
@hierophantos

Copy link
Copy Markdown
Contributor

Post-rebase verified clean: dispatch in tree-node->stx-elements extended from spec-only if to 3-way cond (spec/def/else); duplicate spec helpers correctly absent (delegated to #4 which already landed them); flatten-with-boundaries/def only splices := continuations preserving the bare-body wrap semantics. Same scope as original approval. Merging.

kumavis pushed a commit that referenced this pull request Jul 27, 2026
Merges origin/main (branch point 68f4564, 2026-05-02 -> tip 9bff07f,
2026-07-22) into the OCapN branch and re-audits the pitfalls log against
the merged toolchain on Racket 9.0.

Merge: 4 trivial conflicts (3 docs + the nat.pnet cache, which main now
gitignores). Compiler builds clean.

Audit findings:

- main fixed NONE of the 11 load-bearing pitfalls. Its quarter went to
  typing/records (CIU T6), propagator internals (PPN 4C), Numerics and
  Rel T1; the WS reader, match-lowering, data-inference and reducer perf
  were untouched. PReduce is docs-only (Tracks 1-9 not started).

- #27/#31 (decode perf) are STALE as written: fixed on our own branch by
  4f6b3f0 (looseBVarRange short-circuit on shift), two days after #31 was
  logged. Re-measured: the #31 repro decodes in ~1s, not 150s. Residual is
  a ~100x constant factor, not a wall. This is also the recommended fix for
  still-open upstream issue #58.

- NEW: the .pnet module cache is unsound. Same script, cold cache ->
  correct String value; warm cache -> stuck [reduce ...] term, no error.
  Silently non-reducing definitions; highest-severity new item.

- NEW: transitive-import mis-elaboration pinned to a mechanism -
  step-behavior's BehaviorTag parameter elaborates as String, so no beh-*
  arm matches. Unifies #12 and #33.

- NEW: the merge breaks our harness. main retired current-prelude-env
  (9d166ce) and swept its own tests; our 37 OCapN test files did not get
  swept, and the shared-fixture pattern has no supported replacement.

Also records the measured cost of the workarounds (153 helper defns, 92
explicit type-arg sites, 38 hand-written 11-ctor matches) and an honest
blocker taxonomy separating architectural / silent-wrong / expressiveness
/ throughput.

No code changes; docs + merge only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YM6gc3cMNH2Ymor4jdZY8u
kumavis pushed a commit that referenced this pull request Jul 29, 2026
…ator hints are a map

Three more entries. 9615 unit tests, conformance 24/24.

§1.4 #6 — `op:listen`'s third field was required-but-DISCARDED, so a peer
asking for partial resolutions (a promise resolving to another promise) was
indistinguishable from one that was not. `op-listen` carries it now. A
non-boolean in that slot fails the decode rather than defaulting to false:
the field is not optional, and coercing a malformed one is the same silent
class as the answer position two functions away.

§1.6 M8 — a frame we cannot decode now gets an `op:abort` instead of "", which
the peer could not tell apart from "decoded fine, nothing to say". Every
decode failure is fatal, deliberately: the decoder's gates are arity,
descriptor label and slot well-formedness, so a frame that fails one is a
frame whose meaning we do not know, and continuing is how a desync becomes a
wrong answer several frames later.

This one was blocked for a reason worth recording: the SPEC's plural GC ops
did not decode, so aborting would have fired on ordinary conforming traffic.
Fixing them removed the blocker, and this entry was reachable only after.

§1.10 #12 — locator hints are a MAP (flat alternating key/value, the shape
syrup-dict uses) rather than two fixed scalar fields. Upstream ships an onion
netlayer whose hints are different keys entirely and had no representation at
all. `loc-host`/`loc-port` survive as lookups, so the one consumer and the
tests are unaffected; `loc-port` parses its hint and answers zero when absent
or non-numeric, because a locator with no port is a loopback locator, not an
error. The string→Nat parse the gaps doc named as the blocker lives here for
exactly that reason -- a general one in the stdlib could not choose to be
total.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YM6gc3cMNH2Ymor4jdZY8u
kumavis pushed a commit that referenced this pull request Jul 29, 2026
…wn premises

9616 unit tests, conformance 24/24. Seven entries were open; five are closed
and two turned out to rest on premises that no longer hold.

§1.9 #4 IS A LANGUAGE GAP, NOT AN OCapN ONE

Annotating an FFI binding `:requires (CryptoCap)` makes the whole module
unimportable from a `.prologos` file: the requirement propagates to the caller
and nothing can introduce a root capability. I tried wrapping the bindings in
same-module functions the way `tcp-testing` does, and importing the capability
type alongside. Neither helps.

Then the actual discovery: `prologos::ocapn::tcp-testing`, which this document
cites as proof the convention is live, CANNOT BE IMPORTED EITHER. Nothing in
the tree imports it, and its test loads it through the sexp `imports` path,
which does not hit the check — so a module that no consumer can use has been
sitting there with a green test asserting "just load the module".

It now carries a characterization test that pins the WS-mode failure. That
test asserts behaviour that is WRONG on purpose: when the language grows a
root capability form it fails, and that is the signal to thread the capability
for real. A gap with a witness beats a gap with a comment.

§1.8 M2 HAS LOST ITS CONSEQUENCE

The stated failure was that a process-wide keypair collapses session-ids and
the handoff replay guard, keyed on session, then refuses a peer's legitimate
second handoff. That guard became per-connection when the gift namespace was
split, so two connections no longer share a replay set. What is left is a
conformance deviation with no failing input — which by this document's own
standard is not a finding. Minting per connection also costs a
`process-string` per accept, which the suite's wall-clock budget would notice.

CLOSED

  - §1.1 #5 / §1.10 #3 / §1.10 #11 — sets and floats (previous commit)
  - §1.4 #6 — op:listen carries wants-partial (previous commit)
  - §1.6 M8 — undecodable frames abort (previous commit)
  - §1.10 #10 — the plain-value reason names its value (previous commit)
  - §1.10 #12 — locator hints are a map (previous commit)

WHAT IS ACTUALLY LEFT

Five entries, and four of them are one problem: the gifter and receiver roles
live in Racket because a Prologos behaviour cannot open a socket. §1.7 M8
(double processing), §1.7 M7 (unregistered enliven slots) and §1.10 #8
(unsigned Node peers) are all downstream of that. Both of §1.7 M7's live
consequences are gone; what remains is that the slot reservation is by
convention rather than construction.

The fifth, §1.2 M3, is independent and has a known shape: allocate our own
answer position for a forwarded send and resolve the queued deliver's from it.
The pieces exist — `bs-add-question` already maps a peer answer position to a
local promise and the pump answers from it — but wiring them means threading
BridgeState through a loop that returns bytes only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YM6gc3cMNH2Ymor4jdZY8u
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.

3 participants