Bare symbols are no longer guest sources; add the 'T borrow mode; rename receiver to subject - #151
Conversation
* docs: fix review defects in the arena and global-anchor change Repair the integration half of the memory-model revision and the cross-references it left stale. - Restore the story pointer at memory.md §3.6; it named a chapter heading that no longer exists, so the link resolved to nothing. - Point the sections whose rules the revision changed (§3.1, §3.2, §3.6, §4.1, §4.5, §4.6) at the new story chapter; §4.6 had no pointer at all. - Restore the story's placement chapter to its previous wording: the chapter predates this change, and its in-prose spec reference is a commit-pinned permalink rather than a living relative link. - Update the glossary entries for anchor cell and arena placement, which still described a scope-local anchor region. - Record the both-sides-guests move restriction in lifetimes.md, the home of move legality, as §1.10 with a summary row. - Restore the arena-granularity latitude and the destruction-timing distinction dropped from §3.1 and §3.2, and note that the fixed-size region reclaims nothing individually. - State how the anchor pool avoids issuing identity 0, restore the address formula, drop a contrast with storage the language has no form for, and square the segmented-offset diagram. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016YnNCXtguHg4tSbLCtTT2c * docs: sharpen the scope-drain guarantee and the move-liveness summary Address review on the promotion and move-liveness wording. - State the drain guarantee as reachability: a scope's memory is released at drain and no guest resolves into released memory, with an escaping value promoted out first and its guests following the canonical anchor. The previous phrasing claimed source-arena memory outlives every guest that can reach it, which promotion breaks. - Say in the glossary that promotion copies the fixed-size bytes — the inline payload or the handle — while a dynamic backing store transfers without being copied. - Complete the move-liveness summary row with the case where neither side has live guests: a moved-from slot that stays readable anchors lazily. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016YnNCXtguHg4tSbLCtTT2c --------- Co-authored-by: Claude <noreply@anthropic.com>
Define rehosting as copying the complete hosted representation into destination-owned storage. Inline bytes move into the destination slot, dynamic backing stores relocate into equal-size destination-region allocations, old source storage ceases to be live, and the source host-capable slot becomes a guest through the canonical tether. Keep anchor bookkeeping O(1) in the number of guests while stating the physical relocation cost explicitly. Align the normative memory model, glossary, and design story, and complete the glossary's verb list with operators and lambdas.
A new `&` may now be minted only from a field access or an `&T` parameter. A bare symbol stays a place expression but is no longer a guest source, so nothing can point at a local's own hosting slot and the slot stays free to be overwritten or moved from. This is what makes the aliasing case that started this — a guest to `main`, then `second = main` — a compile error at the line that mints the guest rather than a question about what the guest then denotes. Removing that source leaves a hole where a bare symbol needs to reach a callee, so reference types gain a third passing mode: `'T`, a borrow. The three modes are now `T` (swallow), `&T` (guest), and `'T` (borrow, any place including a bare symbol, read and mut for the call only, never stored, returned, or moved). A reference receiver written bare is an implicit `'T` borrow; `this &T` is the guest receiver a method writes when it stores or returns the receiver. Consequences carried through: a returned `&T` must be rooted in an `&T` parameter; a field reached through a borrow is not a guest source either; overloads may not differ only by passing mode; an `&` field and a recursive `#variant` case are fed from a field or an `&` parameter, so a recursive structure is rooted in a field rather than a bare local. The anchor system is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
…odes stories/memory.md gains "The slot that could not be pointed at" — the five-line aliasing program, the roads tried in order (smarter guests, the name/location/object middle layer, immovable objects, no guests at all) and why each failed, and the narrowing that finally settled it. Then "Three ways to hand over an object": why an `&T`-parameter exemption would have needed interprocedural inference, how the borrow fell out of that, why the receiver became a borrow too, and the defence of `'` over redefining `&`. stories/lifetimes.md gains "Where a guest may be rooted" — what the source rule did to the scope check and the return rule, and why a field reached through a borrow is not a guest source either. Spec sections point into the new chapters; in-prose references are pinned to b10eaed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository: zane-lang/coderabbit/.coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR revises Zane’s guest, borrow, and swallowing modes, tightens guest-source and lifetime rules, updates callable syntax and semantics, and documents new arena, anchor, and tether behavior. Related glossary, stories, README, and validation guidance are synchronized. ChangesGuest and memory model semantics
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CLAUDE.md`:
- Around line 61-68: Update the guest-source validation grep in CLAUDE.md to
ignore trailing whitespace and inline comments while still detecting bare-symbol
ampersand assignments; preserve the existing allowance for field accesses such
as `car.engine` and `&T` parameters.
In `@spec/glossary.md`:
- Line 68: Update the place-expression definition in spec/glossary.md to exclude
field accesses whose base chain passes through a 'T borrow, matching the
restriction in spec/memory.md §2.8. Retain the existing exclusions for bare
symbols and [] expressions while clarifying that only eligible field accesses
can serve as guest sources.
In `@spec/memory.md`:
- Line 517: Update the thread-local anchor cache statement in Section 3.2 so it
does not promise unchanged global LIFO reuse ordering: either make reuse order
explicitly non-normative when caches are used, or require centralized
global-stack behavior for implementations that need exact LIFO semantics.
Preserve the existing anchor identity and lifetime guarantees.
- Line 319: Update the fenced ASCII diagram block in spec/memory.md to include a
language info string, using text or another suitable lexer, so the Markdown
fence satisfies MD040.
In `@spec/syntax.md`:
- Around line 308-309: The passing-mode description in spec/syntax.md must
explicitly limit the T, &T, and 'T choices to reference-type parameters and
receivers. Clarify that value-type parameters are read-only borrows and value
receivers are borrowed, consistent with spec/memory.md §2.9 and spec/effects.md.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ba47af9c-5017-4dd9-98db-2f10ccb8158a
📒 Files selected for processing (15)
CLAUDE.mdREADME.mdspec/adt.mdspec/concurrency.mdspec/effects.mdspec/foundations.mdspec/functions.mdspec/glossary.mdspec/lexical.mdspec/lifetimes.mdspec/memory.mdspec/syntax.mdspec/types.mdstories/lifetimes.mdstories/memory.md
- CLAUDE.md: the guest-source guard was end-anchored, so it matched no line carrying a `// ILLEGAL: ...` comment — which is every deliberate bad example in the spec. It now tolerates an inline comment and trailing whitespace, and catches all three (memory.md 150/159, lifetimes.md 27) instead of none. - glossary.md §3.1, §3.33, §3.36: mirror memory.md §2.8's exclusion of field accesses whose base chain passes through a `'T` borrow. Without it the glossary made `borrowed.field` a legal guest source while the canonical rule rejected it. - syntax.md §3.1, §3.2: scope the three passing modes to reference types. A value parameter is always a read-only borrow and a value receiver is borrowed, so neither selects a mode; the unqualified wording contradicted effects.md §2. - memory.md §3.1: tag the segmented-offset diagram fence `text`. It was the only untagged opening fence in spec/. - memory.md §4.8: stop promising unchanged reuse order under thread-local anchor caches. Which free slot an allocation receives is unobservable from the source language, so §3.2's LIFO describes the central pool rather than binding an implementation; what a cache may not change is identity uniqueness, the §4.6 retirement events, and the lifetime guarantees resting on them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
|
All five findings were valid and are fixed in a9b2a8b. One is worth calling out because it means the guard I added in this PR was not doing its job: The bare-symbol guest-source grep was end-anchored, so it matched no line carrying a The thread-local anchor cache finding is answered on its own thread — it took the "reuse order is non-normative" branch rather than the "require centralized-stack behavior" one, and the reasoning is there. @coderabbitai review Generated by Claude Code |
|
✏️ Learnings added
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
spec/memory.md (1)
174-174: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winScope the non-escaping rule to reference-type borrows.
Line 174 says that a borrow cannot be stored in a field, but value-type parameters are also read-only borrows and may be copied into field or return storage. Change this to “a reference-type borrow (
'T)” so the rule does not contradict the value-type copy semantics stated in the same paragraph.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@spec/memory.md` at line 174, Update the borrow definition in the paragraph to scope the non-escaping restrictions—no anchor, field storage, or return—to reference-type borrows (`'T`). Preserve the following value-type semantics that parameters are read-only borrows and are copied when stored in fields or returns.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CLAUDE.md`:
- Around line 62-68: Update the guest-source guard regex and its surrounding
explanation in CLAUDE.md to recognize identifiers containing underscores, tab or
other whitespace around the assignment, and field accesses such as car.engine.
Keep the trailing comment-aware matching behavior intact and ensure the
documented invalid and valid forms align with the regex.
In `@spec/glossary.md`:
- Line 228: Update the guest definition’s minting rule to require that a field
access has a place as its base and that its base chain does not pass through a
'T borrow, matching the canonical wording in spec/memory.md. Replace the broader
“field access not rooted in a borrow” wording while preserving the other guest
semantics.
---
Outside diff comments:
In `@spec/memory.md`:
- Line 174: Update the borrow definition in the paragraph to scope the
non-escaping restrictions—no anchor, field storage, or return—to reference-type
borrows (`'T`). Preserve the following value-type semantics that parameters are
read-only borrows and are copied when stored in fields or returns.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ad687e35-cb17-4fe2-a4e7-3cb080cf664d
📒 Files selected for processing (4)
CLAUDE.mdspec/glossary.mdspec/memory.mdspec/syntax.md
- memory.md §2.9: the borrow paragraph said a borrow "cannot be stored in a field, and cannot be returned" and then that a value is copied when bound into "a field or return store" — a flat contradiction. Split the two: the borrow is never storage, but binding *through* one copies the value, and the copy escapes fine. A reference type has no copy, so a `'T` leaves nothing. - glossary.md §3.33: restore the place requirement the shortened wording lost. "A field access not rooted in a borrow" would have admitted `makeCar().engine`; mirror memory.md §2.8 in full. - CLAUDE.md: the guard now accepts a leading-underscore private name (`_engine`) and tab-formatted assignments. Prose rewritten to say what the guard does — it matches a bare-symbol right-hand side only, so every hit is a candidate bug and the legal forms never appear. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
|
Second round handled in fe62f1a. The glossary and CLAUDE.md findings are answered on their threads; the third arrived outside the diff range, so here:
The fix separates the borrow from what is read through it, rather than scoping the rule to @coderabbitai review Generated by Claude Code |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- commit fe62f1a summary ---'
git show --stat --oneline --decorate --no-renames fe62f1a
echo
echo '--- focused diff: spec/memory.md ---'
git show --format=fuller --no-ext-diff --unified=35 fe62f1a -- spec/memory.md
echo
echo '--- outline ---'
ast-grep outline spec/memory.md --items all | sed -n '1,220p'
echo
echo '--- borrow/storage/return references with context ---'
rg -n -i -C 4 \
'(§?2\.9|borrow|borrowed|field|return(?:ed)?|store|storage|copy|reference type)' \
spec/memory.mdLength of output: 59800
This also agrees with the surrounding parameter-only and non-escaping 🐇🔍 ✏️ Learnings added
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
spec/glossary.md (1)
198-199: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winClarify that only the borrow capability is non-movable.
The entry says value types are passed by borrow and may be copied into fresh storage, but then says a borrow “cannot be ... moved.” That can be read as forbidding movement/copying of the value itself. Specify that the borrow itself cannot be stored, returned, or moved, while values read through a value borrow may be copied.
Proposed wording
- Unlike a guest, a borrow has no anchor or tether and cannot be stored, returned, or moved. + Unlike a guest, the borrow itself has no anchor or tether and cannot be stored, returned, or used as a move source.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@spec/glossary.md` around lines 198 - 199, Update the glossary entry’s “Why this name” text to clarify that the borrow capability itself cannot be stored, returned, or moved, while values read through a value borrow may still be copied into fresh storage. Preserve the existing distinction between borrowing caller storage and copying the borrowed value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CLAUDE.md`:
- Around line 62-69: Update the guidance for reviewing grep hits so bare RHS
symbols remain candidates by default, but are retained when nearby declarations
establish the RHS as an &T parameter and therefore a legal guest source.
Continue excluding field-access sources syntactically, and keep deliberate //
ILLEGAL: examples or grammar metavariables as non-fixes.
---
Outside diff comments:
In `@spec/glossary.md`:
- Around line 198-199: Update the glossary entry’s “Why this name” text to
clarify that the borrow capability itself cannot be stored, returned, or moved,
while values read through a value borrow may still be copied into fresh storage.
Preserve the existing distinction between borrowing caller storage and copying
the borrowed value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 7b3fde42-9ddd-4fd0-afed-80d06c9dc710
📒 Files selected for processing (3)
CLAUDE.mdspec/glossary.mdspec/memory.md
- CLAUDE.md: the guard's prose claimed the legal sources "never match". That is true only of a field access, which `.` excludes syntactically. An `&T` parameter is written bare, so `r &Node = source` inside a callee matches and is correct — the reviewing rule now lists it as a keep, checked against the enclosing signature rather than the line. - glossary.md §3.27: the borrow entry still carried the contradiction fixed in memory.md §2.9 — "cannot be stored, returned, or moved" against a value being copied into a fresh slot. Scope it to the borrow itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
|
Third round handled in 84c3505. The CLAUDE.md finding is answered on its thread; the glossary one arrived outside the diff range, so here:
Worth noting the pattern, since it is the same defect twice: I fixed the canonical rule and left the glossary summarizing the old version of it. The glossary is a summary of the spec, so a correction to a rule has to be chased into its entry in the same change. @coderabbitai review Generated by Claude Code |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- commit summary ---'
git show --stat --oneline --no-renames 84c3505
echo
echo '--- focused diff: glossary and canonical memory rule ---'
git show --format=fuller --no-ext-diff --unified=30 84c3505 -- spec/glossary.md spec/memory.md
echo
echo '--- glossary borrow entry candidates ---'
rg -n -i -C 12 '(^#{1,6} .*borrow\b|Why this name|cannot be stored|fresh slot|cop(?:y|ied)|move source)' spec/glossary.md
echo
echo '--- canonical borrow-rule candidates ---'
rg -n -i -C 10 '(§?2\.9|borrow itself|cannot be stored|fresh slot|cop(?:y|ied)|reference-type)' spec/memory.mdLength of output: 78376
That matches the canonical wording in 🐇🔍 ✏️ Learnings added
✅ Action performedReview finished.
|
Two findings on #151 were the same defect: a rule was corrected in its canonical home and its glossary entry left paraphrasing the superseded version, so the spec contradicted itself until review caught it. Both times the author missed it, which makes it a habit worth writing down rather than a one-off slip. Adds it to the "Before you edit" list: after editing any normative rule, grep spec/glossary.md for the concept and update the entry in the same commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
|
Two commits are still unreviewed — 84c3505 hit the review limit and 6b0c909 was skipped as an incremental push. Both are small: 84c3505 is the round-three fixes (already confirmed on their threads), and 6b0c909 adds the glossary-check habit to @coderabbitai review Generated by Claude Code |
|
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
spec/glossary.md (1)
242-246: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winClarify that borrow mode is only the non-swallowing way to pass a bare symbol.
This sentence conflicts with §3.34 and §3.37: a bare hosting symbol may also be passed to a
Tswallowing parameter, while'Tis the only non-swallowing mode that accepts it. Please change this to “Passing a bare symbol without swallowing it is the borrow mode’s job” or equivalent.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@spec/glossary.md` around lines 242 - 246, Update the “guest source restriction” Meaning text so the bare-symbol call rule states that borrow mode is the only non-swallowing way to pass a bare symbol, while still allowing a bare hosting symbol to be passed to a T swallowing parameter.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@spec/glossary.md`:
- Around line 242-246: Update the “guest source restriction” Meaning text so the
bare-symbol call rule states that borrow mode is the only non-swallowing way to
pass a bare symbol, while still allowing a bare hosting symbol to be passed to a
T swallowing parameter.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 9878985d-0eb3-4f05-8638-bc7145cf3e3a
📒 Files selected for processing (2)
CLAUDE.mdspec/glossary.md
"Passing a bare symbol into a call is the borrow mode's job" overstated it: a bare symbol is a move-source (lifetimes.md §1.2), so a plain `T` parameter swallows one perfectly well. `'T` is the only *non-swallowing* mode that accepts a bare symbol, which is the claim the surrounding rules actually support. Corrected in all three places that carried it — memory.md §2.8.1 (canonical), glossary.md §3.36, and the foundations.md bullet — rather than only where the review pointed, since the imprecision started in the canonical home and propagated from there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
|
Valid, and fixed in 99e143b. The finding arrived outside the diff range, so answering here. "Passing a bare symbol into a call is the borrow mode's job" overstated it. A bare symbol is a move-source ( I fixed it in all three places that carried the claim, not just the flagged one: @coderabbitai review Generated by Claude Code |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- commit 99e143b ---'
git show --stat --oneline --no-renames 99e143b
git show --format=fuller --no-ext-diff --unified=22 99e143b -- \
spec/memory.md spec/glossary.md spec/foundations.md
echo
echo '--- stated canonical contexts ---'
for f in spec/memory.md spec/glossary.md spec/foundations.md spec/lifetimes.md; do
echo "### $f"
rg -n -i -C 7 \
'(bare symbol|move-source|move source|swallow|swallowing|non-swallowing|borrow mode|passing mode|'\''T\b|plain `?T`?)' \
"$f" || true
done
echo
echo '--- remaining instances of the superseded claim ---'
rg -n -i -C 3 \
'(passing a bare symbol.*borrow|bare symbol.*borrow mode|borrow mode.*bare symbol)' \
spec || trueLength of output: 68579
The bidirectional synchronization point is well taken: summaries must follow canonical corrections, and reviewing a summary can reveal that its canonical source is wrong. No further concern. 🐇🔍 ✏️ Learnings added
✅ Action performedReview finished.
|
|
@coderabbitai review Re-trigger: the full pass over 99e143b hit the review limit. The targeted verification of that commit already ran and confirmed it, so this is just to complete the pass over Generated by Claude Code |
|
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@spec/foundations.md`:
- Line 97: Update the guest-source summary in the “A guest comes from a field”
bullet to match the canonical rule: guests may originate only from a qualifying
field access whose base is a place and does not pass through a 'T borrow, or
from an &T parameter; explicitly exclude bare symbols.
In `@spec/memory.md`:
- Line 165: Update the guest-source description in spec/memory.md to replace “a
field, a container element, a callee” with wording that identifies a qualifying
field access and an &T parameter, consistent with the terminology in §2.8.1.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: zane-lang/coderabbit/.coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 1ed78a36-0387-4442-ba17-a52774f0b2a4
📒 Files selected for processing (3)
spec/foundations.mdspec/glossary.mdspec/memory.md
- foundations.md §7: the bullet said a guest is minted "from a field access or an `&T` parameter", which over-approximates memory.md §2.8.1 — the base must be a place and must not be reached through a `'T` borrow. Named both qualifiers compactly rather than reproducing the rule; foundations.md is a map that defers to canonical homes. - memory.md §2.8.1: "a field, a container element, a callee" listed where a guest may live, not what may mint one, and was read as the latter. Says it in the precise terms instead — an `&` field, a container element, an `&T` parameter inside a callee. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
|
|
||
| // `&` parameter is a guest; it may be stored into an `&` field | ||
| Unit setEngine(this Car, engine &Engine) mut { | ||
| Unit setEngine(this 'Car, engine &Engine) mut { |
There was a problem hiding this comment.
i thought the type for this is implicit borrow? either way, need to decide for one option.
There was a problem hiding this comment.
You're right, and it was specified both ways at once — the prose directly under these examples said a bare this T on a reference type is an implicit 'T borrow, and then every example wrote this 'Car. Decided in dba89ba: ' is never written on this.
The reason to go that way rather than the other is that the alternative isn't really available. If the marker were required on the receiver, then a bare this T would have to mean the remaining mode — swallow — so every method written without a marker would consume its own receiver. Nothing wants that. So the borrow has to be what bare this T means, and once it is, this 'T is just a second spelling for the default.
It also lines the two type worlds up. A value receiver is a borrow and is written bare; now a reference receiver is too. this carries at most one marker, &, and its absence means borrow in both cases — which puts the mark on the exceptional thing (keeping the receiver past the call) rather than on the ordinary one.
Applied across memory.md, functions.md, lifetimes.md, syntax.md, glossary.md, and effects.md. That included dropping the this 'ReceiverType production from syntax.md §3.2 and the matching function-type form in §2.9, so the grammar no longer admits the spelling at all.
Generated by Claude Code
|
|
||
| // plain reference-type parameter: taken by hosting access, then moved into a hosting field of this | ||
| Unit setSpare(this Car, engine Engine) mut { | ||
| Unit setSpare(this 'Car, engine Engine) mut { |
There was a problem hiding this comment.
same issue of using explicit '
There was a problem hiding this comment.
Fixed in dba89ba along with the other four — ' is never written on this now. Reasoning is on the first thread.
Generated by Claude Code
| // `&` parameter, read only: a reference-type object passed without consuming it | ||
| Int inspect(this Car, engine &Engine) { | ||
| // borrow parameter: a reference-type object read without consuming it and without minting a guest | ||
| Int inspect(this 'Car, engine 'Engine) { |
There was a problem hiding this comment.
same issue as above
There was a problem hiding this comment.
| ```zane | ||
| Unit setEngineWrong(this Car, engine Engine) mut { | ||
| this.engine = engine // ILLEGAL: a swallowed host is not an `&` source | ||
| Unit setEngineSwallowed(this 'Car, engine Engine) mut { |
There was a problem hiding this comment.
same issue as above
There was a problem hiding this comment.
| return Unit() | ||
| } | ||
|
|
||
| Unit setEngineBorrowed(this 'Car, engine 'Engine) mut { |
There was a problem hiding this comment.
same issue as above
There was a problem hiding this comment.
| What falls out is a memory model that is uniformly 32-bit and, per tethered object, exactly twelve bytes of machinery: the four-byte tether wherever it is stored, the four-byte anchor cell, and the four-byte backpointer the payload carries home to that cell. The double indirection a tether walks — tether to cell, cell to payload — looks like it should cost two cache misses, and the arena is what makes it cost closer to zero: the cell read is a load into arena memory that is almost always already warm. The first thing we tried for that warmth was to drop each cell right beside the payload that mints it, so the two share a cache line and the second hop is paid for by the first. It worked for the deref, and it opened a loose end the next chapter has to pull on — a cell sitting in the payload stream is a cell a payload-only scan has to step over, and a payload's position starts to depend on how many of its neighbours were tethered before it. | ||
|
|
||
| The one motion this has to survive is escape. A value that outlives its scope is moved into a parent, and under arenas that means its payload is *copied* into the parent's arena — the child arena is about to be unmapped and cannot keep it. The backpointer is what makes that copy invisible: the runtime follows it to the payload's one anchor cell and rewrites the cell with the payload's new location. Every tether still points at that same cell and never learns the payload moved ([`memory.md` §4.5](https://github.com/zane-lang/spec/blob/dc33d1eae46bfc71bfb7e8e3b2f575926aa00059/spec/memory.md#45-moves-overwrites-and-promotion-update-one-cell-not-all-tethers)) — the same one-cell-update that made an in-place move O(1) makes a cross-arena promotion O(1) too. When the scope finally drains, its chunks are unmapped and its cells vanish with the objects they served, and the scope rules that governed tethers all along guarantee nothing still pointing could point into them. | ||
| The one motion this has to survive is escape. A value that outlives its scope is moved into a parent, and under arenas that means its payload is *copied* into the parent's arena — the child arena is about to be unmapped and cannot keep it. The backpointer is what makes that copy invisible: the runtime follows it to the payload's one anchor cell and rewrites the cell with the payload's new location. Every tether still points at that same cell and never learns the payload moved ([`memory.md` §4.5](https://github.com/zane-lang/spec/blob/dc33d1eae46bfc71bfb7e8e3b2f575926aa00059/spec/memory.md#45-moves-overwrites-and-promotion-update-one-cell-not-all-tethers)). The one-cell update keeps anchor bookkeeping O(1) in the number of tethers; physical promotion still costs proportionally to the representation copied. When the scope finally drains, its chunks are unmapped and its cells vanish with the objects they served, and the scope rules that governed tethers all along guarantee nothing still pointing could point into them. |
There was a problem hiding this comment.
it's okay to change, just clarify the motivation behind changing it. it's from pr 147 but still important to take a look at
There was a problem hiding this comment.
Good catch, and you're right that it wasn't clear. Addressed in dba89ba, though by adding rather than editing — this chapter is history, so the guide says append.
What this paragraph describes is the one-cell promotion model: the runtime follows the backpointer to the single anchor cell and rewrites it, so every tether keeps working and never learns the payload moved. That was true when it was written and it is not true now.
It stopped being true for a reason worth naming, which is what was missing. One-cell promotion works only while a payload has exactly one cell to rewrite. As soon as both sides of a move are already anchored — the source has guests and so does the destination — there are two cells and one backpointer, so the runtime can update one path or the other but not both. That is the pressure that eventually takes anchors out of scope arenas entirely and into the runtime-global pool, where a cell may target another cell and a superseded source identity becomes a forwarder instead of needing a rewrite.
The chapter now ends with a short paragraph naming both things that didn't survive — the cell moving out of the payload stream, and one-cell promotion failing on two anchored sides — each pointing at the chapter that takes it up. It also flags that the twelve-byte figure quoted here becomes sixteen later, since that number is the kind of thing a reader will otherwise carry forward as current.
Generated by Claude Code
There was a problem hiding this comment.
but if it's no longer true, it should instead be mentioned in an appended chapter instead of being changed, because the past is not changable. this being said, we should try to not modify the story of the main branch
There was a problem hiding this comment.
Understood, and my fix was the same violation again — bolting a forward pointer onto the end of an existing chapter is still editing that chapter. Reverted in 19806f0.
Diffing against main also turned up the thing your original comment was actually pointing at, which I'd missed. One sentence in this chapter had already been rewritten before I touched it — it came in with the PR 147 commits, replacing
— the same one-cell-update that made an in-place move O(1) makes a cross-arena promotion O(1) too.
with a two-clause version separating anchor bookkeeping from physical copy cost. So the chapter on this branch was not main's chapter. It is now byte-identical to main again.
The correction that was in my forward pointer has moved into the appended chapter, where it names the claim it retires rather than reaching back to amend it: the segmented-offset chapter has promotion follow the backpointer to the one cell and rewrite it, and that holds only while a payload has one cell to rewrite — it fails the moment both sides of a move are anchored, which is what takes anchors out of scopes.
git diff origin/main -- stories/memory.md | grep -E "^-[^-]" now prints nothing.
Generated by Claude Code
Maintainer review on #151. The receiver was specified two ways at once: memory.md §2.9 said a bare `this T` on a reference type is an implicit `'T` borrow, and then every example in that same section wrote `this 'Car`. Decided in favour of the implicit form — `'` is never written on `this`. The rule is forced rather than chosen. If the marker were required on the receiver, a bare `this T` would have to mean the method swallows its own receiver, which no method wants; so the borrow has to be the bare meaning, and `this 'T` is then a second spelling for the default. It also makes the two type worlds agree: value and reference receivers are both borrows and both written bare, with `&` the single marker `this` may carry. Applied to memory.md, functions.md, lifetimes.md, syntax.md, glossary.md, and effects.md — including dropping the `this 'ReceiverType` production from syntax.md §3.2 and the function-type form in §2.9. stories/memory.md: the segmented-offset chapter still ends on the one-cell promotion model, which the later global-pool chapter superseded. Stories are append-only, so rather than rewrite it, that chapter now carries a forward pointer naming what did not survive and why — the cell leaving the payload stream, and one-cell promotion failing once both sides of a move are anchored. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
Maintainer ruling on #151: the story is append-only, and that is verified by diffing against main — an existing chapter is not edited, and a new chapter goes at the end rather than being slotted into the middle. Two violations, both now gone: - One sentence in "The last table problem, and the segmented offset" had been rewritten (it came in with the PR 147 commits, and I then appended a forward-pointer paragraph to the same chapter, which is the same violation again). Both reverted; that chapter is byte-identical to main. - "Two payload streams, and the anchor that leaves the scope" had been inserted between two chapters that already existed on main. Moved to the append position, after "Two vocabularies" and before the chapters this PR adds — which is also the right chronology, since the arena and anchor work came after the host/guest rename. The correction the forward pointer was carrying now lives where it belongs, in the appended chapter: it names the one-cell promotion claim from the segmented-offset chapter and says what retires it — that the claim holds only while a payload has one cell to rewrite, and fails once both sides of a move are anchored. `git diff origin/main -- stories/memory.md` is now additions only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
The story guide licensed exactly what review just rejected: §5 said to "open a new chapter (or extend the relevant one)", which reads as permission to edit an existing chapter, and it said nothing about where a new chapter goes. Both gaps were used in this PR — a chapter was edited and another was inserted between two that already existed. §5 now spells out the two teeth: an existing chapter is not touched at all, not even to bolt a forward pointer onto its end, and a new chapter goes at the end of the file rather than into the middle. The correction a forward pointer would carry belongs in the new chapter, naming the older chapter's claim. Both are mechanically checkable, so the guide and CLAUDE.md now carry the check — `git diff origin/main -- stories/<topic>.md | grep -E "^-[^-]"` must print nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
| ``` | ||
|
|
||
| Binding a plain (swallowed) parameter into `&` storage is illegal, because a swallowed value is hosted at the call site while an `&` field lives with the object that holds it — which may outlive the call, leaving the `&` dangling: | ||
| A reference-type receiver borrows by default, and **`'` is never written on `this`**: a bare `this T` on a reference type *is* the borrow. It has to be — if the marker were required here, bare `this T` would mean the method swallows its own receiver, which is not something a method wants. A method that needs to keep or hand back the receiver writes `this &T`, the guest receiver (see [`functions.md`](functions.md) §2.4). So `this` carries at most one marker, `&`, and its absence means borrow for value and reference receivers alike. |
There was a problem hiding this comment.
i thought the default T is swallowing?
There was a problem hiding this comment.
It is — on an ordinary parameter. The receiver is the exception, and my justification for that was wrong, so I've rewritten it in 5b7616a.
What I had written was that the borrow "has to be" the bare meaning because otherwise a method would swallow its own receiver. That argues from convenience and implies the swallow reading was ever a candidate. It wasn't. main already says it:
For a reference-type receiver,
thisis an implicit&reference to the object (never swallowed).
and memory.md §2.9 on main says the same — "this is an implicit guest to the object, never swallowed". So the receiver has never been a swallow position, and "bare T swallows" is a rule about ordinary parameters that the receiver was already outside of.
What this PR actually changes is narrower than it looked: only which non-swallowing mode a bare receiver is. It was an implicit guest; it is now an implicit borrow, because the receiver expression at a call site is usually a bare symbol and §2.8.1 no longer admits one as a guest source. this therefore picks between two of the three modes, never the third.
§2.9 now says that outright, including the part you'd otherwise have to notice yourself — that bare T does not mean the same thing in both positions — rather than implying a consistency that isn't there. The mode table is scoped to parameters, and functions.md §2.4 and both summary rows match.
Generated by Claude Code
There was a problem hiding this comment.
what exactly do you mean by "the receiver"?
There was a problem hiding this comment.
Fair — the word was doing three jobs at once and this rule is about only one of them. Split in ad39224:
- the receiver is the object the method is called on;
- the receiver parameter is
this, whose surface form fixes the passing mode; - the receiver expression is what stands left of
:/!at the call site and supplies the object.
"Never a swallow position" is a claim about the second one — the declared parameter — so the line now reads "The receiver parameter is never a swallow position" and says this, the first parameter and only it. The third sense is what carries the reason it moved to the borrow (a call-site receiver expression is usually a bare symbol, which §2.8.1 no longer admits as a guest source), and the first is what "stores or returns the receiver" means.
Defined once in functions.md §2.1 with a glossary entry at §3.38, and the loose uses in the passages this PR touches are now spelled with whichever sense applies — including two summary rows that said "reference receiver" where they meant reference-type this.
Generated by Claude Code
There was a problem hiding this comment.
ahh, i overlooked that. so the original sentence was actually right too.
There was a problem hiding this comment.
It was — the sentence was true as written, it just needed the reader to already know which sense of "receiver" was meant. That the question came up at all is the argument for keeping the split: the word is used across functions.md, memory.md, effects.md and concurrency.md and was defined nowhere, so a fresh reader had to infer it from context each time.
Keeping ad39224 unless you'd rather have it out; it changes no rule, only which words state them.
Generated by Claude Code
There was a problem hiding this comment.
why do we call it receiver though? what does it receive?
There was a problem hiding this comment.
Nothing, in Zane. It's inherited from Smalltalk, where a call was a message and the object received the message. Every language that copied the object-dot-method shape carried the word along — Go, Swift, Ruby, Java's docs — long after the messages were gone.
Zane doesn't have messages. A method is a package-scope verb whose first parameter is this, and player!setScale(2) desugars to Pkg$setScale(player, 2). So by the test in naming-terms.md §3 the word neither fights nor feeds — it's simply dead metaphor, a label with no analogy left under it. That's exactly the "flat and descriptive" failure the guide rejects origin form for, except worse: origin form at least described something true.
If it's worth replacing, the candidate the repo has already half-committed to is subject. The spec calls callables verbs (naming-terms.md §1) — so grammar is already the register, and grammar supplies the missing word for free: subject–verb–object. player!setScale(2) is subject player, verb setScale, object 2. It feeds rather than fights (§3): a fresh reader's sense of "subject" points at the thing the verb acts from, which is what this is. It's short and ordinary in dense prose (§2.3), and it makes the whole parameter list nameable in one register instead of one word from Smalltalk and one from grammar.
Against it: subject has a prior programming claim (observer-pattern Subject, and "subject" as a security principal). Weaker than the matrix case the guide rejects, but not zero.
Two things worth saying before you decide:
- It's a real rename, not a word swap. 118 occurrences across 12 spec docs and 9 stories — and the stories are append-only, so the ones there stay as they are and the term ends up split across the two trees by construction.
- It also owes a story passage. §6 makes a coined term's rejected candidates part of the design history, so this would need a chapter in
stories/functions.mdarguing subject over receiver — which means it's an interview, not something I should draft alone.
My recommendation is to do it, but not in this PR — #151 is about passing modes and is otherwise finished, and a 118-site rename would bury that diff. Happy to open it as its own branch. Say the word either way.
Generated by Claude Code
There was a problem hiding this comment.
yes, subject is way better than receiver. so yes, please rename. also, it is important to mention that the story is append only per pr, not per commit, so you can rewrite the story this pr appends and insert the renaming at the beginning of the "story-pr-section".
There was a problem hiding this comment.
Done — 97e5bf2 (rename) and dd1cbcb (story).
The rename. 91 sites across 12 spec docs, plus README.md and naming-terms.md. Four headings changed, which moves their anchors; every story link into them is a pinned permalink, so they all still resolve at their own commits — nothing to chase. Two hits were a different sense of the word and did not become subject: functions.md §7.2–7.3 used "receiver" for the verb taking a lambda argument, which is the callee, not the subject. Those now say callee.
subject is added to the term list in naming-terms.md §1 alongside verb, and the glossary's "why this name" now carries the grammar argument.
The story. "What does a receiver receive?" is appended to stories/functions.md — the term's home doc, and the only chapter this PR adds there, so it's first-and-only in that file's PR section. It records: that the word went unexamined because it arrived free with the object model; that the three-senses problem was the first symptom and your question was the one that landed; the Smalltalk message and its absence here; the dead-metaphor failure against §3 of the naming guide; grammar as the register verb had already chosen; and the candidates weighed — keeping receiver for cross-language familiarity, and target, which collides with "call target".
Two costs are stated rather than smoothed over: subject has prior claims (observer pattern, security principal), and the two doc trees now disagree by construction — merged chapters say "receiver" and always will, so a reader of the design history meets both words. The chapter ends by naming itself as the mapping between them.
On append-only per PR. That's a rule I had written too strictly into contributing/writing-stories-docs.md §5 and CLAUDE.md earlier in this PR — I'd said a chapter is frozen once written. Both now say the frozen unit is what has merged, and that a branch's own new chapters are drafts you may rewrite, reorder, or insert among until it lands. Worth noting the git diff origin/main check already encoded this correctly — it diffs against what's published, so it stays quiet while you rearrange your own branch and fires only when something merged moves. I'd been applying a stricter rule than my own check.
That clarification is also what let me update the two places this PR's own new chapters said "receiver" (stories/memory.md, stories/lifetimes.md) rather than leaving them inconsistent with the spec on day one.
Validation: both guards clean at the expected four hits, story diff against main is additions-only, and both new permalinks verified to resolve at 97e5bf2.
One thing I'd like you to check: the story's account of how the decision came about. I wrote it from this thread — the word survived unexamined until the passing-mode rule forced a sentence about this, the three-senses split came first and didn't fix it, then "what does it receive?" landed and nothing answered. If the actual order in your head was different, say so and I'll replace it; per the repo's own rule your account is the source of truth for the narrative, not my reconstruction of it.
Generated by Claude Code
Maintainer asked why bare `T` borrows on `this` when the default for `T` is swallowing. Fair — the justification I had written was wrong. It said the borrow "has to be" the bare meaning because otherwise a method would swallow its own receiver, which implies the swallow reading was ever on the table. It never was. main already says a reference receiver is an implicit `&` reference "(never swallowed)", and memory.md §2.9 on main says the same. So the receiver has never been a swallow position, and bare `T` meaning swallow is a rule about ordinary parameters. What this PR changes is only *which* non-swallowing mode a bare receiver is — guest before, borrow now — because the call-site receiver is usually a bare symbol, which §2.8.1 no longer admits as a guest source. §2.9 now states that, says plainly that bare `T` does not mean the same thing in both positions, and names the pre-existing rule that makes it consistent rather than arguing from convenience. The §2.9 mode table is scoped to parameters, and functions.md §2.4 and both summary rows match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
"Receiver" was doing three jobs at once — the object a method is called on, the `this` parameter that fixes the passing mode, and the call-site expression that supplies the object — and the rule that it is never a swallow position is about the second of those, not the first. Define the three in functions.md §2.1, add a glossary entry, and use the precise term wherever the distinction carries weight.
"Receiver" is Smalltalk residue — a call was a message and the object received it. Zane has no messages, so the word named nothing: it neither fought its old meaning nor fed the new one, it was simply dead metaphor. "Subject" puts the concept in the register the spec already chose when it named callables verbs. A call reads subject-verb-object, and the subject is what the verb acts from. Merged story chapters keep the old word — they record the language as it was — so the term is split across the two trees by construction. Also records that append-only is per pull request, not per commit: chapters a branch has not merged yet are still drafts.
Appends "What does a receiver receive?" to stories/functions.md: the word arrived free with the object model and went unexamined until a rule about `this` forced us to say which of its three senses we meant, and then until someone asked what a receiver receives. Records the dead metaphor, the grammar register that supplied the replacement, the candidates weighed, and the cost — merged chapters keep the old word, so the two doc trees disagree by design. Pointer added from functions.md §2.1; README row updated.
'T borrow mode'T borrow mode; rename receiver to subject
|
The failing Title check above is stale, not a real finding. It reports that "rename receiver to subject" is not in the changeset; the rename is That single hit is the Not triggering a re-review for this — the title stands as written. Generated by Claude Code |
The rule lived in two places at once, and CLAUDE.md — which is agent notes, not contributor documentation — had grown a full normative restatement of it. Now the story guide §5 owns the rule and says how it is enforced (by hand, by author and reviewer; there is no CI), and CLAUDE.md keeps only the command to run and the two ways sessions have actually got it wrong: editing a merged chapter, and refusing to edit an unmerged one.
Replaces #150, which is closed without merging. Branched from
agent/fix-host-overwrite-reuseand targetingmaindirectly, so the anchor-pool work that was open as #147 lands here too rather than in a separate PR.The change
The starting point was this program:
Line 4 moves the object out of
main's slot. Whatguestthen denotes — the object that left, or the slot it left from — has no answer that is right in both readings, and the spelling gives the reader no way to say which they meant.A bare symbol is no longer a guest source (
memory.md§2.8.1). It stays a place expression — readable, mutable, movable-from, borrowable — but a new&may be minted only from a field access or an&Tparameter. Line 3 is now a compile error, at the line that creates the problem rather than the line that reveals it. A guest to a bare symbol never bought any reach in the first place: the symbol is already in scope everywhere a guest to it could be declared.Removing that source leaves a hole where a bare symbol needs to reach a callee, so a reference type gains a third passing mode (
memory.md§2.9):T&T&storage or return it as&T'T'Tis parameter-only — never a storage, field, element, or return type.What follows from it
this &Tis the guest subject a method writes when it stores or returns the object.'is never written onthis. That the subject is not swallowed is not new — a reference-typethisused to be an implicit guest, likewise never swallowed; the borrow mode only renamed what barethisalready was. This also makes the two type worlds agree: amutsubject is a mutable borrow of the caller's slot for values and references alike.&Tmust be rooted in an&Tparameter (lifetimes.md§1.7). A swallowingTis a bare symbol, and a borrow ends with the call.'Tparameter is not a guest source either — otherwise a borrow escapes wrapped in one layer of indirection.functions.md§4.1), widening the rule that already coveredTvs&T.&field and a recursive#variantcase are fed from a field or an¶meter, so a recursive structure is rooted in a field rather than a bare local (adt.md§4.1,types.md§3.9). This is the one place the change costs something visible —Expr.flip(tree.root)rather thanExpr.flip(leaf).receiver→subjectStating the rule about
thisexposed a word that had never been examined. "Receiver" is inherited from Smalltalk, where a call was a message and the object received it; Zane has no messages, so by the test innaming-terms.md§3 the word neither fights nor feeds — it is dead metaphor. It was also covering three distinct things: the object a method is called on, thethisparameter whose form fixes the passing mode, and the expression left of:/!at the call site.Both are fixed. The three senses are separated and defined in
functions.md§2.1 (glossary §3.38), and the term is renamed to subject — grammar, matching theverbthe spec already chose, so a call reads subject–verb–object. 91 sites across 12 spec docs. Two sites meant the callee taking a lambda argument rather than the method subject, and now say so.Merged story chapters keep the old word, since they record the language as it was — so the two doc trees disagree by construction, and the new story chapter names itself as the mapping.
What is not in this PR
The anchor system is untouched. #150 deleted it and asserted that a hosted object never relocates; that model could not express a conditional move and contradicted itself on backing-store growth. Anchors exist to let objects move, and they still do. There are no changes to
bench/.Stories
stories/memory.mdgains "The slot that could not be pointed at" and "Three ways to hand over an object" — including the defence of'over redefining&, since both forms are marked andTis the unmarked one, so continuity decided it.stories/lifetimes.mdgains "Where a guest may be rooted".stories/functions.mdgains "What does a receiver receive?", which argues subject over receiver and records the candidates rejected. In-prose spec references are pinned to b10eaed and 97e5bf2; all were verified to resolve at their pinned commits.contributing/writing-stories-docs.md§5 also records what append-only actually freezes: the unit is the pull request, not the commit, so a branch's own unmerged chapters are still drafts. Thegit diff origin/maincheck already encoded this correctly.Validation
Both
CLAUDE.mdguards are clean. The guard itself was fixed in this PR:'[A-Z]is removed from the retired-generics-forms list (a leading'is now the borrow marker), a second guard for bare-symbol guest sources is added, and the greps are made recursive — the old form printedbench/: Is a directoryand silently skipped it.🤖 Generated with Claude Code
https://claude.ai/code/session_017AYiJXjzCPEDfYRW1ZtxH7
Generated by Claude Code