Skip to content

Commit 75ef27c

Browse files
author
Douglas Jones
committed
dispatches: fill gaps — parser proposal/post YAMLs, parallel evaluator post pair, session-close pair
Quill and Glyph were missing: - rust-parser-proposal.yaml - rust-parser-post.yaml - parallel-evaluator-proposal.yaml - parallel-evaluator-post.readout.md + .yaml - session-close.readout.md + .yaml (the day's full story) All dispatch pairs now complete. INDEX.md regenerated.
1 parent 1fc8bc3 commit 75ef27c

8 files changed

Lines changed: 533 additions & 3 deletions
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Graph-native parallel evaluator — post-work (2026-05-12)
2+
3+
*By Quill.*
4+
5+
The parallel evaluator is built. The honest story is more interesting
6+
than the headline.
7+
8+
## What landed
9+
10+
`crates/codifide-interpreter/src/parallel.rs`:
11+
12+
- `expr_effects(expr, module)` — static conservative over-approximation
13+
of effect labels reachable from an expression. Uses declared signature
14+
effects for user-defined calls (PE-2: documented, correct).
15+
- `all_disjoint(exprs, module)` — checks all pairs for disjoint effect
16+
sets. The parallelism gate.
17+
- `should_parallelize(exprs, module)` — the full threshold: ≥2 exprs,
18+
all are direct user calls (not mixed arithmetic), all pairs disjoint.
19+
- `eval_parallel_exprs` in `interpreter.rs` — evaluates a slice of
20+
expressions in parallel via `rayon::scope`. Each branch gets its own
21+
`Interpreter` initialized with the parent's current depth (PE-3).
22+
Results collected in indexed slots, sorted by index before trace merge
23+
(PE-1: declaration order guaranteed).
24+
- `call_with_vals` — parallel-path entry point for pre-evaluated args.
25+
26+
All Sable blocking findings (PE-1, PE-3) honored in the implementation.
27+
28+
## What the benchmarks revealed
29+
30+
The parallel evaluator is correct — 70/70 conformance tests pass with
31+
it in place. But for the current benchmark programs, it is slower than
32+
sequential.
33+
34+
The threshold (`all args must be direct user calls`) correctly excludes
35+
`balanced_brackets`'s recursive `walk(s, add(i, 1), step(s, i, d))`
36+
calls. But when the parallel path was enabled on
37+
`list(fizzbuzz_one(1), ..., fizzbuzz_one(15))`, fizzbuzz went from
38+
29 µs to 66 µs — 2× slower. Rayon's thread-spawn overhead (~5-10 µs
39+
per task) exceeds the work in each `fizzbuzz_one` call (~2 µs).
40+
41+
The `Call` eval arm uses sequential evaluation for now. The parallel
42+
infrastructure is in place and correct; it needs programs where each
43+
branch takes >100 µs to show a speedup.
44+
45+
## The honest v2-A performance story
46+
47+
The sequential Rust interpreter is 6–25× faster than Python. That is
48+
the real v2-A story. The parallel evaluator is the foundation for
49+
programs that are larger than the current benchmark suite.
50+
51+
The design principle "parallelism is default; sequencing is declared"
52+
is architecturally delivered: the effect algebra governs what is safe,
53+
the static analysis is correct, the runtime honors it. The current
54+
programs are just too small to benefit.
55+
56+
## What the new example programs demonstrate
57+
58+
`examples/batch_classify.cod` — eight independent model calls. This
59+
is the program the parallel evaluator was designed for. Each
60+
`safe_classify` call is independent (disjoint `model.vision` effects
61+
per call, no shared state). When the mock `vision.classify` is replaced
62+
with a real model call taking >100 µs, the parallel evaluator will
63+
fire and the speedup will be real.
64+
65+
`examples/recursive_sum.cod` — recursive list sum with a postcondition
66+
cross-checking against the `sum` primitive. Clean demonstration of the
67+
cost-dispatch idiom for recursive functions.
68+
69+
`examples/text_stats.cod` — four independent pure functions composed
70+
into a result list. The parallel evaluator opportunity for larger
71+
programs: `word_count`, `char_count`, `has_question`, and
72+
`classify_length` are all independent and pure.
73+
74+
## What I'm not yet sure of
75+
76+
Whether the threshold (`all args must be direct user calls`) is the
77+
right long-term rule, or whether a work-estimation heuristic (e.g.,
78+
"parallelize if estimated work per branch exceeds N µs") would be
79+
better. The current rule is semantically clean and measurable; the
80+
work-estimation approach would require profiling infrastructure we
81+
don't have. The current rule is the right call for now.
82+
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
dispatch:
2+
schema: codifide.dispatch/0.1
3+
subject: Graph-native parallel evaluator — post-work (2026-05-12)
4+
at: 2026-05-12
5+
author: Glyph
6+
intent: >
7+
Attest the parallel evaluator implementation. Infrastructure is
8+
correct and in place. Current programs are too small to benefit;
9+
the sequential Rust interpreter is the real v2-A performance story.
10+
11+
shipped:
12+
new_file: crates/codifide-interpreter/src/parallel.rs
13+
new_method: eval_parallel_exprs (interpreter.rs)
14+
new_method: call_with_vals (interpreter.rs)
15+
dependency_added: rayon = "1.10"
16+
17+
sable_findings_status:
18+
PE-1: resolved-indexed-slots-declaration-order-guaranteed
19+
PE-2: resolved-documented-conservative-over-approximation
20+
PE-3: resolved-per-branch-interpreter-with-inherited-depth
21+
PE-4: resolved-all-args-must-be-direct-user-calls
22+
PE-5: deferred-believe-arm-parallelism
23+
24+
conformance:
25+
tests_passing: 70
26+
tests_failing: 0
27+
note: parallel evaluator does not change observable semantics
28+
29+
benchmark_results:
30+
threshold_correctly_excludes:
31+
- balanced_brackets (recursive mixed-arg calls)
32+
parallel_path_active_for: none-in-current-benchmark-suite
33+
reason: >
34+
Rayon thread-spawn overhead (~5-10us per task) exceeds work per
35+
branch for current programs (~2us per fizzbuzz_one call).
36+
fizzbuzz regressed from 29us to 66us when parallel path was
37+
enabled. Reverted to sequential for Call eval arm.
38+
39+
honest_assessment: >
40+
The sequential Rust interpreter (6-25x faster than Python) is the
41+
real v2-A performance story. The parallel evaluator is architecturally
42+
correct and will show speedup for programs where each branch takes
43+
>100us. Current benchmark programs are too small.
44+
45+
new_examples:
46+
- file: examples/batch_classify.cod
47+
description: 8 independent model calls — primary parallel evaluator target
48+
result: "['cat', 'dog', 'uncertain', 'fish', 'uncertain', 'rabbit', 'uncertain', 'turtle']"
49+
- file: examples/recursive_sum.cod
50+
description: recursive list sum with postcondition cross-check
51+
result: "[15, 60, 0, 100]"
52+
- file: examples/text_stats.cod
53+
description: 4 independent pure functions composed into result list
54+
result: "[[2, 10, false, 'short'], [9, 35, false, 'medium'], [4, 16, true, 'short']]"
55+
56+
unknowns:
57+
- Whether all-args-must-be-direct-user-calls is the right long-term
58+
threshold or whether work-estimation would be better.
59+
- When batch_classify.cod will have a real model backend slow enough
60+
to demonstrate the parallel speedup.
61+
62+
links:
63+
human_readout: dispatches/2026-05-12-parallel-evaluator-post.readout.md
64+
proposal: dispatches/2026-05-12-parallel-evaluator-proposal.readout.md
65+
audit: dispatches/2026-05-12-parallel-evaluator-audit.md
66+
benchmarks: dispatches/2026-05-12-benchmarks.readout.md
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
dispatch:
2+
schema: codifide.dispatch/0.1
3+
subject: Graph-native parallel evaluator — proposal (2026-05-12)
4+
at: 2026-05-12
5+
author: Glyph
6+
intent: >
7+
Propose adding a graph-native parallel evaluator to the Rust
8+
interpreter. Parallelizes independent sub-expressions at the node
9+
level using Rayon. Effect algebra governs what is safe to parallelize.
10+
11+
proposal_type: new-capability
12+
governance_required:
13+
- proposal-dispatch: true
14+
- sable-audit: true
15+
- douglas-approval: standing-go
16+
17+
parallelism_sites:
18+
- Call arguments (all args are direct user calls, disjoint effects)
19+
- Concat parts (same rule)
20+
- Believe arms (deferred — PE-5)
21+
22+
effect_constraint:
23+
mechanism: static-over-approximation via expr_effects()
24+
rule: all pairs of parallel expressions must have disjoint effect sets
25+
note: conservative — may serialize expressions that could theoretically
26+
run in parallel, but never runs two expressions in parallel unsafely
27+
28+
sable_findings:
29+
PE-1:
30+
severity: P1
31+
status: blocking
32+
description: EffectTrace merge order must be enforced via indexed collection
33+
resolution: rayon::scope with indexed slots, sort by index before merge
34+
PE-2:
35+
severity: P2
36+
status: non-blocking
37+
description: Conservative effect analysis uses declared signature effects
38+
resolution: documented
39+
PE-3:
40+
severity: P1
41+
status: blocking
42+
description: Recursion depth counter not thread-safe
43+
resolution: each branch gets its own Interpreter with inherited depth
44+
PE-4:
45+
severity: P2
46+
status: non-blocking
47+
description: Threshold heuristic unspecified
48+
resolution: all args must be direct user calls (not mixed arithmetic)
49+
PE-5:
50+
severity: P3
51+
status: non-blocking
52+
description: Believe arm parallelism lower priority
53+
resolution: deferred
54+
55+
implementation:
56+
crate: crates/codifide-interpreter
57+
new_file: src/parallel.rs
58+
rayon_api: rayon::scope (requires Send, not Sync)
59+
pointer_transmission: usize cast (avoids raw pointer Send/Sync issues)
60+
61+
benchmark_targets:
62+
fizzbuzz: "15 independent calls — primary target"
63+
pipeline: "3 independent calls"
64+
balanced_brackets: "sequential by nature — no parallelism"
65+
classify: "single call chain — no parallelism"
66+
67+
unknowns:
68+
- Whether Rayon thread-spawn overhead will eat gains for small programs
69+
- Whether the threshold is calibrated correctly for current programs
70+
71+
links:
72+
human_readout: dispatches/2026-05-12-parallel-evaluator-proposal.readout.md
73+
audit: dispatches/2026-05-12-parallel-evaluator-audit.md
74+
benchmarks: dispatches/2026-05-12-benchmarks.readout.md
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
dispatch:
2+
schema: codifide.dispatch/0.1
3+
subject: Rust parser landed — codifide-run is fully self-contained (2026-05-12)
4+
at: 2026-05-12
5+
author: Glyph
6+
intent: >
7+
Attest the Rust parser landing. codifide-run no longer calls Python
8+
for anything. Parse, evaluate, output — pure Rust, no subprocess.
9+
Parser conformance: byte-for-byte agreement with Python on all examples.
10+
11+
test_counts:
12+
python_passing: 289
13+
python_skipped: 0
14+
rust_canonical_passing: 28
15+
rust_interpreter_conformance: 70
16+
rust_parser_conformance: 3
17+
18+
shipped:
19+
new_module: crates/codifide-interpreter/src/parser/
20+
files:
21+
- tokens.rs
22+
- lexer.rs
23+
- expr_parser.rs
24+
- mod.rs
25+
new_subcommand: "codifide-run parse <file.cod>"
26+
test_file: tests/test_rust_parser.py
27+
28+
bugs_fixed:
29+
- id: compose-steps-flattening
30+
description: >
31+
compose_steps was flattening multi-step candidate bodies into a
32+
single Seq instead of nesting them as Python does. Fixed: Rust
33+
now mirrors Python's recursive Seq(head, compose_steps(tail)).
34+
35+
performance:
36+
conformance_bridge_before: "4.78s"
37+
conformance_bridge_after: "0.78s"
38+
speedup: "6x — entirely from removing Python subprocess for parsing"
39+
40+
not_yet_done:
41+
- from-identity-import-requires-store
42+
- python-cli-canonical-still-uses-python-parser
43+
- rust-parser-unit-tests-beyond-conformance-bridge
44+
45+
unknowns:
46+
- Whether from-import deferral is the right call before the
47+
parallel evaluator. Gap is invisible to the conformance bridge.
48+
49+
links:
50+
human_readout: dispatches/2026-05-12-rust-parser-post.readout.md
51+
proposal: dispatches/2026-05-12-rust-parser-proposal.readout.md
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
dispatch:
2+
schema: codifide.dispatch/0.1
3+
subject: Rust parser — proposal (2026-05-12)
4+
at: 2026-05-12
5+
author: Glyph
6+
intent: >
7+
Propose porting the Codifide surface-syntax parser from Python to
8+
Rust. Removes the Python subprocess dependency from codifide-run.
9+
After this lands the binary is fully self-contained.
10+
11+
proposal_type: new-capability
12+
governance_required:
13+
- proposal-dispatch: true
14+
- sable-audit: false
15+
- douglas-approval: standing-go
16+
17+
in_scope:
18+
- line-oriented-outer-parser
19+
- expression-lexer
20+
- recursive-descent-expr-parser
21+
- infix-desugaring
22+
- multi-line-expression-continuation
23+
- all-surface-keywords-ascii-and-unicode
24+
- import-direct-identity-binding
25+
- module-declaration
26+
- comment-stripping
27+
28+
out_of_scope:
29+
- from-identity-import-requires-store
30+
- store-integration-in-rust-binary
31+
32+
conformance_surface:
33+
mechanism: parse-both-compare-canonical-json-bytes
34+
new_test_file: tests/test_rust_parser.py
35+
rust_subcommand: "codifide-run parse <file.cod>"
36+
authority: Python parser output via to_canonical()
37+
38+
crate_structure:
39+
location: crates/codifide-interpreter/src/parser/
40+
files:
41+
- tokens.rs
42+
- lexer.rs
43+
- expr_parser.rs
44+
- mod.rs
45+
46+
unknowns:
47+
- Whether infix desugaring as a pre-pass (matching Python) is
48+
better than integrating it into the recursive-descent parser.
49+
Decision: pre-pass to match Python exactly.
50+
51+
links:
52+
human_readout: dispatches/2026-05-12-rust-parser-proposal.readout.md
53+
post: dispatches/2026-05-12-rust-parser-post.readout.md

0 commit comments

Comments
 (0)