Skip to content

feat(slc): accept end=-1 as 'to end' sugar (#15)#540

Merged
danieljohnmorris merged 4 commits into
mainfrom
fix/slc-end-negative
May 21, 2026
Merged

feat(slc): accept end=-1 as 'to end' sugar (#15)#540
danieljohnmorris merged 4 commits into
mainfrom
fix/slc-end-negative

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Pending entry P2 #15: slc s n -1 doesn't mean "to end". Today ilo's slc reads negative end indices Python-style (slc xs 0 -1 drops the last element), but agents trained on Python/JS keep reaching for -1 to mean "to end of list/text", and the friction shows up in persona transcripts.

This PR special-cases one narrow shape: when start >= 0 AND end == -1, end is read as len xs. Every other shape (negative start, or end <= -2) keeps the existing Python-style relative-offset semantics, so slc xs -3 -1 is still the penultimate window and slc xs 0 -2 still drops the last two.

Manifesto framing: matches the agent's first guess, zero extra tokens at the call site, narrow enough that the existing Python-style negative-offset path stays intact for the cases that already depend on it. "Drop last" continues to live on take -1 xs, which I now point at from the docs.

Repro before/after

Before:

$ ilo 'f xs:L n>L n;slc xs 0 -1' '[10,20,30,40,50]'
[10, 20, 30, 40]

After:

$ ilo 'f xs:L n>L n;slc xs 0 -1' '[10,20,30,40,50]'
[10, 20, 30, 40, 50]
$ ilo 'f xs:L n>L n;take -1 xs' '[10,20,30,40,50]'   # new shape for 'drop last'
[10, 20, 30, 40]
$ ilo 'f xs:L n>L n;slc xs -3 -1' '[10,20,30,40,50]' # negative start kept Python-style
[30, 40]
$ ilo 'f xs:L n>L n;slc xs 0 -2' '[10,20,30,40,50]'  # end<-1 kept Python-style
[10, 20, 30]

What's in the diff (per commit)

  1. slc: accept end=-1 as 'to end' sugar when start is non-negative - new resolve_slc_end helper in src/builtins.rs; tree-walker (src/interpreter/mod.rs), VM OP_SLC handler, and Cranelift JIT jit_slc all route through it.
  2. tests: pin slc end=-1 sugar and adjacent Python-style shapes - cross-engine regression suite updated in tests/regression_neg_index_slice.rs. The existing slc xs 0 -1 / slc "hello" 0 -1 / quant-trader fencepost tests now assert the sugar; new pins for slc xs 2 -1 (mid-start sugar), slc xs -1 -1 (negative start keeps Python), slc xs 0 -2 (end<-1 keeps Python), and take -1 xs as the canonical drop-last shape. 34/34 pass on every available engine.
  3. examples: slc-to-end demo + refresh negative-indices for the new sugar - examples/slc-to-end.ilo for the engines harness; examples/negative-indices.ilo switches its drop-last and trim-edges demos onto take -1 / explicit (- (len s) 1) and adds a penultimate example that exercises the negative-start path.
  4. docs: document slc end=-1 'to end' sugar in SPEC, ai.txt, and skill - SPEC.md slc row, ai.txt slc line, skills/ilo/ilo-builtins-core.md list section. Companion PR on the site repo (already merged) updates docs/builtins/collections.md.

Behaviour change worth flagging

This is a behaviour change for one specific shape (slc xs 0 -1 was Python-style "drop last" before this PR). I checked every test that hit that shape; the only callers were three regression tests (added in PR #183) and the negative-indices.ilo example, all of which I updated to the new contract or moved onto take -1. The quant-trader-fencepost flagship example in the original PR moves to take -1 eq, which is the shape I'd reach for now.

If we want to add a deprecation hint for slc xs <non-neg> -1 users who are on the old contract, we could surface a one-time advisory the first time they hit the shape - happy to layer that as a follow-up, but the verifier doesn't track call-site provenance for builtins right now.

Test plan

  • cargo test --release --features cranelift - full suite green (200 result groups, 0 failures)
  • cargo fmt --check - clean
  • cargo clippy --release --features cranelift --all-targets -- -D warnings - clean
  • examples/slc-to-end.ilo runs on every engine via tests/examples_engines.rs
  • examples/negative-indices.ilo updated assertions run on every engine

Follow-ups

  • Consider a deprecation hint or comparison example for the old slc xs 0 -1 drop-last shape if persona transcripts show agents still reaching for it (would land separately once we have signal).

Add resolve_slc_end helper that special-cases end_raw == -1 with a
non-negative start as len(xs), so 'slc xs 0 -1' returns the whole list
instead of dropping the last element. Other shapes keep the existing
Python-style relative-offset semantics (slc xs -3 -1 is still the
penultimate window, slc xs 0 -2 still drops the last two).

The sugar matches the Python/JS mental model agents reach for first when
they want 'from index N to the end'. Drop-last continues to live on
take -1 xs.

Tree-walker, VM (OP_SLC), and Cranelift JIT helper all route through
the same helper.
Update the cross-engine slc regression suite to match the new sugar:
the slc xs 0 -1 / slc "hello" 0 -1 / quant-trader cases now assert
'to end' behaviour. Add new pins for slc xs 2 -1 (mid-start sugar),
slc xs -1 -1 (negative start keeps Python style), slc xs 0 -2 (other
negative ends untouched), and slc xs -3 -1 (penultimate window). Adds
take -1 xs as the canonical 'drop last' shape.
Add examples/slc-to-end.ilo so the engines harness pins the new
agent-facing shape (slc xs s -1 with s>=0 means 'to end'). Update
examples/negative-indices.ilo: the drop-last shape moves to 'take -1 xs',
the trim-edges example uses explicit (- (len s) 1), and a penultimate
example is added so the file still covers the negative-start path.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

❌ Patch coverage is 88.23529% with 2 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/interpreter/mod.rs 71.42% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Note the sugar (a>=0 + b==-1 means 'to end of list/text') in the slc
row of SPEC.md, the canonical ai.txt builtins line, and the list
section of the ilo-builtins-core skill. Each pointer also nudges
agents toward take -1 xs for the 'drop last' shape so the sugar stays
ergonomic without losing the Python-style negative-offset path.
@danieljohnmorris danieljohnmorris merged commit c118f51 into main May 21, 2026
4 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/slc-end-negative branch May 21, 2026 12:37
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