Skip to content

Refusals of an element loop through an index and of a lane's let mut offset give the loop to write, and cairn rules CODE leads with that code's rule - #184

Open
SamMausberg wants to merge 6 commits into
mainfrom
c13/refusal-messages
Open

SamMausberg wants to merge 6 commits into
mainfrom
c13/refusal-messages

Conversation

@SamMausberg

Copy link
Copy Markdown
Owner

Summary

Three refusals that sent an agent the wrong way now give the change to make. for x in slots.data[k].buf is refused with the index loop to write, where it said to bind the array to a name, which would move an owner out of its place. A lane's block write through a let mut offset is refused with the reason its bound is unknown and a rewrite that is accepted. cairn rules CODE puts the card's sentences that name the code first.

Motivation

The cold probe of main at dda8208 met each of these writing correct programs. In pool, E-ELEMENT-LOOP named a fix that cannot work, and cairn rules E-ELEMENT-LOOP printed 1.3k of the base card with no sentence about the code: one wasted round and a card read for nothing. In sieve, while j < S { comp[b * S + j] = 1; j += p; } was refused with E-PARALLEL-RACE three times over, the message restating the rule the write already followed, until a 30-line experiment showed that only a for binder or an immutable let carries a bound: four rounds. Part of #124.

Changes

  • compiler: unnamed writes the E-ELEMENT-LOOP message for what is neither a name nor a field path: the index loop for a place reached through an index (v.len and v.data[i] for a Vec, len(p) and p[i] otherwise) or for a part, and a let for any other value (src/cairn/compiler/check/statements.py).
  • compiler: unbounded adds to E-PARALLEL-RACE, for an index b * S + j with j a let mut local, that a bound is known only of a for binder or an immutable let, and the rewrite let at = j; if at < S { out[b * S + at] = ...; } (src/cairn/compiler/check/concurrency.py).
  • agent: rules of a code leads its card with the sentences that name the code and keeps every other sentence where it stood (led); the base card names E-ELEMENT-LOOP where it states for i, x in xs, as (copyable; E-ELEMENT-LOOP) in place of (copyable elements), and the parallel card says that the block's offset is a for binder or an immutable let (src/cairn/agent/teaching.py). make editors, tools/corpus/curriculum.py and tools/corpus/protocol_curriculum.py regenerate what quotes the cards.
  • docs: language.md (element loops), concurrency.md (a lane's block), tools.md (cairn rules); evidence/v1_2/loop gains the sizes below.
  • tests: each element-loop refusal's rewrite, which the test then checks is accepted (tests/language/test_short_forms.py); the race's rewrite, accepted, and no such sentence for an offset that is not a let mut (tests/soundness/test_blocks.py); cairn rules leading with the code's sentences while keeping each sentence once, and a card asked by name unchanged (tests/tooling/test_rules.py).

Behaviour and compatibility

Only messages and the order of a card's sentences change. Every program is accepted or refused as before, with the same code at the same place. cairn rules CARD and every card a host sends keep their order; cairn rules CODE returns the same sentences led by the code's, and a code its card never names, 86 of 215, reads as before. The base card's wording was chosen to stay inside the context budget tests/tooling/test_tools.py::test_measure_context holds the cards to: 23 packets now carry 130,295 bytes of current cards against 130,366 with the 0.5 cards, where a longer clause had crossed it.

Testing

make lint                         # clean
python -m pytest -q tests -n 4    # 6214 passed, 6 failed, 58 skipped

The 6 failures are GCC 11's thread sanitizer failing its own CHECK on this aarch64 host, known here and passing in CI. After #182 was merged in, the refusal, rules, block, element-loop, incremental, docs-example and tool tests ran again: 544 passed. The full run first showed test_measure_context failing, because the base card's first wording (xs a name or a field path: E-ELEMENT-LOOP) put the current cards 757 bytes over the 0.5 cards across its 23 packets; the shorter clause above leaves them 71 bytes under.

Evidence and claims

No claim changes. evidence/v1_2/loop gains the sizes below; no model ran.

Performance

Bytes an agent reads, piped, on the programs of the new tests:

output main this branch
check of for x in slots.data[k].buf 244 353
check of a lane's let mut offset 311 515
cairn rules E-ELEMENT-LOOP 1,438 1,446
cairn rules E-PARALLEL-RACE 4,722 4,859
cairn rules E-EFFECT-ORDER 779 780

Each refusal grows by the rewrite it now writes out, 110 to 200 bytes; for the probe each one replaced one round or more of a whole request. Checking time is unchanged: the new text is built only when the refusal is.

Checklist

  • The title is one plain sentence saying what is now true; it becomes the squash commit's subject
  • Every behaviour added has a test, and every new refusal a rejection test naming its code (no new refusal)
  • The documentation is updated in the file that owns the subject, and its examples compile
  • Generated files are current (make editors, make docs)
  • A language change carries everything AGENTS.md lists (no language change)
  • No emojis, no attribution trailers, nothing unrelated to the change

@SamMausberg
SamMausberg enabled auto-merge (squash) September 28, 2026 19:27
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-28T19:30:08.438510Z 42246b0 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 42246b0f5a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +338 to +342
lent = c.p.lends.get(ty.name) if not is_view(ty) else None
count, one = f"len({whole})", f"{whole}[i]"
if lent and lent[1] == "0": # a Vec: v.len elements of v.data
count, one = f"{whole}.{lent[2]}", f"{whole}.{lent[0]}[i]"
return f"{whole} is reached through an index, and {only}: write for i in 0..{count} {{ let {s.name} = {one}; }}."

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate the value before suggesting an index loop

When the indexed expression is not itself iterable, this fallback presents an invalid rewrite. For example, for x in a[k] {} with a: ro<u64>[n] now recommends for i in 0..len(a[k]) { let x = a[k][i]; }, but that rewrite is rejected with E-LEN because a[k] is a scalar. Use the type obtained above to emit the direct index loop only for supported iterable types; otherwise retain a value/type-specific diagnostic.

Useful? React with 👍 / 👎.

Comment on lines +203 to +204
for lane, size in (block.args, block.args[::-1]):
if lane.tag == "name" and lane.val == binder and size.tag in {"name", "int"}:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict the offset rewrite to constant strides

This condition treats every named stride as the constant S required by the lane rule. If S is a function parameter in out[b * S + j], the diagnostic recommends either binding j immutably or using for j in 0..S, but both rewrites remain E-PARALLEL-RACE because the checker cannot establish a constant stride. Only append this guidance after confirming that size resolves to a compile-time constant.

Useful? React with 👍 / 👎.

This branch has not been deployed

No deployments
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