docs(kernel): authorize unary state-change delta slice
This commit is contained in:
parent
ca83d3846c
commit
7ee849970b
4 changed files with 1453 additions and 0 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
580
docs/sessions/unary-state-change-delta-preflight-2026-06-21.md
Normal file
580
docs/sessions/unary-state-change-delta-preflight-2026-06-21.md
Normal file
|
|
@ -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 == []`.
|
||||
355
docs/specs/foundational-families/unary-state-change-delta.md
Normal file
355
docs/specs/foundational-families/unary-state-change-delta.md
Normal file
|
|
@ -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.
|
||||
Loading…
Reference in a new issue