Skip to content

docs: tighten experimental banner in README#568

Merged
danieljohnmorris merged 1 commit into
mainfrom
docs/experimental-banner
May 21, 2026
Merged

docs: tighten experimental banner in README#568
danieljohnmorris merged 1 commit into
mainfrom
docs/experimental-banner

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Replace the existing > Experimental pre 1.0 language, expect breaking changes banner with the punchier > Experimental language. Expect breaking changes. recommended by the external ilo-lang analysis (pending.md #44).

Rationale

  • "Experimental language" is honest scoping, not apology. "Expect breaking changes" sets expectation without qualification.
  • Removes the pre 1.0 qualifier, which reads as defensive. Zero's equivalent warning increased professional perception; honesty signals maturity.
  • Top recommendation from Downloads/ilo-lang-analysis.md. Without it, every breaking change between versions feels like a regression instead of expected dev-phase churn.

Diff

One-line copy edit at README.md:5. No SPEC, ai.txt, or skills impact.

Test plan

  • README renders cleanly on GitHub (blockquote stays between the Toki Pona tagline and the badge row).

Follow-ups

None.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
4727 2 4725 0
View the top 2 failed test(s) by shortest run time
ilo::examples::examples
Stack Traces | 8.21s run time
thread 'examples' (41981) panicked at tests/examples.rs:158:9:
9/1167 example test(s) failed:

crypto-primitives.ilo (line 42): `ilo .../ilo/examples/crypto-primitives.ilo sha-empty`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43055) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 44): `ilo .../ilo/examples/crypto-primitives.ilo sha-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43061) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 46): `ilo .../ilo/examples/crypto-primitives.ilo hmac-rfc2`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43066) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 48): `ilo .../ilo/examples/crypto-primitives.ilo b64-roundtrip`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43072) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 50): `ilo .../ilo/examples/crypto-primitives.ilo b64-padded`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43078) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 52): `ilo .../ilo/examples/crypto-primitives.ilo hex-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43084) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 54): `ilo .../ilo/examples/crypto-primitives.ilo ct-eq-yes`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43090) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 56): `ilo .../ilo/examples/crypto-primitives.ilo ct-eq-no`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43096) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 58): `ilo .../ilo/examples/crypto-primitives.ilo ct-eq-len`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43103) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
ilo::examples_engines::examples_all_engines
Stack Traces | 8.27s run time
thread 'examples_all_engines' (41982) panicked at tests/examples_engines.rs:235:9:
9/1153 multi-engine example test(s) failed:

crypto-primitives.ilo [vm] (line 42): `ilo .../ilo/examples/crypto-primitives.ilo --vm sha-empty`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43246) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 44): `ilo .../ilo/examples/crypto-primitives.ilo --vm sha-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43248) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 46): `ilo .../ilo/examples/crypto-primitives.ilo --vm hmac-rfc2`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43250) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 48): `ilo .../ilo/examples/crypto-primitives.ilo --vm b64-roundtrip`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43256) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 50): `ilo .../ilo/examples/crypto-primitives.ilo --vm b64-padded`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43262) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 52): `ilo .../ilo/examples/crypto-primitives.ilo --vm hex-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43265) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 54): `ilo .../ilo/examples/crypto-primitives.ilo --vm ct-eq-yes`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43269) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 56): `ilo .../ilo/examples/crypto-primitives.ilo --vm ct-eq-no`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43274) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 58): `ilo .../ilo/examples/crypto-primitives.ilo --vm ct-eq-len`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43278) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@danieljohnmorris danieljohnmorris merged commit 52d9209 into main May 21, 2026
1 of 4 checks passed
@danieljohnmorris danieljohnmorris deleted the docs/experimental-banner branch May 21, 2026 20:27
danieljohnmorris added a commit that referenced this pull request May 21, 2026
Pre-1.0 ilo churns at very different rates per surface. The README banner
says "expect breaking changes" at the language-as-a-whole level, but
agents need finer signal: schemaVersion:1 JSON envelopes, ILO-XXXX error
codes, serv protocol phases, the file-version pragma, and the manifesto
are all things you can pin to and carry forward. CLI flag names, builtin
signatures, error message prose, and the examples corpus are not.

Three tiers (Stable, Provisional, Experimental), each entry naming what
is locked in, what isn't, and the change policy. Pairs with the
experimental banner in #568.

README banner now points at STABILITY.md so the first thing an agent
reads after "expect breaking changes" is where to find the actual
matrix.

Closes #55. Exposing the matrix via 'ilo spec --json' deferred to a
follow-up - non-trivial JSON plumbing.
danieljohnmorris added a commit that referenced this pull request May 22, 2026
Pre-1.0 ilo churns at very different rates per surface. The README banner
says "expect breaking changes" at the language-as-a-whole level, but
agents need finer signal: schemaVersion:1 JSON envelopes, ILO-XXXX error
codes, serv protocol phases, the file-version pragma, and the manifesto
are all things you can pin to and carry forward. CLI flag names, builtin
signatures, error message prose, and the examples corpus are not.

Three tiers (Stable, Provisional, Experimental), each entry naming what
is locked in, what isn't, and the change policy. Pairs with the
experimental banner in #568.

README banner now points at STABILITY.md so the first thing an agent
reads after "expect breaking changes" is where to find the actual
matrix.

Closes #55. Exposing the matrix via 'ilo spec --json' deferred to a
follow-up - non-trivial JSON plumbing.
danieljohnmorris added a commit that referenced this pull request May 22, 2026
Pre-1.0 ilo churns at very different rates per surface. The README banner
says "expect breaking changes" at the language-as-a-whole level, but
agents need finer signal: schemaVersion:1 JSON envelopes, ILO-XXXX error
codes, serv protocol phases, the file-version pragma, and the manifesto
are all things you can pin to and carry forward. CLI flag names, builtin
signatures, error message prose, and the examples corpus are not.

Three tiers (Stable, Provisional, Experimental), each entry naming what
is locked in, what isn't, and the change policy. Pairs with the
experimental banner in #568.

README banner now points at STABILITY.md so the first thing an agent
reads after "expect breaking changes" is where to find the actual
matrix.

Closes #55. Exposing the matrix via 'ilo spec --json' deferred to a
follow-up - non-trivial JSON plumbing.
danieljohnmorris added a commit that referenced this pull request May 22, 2026
Pre-1.0 ilo churns at very different rates per surface. The README banner
says "expect breaking changes" at the language-as-a-whole level, but
agents need finer signal: schemaVersion:1 JSON envelopes, ILO-XXXX error
codes, serv protocol phases, the file-version pragma, and the manifesto
are all things you can pin to and carry forward. CLI flag names, builtin
signatures, error message prose, and the examples corpus are not.

Three tiers (Stable, Provisional, Experimental), each entry naming what
is locked in, what isn't, and the change policy. Pairs with the
experimental banner in #568.

README banner now points at STABILITY.md so the first thing an agent
reads after "expect breaking changes" is where to find the actual
matrix.

Closes #55. Exposing the matrix via 'ilo spec --json' deferred to a
follow-up - non-trivial JSON plumbing.
danieljohnmorris added a commit that referenced this pull request May 22, 2026
* feat(http): add getx/pstx with status + headers + body Ok-map

`get` / `pst` return `R t t`, body only. That blocks every workflow that
needs response metadata: conditional requests (304 Not Modified
branching), redirect following, pagination Link headers, rate-limit
headers, cookie capture, status-code branching beyond Ok/Err.

This adds `getx` / `pstx`: rich-response variants that surface status,
headers, and body as a Map[Text, _] in the Ok arm. Existing `get` / `pst`
shapes stay untouched, so token-cheap GETs keep their footprint and
there's zero migration cost.

  getx url              > R (M t _) t
  getx url headers      > R (M t _) t
  pstx url body         > R (M t _) t
  pstx url body hdrs    > R (M t _) t

Ok-map carries three keys: `status` (n), `headers` (M t t, lowercased),
`body` (t). Non-2xx responses surface as Ok with the status on the map;
only transport failure (DNS, connection refused, timeout) returns Err.

Tree-bridge dispatched (returns Result, no FnRef args), so VM and
Cranelift JIT inherit identical semantics without dedicated opcodes.
Appended last to `Builtin::ALL` to preserve every existing on-wire tag,
and added to `tree_bridge_returns_result` so the bang forms (`getx!`,
`pstx!`) auto-unwrap correctly.

* test: rename graph subgraph-coverage user fn off the new getx builtin

src/graph.rs::test_subgraph_type_inclusion_via_dep used getx as a user fn
name. Renamed to getp so the test keeps exercising the same code path now
that getx is reserved.

* test: cross-engine coverage for getx/pstx against wiremock

Seven new wiremock-backed integration tests in tests/http.rs covering
status/headers/body extraction, the 304-stays-Ok invariant, both arg-count
forms, transport-failure Err, and two verify-time arg-type rejection cases.
All run on --vm and --jit (cranelift feature).

examples/http-rich-response.ilo for the examples_engines harness covers
the bang-form Err propagation across the 1- / 2- / 3-arg variants.

* docs: sync getx/pstx to SPEC.md, ai.txt, and skill

Four rows in the SPEC builtins table, paragraph + example in the HTTP
section, regenerated ai.txt via build.rs, plus a 'when to reach for getx
vs get' paragraph in skills/ilo/ilo-builtins-io.md.

Site companion change pushed separately to ilo-lang/site.

* feat: ilo test subcommand

Surfaces the in-tree -- run: / -- out: / -- err: annotation format as a
user-facing command. The same format that tests/examples_engines.rs
already exercises, exposed so end-user programs and test suites can
assert behaviour from the same files agents read as in-context
examples. ilo test <file> runs one file; ilo test <dir> walks .ilo
files recursively. Each case spawns the current ilo binary with the
chosen engine flag (defaults to --vm; --engine jit / --engine all
widen the matrix), asserts stdout (-- out:) or stderr (-- err:) against
the expected payload, and prints PASS / FAIL with the source line.
Final line is N passed, M failed; exit 0 on all-pass, 1 otherwise.

The runner lives in src/cli/test_runner.rs, dispatched from main on
Cmd::Test. Path defaults to examples/ when omitted so ilo test in a
fresh checkout does something useful out of the box.

* test: end-to-end coverage for ilo test

Seven subprocess tests pin the dispatch path: passing case, failing
case (exit 1 + FAIL line), -- err: assertion shape, directory
recursion, --engine all running both engines, missing-path error, and
no-annotations-found error. Each spawns the ilo binary so the test
exercises real CLI parsing + the runner's own subprocess fan-out, two
layers deep.

* fix(verify): hint two-kebab-half subtraction in ILO-T004

When an unbound 3+ segment kebab ident splits uniquely into two halves
that ARE bound, suggest the subtraction reading. Hit by mandelbrot
persona writing `zr-sq-zi-sq` with no spaces around the operator,
expecting it to parse as `(zr-sq) - (zi-sq)`. Pre-fix the verifier
emitted bare ILO-T004 with no hint (single segments unbound, levenshtein
distance > 3 too).

The 2-segment path now also requires both halves to resolve before
firing the existing hint, matching the new 3+ logic and dropping a
small false-positive case where only the legacy "all segments bound"
test gated.

Hint shows both prefix (`- zr-sq zi-sq`) and infix-with-spaces
(`zr-sq - zi-sq`) forms so the persona has two unambiguous canonical
spellings.

* test: pin the two-kebab-half subtraction case via examples_engines

Adds a `mandel zr-sq:n zi-sq:n>n;- zr-sq zi-sq` line to the kebab-vs-
subtract example so the cross-engine examples harness exercises the
canonical "subtraction between two hyphenated names" form on every
engine. Comment block above it explains why the no-space form would
fail and points at the new diagnostic.

* parser: reattach trailing .N to multi-token call results

The greedy call-args loop in parse_call_or_atom and the nested-call
expansion in parse_call_arg stopped at the leading `.` because Dot
doesn't start an operand. The trailing `.N` was left dangling for
the infix parser to choke on (ILO-P001 'expected declaration, got `.`').

Route every Call return site through parse_field_chain so the postfix
chain reattaches to the call result the same way it does for (expr).N
and xs.N. This means `spl "a.b" "." .0` and `num spl "1.2.3" ".".1`
now parse without parens, matching the bare-ident and parenthesised
shapes.

* test: cross-engine coverage for call-result dot-index

Pins the new shape across every public engine (VM and Cranelift JIT
when the feature is on):
- top-level multi-token call with trailing .N (glued and spaced)
- nested call inside outer arity-1 caller (the originating repro)
- safe .?N shorthand on the call result
- arity-known call (`at xs 0 .1`) inside list-element context
- equivalence with the parenthesised workaround
- existing bare-ident and paren shapes unchanged

examples/call-result-dot-index.ilo demonstrates the canonical shape
with -- run / -- out assertions so the examples_engines harness picks
it up as a regression test too. Notes the bare-ident-last-arg caveat
inline so agents reading the example don't get surprised when
`at rows i .1` glues .1 to i instead of the call result.

* docs: document call-result dot-index reattachment

SPEC.md records section and ai.txt RECORDS section both now describe
the new multi-token-call shape alongside the existing parenthesised
and bare-ident shapes, with the bare-ident-last-arg caveat called
out so agents know when to fall back to parens or bind-first.

* feat: O(n) rolling-window reducers rsum/ravg/rmin

Add three new builtins for sliding-window aggregations over numeric
lists: rsum (running sum), ravg (running mean), and rmin (running
minimum). Output length is len xs - n + 1; empty when n > len xs.

The asymptotic point is the whole point. The natural recipe in ilo
today is map (i:n>n;sum (slc xs i (+ i n))) ..., which is O(n*w) and
explodes for fat windows on long inputs. rsum/ravg use a running-sum
(one add and one subtract per step), and rmin uses a monotonic deque,
giving O(n) amortised total for all three.

Tree-bridge eligible alongside the cumsum/cprod/ewm aggregate family:
the tree interpreter does the work, VM and Cranelift inherit through
OP_CALL_BUILTIN_TREE at zero opcode cost. ILO-R009 propagates on
Cranelift via tree_bridge_propagates_error so the n=0 / negative-n /
non-numeric-element error parity holds across engines.

Builtin tags appended last to preserve every existing on-wire tag.

* test: cross-engine coverage for rsum/ravg/rmin

25 tests across tree, register VM, and Cranelift JIT covering:
- basic three-wide windows for all three reducers
- boundary conditions: window=1 (identity), window=len (single point),
  window>len (empty output), empty input
- error paths: n=0, negative n, fractional n
- shape-specific cases: rmin against ascending / descending /
  repeated-minimum inputs to exercise the monotonic-deque branches
- verify-time ILO-T013 for wrong xs type, wrong n type, non-list xs

Includes a hand-computed parity case for rsum so a future bug that
silently changes the output shape doesn't pass just because all three
engines agree on the wrong answer.

* test: ignored microbench locking O(n) rolling-window vs slc baseline

Pins the asymptotic improvement of rsum/rmin against the slc + sum/min
baseline at n=10k, w=500. The naive recipe does ~5M work; the
running-window form does ~10k. Bound set loosely at 2x to absorb CI
noise but still catch a regression to the naive O(n*w) shape.

Marked #[ignore] so the suite runtime stays bounded; run on demand via
`cargo test --release --features cranelift -- --ignored rolling_perf`.

* docs: example for rolling-window reducers

examples/rolling-aggregates.ilo doubles as an in-context learning
example for agents that hit this pattern in future, and as a
higher-level regression test the engine harness already runs across
every backend. Skill text deliberately omitted: the existing
ilo-builtins-math file is over the 1000-token cap by 146 tokens
already (a pre-existing lint failure on main), so adding to it would
deepen the regression. ai.txt + SPEC.md are the canonical references.

* docs: tighten experimental banner in README

* docs: surface get/pst headers, jpth dot-path, text-concat ref

Three doc-only discoverability fixes hit by multiple personas in 2026-05-20
sessions. Canonical signatures in SPEC.md and src/verify.rs were already
correct, but the skill docs weren't front-loading them enough for agents to
find on first read.

- SKILL.md gets a "Quick reference - things agents miss" block covering
  text concatenation (+, fmt, cat), HTTP custom headers (every verb takes
  an optional M t t map), and jpth being dot-path not JSONPath.
- ilo-builtins-io.md HTTP section gets a bold lead on headers and a
  runnable mset/get!/pst! example.
- ilo-builtins-io.md JSON section front-loads the dot-path warning before
  the jpth signature line.
- ilo-builtins-text.md gets a concat/format quick-reference at the top so
  agents reaching for "how do I join two strings" pick + over cat.

Personas: scrapingbee, tui-client (#26b headers), bearer-token-client (#26c
jpth), 5+ across sessions (#26e concat confusion).

* docs: tighten io/text doc additions to fit token budget

Trim the headers/jpth/concat additions to single dense lines so the
budget-checker doesn't get worse than baseline. Net result: io drops
from 1837 to 1748 (under its previous overage), text returns to ~baseline.

* examples: tail-recursive find-idx pattern

Adds examples/find-idx-tail-recursive.ilo showing the canonical
recursive linear-search idiom: base cases as braceless guards first,
recursive call as the tail statement. Documents the common footgun -
appending a literal '-1' after the recursive call puts the call out of
tail position, so its return is discarded and the function silently
falls through to -1.

Surfaced by the interp1d persona report (2026-05-21), which read as a
braceless-guard early-return bug but turned out to be the discarded
non-tail-call shape. The braceless guard itself fires correctly in
isolation. Pinned by tests/examples_engines.rs across tree/VM/JIT/AOT.

* test: cross-engine coverage for three conditional forms

Pin each canonical conditional shape - braceless guard cond expr,
braced conditional cond{body}, brace ternary cond{a}{b}, prefix
ternary ?h cond a b - as a runnable example so the examples_engines
harness exercises them on tree/VM/JIT/AOT. Add a focused regression
test that asserts both the parse-cleanly invariant for each form
AND the wrong-form ?h cond{body} hint enumerates all three canonical
shapes, so the parser hint copy and the example shapes stay in lockstep
if either side drifts.

Backs the side-by-side comparison agents now reach for in
skills/ilo/ilo-language.md and site guards reference page.
ten date/time personas tripped on this in the same session;
the parse-cleanly assertions catch the regression before the
docs lie.

* doc(skill): merge guards section into guards & conditionals

Ten date/time personas in the 2026-05-21 dispatch all tripped on
?h cond a b vs cond{body} vs braceless guard. The parser already
lands a context-aware ILO-P009 hint enumerating the three canonical
shapes; the skill doc had only a terse note under ## guards with no
reference to ternary or braced conditional, so the agent had nothing
to consult after reading the hint.

Replace ## guards with ## guards & conditionals: same single-paragraph
density, but now names all three shapes (early-return guard, braced
conditional, brace ternary) plus the two prefix-ternary subjects
(?h a b bare-bool and ?h cond a b keyword). One-line callout names
the wrong form ?h cond{...} so the hint copy lines up with the doc.

Kept terse on purpose - the file is already over the per-file token
cap from PR #566; net add over baseline is ~57 tokens. Full
side-by-side table lives in site/src/content/docs/docs/reference/
guards.md where there is no token budget.

* docs: multi-request HTTP flow patterns (OAuth + paginated)

Document the two canonical patterns for multi-request flows in ilo,
since there are no globals: closure-threading for in-run state
(paginated fetch, rate-limit windows) and file-backed for cross-run
state (OAuth refresh tokens, multi-hour TTLs).

Closes #26d (pending.md). Hit by bearer-token-client persona 2026-05-20;
agents were inventing the pattern badly (top-level vars rejected,
shelling out, misusing closures).

Also dedupes three accidental triplicate JSON-section paragraphs and a
duplicated 'run' block, net -55 tokens on the file.

* examples: oauth-token-cache + paginated-fetch

Worked patterns for the multi-request flow doc:

- paginated-fetch.ilo: closure-threading via recursion. Cursor + acc
  thread through fetch-loop; fake-page makes it offline-runnable
  cross-engine so the harness exercises the shape itself.

- oauth-token-cache.ilo: file-backed cache lifecycle. load reads or
  defaults, refresh produces a new value (real flow would pst! to the
  IdP), save persists. Uses now-ms in the path so re-invocations are
  hermetic.

Both run clean across tree (transitive via VM bridge), VM, and JIT.

* verify: warn on non-tail recursive self-call return discard

A recursive self-call followed by another statement in the same body
silently discards its return value, since ilo only uses the tail
expression as the function's return. The interp1d persona hit this
shape (find-idx xs target +i 1; -1) and mis-diagnosed it as a broken
braceless guard because the verifier emitted no signal. Add ILO-T043
to close the gap.

Narrowly scoped to recursive self-calls (callee name == caller name)
with a non-nil declared return type. Bare non-recursive user-fn calls
at non-tail position do not warn; the callee may legitimately be
side-effecting. Broaden later if reruns surface other classes.

* test: regression for ILO-T043 recursive discard

Five cases covering the new warning surface: warns on non-tail
recursive self-call (the persona's shape), no-warn on tail recursive
self-call, no-warn on non-recursive user-fn call at non-tail, no-warn
when the call is wrapped in ret, and two warnings emitted when two
non-tail recursive calls appear in a row.

examples/recursive-tail-position.ilo pins the canonical fix shape
under examples_engines so every engine asserts the same output. The
example doubles as the in-context example any future agent reads when
ILO-T043 fires; the hint points at this file.

* fix(verify): targeted hint on ILO-T003 ternary branch mismatch

Before, the verifier emitted the generic 'both branches of a ternary
must return the same type' hint with no signal on which side to
convert. Agents either guessed (often producing a follow-on type
error) or restructured unnecessarily.

The new hint reads the two branch types and:
- for n vs t, surfaces both conversion directions: 'str <num-branch>'
  to make both text, or 'default-on-err (num <text-branch>) <fallback>'
  to make both number (since 'num' returns R n t, an unwrapped scalar
  is not enough);
- for everything else (bool vs text, L n vs M t n, two named records,
  R T E vs n, ...), falls back to restructure advice because str/num
  are the only built-in scalar coercions; suggesting one outside that
  pair would just trip ILO-T013.

* test: regression coverage for ternary type-mismatch hint

Pins the new ILO-T003 hint shape across the four cases that matter:
- n vs t in either branch position must surface both 'str <num-branch>'
  and 'default-on-err (num <text-branch>)' so the agent can pick the
  direction matching intent;
- matching-type ternaries still verify clean (false-positive guard);
- bool vs text and list vs map fall back to the restructure hint,
  with explicit negative asserts that we don't suggest a str/num
  conversion that wouldn't apply.

Adds examples/ternary-types.ilo showing the canonical correct shapes
after applying each hint. The examples_engines harness runs it across
every available engine on every CI run, so the docs and the verifier
hint can't drift apart silently.

* doc: note ternary branch-type hint in SPEC ternary section

SPEC already documents the ILO-T038 'condition must be b' rule next to
the prefix-ternary explanation; this slots the matching ILO-T003 rule
in the same place so agents reading the ternary spec see both checks
together. ai.txt regenerates from SPEC.md via build.rs.

* test: regression guard for ILO-50 fld multi-statement lambda body

Add two tests to regression_inline_lambda.rs that pin the correct
behaviour for a fld inline lambda with a let-stmt + final-expr body
(the exact shape reported in ILO-50). Could not reproduce the alleged
ambiguity; these tests ensure any future parser regression is caught.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add STABILITY.md at repo root

Pre-1.0 ilo churns at very different rates per surface. The README banner
says "expect breaking changes" at the language-as-a-whole level, but
agents need finer signal: schemaVersion:1 JSON envelopes, ILO-XXXX error
codes, serv protocol phases, the file-version pragma, and the manifesto
are all things you can pin to and carry forward. CLI flag names, builtin
signatures, error message prose, and the examples corpus are not.

Three tiers (Stable, Provisional, Experimental), each entry naming what
is locked in, what isn't, and the change policy. Pairs with the
experimental banner in #568.

README banner now points at STABILITY.md so the first thing an agent
reads after "expect breaking changes" is where to find the actual
matrix.

Closes #55. Exposing the matrix via 'ilo spec --json' deferred to a
follow-up - non-trivial JSON plumbing.

* add rand alias for rnd

* docs: rand alias in SPEC, ai.txt, skill, and CHANGELOG

- SPEC.md builtins table notes that `rnd` returns random, not round,
  with the alias pair `rand`/`random` and a pointer at `rou`/`round`
  for rounding.
- SPEC.md aliases table gains the `rand` -> `rnd` row.
- ai.txt regenerated from SPEC.md via build.rs.
- skills/ilo/ilo-builtins.md math section now spells out the
  round-vs-random trap so agents writing programs through the skill
  see the disambiguation in context.
- CHANGELOG 0.12.1 lists `rand` as an additive ergonomic alias.

* feat: bisect for O(log N) sorted-list search (#5bb)

Add bisect xs:L n target:n > n, Python bisect_left semantics: returns
the leftmost index i such that xs[0..i] < target <= xs[i..]. Empty list
returns 0; target greater than every element returns len xs; ties
resolve to the leftmost equal index. NaN target propagates as NaN to
match argmax/argmin policy.

The asymptotic point is the whole point. The natural recipe in ilo
today is len (flt (x:n>b;< x target) xs) or hd (flt fn (enumerate xs)),
which is O(n) per lookup. interp1d, sorted-lookup, percentile pickers
and histogram binning all need this on inner loops; collapsing the
scan to one builtin call gives O(log n) at the same token cost.
Caller owns the sortedness precondition; we do not validate it,
matching srt/unq/Python bisect precedent.

Tree-bridge eligible alongside the argmax/argmin/argsort index-
returning aggregates: the tree interpreter does the work, VM and
Cranelift inherit through OP_CALL_BUILTIN_TREE at zero opcode cost.
Returns plain n (not Result), so no tree_bridge_returns_result entry
needed. No error propagation needed either: bisect cannot fail on
sorted-numeric input. Type errors at the bridge raise ILO-R009
identically across engines via the normal interpreter arm.

Builtin tag appended last to preserve every existing on-wire tag.

* test: cross-engine coverage for bisect

12 tests pinning the bisect_left contract across tree, VM, and JIT
(when built with --features cranelift): in-range insertion, before-
first, after-last, duplicates (leftmost wins), empty list (returns 0),
single-element under/equal/over, exact-match resolves leftmost,
negative numbers, NaN target propagates, and a replaces-filter-count
comparison pinning the manifesto framing.

NaN is generated with sqrt (0 - 1) rather than / 0 0 because the
latter raises ILO-R003 in tree and VM before bisect ever sees the
value.

* docs: example for bisect

examples/bisect.ilo demonstrates the six pinned shapes from the
regression test (in-range, before-first, after-last, duplicates,
empty, single-element exact-match) under the examples_engines
cross-engine harness. Header comment names the Python bisect_left
semantics and the caller-owned sortedness precondition so agents
encountering this for the first time read the contract before the
syntax.

* chore: trim bisect skill entry to keep ilo-builtins-math token budget tight

The original wording added 100+ tokens to ilo-builtins-math.md (already
1146 / 1000 cap pre-PR). The new builtin still needs to appear there so
agents discover it via skill load, but the contract details belong in
SPEC.md / site docs / the example file - not in the skill bundle that
agents pay for on every task.

Compress to the same one-line-list pattern the rest of the Statistics
section already uses, with a one-sentence trailer naming the semantics.
That cuts +103 tokens to +30, keeping the per-PR drift minimal while
the broader skill-token cap conversation plays out separately.

* docs: skills/ilo/ilo-builtins.md uses pst as canonical (closes ILO-80)

Explicitly note that pst is the canonical name since 0.12.0 and that
the old name post is not accepted, preventing agents loading the skill
from using the wrong builtin name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: gate AOT tests behind #[ignore] (need libilo.a prereq) — closes ILO-290

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: flip all PR3c srt/grp TODO tests to run_all now that #391 merged

PR #391 (srt/grp/uniqby off tree-bridge) is merged. Remove all
`run_tree_only` gates and TODO comments referencing #391 in
regression_inline_lambda.rs, flip each call site to `run_all`, and
delete the now-unused `run_tree_only` helper. All 21 tests pass on
tree, VM, and Cranelift.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: enforce 16 MiB test stack via .cargo/config.toml + explicit guard on fib canary (partial ILO-289)

- Add .cargo/config.toml with RUST_MIN_STACK=16777216 so local cargo
  test runs match the CI env var setting in rust.yml.
- Unignore .cargo/config.toml in .gitignore (directory was globally
  ignored; allow the one tracked file via negation rule).
- Wrap interpret_braceless_guard_fibonacci in an explicit 8 MiB
  std::thread::Builder stack so the canary test passes independently of
  the env var, mirroring the run_on_fat_stack pattern in
  tests/parser_depth_cap.rs.

Defers CI lint for oversized match arms to a follow-up ticket.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add: paren-form call syntax as sugar over postfix

Closes ILO-51. `spl(row, ",")` now parses identically to `spl row ","`.
Same AST node; postfix remains canonical idiom. Disambiguates via adjacency:
`f(x)` is call, `f (x)` keeps `(x)` as grouped-expr arg.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix: ILO-P101 hint also mentions paren-form (closes ILO-92)

Update the ILO-P101 diagnostic hint to mention both the postfix-around-call
form `({name} <args>)` and the new paren-form `{name}(<args>)` introduced
by ILO-51, giving agents a clearer choice of syntax.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add quantile worked example (closes ILO-55)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: surface stability matrix in ilo spec --json ai and ai.txt (ILO-75)

Adds a `stability` field to the `ilo spec --json ai` JSON envelope listing
stable/provisional/experimental surfaces, cross-linked to STABILITY.md.
Adds a STABILITY line to ai.txt so agents consuming the compact spec see
the stability contract directly. Per-item stability in the spec output is
scoped as a follow-up (the current ai.txt format is a flat blob, not
per-item; restructuring that is L scope).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: add dispatch-arm size lint to prevent stack-pressure regressions (ILO-339)

Adds scripts/check-dispatch-arms.sh which scans the call_function
if-builtin dispatch chain in src/interpreter/mod.rs and fails if any
arm exceeds 40 lines. Large inline arms inflate the debug-build stack
frame and have caused recurring stack-overflow failures in deep-recursion
tests. Engineers hitting the lint should extract the arm body into a
#[inline(never)] helper.

The workflow step (rust.yml) is not included here because the token
lacks workflow scope; the snippet to add is in the PR description.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(manifesto): resolve principle 4 contradiction re English keywords (ILO-77)

Principle 4 listed the residual English keywords but left the contradiction
with "language-agnostic" implicit. Adds an explicit "by design" paragraph
explaining why the 11 kept keywords pass the token-cost test (each is already
a single token, sigil alternatives scored lower on generation accuracy) and
marks the set as frozen — no new English keywords will be added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: apply cargo fmt to parser_paren_call.rs

Followup to PR #588 - the merged tests file landed with formatting that
cargo fmt --check rejects. CI lint job has been red on every PR since.
No behaviour change.

* chore: regenerate ai.txt to match SPEC.md

build.rs regen drops a stale STABILITY line that no longer appears in
SPEC.md. CI's 'Verify ai.txt is in sync' guard has been red on every PR
in the queue since this slipped through.

* chore: drop needless Ok wrapper around ? in field-chain return

clippy::needless_question_mark is denied in CI ('cargo clippy ... -- -D
warnings'); a stray Ok(...?) at src/parser/mod.rs:4158 has been failing
the lint job on every PR in the queue. No behaviour change.

* chore: document dev vs release tag convention (closes ILO-66)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: restore cross-language benchmark suite (ILO-65)

Add bench/ directory with five canonical benchmarks (fib, hof, listproc,
pattern-match, sum-loop) each implemented in ilo + Python / Node.js / Rust.
bench/run.sh runs all impls, verifies correctness, and emits bench/results.json.
CI nightly workflow (.github/workflows/bench.yml) runs the suite and gates
on >10% regression vs previous run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Lexer: add hex/binary/octal numeric literal support (0xFF, 0b1010, 0o755)

Extend the logos Number token with three additional regex handlers that
parse 0x…/0X… (hex), 0b…/0B… (binary), and 0o…/0O… (octal) prefixed
literals, converting them to f64 at lex time so the parser and all
evaluation engines require no changes. Bare prefixes with no digits lex
as an error (non-zero exit). 25 tests covering all three bases,
case-insensitive prefixes, mixed arithmetic, and empty-digit error cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add ilo trace subcommand for JSON-line value snapshots (ILO-72)

Implements `ilo trace <file.ilo> [func] [args...]` which runs the
tree-walking interpreter and emits one JSON line per statement execution.
Each line carries schemaVersion, line number, source text, all current
bindings, and the statement result value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: resolve clippy warnings in trace interpreter and parser

* chore: cargo fmt + regenerate ai.txt

* feat: per-item stability field in ilo spec --json ai (ILO-340)

Add a `stability()` method to `Builtin` that returns `"provisional"` for
all builtins shipped in 0.12.1 or earlier and `"experimental"` for the
twelve unreleased builtins above 0.12.1 in CHANGELOG.md (matvec, lstsq,
jpar-list, get-to, pst-to, tz-offset, run2, rgxall-multi, fmod,
dtparse-rel, dur-parse, dur-fmt).

`ilo spec --json ai` now emits a `builtins` array where every entry carries
`{name, stability}`, sourced from `Builtin::ALL` via the new method. The
existing top-level `stability` summary is preserved unchanged for backward
compat. SPEC.md gains a `## Stability` section so build.rs regenerates the
matching `STABILITY:` line in ai.txt (fixing the pre-existing divergence
between SPEC.md and ai.txt introduced in PR #598). One new integration test
asserts the builtins array is present, non-empty, and uses only known tiers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: cargo fmt + regenerate ai.txt

* feat: optional labelled args for all callable arities (ILO-71)

Adds `label:value` syntax at call sites for any function or builtin with
declared parameter names. Labels resolve to positional by name at parse
time, so `dtfmt epoch:e fmt:"%Y"` is identical to `dtfmt e "%Y"`, and
`dtfmt fmt:"%Y" epoch:e` (reversed) also works.

- Parser: `fn_param_names` table (mirrors `fn_arity`) populated for all
  builtins and user-defined functions via `register_user_fn`.
- `peek_labelled_arg_label`: lookahead that returns the label name when
  `ident:non-type-token` is ahead; disambiguates from param declarations
  using `>` / `:` after the type ident as the type-context signal.
- `resolve_labelled_args`: collects all labelled args following positional
  args and reorders to declaration order, emitting `ILO-P019` on unknown,
  duplicate, or conflicting labels.
- `parse_paren_call_args_for(fn_name)`: paren-form call parsing now also
  recognises `label:value` items when called with a known function name.
- Resolution is 100% at parse time — no new AST nodes; all three engines
  (tree, VM, JIT) see ordinary positional `Expr::Call` args.
- `examples/labelled-args.ilo` exercises all-labelled, reversed, mixed
  positional+labelled, builtin `dtfmt`, paren form, and positional fallback.
- Doc touchpoints updated: SPEC.md, ai.txt, skills/ilo/SKILL.md.

All 3330 existing tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: cargo fmt + regenerate ai.txt

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <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