diff --git a/HANDOFF-gpt55-2026-06-20.md b/HANDOFF-gpt55-2026-06-20.md index 816aaf8e..5da794b6 100644 --- a/HANDOFF-gpt55-2026-06-20.md +++ b/HANDOFF-gpt55-2026-06-20.md @@ -769,3 +769,44 @@ counts. That PR is the explicit authorization boundary for this rebaseline. ### Workbench / UI Alignment Scope Note The alignment scope for the CORE Workbench/UI under the latest proposal-first pivots is documented in `docs/sessions/workbench-proposal-first-alignment-scope-2026-06-20.md`. This is a docs-only session note that identifies UI gaps, non-goals, future acceptance criteria, and next PR briefs to align the frontend observatory with the backend's proposal-first and contract assessment authority architecture. + +--- + +## Unary state-change delta authorization pointer + +The 2026-06-21 design investigation found that broad +`state_change.transition` is not one safe organ. It combines unary delta, +binary transfer, containment movement, and before/after state topology. + +Authorized for one future diagnostic-only implementation PR: + +- `state_change.unary_delta` only; +- exact cue inventory `gained → increase`, `lost → decrease`; +- one exact scalar/object `quantity_entity` edge; +- one family-local exact action-cue record; +- `ConstructionProposal(status="proposed")` before binding; +- `ContractAssessment` as the sole runnable/refused authority; +- `diagnostic_only=True`, `serving_allowed=False`. + +Explicitly not authorized: broad `state_change.transition`, transfer, +containment, before/after state, measurement delta, actor/owner inference, +coreference, cross-sentence binding, arithmetic, answers, serving, derivation +ledger integration, or quantity-family widening. + +Controlling artifacts: + +- `docs/sessions/unary-state-change-delta-authorization-2026-06-21.md` +- `docs/sessions/unary-state-change-delta-preflight-2026-06-21.md` +- `docs/specs/foundational-families/unary-state-change-delta.md` + +The next PR should implement the preflight's copy-paste brief. Stop if it +requires changes to `_quantity_entity_proposals()`, +`_quantity_kind_dispositions()`, `generate.derivation.state`, actor/owner +parsing, a broader cue inventory, synthetic spans, serving paths, or any +non-empty wrong-ID list. + +Validation performed in the fresh worktree: startup guard passed with +`HEAD == origin/main == ca83d384`; baseline and post-doc +`uv run python -m core.cli test --suite smoke -q` completed successfully; +requested positive/confuser ProblemFrame probes were run read-only; +`git diff --check` passed. No subagents were used. diff --git a/docs/sessions/unary-state-change-delta-authorization-2026-06-21.md b/docs/sessions/unary-state-change-delta-authorization-2026-06-21.md new file mode 100644 index 00000000..c494be39 --- /dev/null +++ b/docs/sessions/unary-state-change-delta-authorization-2026-06-21.md @@ -0,0 +1,477 @@ +# Authorization: Unary State-Change Delta Foundational Slice + +**Status:** Authorized for one future diagnostic-only implementation PR after +this authorization PR merges +**Date:** 2026-06-21 +**Authorized family:** `state_change.unary_delta` +**Broad family:** `state_change.transition` remains unauthorized and +unimplemented +**Serving status:** `diagnostic_only=True`, `serving_allowed=False` +**Governing doctrine:** ADR-0223, ADR-0224, PRs #851 and #853 + +## 1. Decision + +Do not implement the broad `state_change.transition` family next. + +Authorize only `state_change.unary_delta`: one explicit, local, count-valued +gain or loss event over one exactly grounded quantity/object pair. The first +cue inventory is deliberately closed: + +| Exact cue | Direction disposition | +|---|---| +| `gained` | `increase` | +| `lost` | `decrease` | + +The authorized construction recognizes that an explicit delta event is locally +present. It does not construct a before state, after state, owner state, +transfer, containment move, arithmetic equation, answer, or serving action. + +The safe first relation is: + +```text +one exact action cue ++ one exact scalar mention ++ one exact changed-object mention ++ one exact local quantity_entity edge +------------------------------------------------ +one proposed unary-delta relation, assessed diagnostically +``` + +This is the exact capability unlocked by the hardened quantity-entity seam: +CORE can now distinguish a quantity-bearing object from an unbound number. The +next valid step is to bind that grounded pair to one explicit local change cue, +not to parse an entire story. + +## 2. Serious design finding + +`state_change.transition` is not one organ. The current constitutional spec +combines at least four distinct geometries: + +1. unary delta: `gained/lost N object`; +2. containment movement: `put/took N object in/from container`; +3. binary transfer: `gave/sent N object to target`; +4. before/after state relation: `was N, now M`. + +They do not share the same role topology. Unary delta has one changed object and +one signed delta. Transfer has at least two state owners plus a conservation +relation. Containment movement has source/target containers. Before/after has +two temporal states and may have no delta cue at all. Treating these as one +family would force optional roles to carry the semantics and would make illegal +partial states representable. + +The current code confirms the split: + +- `ProblemFrame` can expose exact scalar/object mentions and + `MentionBinding(binding_type="quantity_entity")` for `Ana gained 3 marbles.`; +- it does not expose a typed exact event/action cue or a bound unary-delta + relation; +- it already contains a separate narrow `BoundRelation(relation_type="transfer")` + regex path for `Tom gave Ana 3 apples.`; +- `generate.derivation.state` contains a legacy arithmetic-oriented + `SemanticLedger`, but it uses a different raw-text path, float quantities, + loose subject/pronoun continuation, and a replay bridge to derivation + candidates. It is not suitable as exact `ProblemFrame` grounding evidence. + +Reusing the transfer relation would silently define every change as transfer. +Reusing the semantic ledger would bypass the proposal-first substrate and +couple diagnostic recognition to arithmetic candidate production. Both are +rejected. + +## 3. Map: directions considered + +The intrinsic space is not a story. It is a typed local event relation over an +already grounded scalar/object edge, with exact source coordinates and a +conjugate assessment that refuses incomplete topology. + +| Direction | Structural effect | Decision | +|---|---|---| +| Implement broad `state_change.transition` | Mixes unary delta, transfer, containment, and temporal-state topology | Rejected | +| Reuse `generate.derivation.state.SemanticLedger` | Bypasses `ProblemFrame`; imports legacy raw parsing and arithmetic candidate semantics | Rejected | +| Add event-cue proposals only, with no closable relation | Safe but does not prove the new quantity/entity seam composes into a useful organ | Deferred as an internal stage, not the family boundary | +| Add one unary-delta relation over exact local evidence | Preserves proposal → grounding → assessment and has a closed first topology | Selected | + +## 4. Safe capability ladder + +The broad family is split into separately authorized rungs: + +| Rung | Family | Status after this PR | +|---|---|---| +| SCT-0 | exact local action-cue publication | Internal prerequisite to SCT-1; no independent serving or assessment authority | +| SCT-1 | `state_change.unary_delta` | Authorized for one future diagnostic implementation PR | +| SCT-2 | containment movement | Not authorized | +| SCT-3 | binary transfer | Not authorized | +| SCT-4 | before/after state relation | Not authorized | +| SCT-5 | arithmetic state equation or answer production | Not authorized | + +`state_change.transition` remains the broad constitutional umbrella only. Its +existing foundational registry entry must remain +`implementation_authorized=False`. + +## 5. Why now, and why only after #853 + +#851 proved that a local quantity/object edge can be proposed, grounded, and +assessed. #853 then closed two authority leaks: + +- `ConstructionProposal` can represent only `status="proposed"`; +- quantity-kind dispositions are emitted only inside their exact proposal + boundary, so no-proposal percent, rate, pronoun, list, or transfer confusers + receive quantity-family authority. + +Those corrections are prerequisites. A state-change slice built before #853 +could have mistaken proposal state for an assessment verdict or consumed a +quantity-kind disposition synthesized for a confuser. This authorization +therefore treats #853's hardened seam as immutable: + +- no proposal carries `runnable`, `refused`, missing-role, or hazard authority; +- `ContractAssessment` is the sole diagnostic runnable/refused authority; +- the unary family must not widen `_quantity_entity_proposals()`; +- the unary family must not widen `_quantity_kind_dispositions()`; +- a raw `MentionBinding` may be consumed as grounding evidence, but the + `binding.quantity_entity` proposal and assessment are not backdoored. + +## 6. Family metadata + +| Field | Authorized value | +|---|---| +| `family_id` | `state_change.unary_delta` | +| display name | Unary state-change delta | +| relation type | `unary_delta` | +| candidate organ | `unary_delta_transition` | +| proposal status | Always `proposed` | +| diagnostic posture | `diagnostic_only=True` | +| serving posture | `serving_allowed=False` | +| implementation authorization | Authorized only by this merged document for one future bounded PR | +| implementation status after this docs PR | Unimplemented; no runtime registry/catalog/dispatch change | +| relation to `binding.quantity_entity` | Consumes a compatible exact local `quantity_entity` edge; does not require or create its proposal, disposition, or assessment | + +The future implementation may add a new descriptive foundational registry +entry for `state_change.unary_delta`. It must not flip the existing broad +`state_change.transition` entry to authorized. + +## 7. Authority gradient + +The only authorized flow is: + +```text +exact lexical cue (`gained` or `lost`) +→ ConstructionProposal( + family_id="state_change.unary_delta", + status="proposed", + diagnostic_only=True, + serving_allowed=False, + ) +→ exact GroundedActionCue +→ existing exact GroundedScalar / GroundedMention / quantity_entity binding +→ BoundRelation(relation_type="unary_delta") +→ assess_unary_delta(frame) +→ ContractAssessment(runnable or refused) +``` + +The proposal is a hypothesis only. Exact role bindings ground it. The +assessment is its corrective/conjugate and is the only runnable/refused +authority. No step derives an answer. + +## 8. Minimum evidence tuple + +A proposed candidate may close only from this tuple: + +```text +( + action_cue_span, + action_kind, + explicit_direction, + delta_quantity_mention, + changed_object_mention, + quantity_entity_binding, +) +``` + +Every member is source-backed. Every `SourceSpan` must satisfy: + +```text +problem_text[span.start:span.end] == span.text +``` + +No composite or synthetic span may be manufactured by concatenating role +surfaces. A contiguous event envelope may be computed as offsets for locality +checking, but it is not a new semantic fact and must not replace the individual +role spans. + +## 9. Required roles + +| Role | Requirement | +|---|---| +| `action_cue` | Exactly one exact `gained` or `lost` span | +| `changed_object` | Exactly one local object mention targeted by the selected quantity/entity edge | +| `delta_quantity` | Exactly one source-grounded scalar mention | +| `direction` | Required in SCT-1 because the authorized cue itself explicitly states `increase` or `decrease`; it may not be inferred from context | +| `local_binding_relation` | Exactly one `MentionBinding(binding_type="quantity_entity")` joining the selected delta to the changed object | +| `local_event_containment` | Cue, quantity, and object occur in that order in one sentence/clause with no sentence boundary | +| `provenance_span` | Exact action, quantity, object, and binding evidence spans | + +SCT-1 is count-valued only. A quantity/unit/object span collision, including +`3 pounds` where `pounds` is both unit and object under current extraction, +refuses. Measurement-valued state change requires a separate extension after +unit/property topology is designed. + +## 10. Optional and deferred roles that must not be invented + +`actor` is the only optional SCT-1 role. It may be carried only when an exact +actor mention is already grounded by the existing frame; the implementation is +not authorized to widen actor extraction to obtain it. Its absence does not +block the relation and does not license an owner inference. + +The following are optional in the broader state-change problem space but are +not representable by SCT-1 at all: + +- source; +- target; +- before quantity; +- after quantity; +- container or location; +- temporal marker; +- unit/measurement property. + +Their presence therefore refuses or routes to a future family. In particular, +`Ana` in `Ana gained 3 marbles.` is not automatically asserted as a state owner. +SCT-1 asserts only that the local text contains an increase cue over the +explicitly quantified object `marbles`. + +## 11. Non-inference rule + +The family cannot infer or compute: + +- an implicit source or target; +- a pronoun antecedent; +- a cross-sentence event binding; +- an initial, before, final, or after quantity; +- a total, remainder, result, or answer; +- a transfer partner or conservation law; +- ownership or possession continuity; +- a containment path; +- arithmetic sign application (`+N` or `-N`) to stored state; +- that `gave` subtracts from a giver or adds to a receiver; +- that `put` moves an object into a container; +- that `bought`, `received`, `ate`, `spent`, `added`, or `removed` are synonyms + for an authorized cue; +- any event hidden behind passive voice, negation, modality, or temporal order. + +Unknown is not false. Missing or ambiguous evidence refuses. + +## 12. Relationship to quantity/entity grounding + +The relationship is monotonic but non-authoritative: + +1. Existing `GroundedScalar`, `GroundedMention`, and + `MentionBinding(binding_type="quantity_entity")` may ground the delta and + changed-object roles. +2. The presence of such a binding does not imply an event. `There are 12 + apples.` remains only a quantity/entity construction. +3. The presence of an event cue does not imply a valid delta relation. `Ana + gained apples.` lacks an explicit quantity and must refuse. +4. A unary-delta proposal does not create a `binding.quantity_entity` proposal, + `QuantityKindDisposition`, or quantity-entity assessment. +5. The quantity-entity family does not create a unary-delta proposal. +6. No-proposal quantity-entity confusers must continue to have no + quantity-kind disposition, exactly as hardened by #853. + +## 13. Interaction safety with existing families + +The future implementation must preserve these families byte-for-byte in +behavior and authority: + +- `proportional_change.decrease_to_fraction`; +- `partition.percent_partition`; +- `binding.quantity_entity`. + +Unary delta must not propose when any of these competing surfaces are active: + +- percent or percentage; +- rate, `per`, or `each`; +- comparison (`more than`, `less than`, `fewer than`, `times as`); +- proportional `decrease to`, `decrease by`, or fraction-scale language; +- plain quantity/entity binding with no authorized action cue; +- transfer, transaction, or containment movement; +- list/enumeration topology; +- more than one state-change cue. + +The generic `consumption` process-frame candidate may coexist because `gained` +and `lost` currently surface it. No other process-frame family may coexist in +the first slice. The process frame is candidate context only; it is not the +proposal, relation, or assessment authority. + +## 14. ContractAssessment obligations + +The future `assess_unary_delta(frame)` may report `runnable=True` only when: + +- exactly one unary-delta proposal exists; +- exactly one authorized exact action cue exists; +- exactly one scalar mention and one compatible quantity/entity binding exist; +- the binding selects exactly one changed object; +- the cue direction is explicit and unambiguous; +- cue, delta, and object are ordered locally in one sentence/clause; +- all role and relation spans are exact slices of `problem_text`; +- no unit/object collision exists; +- no competing family, actor coordination, object list, negation, modality, + passive voice, temporal ambiguity, or second change cue is present. + +The proposal must remain `status="proposed"` whether the assessment is runnable +or refused. A proposal-free frame must not dispatch the unary-delta contract. +The assessment grants diagnostic readiness only, never serving authority. + +## 15. Stable refusal taxonomy + +The first implementation must use stable organ-specific blocker/hazard codes. +The exact storage field follows current `ContractAssessment` conventions; +missing structural evidence belongs in `missing_bindings`, while active +ambiguity/context belongs in `unresolved_hazards`. + +| Class | Required code | Expected locus | +|---|---|---| +| missing event cue | `unary_delta_proposal_required` / no proposal | No dispatch | +| missing quantity | `delta_quantity_unbound` | Proposed then refused | +| missing changed object | `changed_object_unbound` | Proposed then refused | +| multiple actors/coordination | `multiple_actor_surface` | No proposal or refused | +| multiple objects/list | `changed_object_ambiguous` / `list_context_ambiguous` | No proposal or refused | +| ambiguous direction | `delta_direction_ambiguous` | Refused | +| source/target ambiguity | `source_target_unlicensed` | Refused | +| pronoun without exact antecedent | `pronoun_antecedent_unresolved` | Refused | +| cross-sentence event | `cross_sentence_event` | Refused | +| implicit total | `implicit_total_unlicensed` | No proposal or refused | +| comparative phrasing | `comparative_context` | No proposal | +| rate phrasing | `rate_context` | No proposal | +| percent phrasing | `percent_context` | No proposal | +| unit conflict | `unit_object_conflict` | Refused | +| passive voice | `passive_voice_unsupported` | Refused | +| temporal ambiguity | `temporal_context_ambiguous` | Refused | +| quantity/entity binding but no event | no unary proposal or assessment | No dispatch | +| event cue but no grounded pair | `local_binding_relation_unbound` | Proposed then refused | +| transfer verb without explicit roles | `transfer_unlicensed` / no unary proposal | No dispatch | +| two state changes | `multiple_state_change_cues` | No proposal | +| chained state changes | `chained_state_changes` | No proposal | +| inexact/synthetic evidence | `provenance_span_inexact` | Refused | +| negated or modal event | `event_assertion_unlicensed` | Refused | + +No blocker may be silently repaired by choosing the nearest mention. + +## 16. Tiny positive seed set + +These are illustrative future fixtures, not implementation in this PR: + +| Surface | Expected diagnostic roles | +|---|---| +| `Ana gained 3 marbles.` | cue=`gained`, direction=`increase`, delta=`3`, changed_object=`marbles` | +| `The jar lost 2 cookies.` | cue=`lost`, direction=`decrease`, delta=`2`, changed_object=`cookies` | +| `The team gained 4 points.` | cue=`gained`, direction=`increase`, delta=`4`, changed_object=`points` | + +The subject is not asserted as owner or actor. The result is not computed. + +## 17. Negative and confuser set + +The future PR must prove these dispositions: + +| Surface | Required result | +|---|---| +| `There are 12 apples.` | No unary proposal: binding exists, event absent | +| `Tom has 12 apples.` | No unary proposal: state description, not change | +| `Tom gave Ana 3 apples.` | No unary proposal: binary transfer is deferred | +| `Tom gave her 3 apples.` | No unary proposal: transfer plus unresolved pronoun | +| `Tom gave Ana some apples.` | No unary proposal: no explicit quantity | +| `Tom had 12 apples and gave Ana 3.` | No unary proposal: transfer and unbound changed object | +| `Ana has 3 more apples than Tom.` | No unary proposal: comparison | +| `20% of the apples are red.` | No unary proposal: percent partition surface | +| `3 apples per child.` | No unary proposal: rate | +| `Tom bought 3 apples and 2 oranges.` | No unary proposal: transaction and list ambiguity | +| `The basket was filled with apples.` | No unary proposal: passive containment, no quantity | +| `The tank has 84 degrees.` | No unary proposal; existing quantity assessment remains refused | +| `There are 12 apples. Tom ate 3.` | No unary proposal: disallowed cue and cross-sentence binding | +| `Tom and Ana each got 3 apples.` | No unary proposal: multiple actors/rate-like `each` | +| `Tom moved 3 apples from the box to the bag.` | No unary proposal: containment movement | +| `The box now has 5 more pencils.` | No unary proposal: comparison/current-state surface | +| `Tom put 4 shells in the bag.` | No unary proposal: containment movement | +| `Ana gained apples.` | Proposal may exist; assessment refuses missing quantity | +| `Ana gained 3.` | Proposal may exist; assessment refuses missing object/binding | +| `Ana gained 3 apples and lost 2.` | No proposal: two/chained state-change cues | +| `Ana did not gain 3 apples.` | No proposal because `gain` is outside v1; future widening must also preserve negation refusal | +| `3 apples were gained by Ana.` | Proposal may exist; assessment refuses passive/order topology | +| `Ana gained 3 pounds.` | Proposal may exist; assessment refuses unit/object collision in count-only SCT-1 | + +## 18. Explicit non-authorization + +This PR does not authorize: + +- `state_change.transition` implementation; +- the existing broad `state-change.md` required-role topology; +- source/target, ownership, possession, or conservation modeling; +- `gave`, `received`, `bought`, `sold`, `ate`, `spent`, `put`, `took`, `moved`, + `added`, or `removed` cues; +- initial/final or before/after state binding; +- measurement-valued delta events; +- multiple events, multiple quantities, multiple objects, or coordinated actors; +- pronoun/coreference resolution; +- cross-sentence inference; +- `SemanticLedger` integration or derivation candidate production; +- arithmetic, answer production, eval score movement, report mutation, serving, + teaching, memory, policy, identity, recall, Vault, field, or algebra changes. + +## 19. Future implementation gates + +The implementation PR may begin only after this authorization and its preflight +map merge. It must: + +1. start from fresh `origin/main` and pass the startup guard; +2. add `state_change.unary_delta` without authorizing + `state_change.transition`; +3. introduce only the minimum exact action-cue type and ProblemFrame publication + path defined by the preflight; +4. preserve proposal-first ordering and #853's proposal schema; +5. keep all three current proposal-first families green; +6. prove deterministic replay and exact spans; +7. prove every confuser above refuses or does not propose as specified; +8. preserve train and holdout `wrong_ids == []` without updating reports; +9. avoid all serving and derivation paths; +10. stop if the positive cases require actor parsing, coreference, a broad verb + parser, synthetic spans, or quantity-family widening. + +## 20. Stop conditions + +Stop implementation and return to design if any of these becomes necessary: + +- changing `_quantity_entity_proposals()` or `_quantity_kind_dispositions()`; +- consuming `QuantityKindDisposition` without its quantity-family proposal; +- importing `generate.derivation.state` into the ProblemFrame path; +- adding a generic verb/event parser or universal event IR; +- inferring an actor, owner, source, target, initial value, or final value; +- accepting more than the exact `gained`/`lost` cue inventory; +- touching serving, eval/report artifacts, teaching, policy, identity, recall, + Vault, field, or algebra; +- producing a runnable assessment with inexact, synthetic, ambiguous, or + cross-sentence evidence; +- any non-empty wrong-ID list. + +## 21. Justification: the masterstroke + +The masterstroke is the refusal to model a story. The new organ models a local +field differential: a direction-bearing action cue is conjugated with one +grounded delta/object edge. The proposal propagates a hypothesis; exact role +bindings reconstruct its coordinates; `ContractAssessment` opposes and +corrects every incomplete topology. + +This preserves the intrinsic geometry. Unary delta, transfer, containment, and +before/after state are not optional-role variants of one object. They are +different manifolds and receive separate future contracts. The resulting slice +is small enough to make illegal states unrepresentable, yet powerful enough to +establish the first event-bearing composition over the quantity/entity seam. + +## 22. Remaining risks discovered + +- `docs/sessions/workbench-proposal-first-alignment-scope-2026-06-20.md` + predates #853 and still describes proposal statuses such as `partial`, + `closed`, and `refused`. That text is stale and must not be copied into this + implementation. Updating Workbench docs is a separate PR. +- The legacy semantic-state ledger is semantically adjacent and therefore a + likely source of accidental coupling. The implementation preflight requires + an explicit no-import/no-call proof. +- Existing mention extraction does not ground the subject of `gained/lost` as + an actor. This is a deliberate first-slice limit, not a gap to repair in the + implementation PR. diff --git a/docs/sessions/unary-state-change-delta-preflight-2026-06-21.md b/docs/sessions/unary-state-change-delta-preflight-2026-06-21.md new file mode 100644 index 00000000..829dfe3a --- /dev/null +++ b/docs/sessions/unary-state-change-delta-preflight-2026-06-21.md @@ -0,0 +1,580 @@ +# Preflight: `state_change.unary_delta` Implementation Map + +**Status:** Design complete; implementation remains future work +**Date:** 2026-06-21 +**Controlling authorization:** +`docs/sessions/unary-state-change-delta-authorization-2026-06-21.md` +**Target future PR:** `feat(kernel): introduce diagnostic unary-delta proposal seam` + +## 1. Preflight result + +The implementation is feasible without a broad story parser, but only after +splitting `state_change.transition` into the narrower +`state_change.unary_delta` family. + +The current substrate already carries the scalar, object, and exact +`quantity_entity` edge needed by the two seed positives. It does not carry an +exact typed action cue. The minimum necessary type addition is therefore one +family-local cue record, not a universal event IR. + +No implementation is present in this documentation PR. + +## 2. Current relevant code map + +### 2.1 Descriptive family gate + +`generate/foundational_families.py` + +- contains exactly two current registry entries; +- marks `binding.quantity_entity` implemented/authorized; +- keeps broad `state_change.transition` proposed and + `implementation_authorized=False`; +- is descriptive metadata only and is not runtime dispatch. + +Future action: add a third exact entry for `state_change.unary_delta`. Do not +authorize or repurpose `state_change.transition`. + +### 2.2 Construction proposal catalog + +`generate/construction_affordances.py` + +- `ConstructionProposal.status` is structurally restricted to `"proposed"`; +- proposals cannot store missing roles or active hazards; +- all catalog entries are diagnostic-only and serving-disallowed; +- `propose_construction(family_id, evidence_spans)` has hypothesis-only inputs; +- `make_proposal()` rejects all current catalog families and must remain a + loud legacy fence. + +Future action: add `_UNARY_DELTA_FAMILY`, catalog it, and include it in the +proposal-first set. Do not change proposal schema or `make_proposal()`. + +### 2.3 ProblemFrame types + +`generate/problem_frame.py` + +- carries exact mentions, bindings, bound relations, proposals, and + quantity-kind dispositions; +- has no event/action cue record; +- has no unary-delta-specific state; +- `QuantityKindDisposition` is family-local to quantity/entity and is not + available for state-change confusers after #853. + +Future action: add one family-local `GroundedUnaryDeltaCue` and a +`ProblemFrame.unary_delta_cues` tuple with a matching builder method. Do not +widen `MentionKind` to a generic `event`/`action` universe. + +### 2.4 Frame builder + +`generate/problem_frame_builder.py::build_problem_frame` + +Current order: + +```text +scalars / units / hazards / process-frame candidates +→ proposal-first construction proposals +→ mentions / bindings / quantity disposition / bound relations / target +→ initial ProblemFrame +→ ContractAssessment dispatch +``` + +Relevant existing facts: + +- `Ana gained 3 marbles.` produces one `consumption` process-frame candidate, + one exact scalar mention, one exact object mention, and one exact + `quantity_entity` binding; it produces no proposal or bound relation. +- `The jar lost 2 cookies.` has the same topology. +- `_quantity_entity_proposals()` intentionally refuses any active process + frame, so the state-change positive has no quantity-family proposal. +- `_quantity_kind_dispositions()` now requires exactly one + `binding.quantity_entity` proposal. This is the #853 hardening boundary and + must not be widened. +- `_TRANSFER_RE` separately constructs a `transfer` relation for a narrow + `gave` surface. It must not be reused as unary state change. + +Future action: add one cue-proposal helper before mention binding, then publish +the exact cue and unary relation after mentions/bindings exist. + +### 2.5 Contract assessment + +`generate/problem_frame_contracts.py` + +- `assess_contracts()` dispatches current catalog contracts only from an + existing family proposal; +- dedicated assessment functions hold family-specific closure logic; +- `ContractAssessment` is the sole runnable/refused authority; +- output ordering is deterministic by `candidate_organ`. + +Future action: register `unary_delta_transition`, implement +`assess_unary_delta(frame)`, and dispatch only when the exact proposal exists. + +### 2.6 Process-frame candidates + +`generate/process_frames.py` + +- the broad `consumption` candidate frame includes both `gain*` and `lose*`; +- its own `not_licensed` declarations forbid deciding gain/loss without exact + surface evidence and forbid computing remaining quantities; +- it is candidate context, not a grounded event role or contract. + +Future action: allow the existing `consumption` candidate to coexist, but do +not treat it as cue, proposal, relation, or closure authority. + +### 2.7 Legacy semantic-state derivation path + +`generate/derivation/state/*` + +- already models `set`, `gain`, and `loss` transitions for accumulation; +- parses raw clauses independently of `ProblemFrame`; +- stores numeric values as float-backed `SemanticQuantity` rather than exact + `GroundedScalar` fractions; +- permits loose pronoun continuation; +- replays to `GroundedDerivation` candidates. + +Future action: none. The new diagnostic family must not import, call, wrap, +compare against, or emit `SemanticLedger`, `StateTransition`, or +`GroundedDerivation`. + +### 2.8 Callers and consumers + +The import/call-site sweep found: + +- `build_problem_frame()` and `assess_contracts()` are consumed by focused + tests plus `scripts/gsm8k_problem_frame_adequacy.py` and + `scripts/gsm8k_substrate_morphology.py`; +- no `calibration/` module directly consumes this seam; +- no serving module directly imports `problem_frame_contracts`; +- current proposal-first tests enforce proposal ordering, exact spans, + diagnostic posture, and proposal-gated dispatch. + +The future PR must preserve these callers and must not add a serving import. + +## 3. Required type addition + +Add only this family-local shape in `generate/problem_frame.py`: + +```python +UnaryDeltaDirection = Literal["increase", "decrease"] +UnaryDeltaActionKind = Literal["gain", "loss"] + +@dataclass(frozen=True, slots=True) +class GroundedUnaryDeltaCue: + cue_id: str + surface: str + action_kind: UnaryDeltaActionKind + direction: UnaryDeltaDirection + span: SourceSpan +``` + +Construction obligations: + +- `surface` must equal `span.text`; +- `gained` must map only to `action_kind="gain"`, + `direction="increase"`; +- `lost` must map only to `action_kind="loss"`, + `direction="decrease"`; +- no other cue value is constructible; +- the frame carries cues as `tuple[GroundedUnaryDeltaCue, ...]` in stable source + order. + +This type is intentionally not: + +- a generic `EventMention`; +- a verb parse tree; +- an actor/source/target model; +- a temporal graph; +- a `SemanticLedger` transition; +- an arithmetic operator. + +Do not add a new `SubstrateFact` variant or widen `MentionKind` in the first PR. + +## 4. Closed cue inventory and recognition + +The future builder owns one immutable mapping: + +```text +gained → (gain, increase) +lost → (loss, decrease) +``` + +Recognition rules: + +1. enumerate exact lexical-boundary occurrences using the existing lexical + boundary helper style; +2. zero hits: no proposal; +3. more than one hit, including repeated identical cues: no proposal; +4. one hit: use only its exact `SourceSpan` to create the proposal; +5. do not capture surrounding grammar or synthesize an event chunk; +6. block proposal creation when an explicit competing surface is already + visible: percent, rate/per/each, comparison, transfer, transaction, + containment, list coordination, or multiple event cues; +7. the only allowed process-frame set is empty or exactly `{"consumption"}`; + the two authorized cues currently produce `consumption`. + +The proposal helper must be a closed lexical recognizer, not a broad regex with +verb, subject, object, source, target, and clause capture groups. + +## 5. Required frame publication path + +The future build order is fixed: + +```text +1. Existing scalar/unit/hazard/process-frame extraction +2. Detect exactly one authorized cue span +3. ConstructionProposal(status="proposed") from cue span only +4. Existing mention and MentionBinding extraction +5. GroundedUnaryDeltaCue from the proposal-backed exact cue +6. BoundRelation(relation_type="unary_delta") from cue + existing binding +7. ProblemFrame publication +8. Proposal-gated ContractAssessment +``` + +The unary relation uses existing `BoundRelation` / `BoundRole`: + +| Bound role | Target | +|---|---| +| `action_cue` | `GroundedUnaryDeltaCue.cue_id`, kind `unary_delta_cue` | +| `delta_quantity` | selected quantity `GroundedMention.mention_id` | +| `changed_object` | selected object `GroundedMention.mention_id` | + +`direction` is an explicit field of the exact cue. The construction catalog +still declares direction as a required semantic obligation; assessment checks +the cue's typed disposition. + +Relation publication requirements: + +- exactly one `quantity_entity` binding; +- its source resolves to exactly one source-grounded scalar mention; +- its target resolves to exactly one object mention; +- cue precedes quantity, which precedes object; +- no `.`, `?`, or `!` boundary lies between the three spans; +- relation evidence is the ordered tuple of exact cue, quantity, and object + spans; +- no actor, owner, source, target, before, after, container, or temporal role + is synthesized. + +If any requirement fails, publish no bound relation. The proposal may still be +assessed and refused from the missing evidence. + +## 6. Construction catalog addition + +The future `_UNARY_DELTA_FAMILY` must declare: + +```text +family_id: state_change.unary_delta +relation_type: unary_delta +candidate_organ: unary_delta_transition +required roles: action_cue, changed_object, delta_quantity, direction, + local_binding_relation, local_event_containment, + provenance_span +optional roles: actor +diagnostic_only: True +serving_allowed: False +``` + +`unit`, `source`, `target`, `before_quantity`, `after_quantity`, `container`, +and `temporal_marker` stay absent from the first catalog signature. Listing +them as optional would imply the first relation can lawfully carry those +states; it cannot. They remain future-family roles, not optional SCT-1 data. + +## 7. Contract assessment path + +Add: + +```python +def assess_unary_delta(frame: ProblemFrame) -> ContractAssessment: + ... +``` + +Assessment order: + +1. require exactly one `state_change.unary_delta` proposal; +2. require exactly one `GroundedUnaryDeltaCue` contained by proposal evidence; +3. require exactly one `BoundRelation(relation_type="unary_delta")`; +4. require exactly one action, delta, and changed-object role target; +5. prove delta mention → scalar provenance; +6. prove the exact `quantity_entity` edge joins the selected mentions; +7. prove typed cue/action/direction consistency; +8. prove local ordering and sentence containment; +9. prove every proposal, cue, role, binding, and relation span is an exact + `problem_text` slice; +10. reject unit/object collisions and all active competing contexts; +11. report `runnable=True` only if both blocker and hazard sets are empty. + +The function does not calculate a signed number. It does not inspect a question +target. It does not produce a target value. Its meaning is only “this local +unary-delta construction is role-complete.” + +## 8. Refusal code placement + +### `missing_bindings` + +- `unary_delta_proposal_required` +- `action_cue_unbound` +- `action_cue_ambiguous` +- `delta_quantity_unbound` +- `delta_quantity_ambiguous` +- `changed_object_unbound` +- `changed_object_ambiguous` +- `local_binding_relation_unbound` +- `local_binding_relation_ambiguous` +- `local_event_containment_unproven` +- `delta_direction_unbound` +- `unit_object_conflict` +- `provenance_span_inexact` + +### `unresolved_hazards` + +- `multiple_actor_surface` +- `delta_direction_ambiguous` +- `source_target_unlicensed` +- `pronoun_antecedent_unresolved` +- `cross_sentence_event` +- `implicit_total_unlicensed` +- `comparative_context` +- `rate_context` +- `percent_context` +- `passive_voice_unsupported` +- `temporal_context_ambiguous` +- `list_context_ambiguous` +- `transfer_unlicensed` +- `multiple_state_change_cues` +- `chained_state_changes` +- `event_assertion_unlicensed` + +The implementation may use a strict subset on naturally unreachable +no-proposal paths, but direct assessment of a deliberately malformed frame must +remain fail-closed and legible. + +## 9. Relationship to current quantity disposition + +Do not reuse or widen `QuantityKindDisposition` in SCT-1. + +Reason: #853 made that disposition valid only when an exact +`binding.quantity_entity` proposal exists. State-change text deliberately has a +process-frame context, so the quantity family does not propose. Making its +disposition generic again would recreate the authority leak #853 removed. + +SCT-1 instead closes only a family-specific count-valued delta relation from +its own proposal plus the exact existing scalar/object binding. Measurement +support is deferred. + +## 10. Required future code files + +The bounded implementation is expected to touch only: + +- `generate/foundational_families.py` +- `generate/construction_affordances.py` +- `generate/problem_frame.py` +- `generate/problem_frame_builder.py` +- `generate/problem_frame_contracts.py` +- focused tests listed below + +Do not touch: + +- `generate/derivation/*` +- `generate/math_candidate_graph.py` +- `evals/*` or reports +- runtime/serving paths +- teaching/proposals, packs, policy, identity, recall, Vault, field, algebra, + sealed artifacts, or `report.json` + +## 11. Likely test files + +Extend: + +- `tests/test_foundational_families.py` +- `tests/test_construction_affordances.py` +- `tests/test_construction_proposal_seam.py` +- `tests/test_problem_frame_builder.py` +- `tests/test_problem_frame_contracts.py` +- `tests/test_quantity_entity_proposal.py` +- `tests/test_proportional_decrease_proposal.py` +- `tests/test_percent_partition_proposal.py` + +Add: + +- `tests/test_unary_delta_proposal.py` + +No eval fixture or report file is required. + +## 12. Exact future tests + +### Type and catalog + +1. only `gained` and `lost` construct `GroundedUnaryDeltaCue`; +2. cue action kind/direction pairs cannot be mismatched; +3. catalog entry is diagnostic-only and serving-disallowed; +4. broad `state_change.transition` stays implementation-unauthorized; +5. `state_change.unary_delta` is a distinct foundational entry; +6. `ConstructionProposal` remains proposed-only. + +### Ordering and authority + +7. proposal is published before cue binding/relation assessment; +8. proposal-free frame does not dispatch `unary_delta_transition`; +9. positive proposal remains `status="proposed"` after runnable assessment; +10. legacy `make_proposal()` is never used; +11. direct malformed-frame assessment refuses with stable codes. + +### Exact evidence + +12. every proposal/cue/relation/assessment span equals its source slice; +13. synthetic proposal span refuses; +14. synthetic cue span refuses; +15. mismatched relation role target refuses; +16. cue/quantity/object crossing a sentence boundary refuses; +17. repeated build produces identical cue, proposal, relation, and assessment. + +### Positive fixtures + +18. `Ana gained 3 marbles.` is diagnostically runnable; +19. `The jar lost 2 cookies.` is diagnostically runnable; +20. `The team gained 4 points.` is diagnostically runnable; +21. no positive produces an answer, target, serving flag, or derivation candidate. + +### Negative/confuser fixtures + +22. every table entry in authorization §17 has the specified no-proposal or + refusal disposition; +23. `Ana gained apples.` refuses `delta_quantity_unbound`; +24. `Ana gained 3.` refuses `changed_object_unbound`; +25. `3 apples were gained by Ana.` refuses passive/order topology; +26. `Ana gained 3 pounds.` refuses unit/object conflict; +27. `Ana gained 3 apples and lost 2.` has no proposal; +28. `Tom gave Ana 3 apples.` still has no quantity disposition and no unary + proposal; +29. plain `There are 12 apples.` keeps its quantity proposal and gains no + unary assessment; +30. current proportional-decrease and percent-partition positives remain + unchanged. + +### Structural firewall + +31. an AST/import guard proves the new ProblemFrame/catalog/contract path does + not import `generate.derivation.state`, `generate.derivation.accumulate`, + `generate.derivation.pool`, or `generate.derivation.verify`; +32. no new raw-prose parser appears under `generate/derivation/`; +33. the existing no-new-legacy guard remains green. + +## 13. Manual probe plan + +For every seed/confuser, print only: + +```text +process frame names +proposal family IDs + exact evidence spans +unary cue records +mention IDs/kinds/spans +quantity_entity bindings +unary bound roles + spans +ContractAssessment organ/runnable/blockers/hazards +``` + +Required assertions: + +- positives: exactly one unary proposal, cue, relation, and assessment; +- no-proposal confusers: no unary cue authority, relation, or assessment; +- proposed/refused cases: proposal remains proposed and blockers are exact; +- no case produces a quantity-kind disposition unless the independent + quantity-family proposal exists; +- no case touches or compares against legacy semantic-ledger output. + +## 14. Validation plan + +Run targeted tests first: + +```bash +uv run python -m pytest -q \ + tests/test_foundational_families.py \ + tests/test_construction_affordances.py \ + tests/test_construction_proposal_seam.py \ + tests/test_problem_frame_builder.py \ + tests/test_problem_frame_contracts.py \ + tests/test_quantity_entity_proposal.py \ + tests/test_proportional_decrease_proposal.py \ + tests/test_percent_partition_proposal.py \ + tests/test_unary_delta_proposal.py \ + tests/test_kernel_no_new_legacy_derivation_surfaces.py + +uv run python -m core.cli test --suite smoke -q + +uv run python -m compileall -q generate tests + +uv run ruff check \ + generate/foundational_families.py \ + generate/construction_affordances.py \ + generate/problem_frame.py \ + generate/problem_frame_builder.py \ + generate/problem_frame_contracts.py \ + tests/test_foundational_families.py \ + tests/test_construction_affordances.py \ + tests/test_construction_proposal_seam.py \ + tests/test_problem_frame_builder.py \ + tests/test_problem_frame_contracts.py \ + tests/test_quantity_entity_proposal.py \ + tests/test_proportional_decrease_proposal.py \ + tests/test_percent_partition_proposal.py \ + tests/test_unary_delta_proposal.py + +git diff --check +``` + +Run the canonical train and holdout serving probes without writing reports. +Both must retain: + +```text +wrong_ids == [] +``` + +The PR must state any correct/refused count movement observed live, but it may +not update governed report artifacts. + +## 15. Implementation stop conditions + +Stop immediately if implementation requires: + +- actor, owner, source, target, pronoun, or temporal resolution; +- more than the exact `gained`/`lost` cue inventory; +- a generic action/event parser; +- widening quantity-entity proposal or disposition generation; +- reusing `transfer` as unary delta; +- importing or invoking the semantic-state derivation ledger; +- synthetic spans or nearest-mention selection; +- question-target or arithmetic logic; +- serving, report, eval fixture, teaching, memory, policy, identity, recall, + Vault, field, or algebra changes; +- a non-empty wrong-ID list. + +If exact local topology cannot make both seed positives runnable under these +constraints, return to design. Do not widen the parser to make fixtures pass. + +## 16. Copy-paste future implementation brief + +> **PR title:** `feat(kernel): introduce diagnostic unary-delta proposal seam` +> +> **Dependency:** Begin only after +> `docs(kernel): authorize unary state-change delta slice` merges. Create a +> fresh worktree from current `origin/main`; run `source +> scripts/agent_startup.sh`; prove the authorization and preflight documents +> are in the base. +> +> Implement only `state_change.unary_delta`, not broad +> `state_change.transition`. Add a family-local frozen +> `GroundedUnaryDeltaCue` with the closed exact cue mapping `gained → +> gain/increase`, `lost → loss/decrease`. Create +> `ConstructionProposal(status="proposed", diagnostic_only=True, +> serving_allowed=False)` from the exact cue span before mention/relation +> binding. Reuse the existing exact `GroundedScalar`, `GroundedMention`, and +> `MentionBinding(binding_type="quantity_entity")` to publish one +> `BoundRelation(relation_type="unary_delta")` with action cue, delta quantity, +> and changed object roles. Add proposal-gated `assess_unary_delta(frame)` as +> the sole runnable/refused authority. +> +> Keep the first slice count-only, single-cue, single-scalar, single-object, +> and single-sentence. Do not assert actor/ownership, source/target, +> before/after values, containment, transfer, arithmetic, answers, or serving. +> Do not widen `_quantity_entity_proposals()` or +> `_quantity_kind_dispositions()`. Do not import or call +> `generate.derivation.state`. Use the exact refusal taxonomy, tests, manual +> probes, validation commands, and stop conditions in this preflight. Preserve +> every current proposal-first family and train/holdout `wrong_ids == []`. diff --git a/docs/specs/foundational-families/unary-state-change-delta.md b/docs/specs/foundational-families/unary-state-change-delta.md new file mode 100644 index 00000000..a2d7dd78 --- /dev/null +++ b/docs/specs/foundational-families/unary-state-change-delta.md @@ -0,0 +1,355 @@ +# Foundational Family Specification: Unary State-Change Delta + +**Family ID:** `state_change.unary_delta` +**Status:** Authorized for one future diagnostic-only implementation slice; +currently unimplemented +**Related ADRs:** ADR-0223, ADR-0224 +**Controlling authorization:** +`docs/sessions/unary-state-change-delta-authorization-2026-06-21.md` +**Serving:** `diagnostic_only=True`, `serving_allowed=False` + +## Purpose + +This family represents the smallest locally groundable state-change event: one +explicit unary gain or loss cue applied to one explicit quantity/object pair. + +It answers only this diagnostic question: + +> Does this one local clause contain enough exact evidence to ground a unary +> delta relation? + +It does not answer a word problem and does not construct a complete state +history. It is a subordinate family beneath the broad constitutional +`state_change.transition` idea. The broad family remains implementation- +unauthorized because transfer, containment movement, before/after state, and +unary delta have different role geometries. + +## Family shape + +```text +Signature: unary_delta +Candidate organ: unary_delta_transition +Relation type: unary_delta +``` + +The proposal-first chain is: + +```text +exact action cue +→ ConstructionProposal(status="proposed") +→ exact cue + scalar/object binding +→ BoundRelation(relation_type="unary_delta") +→ ContractAssessment +→ diagnostic runnable/refused +``` + +`ConstructionProposal` is hypothesis only. `ContractAssessment` is the sole +runnable/refused authority. + +## Allowed cue surface + +The first implementation has a closed inventory: + +| Surface | Action kind | Explicit direction | +|---|---|---| +| `gained` | `gain` | `increase` | +| `lost` | `loss` | `decrease` | + +No stemming, lemmatization, synonym expansion, semantic similarity, or broad +verb classification is authorized. Exact lexical-boundary matching is allowed. + +The following are explicitly outside the first inventory even when they may +describe change in ordinary language: + +```text +gain, gains, gaining +lose, loses, losing +gave, received, bought, sold +ate, spent, used +put, took, moved +added, removed +grew, heated, cooled +was, became, now +``` + +Each widening requires evidence, confusers, and separate authorization. + +## Required roles + +| Role | Type and obligation | +|---|---| +| `action_cue` | Exactly one source-backed `GroundedUnaryDeltaCue` | +| `delta_quantity` | Exactly one quantity mention resolving to one exact `GroundedScalar` | +| `changed_object` | Exactly one exact local object mention | +| `direction` | `increase` or `decrease`, carried only by the explicit authorized cue | +| `local_binding_relation` | Exactly one existing `quantity_entity` edge from delta to changed object | +| `local_event_containment` | Cue, quantity, and object in that order inside one sentence/clause | +| `provenance_span` | Exact source spans for cue, scalar, object, binding, and relation | + +The first slice is count-valued. Unit/object collision refuses. + +## Optional and deferred roles + +`actor` is the only catalog-visible optional SCT-1 role. It may be carried only +when exact grounded evidence already exists. The first implementation is not +required to publish it and may not add parsing to obtain it. + +These are broader state-change roles, not roles in the first relation, and must +not be invented: + +- owner; +- source; +- target; +- before quantity; +- after quantity; +- container or location; +- temporal marker. +- unit or measurement property. + +They belong to future family geometries. + +## Minimal typed cue + +The future ProblemFrame may add one family-local frozen record: + +```python +@dataclass(frozen=True, slots=True) +class GroundedUnaryDeltaCue: + cue_id: str + surface: str + action_kind: Literal["gain", "loss"] + direction: Literal["increase", "decrease"] + span: SourceSpan +``` + +This is not a generic event IR. It cannot represent tense, modality, negation, +actors, source/target, temporal ordering, causality, or arithmetic effects. + +## Bound relation + +The future relation uses existing `BoundRelation` / `BoundRole`: + +```text +BoundRelation( + relation_type="unary_delta", + roles=( + action_cue → GroundedUnaryDeltaCue.cue_id, + delta_quantity → GroundedMention(quantity).mention_id, + changed_object → GroundedMention(object).mention_id, + ), + evidence_spans=(action_span, quantity_span, object_span), +) +``` + +The relation carries no signed arithmetic operand. `increase` and `decrease` +are lexical event dispositions, not instructions to mutate state. + +## Evidence span rules + +Every evidence span must be an exact original-text slice: + +```text +0 <= start <= end <= len(problem_text) +problem_text[start:end] == text +``` + +Additional rules: + +- action, quantity, and object spans are non-overlapping; +- source order is `action < quantity < object`; +- no sentence terminator occurs between them; +- binding evidence is exactly `(quantity_span, object_span)`; +- relation evidence contains only exact role spans in deterministic order; +- proposal evidence is the action cue span only; +- no span is widened, concatenated, repaired, or synthesized; +- a computed event envelope is locality telemetry only and is not a semantic + evidence replacement. + +## Proposal conditions + +A proposal may be created only when: + +- exactly one authorized cue occurs; +- no second/repeated unary cue occurs; +- no explicit percent, rate, comparison, transfer, transaction, containment, + actor-coordination, or list surface competes; +- the only compatible process-frame candidate is `consumption`. + +A cue may propose even if downstream quantity/object evidence is missing. That +is the purpose of the proposal/assessment split. Missing evidence then refuses +at `ContractAssessment`. + +## Assessment obligations + +`assess_unary_delta(frame)` may report `runnable=True` only if: + +1. exactly one proposal exists; +2. exactly one typed cue exists and is contained by proposal evidence; +3. cue surface, action kind, and direction match the closed mapping; +4. exactly one unary-delta bound relation exists; +5. the selected delta resolves to exactly one source-grounded scalar; +6. the selected changed object is exact and unambiguous; +7. exactly one existing `quantity_entity` edge joins them; +8. cue, delta, and object satisfy exact local order/containment; +9. every proposal, cue, binding, role, and relation span is exact; +10. no blocking context or ambiguity remains. + +`runnable=True` means only that the diagnostic relation is role-complete. It +does not authorize a derived value, answer, state mutation, or serving path. + +## Refusal classes + +### Structural blockers + +- `unary_delta_proposal_required` +- `action_cue_unbound` +- `action_cue_ambiguous` +- `delta_quantity_unbound` +- `delta_quantity_ambiguous` +- `changed_object_unbound` +- `changed_object_ambiguous` +- `local_binding_relation_unbound` +- `local_binding_relation_ambiguous` +- `local_event_containment_unproven` +- `delta_direction_unbound` +- `unit_object_conflict` +- `provenance_span_inexact` + +### Ambiguity/context hazards + +- `multiple_actor_surface` +- `delta_direction_ambiguous` +- `source_target_unlicensed` +- `pronoun_antecedent_unresolved` +- `cross_sentence_event` +- `implicit_total_unlicensed` +- `comparative_context` +- `rate_context` +- `percent_context` +- `passive_voice_unsupported` +- `temporal_context_ambiguous` +- `list_context_ambiguous` +- `transfer_unlicensed` +- `multiple_state_change_cues` +- `chained_state_changes` +- `event_assertion_unlicensed` + +No-proposal confusers do not dispatch the assessment. Direct assessment of a +malformed frame remains fail-closed. + +## Relationship to `binding.quantity_entity` + +The family may consume the existing exact scalar/object +`MentionBinding(binding_type="quantity_entity")` as grounding evidence. + +It must not: + +- create or require a `binding.quantity_entity` proposal; +- create or consume a quantity-family `QuantityKindDisposition` outside its + proposal boundary; +- dispatch `assess_quantity_entity()`; +- make a plain quantity/entity surface look eventful; +- make an event cue sufficient without the exact binding. + +This separation preserves #853. Shared evidence is not shared authority. + +## Interaction exclusions + +The family must not capture: + +- `proportional_change.decrease_to_fraction`; +- `partition.percent_partition`; +- plain `binding.quantity_entity`; +- comparison language; +- percent or rate language; +- transfer or transaction language; +- containment movement; +- measurement changes; +- multiple or chained events. + +## Positive examples + +```text +Ana gained 3 marbles. +The jar lost 2 cookies. +The team gained 4 points. +``` + +For each, only cue, direction, delta quantity, and changed object are asserted. +No owner, before state, after state, or result is inferred. + +## Negative examples + +These must not propose: + +```text +There are 12 apples. +Tom has 12 apples. +Tom gave Ana 3 apples. +Tom gave her 3 apples. +Tom gave Ana some apples. +Tom had 12 apples and gave Ana 3. +Ana has 3 more apples than Tom. +20% of the apples are red. +3 apples per child. +Tom bought 3 apples and 2 oranges. +The basket was filled with apples. +The tank has 84 degrees. +There are 12 apples. Tom ate 3. +Tom and Ana each got 3 apples. +Tom moved 3 apples from the box to the bag. +The box now has 5 more pencils. +Tom put 4 shells in the bag. +Ana gained 3 apples and lost 2. +``` + +These may propose from the exact cue but must refuse: + +```text +Ana gained apples. # no explicit delta +Ana gained 3. # no changed object/binding +3 apples were gained by Ana. # passive/order topology +Ana gained 3 pounds. # unit/object collision in count-only SCT-1 +``` + +## Non-goals + +- broad `state_change.transition`; +- event/story parsing; +- lemmatization or synonym expansion; +- actor/owner or pronoun resolution; +- source/target and transfer conservation; +- containment movement; +- before/after or initial/final state; +- multiple events; +- measurement-valued delta; +- arithmetic or answer production; +- `SemanticLedger` or derivation integration; +- serving, eval/report mutation, teaching, memory, packs, policy, identity, + recall, Vault, field, or algebra changes. + +## Implementation status and future gates + +After this documentation PR: + +- this family is authorized for one future bounded implementation PR; +- no code implements it; +- broad `state_change.transition` stays unauthorized; +- no serving behavior changes. + +Implementation requires all gates in +`docs/sessions/unary-state-change-delta-preflight-2026-06-21.md`, including: + +- fresh-base startup guard; +- exact proposal-first ordering; +- family-local cue type only; +- full positive/confuser tests; +- no legacy semantic-state imports; +- deterministic replay; +- exact span proof; +- current proposal-first family preservation; +- train and holdout `wrong_ids == []`; +- no governed report changes. + +Any need to widen cue inventory, parse actors, infer cross-sentence roles, or +touch serving returns the work to design.