Skip to content

Unify single-child same-id assignment on continuity#28

Merged
mansbernhardt merged 1 commit into
mainfrom
claude/single-child-continuity
Jun 22, 2026
Merged

Unify single-child same-id assignment on continuity#28
mansbernhardt merged 1 commit into
mainfrom
claude/single-child-continuity

Conversation

@mansbernhardt

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #26. Makes the single @Model child write path consistent with the collection ([Model]) and optional (Model?) paths for same-id assignment.

Before this PR, the three child shapes disagreed on what child = NewInstance(sameId) means for a @Model with an explicit, reusable id:

var child: M var child: M? var items: [M]
replace (new state) continuity continuity

This unifies them on continuity, so there is one rule across all shapes:

.id is identity. To change a child you mutate it. Assigning a fresh instance that reuses an existing id continues that live child (its context, activation, tasks and state are preserved; the new instance's state is ignored). Only a different id is a replacement.

Change

In the single-Model write path (ModelSourceBox subscript<T: Model> set):

  • Tear down the old child only on a different .id (child.id != newValue.id); a same-id assignment leaves the existing context registered so updateContext reuses it (continuity).
  • Key the write-skip by .id (was modelID) so a same-id assignment fires no spurious observation, matching the collection/optional paths.

Two-line functional change.

Scope / compatibility

  • Only affects @Model types that declare an explicit, reusable id. For a default-id model .id == modelID, so every distinct instance still replaces exactly as before — no behavior change.
  • To intentionally swap in different state under the same domain key: mutate the existing child, or give the new instance a distinct id.

No DEBUG "you replaced instead of mutating" notice was added: the framework cannot distinguish "replace to update state" from "pass a fresh same-id instance for continuity" (the latter is the legitimate, tested pattern in ActivateTests.testChildrenCaseActivation), so such a notice would false-positive on correct usage. The contract is documented in the CHANGELOG instead.

Validation

Full suite green on serial and parallel (782 tests; only the documented known-issue flakes). CHANGELOG [Unreleased] updated; ExplicitIdReplacementTests updated to assert single-child continuity plus a different-id replacement control.

🤖 Generated with Claude Code

A single `var child: M` property replaced its child on any different-instance
assignment, while `[Model]` and `Model?` properties treat a same-Identifiable-id
assignment as continuity (keep the live child, ignore the new instance's state).
Bring the single-Model write path in line: identity is the Identifiable `.id`, so
a same-id assignment reuses the existing child's context (skip removeChild ->
updateContext reuses it) and only a different `.id` is a genuine replacement. The
write-skip is keyed by `.id` so a same-id assignment fires no spurious
observation, matching the collection/optional paths.

Only affects @model types with an explicit, reusable `id`; for a default-id model
`.id == modelID`, so every distinct instance still replaces exactly as before.

Validated: full suite green on serial and parallel (782 tests, only the
documented known-issue flakes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mansbernhardt mansbernhardt force-pushed the claude/single-child-continuity branch from e0f1ec9 to 5ad4c9e Compare June 20, 2026 08:55
@mansbernhardt mansbernhardt merged commit 6ef0ef1 into main Jun 22, 2026
6 checks passed
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