Record the witness operation in the docs

One speakerless event over a place subtree: recipients derived at apply
time by the one reach owner, already-holders untouched, an empty subtree
refused at both layers, confinement through the operation's place, an
affordance slot so a world can author a beacon or a blast. Schemas at
consumer.v2; the operation count aligned to the catalog's pinned count.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-05 23:42:38 +02:00
commit 12445955d5
3 changed files with 92 additions and 29 deletions

View file

@ -139,6 +139,10 @@ those subjects (`confine_to_ground`, the `PatchGround::Consumer` arm):
- a declared place, a declared route, `Relocate`, `OpenRoute`, `CloseRoute`,
and `AlterCost` are always refused — they carry a place or a route, and a
consumer's ground names no place;
- `Witness` is always refused, for the same reason: its ground is the named
place, never the acting subject, and a consumer's ground names no place.
A consumer tells what its mirror knows through `AcquireKnowledge` on its own
bound subject; it cannot cause a place-wide witnessed event;
- a `Claimed { by }` fact must name a bound subject; a `Canonical` fact
declaration is refused outright — a consumer's evidence buys an `Admit`
into its own custody and a `Claimed` fact, never a canonization;
@ -175,8 +179,8 @@ enters a log, a receipt, or the journal.
The consumer capability's tag is part of the commit digest and the
externally controlled assignment is part of the state shape, so both bump the
schema: state schema `ghostlight.world_state.consumer.v1` (state-schema
generation `world-v3`), commit schema `ghostlight.world_commit.consumer.v1`.
schema: state schema `ghostlight.world_state.consumer.v2` (state-schema
generation `world-v3`), commit schema `ghostlight.world_commit.consumer.v2`.
A store written under an earlier schema is refused, not migrated.
## Not in this pass

View file

@ -103,7 +103,9 @@ target, otherwise `ActionMismatch::DelegationNotMonotone`.
A `Fact` is an entity with a `Statement` and a `FactStanding`: `Canonical`
with evidence, or `Claimed { by }`. `Knowledge` is subject-keyed per fact with
a `Confidence` and a `KnowledgeSource { Witnessed, Told { by, via }, Evidenced }`;
a telling never overwrites a holder. A `Channel` is an entity with a
a telling never overwrites a holder. `Witnessed` is minted by an authored
patch's `AcquireKnowledge` and, with no author at all, by `Witness` (below). A
`Channel` is an entity with a
`Reach { Subjects, Place }` and a controller. Speech is an affordance whose
entry declares exactly one `Audience { Colocated, Channel }`: `exercise`
mints the `Claimed` fact through `derive_id` (one of the fixed set of `derive_id`
@ -120,6 +122,31 @@ controller lane cannot name because `ControllerRunner` holds a
`ControllerPort`, not the mailbox. `WorldMailbox::create` declares the genesis
place `commons` and stands genesis subjects there.
`Witness` is one speakerless event over a place subtree: a beacon, a bell, a
blast. It names a fact, a place, and a confidence, and no speaker at all.
Recipients are never stored; they are derived at apply time by `under_place`,
the same subtree walk `audience`'s `Reach::Place` arm uses, so a declared
broadcast area and a witnessed event cannot disagree about who is "under" a
place. Every subject standing anywhere under the place learns the fact;
whoever already holds it is untouched (`unheld`, the same never-overwrite rule
`Communicate`'s fan-out applies). The landed row is `KnowledgeSource::Witnessed`
at the authored `confidence`. A telling still occurred even into an empty room,
so `Communicate`'s empty fan-out is a legal no-op; a witnessed event is only
its reception, so a `Witness` over an empty subtree — or one where every
standing subject already holds the fact — is refused as
`Mismatch::NoOperationEffect` at resolve and `KernelError::Invariant` at apply,
the same rule at both layers. Confinement runs through `operation_ground`
returning the named place as the whole ground: an elaborator may witness only
under its own jurisdiction root, and a consumer patch may never witness at all,
because `PatchGround::Consumer` and `Jurisdiction(Uncovered)` refuse every
place. `ComponentOpKind::Witness { confidence }` puts the same operation behind
an affordance slot naming `[fact, place]` roles and no subject role, so a
world-authored affordance can cause a witnessed event with no proposer ever
choosing a speaker. Because every recipient's `knows` component changes, every
recipient's scope digest moves — nobody else's does — which is what lets a
regional or global event interrupt whoever was mid-thought under that place
(the interruption mechanism of pass 9).
Time is `now: FictionalMinutes`, moved only by `CommandBody::AdvanceTime`
from `CallerId::System(SystemCapability::Clock)`; the runtime tick submits
the constant `CLOCK_TICK_MINUTES`, never a measured duration. `world/clock.rs`
@ -205,7 +232,7 @@ under four caps — `MAX_PATCH_BYTES`, `MAX_PATCH_DECLARATIONS`,
`MAX_PATCH_OPERATIONS`, `MAX_PATCH_EVIDENCE` — and the consumer document
travels over two wire schema constants, `CONSUMER_PATCH_SCHEMA` and
`CONSUMER_RECEIPT_SCHEMA`, both `.v0`. The state and commit schemas bump to
`ghostlight.world_state.consumer.v1` and `ghostlight.world_commit.consumer.v1`
`ghostlight.world_state.consumer.v2` and `ghostlight.world_commit.consumer.v2`
(state-schema generation `world-v3`); a store written under an earlier schema
is refused, not migrated.
@ -254,8 +281,8 @@ is the typed component diff against the fresh components plus the
knowledge row, and never another subject's snapshot. The re-lowering's
provider request is a distinct round (`interpreter_round`) with its own
content-addressed request id, so it cannot collide with the first lowering's
request. State schema is `ghostlight.world_state.consumer.v1`, commit schema
`ghostlight.world_commit.consumer.v1`, controller work `controller_work.v11`,
request. State schema is `ghostlight.world_state.consumer.v2`, commit schema
`ghostlight.world_commit.consumer.v2`, controller work `controller_work.v11`,
Persona turn receipt `ghostlight.persona_turn_receipt.v3`; earlier stores and
earlier rows are refused. Ghostlight owns a conserved narrative ledger;
Delvehold owns the economy (`delvehold-forced-ontology-integration.md`).
@ -404,7 +431,7 @@ Authority grant(subject, scope, kind), revoke
Selection open_office, close_office, install(office, incumbent), vacate
Redress open_forum(kind, forum, standing), close_forum
Knowledge acquire(subject, fact, source, confidence), communicate(speaker, fact,
channel), forget
channel), witness(fact, place, confidence), forget
Channel set_reach, set_controller
Commitment create(subject, counterparty, kind, due, period, checks), discharge
Pressure advance, reduce, resolve
@ -413,7 +440,7 @@ Retirement retire(referent, reason)
Time advance(minutes)
```
Twenty-seven operations. Adding one is a design change to this document and a
Twenty-nine operations. Adding one is a design change to this document and a
code change to the reducer; it is never a runtime patch.
## Patch construction
@ -468,7 +495,7 @@ counts do not appear here and may not be added.
The kernel owns component operations. A **world** owns its affordance catalog.
This is the generality lever: a setting with spellcraft, insurance claims,
oath-rhythms, or memetic sovereignty authors affordances from the same
twenty-seven operations, and the kernel never learns a genre.
twenty-nine operations, and the kernel never learns a genre.
```text
Affordance
@ -766,7 +793,7 @@ Deleted before replacement behavior is added, with no compatibility path:
## Subtraction budget
- 20 component kinds → 12; 18 `WorldMutation` variants → 27 named operations
- 20 component kinds → 12; 18 `WorldMutation` variants → 29 named operations
under 1 patch primitive, replacing both the mutation enum and the separate
outcome-effect sum;
- 5 qualification and verification types plus 1 outcome resolver → 0;

View file

@ -99,8 +99,8 @@ projection of committed history.
Interpreter does not accept, reject, or commit world state.
5. Structural invalidity is rejected deterministically before persistence and
without a model verifier. A failed batch changes nothing.
6. Private knowledge remains scoped until an explicit communication command
changes it.
6. Private knowledge remains scoped until an explicit communication command or
a witnessed event under the subject's place changes it.
7. External consumers retain sovereignty. Ghostlight views, proposals, and
acknowledgements cannot mutate external-owned state.
8. Sparse causal sufficiency governs structural validity; counts and
@ -429,29 +429,61 @@ a live provider — none exists yet for the narrative lane's re-lowering
round, so the next road run is the first evidence of `interpreter_round`
producing a real second request.
### 10. Witnessed events over a place subtree — in design
### 10. Witnessed events over a place subtree — landed
Objective: stories that carry regional and global effects. An asteroid is
witnessed by everyone under a region's root; the moon going dark by everyone
under a hemisphere's; a subject standing in a village under both sees both.
Current mechanism: knowledge enters a subject through `AcquireKnowledge`
(one operation per subject), through speech fan-out (`Communicate`, which
names a speaker and follows a channel's `Reach`), or through evidence. An
event with no speaker that a thousand people saw costs a thousand operations
and hits the patch caps.
Intended change: one operation, `Witness { fact, place }`, that lands
Landed mechanism: `ComponentOp::Witness { fact, place, confidence }` lands
`Witnessed` knowledge of the fact on every subject positioned anywhere under
the place, recipients derived at apply time from live positions exactly as
speech fan-out derives its audience, never stored. Every witness's scope
digest moves, which is what makes a global event interrupt whoever was
mid-thought (step 9).
the place. Recipients are never stored; `ResolvedOp::Witness`'s apply arm
derives them at apply time through `under_place`, the same subtree walk
`audience`'s `Reach::Place` arm now calls, so a declared broadcast area and a
witnessed event share one definition of "under a place". Whoever already
holds the fact is dropped through `unheld`, factored out of `fan_out`'s old
inlined filter so `Communicate` and `Witness` cannot disagree about who
already knows. A telling still occurs into an empty room, so `Communicate`'s
empty fan-out stays a legal no-op; a witnessed event is only its reception,
so a `Witness` over an empty subtree, or one where every standing subject
already holds the fact, is refused as `Mismatch::NoOperationEffect` at
resolve (over the candidate graph, including this patch's own relocations)
and `KernelError::Invariant("a witness reaches nobody who does not already
hold the fact")` at apply — one rule, both layers. Confinement runs through
one new `operation_ground` arm returning the named place as the whole
ground: an elaborator may witness only under its own jurisdiction root, and a
`PatchGround::Consumer` patch can never witness, because its ground names no
place. Every recipient's scope digest moves and nobody else's does, which is
what makes a regional or global event interrupt whoever was mid-thought
(step 9).
Cut line: no second fan-out owner; no stored recipient list; a witness names
no speaker; the subtree walk reuses the one covering predicate; the tool
surface and exemplar lists grow by exactly one; confinement treats the place
like any other operation ground.
Beyond the operation itself, an affordance slot,
`ComponentOpKind::Witness { confidence }` with roles `[fact, place]` and no
subject role, was not anticipated by this plan's text but was needed to let
a world-authored affordance (a bell, a beacon) cause a witnessed event
without a proposer ever naming a speaker; the source is `Witnessed` by
construction, since `ComponentOp::Witness` has no field a teller could enter.
One `PATCH_TOOLS` entry, `witness`, rounds out the model-facing surface.
Both store schemas moved to `ghostlight.world_state.consumer.v2` and
`ghostlight.world_commit.consumer.v2`; a store written under the prior schema
is refused, not migrated.
Cut line held: one fan-out owner (`under_place`), shared by `audience` and
`Witness`; one never-overwrite owner (`unheld`), shared by `fan_out` and
`Witness`; no stored recipient list; a witness names no speaker, so `Told`
stays unrepresentable outside `Communicate`; no new `Mismatch`,
`KernelError` variant, `CommandBody`, `SystemCapability`, or
`CompositeShape`; `verify_state_shape`, `can_broadcast`, and
`candidate_in_audience` untouched.
What is undone: the affordance-declaration refusal for a slot whose second
role binds a subject is proved directly through `arity` and
`role_kind_fits`, not by submitting a malformed `AffordanceDeclaration`
through admission — the gate is the one gate either way, but the submission
path itself is untested. The grouped lane's cost is untouched: a
region-wide or global witness still discards a grouped cell's in-flight work
for every constituent it touches, which is the debt this pass explicitly
declined to pay.
## Subtraction budget