improvise-b114: no coordinate of a wire write may name a group

App::group_refusal asks the document, not the layout, whether any
coordinate names a group rather than an item, and runs first in
unwritable: a measure-half fold hides from both the stored arm and the
projection. Rival (rule removed) fails the new test and nothing else.
command.md and the ciw8 fold evidence now say the aggregate arm guards
only the None-axis case.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Edward Langley
2026-09-23 08:29:28 -07:00
parent 5cd00770cf
commit 872f9bc07b
2 changed files with 30 additions and 2 deletions
+10
View File
@@ -3535,6 +3535,16 @@ the refusal marks the batch, `Host::mutating_frame` undoes the frame, the asker
gets `Rejected(MutationRefused)`, and the reason is on the host's status line.
The question it asks is the projection's own, never a second derivation of it:
- A coordinate that names a **group** rather than an item is refused, in any
view (improvise-b114). A group has no cell of its own, so writing one stores a
non-item coordinate. This rule asks the document, not the layout, and runs
first, because a fold in the measure half hides from both rules below. Take
`{Month=Feb, _Measure=Money}`, where `Money` groups Revenue and Cost. Its
record `{Month=Feb}` is stored, so the second rule below accepts it and never
asks the projection. And with `Money` expanded the host draws no such cell at
all, so the third rule has no position to ask. A fold on the row axis is
refused here too, so the `Aggregate` arm below now guards only the None-axis
aggregate.
- A cell a formula owns is refused. That is `cell_id`'s own `Derived` rule,
`formula_for(&key)`. It is asked directly because a stored record may sit on
no position in the host's current view.
+20 -2
View File
@@ -5789,8 +5789,10 @@ client sends `SetCell` (then, separately, `ClearCell`) for `{Month=Q2, _Measure=
with a caught-up basis. At `ea119bd9` both were answered `Accepted`, and the
`SetCell` stored `Month=Q2, _Measure=Cost = 7`, the non-item coordinate
improvise-gw18 exists to prevent. The test asserts `Rejected(MutationRefused)`
and an unchanged document for both. Rival: the aggregate refusal disabled. This
test and the None-axis test fail. The control is
and an unchanged document for both. Rival: the aggregate refusal disabled. At
`46e75c41` that failed this test and the None-axis test. Since improvise-b114 a
fold is refused first by the group rule, so this rival fails only the None-axis
test. Disabling both rules fails this test. The control is
`a_wire_write_to_a_stored_cell_beside_a_fold_is_accepted`, so a host that refused
every wire write cannot pass.
@@ -5833,6 +5835,22 @@ that the target projects as `Record`, so a fixture where the first column is
harmless cannot pass. The fix looks the cell up by its whole key. The rival is
the old lookup by record, which is this test's red commit, `6e021699`.
### a_wire_write_naming_a_measure_group_is_rejected_in_any_view
improvise-b114, found in the review of PR #217 at `bc060f67` (the reviewer's r3).
`_Measure` is on Column, Revenue and Cost are grouped as `Money`, and `Units` is
ungrouped. Every month is funded. A caught-up `SetCell` on
`{Month=Feb, _Measure=Money}` was answered `Accepted` and stored
`Month=Feb, _Measure=Money = 7`, a non-item coordinate. Its record `{Month=Feb}`
is stored, so the stored arm accepted it without asking the projection. The test
runs with `Money` collapsed, where the host draws that cell as the fold, and
expanded, where the host draws no such cell. It asserts `MutationRefused` and an
unchanged document in both, so a rule that asks the host's layout cannot pass
the expanded half. Red at `5eaafbae`. The fix is `App::group_refusal`, which asks
the document whether any coordinate names a group rather than an item. Rival:
the group rule removed. Injected into a full workspace run, it fails this test
and nothing else.
### a_stored_record_the_hosts_view_aggregates_is_writable
improvise-sjdb, from the #217 review. South/Revenue is stored, then `Product` is