From 12445955d59a7ef5d643d4af1eba2e40cd7802ed Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 5 Sep 2026 23:42:38 +0200 Subject: [PATCH] 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 --- .../ghostlight-world-consumer-api.md | 8 ++- .../architecture/ghostlight-world-ontology.md | 43 +++++++++--- notes/ghostlight-implementation-plan.md | 68 ++++++++++++++----- 3 files changed, 91 insertions(+), 28 deletions(-) diff --git a/docs/architecture/ghostlight-world-consumer-api.md b/docs/architecture/ghostlight-world-consumer-api.md index 44c5277..4749f38 100644 --- a/docs/architecture/ghostlight-world-consumer-api.md +++ b/docs/architecture/ghostlight-world-consumer-api.md @@ -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 diff --git a/docs/architecture/ghostlight-world-ontology.md b/docs/architecture/ghostlight-world-ontology.md index 71d43b2..6923434 100644 --- a/docs/architecture/ghostlight-world-ontology.md +++ b/docs/architecture/ghostlight-world-ontology.md @@ -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; diff --git a/notes/ghostlight-implementation-plan.md b/notes/ghostlight-implementation-plan.md index ad8a0e7..401ad7d 100644 --- a/notes/ghostlight-implementation-plan.md +++ b/notes/ghostlight-implementation-plan.md @@ -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