docs: select second candidate operator
This commit is contained in:
parent
ff1280ffa2
commit
038c8bc457
1 changed files with 616 additions and 0 deletions
616
docs/adr/ADR-0234-second-candidate-operator-selection.md
Normal file
616
docs/adr/ADR-0234-second-candidate-operator-selection.md
Normal file
|
|
@ -0,0 +1,616 @@
|
||||||
|
# ADR-0234: Second Candidate Operator Selection
|
||||||
|
|
||||||
|
**Status:** Proposed
|
||||||
|
|
||||||
|
**Date:** 2026-06-23
|
||||||
|
|
||||||
|
**Scope:** Kernel diagnostics, candidate operator set expansion, residual-gated practice loop
|
||||||
|
|
||||||
|
**Depends on:**
|
||||||
|
|
||||||
|
- ADR-0225 ContractResidual read model
|
||||||
|
- ADR-0226 Residual-Gated Practice Loop v1
|
||||||
|
- ADR-0227 ComputeBudgetPolicy envelope
|
||||||
|
- ADR-0228 GeometricSearchRun envelope
|
||||||
|
- ADR-0229 Contract/Proof Replay Adapter boundary
|
||||||
|
- ADR-0231 First Candidate Operator boundary
|
||||||
|
- ADR-0232 CandidateAttempt Run-Binding boundary
|
||||||
|
- ADR-0233 Bound Practice Episode Sealing
|
||||||
|
|
||||||
|
## 1. Summary
|
||||||
|
|
||||||
|
This ADR selects the second candidate operator after:
|
||||||
|
|
||||||
|
```text
|
||||||
|
missing_role_candidate.v1
|
||||||
|
```
|
||||||
|
|
||||||
|
The selected operator is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
operator_family = "residual_missing_relation"
|
||||||
|
operator_name = "quantity_entity_binding_candidate"
|
||||||
|
operator_version = "quantity_entity_binding_candidate.v1"
|
||||||
|
candidate_organ = "quantity_entity_binding"
|
||||||
|
```
|
||||||
|
|
||||||
|
The operator consumes one eligible `ContractResidual`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
residual_kind = "missing_relation"
|
||||||
|
residual_code = "local_binding_relation_unbound"
|
||||||
|
candidate_organ = "quantity_entity_binding"
|
||||||
|
```
|
||||||
|
|
||||||
|
It emits at most one deterministic candidate attempt that proposes one local
|
||||||
|
`quantity_entity` mention-binding edge from already-realized typed evidence. It
|
||||||
|
does not parse prose, call the rate reader, solve arithmetic, produce answers,
|
||||||
|
select candidates, rank candidates, mutate the original `ProblemFrame`, or
|
||||||
|
grant serving authority.
|
||||||
|
|
||||||
|
All candidates must still pass:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CandidateOperatorResult
|
||||||
|
-> CandidateAttemptRunBinding
|
||||||
|
-> ReplayAdapterInput
|
||||||
|
-> ReplayAdapterResult | ReplayAdapterRefusal
|
||||||
|
-> SealedPracticeTrace
|
||||||
|
```
|
||||||
|
|
||||||
|
This is documentation and implementation planning only. It does not implement
|
||||||
|
the operator, alter runtime/kernel execution, mutate packs, change policy,
|
||||||
|
touch Workbench, add serving behavior, or add dynamic operator discovery.
|
||||||
|
|
||||||
|
## 2. Repo Evidence Inspected
|
||||||
|
|
||||||
|
The current candidate spine is inert and complete:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CandidateOperatorResult
|
||||||
|
-> CandidateAttemptRunBinding
|
||||||
|
-> ReplayAdapterInput
|
||||||
|
-> ReplayAdapterResult | ReplayAdapterRefusal
|
||||||
|
-> SealedPracticeTrace
|
||||||
|
```
|
||||||
|
|
||||||
|
Files inspected:
|
||||||
|
|
||||||
|
- `generate/candidate_operator.py`
|
||||||
|
- `generate/run_attempt_binding.py`
|
||||||
|
- `generate/replay_adapter.py`
|
||||||
|
- `generate/sealed_practice_trace.py`
|
||||||
|
- `generate/contract_residuals.py`
|
||||||
|
- `generate/search_gate.py`
|
||||||
|
- `generate/compute_budget.py`
|
||||||
|
- `generate/problem_frame_contracts.py`
|
||||||
|
- `generate/problem_frame_builder.py`
|
||||||
|
- `generate/construction_affordances.py`
|
||||||
|
- `generate/foundational_families.py`
|
||||||
|
- `tests/test_candidate_operator.py`
|
||||||
|
- `tests/test_run_attempt_binding.py`
|
||||||
|
- `tests/test_replay_adapter_bound_attempt.py`
|
||||||
|
- `tests/test_sealed_practice_trace_bound_episode.py`
|
||||||
|
- `tests/test_quantity_entity_proposal.py`
|
||||||
|
- `tests/test_contract_residuals.py`
|
||||||
|
- `tests/test_search_gate.py`
|
||||||
|
- `tests/test_compute_budget.py`
|
||||||
|
- `tests/test_rate_reader.py`
|
||||||
|
- `tests/test_rate_units.py`
|
||||||
|
- `tests/test_rate_conversion_solving.py`
|
||||||
|
- `docs/specs/foundational-families/quantity-entity-binding.md`
|
||||||
|
- `docs/analysis/gsm8k-xhigh-capability-sprint13-lookback-2026-06-18.md`
|
||||||
|
- `docs/analysis/gsm8k-workstream-a-increment-2-rate-injection-ratification-2026-06-17.md`
|
||||||
|
- `docs/analysis/gsm8k-workstream-a-increment-3-lookback-2026-06-17.md`
|
||||||
|
|
||||||
|
Load-bearing observations:
|
||||||
|
|
||||||
|
1. `missing_role_candidate.v1` is a closed one-row static operator table with
|
||||||
|
`candidate_operators.v1`, one attempt, one step, one depth, serial budget,
|
||||||
|
canonical JSON SHA-256 identities, and no answer/proof/serving fields.
|
||||||
|
2. Run binding, replay input construction, replay classification, and practice
|
||||||
|
sealing are operator-agnostic after a lawful `CandidateOperatorResult`.
|
||||||
|
3. `quantity_entity_binding` is already registry-backed in
|
||||||
|
`generate/problem_frame_contracts.py`, proposal-first in
|
||||||
|
`generate/construction_affordances.py`, and mirrored as diagnostic-only in
|
||||||
|
`generate/foundational_families.py`.
|
||||||
|
4. The residual projector already classifies `local_binding_relation_unbound`
|
||||||
|
as `ResidualKind.MISSING_RELATION`, and the search gate plus compute budget
|
||||||
|
already authorize `eligible_missing_relation` and
|
||||||
|
`budget_allowed_missing_relation`.
|
||||||
|
5. Direct rate surfaces exist in older serving/eval paths:
|
||||||
|
`generate.rate_comprehension.*`, `generate.recognizer_anchor_inject`, and
|
||||||
|
`generate.derivation.bounded_rate_projection`. Those are useful evidence,
|
||||||
|
but they are not yet a registry-backed ProblemFrame contract/residual target
|
||||||
|
for this candidate spine.
|
||||||
|
6. The only direct ProblemFrame rate assessment currently seen is the inline
|
||||||
|
`labor_rate` skeleton with candidate organ `temporal_tariff`; its labels
|
||||||
|
`worker`, `rate`, and `duration` currently project to
|
||||||
|
`CONTRACT_GAP_UNCLASSIFIED`, which the search gate blocks.
|
||||||
|
|
||||||
|
## 3. Intrinsic Space
|
||||||
|
|
||||||
|
The next candidate operator should not be selected by surface frequency alone.
|
||||||
|
The intrinsic space is a residual field whose legal forward move is a bounded
|
||||||
|
candidate reconstruction, paired with corrective replay and sealing.
|
||||||
|
|
||||||
|
The second operator should therefore minimize structural distortion:
|
||||||
|
|
||||||
|
- consume an already-classified eligible residual;
|
||||||
|
- consume typed evidence already built by the ProblemFrame substrate;
|
||||||
|
- produce one bounded candidate reconstruction;
|
||||||
|
- preserve static operator-set identity;
|
||||||
|
- avoid parser, solver, model, time, filesystem, environment, network, and
|
||||||
|
runtime authority; and
|
||||||
|
- widen candidate-generation breadth without widening truth authority.
|
||||||
|
|
||||||
|
Rate application is capability-attractive, but in the current repo it would
|
||||||
|
pull from a serving/eval reader path or require new rate residual taxonomy
|
||||||
|
before it can lawfully enter this spine. Quantity-entity binding is the smaller
|
||||||
|
operator because its residual, gate, budget, catalog, and diagnostic contract
|
||||||
|
already exist.
|
||||||
|
|
||||||
|
## 4. Candidate Comparison
|
||||||
|
|
||||||
|
| Option | Residual consumed | Candidate organ emitted | Evidence required | Attempt/budget charge | Replay target | Confusers to test | Existing substrates | Authority risk | Decision |
|
||||||
|
|---|---|---|---|---|---|---|---|---|---|
|
||||||
|
| A. `missing_quantity_candidate` | `missing_role` codes such as `quantity_unbound`, `delta_quantity_unbound`, `base_quantity_unbound`, `original_whole_unbound` | Varies by organ | One exact scalar mention plus role target evidence | 1 attempt, 1 candidate, 1 step if narrowed | Organ-specific | wrong residual kind/code, missing spans, ambiguous scalars, entity mismatch, unit mismatch, template/prose-only trigger | `ContractResidual`, ProblemFrame quantities, multiple contracts | Too broad across organs; can become parser/repair | Defer |
|
||||||
|
| B. `missing_rate_candidate` | Ideally `missing_role` rate code, but current `rate` label is unclassified | `temporal_tariff` or future rate organ | Bound numerator, denominator, actor/entity, duration basis | 1 attempt, likely 1 step after rate contract exists | Future rate contract target | ambiguous rates, unit mismatch, currency mismatch, reciprocal rate, duration mismatch | old rate reader/solver; inline `labor_rate` skeleton | Current residual is `CONTRACT_GAP_UNCLASSIFIED`; gate blocks | Defer |
|
||||||
|
| C. `rate_application_candidate` | Future `rate_application_unbound` or equivalent; not present today | future `rate_application` or registry-backed `temporal_tariff` | One grounded rate, one compatible duration/count, one target entity, one question target | 1 attempt per grounded rate residual, 1 candidate, 1 step | Future `problem_frame_contracts.rate_application` | all required confusers plus ambiguous multiple rates, direction/sign, currency-rate, target inversion | old `apply_rate` operation, rate units, rate_with_currency workstream | High if implemented now: could call legacy reader/solver or produce answers | Defer behind rate contract/residual PR |
|
||||||
|
| D. `quantity_entity_binding_candidate` | `missing_relation`, `local_binding_relation_unbound` | `quantity_entity_binding` | One exact quantity mention, one exact local entity mention, optional compatible unit, one proposal trace, source spans already covered by residual/problem-frame evidence | 1 attempt, 1 candidate, 1 step, depth 1, serial | `problem_frame_contracts.quantity_entity` | all required confusers, especially ambiguous entities, nonlocal binding, unit/currency mismatch, template-only trigger | registry-backed quantity-entity contract, residual map, gate, budget, foundational spec | Low if narrowed to one local edge and no parser | Selected |
|
||||||
|
| E. `arithmetic_transition_candidate` | Likely `target_unbound` or mixed missing-role residuals | future arithmetic transition organ or existing state-change organ | Bound operands, operator, direction, target, question | 1 attempt only after a complete arithmetic contract exists | Future arithmetic replay target | sign mismatch, unused numerals, operation ambiguity, answer leakage | derivation modules, math candidate graph | High answer/proof leakage risk | Defer |
|
||||||
|
| F. `unit_normalization_candidate` | `unit_object_conflict` or `unit_continuity_unproven` | future unit normalization organ | Reviewed conversion relation, source unit, target unit, dimension proof | 1 attempt if conversion table is static and reviewed | Future unit contract target | currency mismatch, incompatible dimensions, hidden conversion | rate units, language pack unit dimensions | Gate currently blocks conflicts; conversion can become hidden normalization | Defer |
|
||||||
|
|
||||||
|
## 5. Decision
|
||||||
|
|
||||||
|
Select:
|
||||||
|
|
||||||
|
```text
|
||||||
|
quantity_entity_binding_candidate.v1
|
||||||
|
```
|
||||||
|
|
||||||
|
Reject the default hypothesis, `rate_application_candidate.v1`, for this pass.
|
||||||
|
The rate family remains the right capability target, but the current candidate
|
||||||
|
spine lacks a registry-backed rate-application contract, a classified rate
|
||||||
|
residual code, and an eligible search-gate path. Implementing rate application
|
||||||
|
now would either consume the older reader/solver path or require upstream
|
||||||
|
contract/residual work in the same PR. Both distort this ADR's goal: the next
|
||||||
|
smallest operator addition should preserve the existing evidence spine.
|
||||||
|
|
||||||
|
Quantity-entity binding is the masterstroke because it is both foundational and
|
||||||
|
structurally ready. It improves GSM8K/math substrate breadth by proposing the
|
||||||
|
local binding that almost every later rate, partition, comparison, and state
|
||||||
|
change needs, while avoiding answer production and solver authority.
|
||||||
|
|
||||||
|
## 6. Selected Operator Specification
|
||||||
|
|
||||||
|
### 6.1 Identity
|
||||||
|
|
||||||
|
```text
|
||||||
|
operator_family = "residual_missing_relation"
|
||||||
|
operator_name = "quantity_entity_binding_candidate"
|
||||||
|
operator_version = "quantity_entity_binding_candidate.v1"
|
||||||
|
candidate_organ = "quantity_entity_binding"
|
||||||
|
```
|
||||||
|
|
||||||
|
The implementation should introduce:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CANDIDATE_OPERATOR_SET_VERSION = "candidate_operators.v2"
|
||||||
|
```
|
||||||
|
|
||||||
|
The v2 static table contains exactly two rows:
|
||||||
|
|
||||||
|
```text
|
||||||
|
missing_role_candidate.v1
|
||||||
|
quantity_entity_binding_candidate.v1
|
||||||
|
```
|
||||||
|
|
||||||
|
No dynamic registry, plugin loading, filesystem discovery, environment reads,
|
||||||
|
or operator fan-out is authorized.
|
||||||
|
|
||||||
|
### 6.2 Supported residuals
|
||||||
|
|
||||||
|
Supported residual kind:
|
||||||
|
|
||||||
|
```text
|
||||||
|
missing_relation
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported residual code:
|
||||||
|
|
||||||
|
```text
|
||||||
|
local_binding_relation_unbound
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported candidate organ:
|
||||||
|
|
||||||
|
```text
|
||||||
|
quantity_entity_binding
|
||||||
|
```
|
||||||
|
|
||||||
|
All other residual kinds, residual codes, and candidate organs refuse.
|
||||||
|
|
||||||
|
### 6.3 Input record shape
|
||||||
|
|
||||||
|
Reuse the existing `CandidateOperatorInput` identity envelope and add only the
|
||||||
|
typed evidence argument needed by the builder function. The new evidence record
|
||||||
|
should be a frozen dataclass:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class GroundedQuantityEntityCue:
|
||||||
|
quantity_mention_id: str
|
||||||
|
entity_mention_id: str
|
||||||
|
quantity_kind: str
|
||||||
|
evidence_spans: tuple[SourceSpan, ...]
|
||||||
|
unit_mention_id: str | None = None
|
||||||
|
```
|
||||||
|
|
||||||
|
The builder should be conceptualized as:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def build_quantity_entity_binding_candidate(
|
||||||
|
*,
|
||||||
|
residual: object,
|
||||||
|
search_gate: SearchGateDecision,
|
||||||
|
compute_budget: ComputeBudgetDecision,
|
||||||
|
run: GeometricSearchRun,
|
||||||
|
problem_frame_digest: str,
|
||||||
|
original_contract_assessment_id: str,
|
||||||
|
grounded_quantity_entity_cues: tuple[GroundedQuantityEntityCue, ...],
|
||||||
|
attempt_index: int = 0,
|
||||||
|
schema_versions: tuple[tuple[str, str], ...] = (),
|
||||||
|
policy_versions: tuple[tuple[str, str], ...] = (),
|
||||||
|
explanation: str = "",
|
||||||
|
) -> CandidateOperatorOutcome:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
V1 requires exactly one cue. Zero cues refuse. Multiple cues refuse. Cue spans
|
||||||
|
must be valid `SourceSpan` values and must be grounded in the residual or
|
||||||
|
ProblemFrame evidence chain. The builder must not reread source text.
|
||||||
|
|
||||||
|
### 6.4 Output record shape
|
||||||
|
|
||||||
|
Reuse:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CandidateOperatorResult
|
||||||
|
CandidateOperatorRefusal
|
||||||
|
CandidateReconstruction
|
||||||
|
CandidateAttempt
|
||||||
|
```
|
||||||
|
|
||||||
|
The candidate payload should be canonical tuple-pairs, not prose:
|
||||||
|
|
||||||
|
```text
|
||||||
|
("binding_type", "quantity_entity")
|
||||||
|
("candidate_organ", "quantity_entity_binding")
|
||||||
|
("entity_mention_id", <entity_mention_id>)
|
||||||
|
("kind", "mention_binding")
|
||||||
|
("quantity_kind", <count|measurement>)
|
||||||
|
("quantity_mention_id", <quantity_mention_id>)
|
||||||
|
("relation_type", "quantity_entity")
|
||||||
|
("source", "GroundedQuantityEntityCue")
|
||||||
|
("unit_mention_id", <unit_mention_id or "">)
|
||||||
|
```
|
||||||
|
|
||||||
|
The payload ordering is part of identity. Explanatory prose is excluded from
|
||||||
|
all canonical digests.
|
||||||
|
|
||||||
|
### 6.5 Budget and attempt count
|
||||||
|
|
||||||
|
```text
|
||||||
|
max_attempts_per_run = 1
|
||||||
|
budget_charge = BudgetCharge(candidates=1, steps=1)
|
||||||
|
depth = 1
|
||||||
|
max_parallelism = 1
|
||||||
|
attempt_index = 0 only
|
||||||
|
```
|
||||||
|
|
||||||
|
The builder must refuse if:
|
||||||
|
|
||||||
|
- `attempt_index >= ComputeBudgetDecision.max_candidates`;
|
||||||
|
- `attempt_index >= CandidateOperatorPolicy.max_attempts_per_run`;
|
||||||
|
- fixed charge exceeds `max_candidates`, `max_depth`, or `max_steps`; or
|
||||||
|
- `ComputeBudgetDecision.max_parallelism != 1`.
|
||||||
|
|
||||||
|
### 6.6 Operator-set identity impact
|
||||||
|
|
||||||
|
`candidate_operator_set_id()` changes under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
candidate_operators.v2
|
||||||
|
```
|
||||||
|
|
||||||
|
The v2 table digest must be computed from the static table rows only:
|
||||||
|
|
||||||
|
```text
|
||||||
|
operator_family
|
||||||
|
operator_name
|
||||||
|
operator_version
|
||||||
|
allowed_residual_kinds
|
||||||
|
allowed_residual_codes
|
||||||
|
allowed_candidate_organs
|
||||||
|
max_attempts_per_run
|
||||||
|
budget_charge
|
||||||
|
depth
|
||||||
|
max_parallelism
|
||||||
|
```
|
||||||
|
|
||||||
|
No generated prose, examples, environment values, local paths, current time,
|
||||||
|
randomness, test order, or discovered files may enter the digest.
|
||||||
|
|
||||||
|
### 6.7 Replay target expectation
|
||||||
|
|
||||||
|
The expected replay target is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
quantity_entity_binding -> problem_frame_contracts.quantity_entity
|
||||||
|
```
|
||||||
|
|
||||||
|
Current `generate/replay_adapter.py` does not yet allowlist
|
||||||
|
`quantity_entity_binding`. The implementation path should either:
|
||||||
|
|
||||||
|
1. keep the first operator PR limited to candidate construction and accept that
|
||||||
|
replay input construction returns `ReplayAdapterRefusal` with
|
||||||
|
`contract_replay_unavailable`; or
|
||||||
|
2. add the one-row replay target allowlist change in the same PR with a focused
|
||||||
|
test, explicitly justified as the minimum needed for the selected operator
|
||||||
|
to traverse the full replay-adapter input boundary.
|
||||||
|
|
||||||
|
No replay contract execution, proof execution, or serving behavior is
|
||||||
|
authorized by this ADR.
|
||||||
|
|
||||||
|
### 6.8 Forbidden authority
|
||||||
|
|
||||||
|
The selected operator may not read or write:
|
||||||
|
|
||||||
|
```text
|
||||||
|
packs
|
||||||
|
policy
|
||||||
|
identity
|
||||||
|
teaching
|
||||||
|
eval reports
|
||||||
|
runtime/session state
|
||||||
|
Workbench state
|
||||||
|
filesystem paths
|
||||||
|
environment variables
|
||||||
|
network
|
||||||
|
time
|
||||||
|
random
|
||||||
|
model calls
|
||||||
|
plugin registries
|
||||||
|
dynamic operator registries
|
||||||
|
```
|
||||||
|
|
||||||
|
It may not produce:
|
||||||
|
|
||||||
|
```text
|
||||||
|
answer
|
||||||
|
final_answer
|
||||||
|
served_output
|
||||||
|
proof
|
||||||
|
verdict
|
||||||
|
confidence
|
||||||
|
score
|
||||||
|
rank
|
||||||
|
selected_candidate
|
||||||
|
best_candidate
|
||||||
|
promotion
|
||||||
|
mutation
|
||||||
|
teaching_update
|
||||||
|
pack_update
|
||||||
|
policy_update
|
||||||
|
identity_update
|
||||||
|
```
|
||||||
|
|
||||||
|
Explicit preservation:
|
||||||
|
|
||||||
|
```text
|
||||||
|
candidate != truth
|
||||||
|
candidate != proof
|
||||||
|
candidate != answer
|
||||||
|
candidate != replay success
|
||||||
|
candidate != sealed evidence
|
||||||
|
candidate != learning
|
||||||
|
candidate != promotion
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.9 Confuser set
|
||||||
|
|
||||||
|
The implementation PR must include tests for:
|
||||||
|
|
||||||
|
- wrong residual kind;
|
||||||
|
- wrong residual code;
|
||||||
|
- unsupported candidate organ;
|
||||||
|
- missing evidence spans;
|
||||||
|
- ungrounded rate, unit, or entity;
|
||||||
|
- ambiguous multiple rates;
|
||||||
|
- entity mismatch;
|
||||||
|
- unit mismatch;
|
||||||
|
- currency mismatch;
|
||||||
|
- direction/sign mismatch;
|
||||||
|
- operator budget exceeded;
|
||||||
|
- non-deterministic ordering;
|
||||||
|
- attempt count greater than declared max;
|
||||||
|
- template/prose-only trigger;
|
||||||
|
- multiple quantity-entity cues;
|
||||||
|
- nonlocal entity binding;
|
||||||
|
- pronoun or alias entity binding;
|
||||||
|
- count/measurement unit-state conflict; and
|
||||||
|
- state-change, percent, comparison, rate, and currency-rate surfaces trying to
|
||||||
|
backdoor a generic quantity-entity candidate.
|
||||||
|
|
||||||
|
## 7. Follow-up Implementation PR Brief
|
||||||
|
|
||||||
|
Suggested PR title:
|
||||||
|
|
||||||
|
```text
|
||||||
|
feat(kernel): add quantity-entity binding candidate operator shell
|
||||||
|
```
|
||||||
|
|
||||||
|
Suggested branch:
|
||||||
|
|
||||||
|
```text
|
||||||
|
codex/quantity-entity-binding-candidate-operator
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected changed files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
generate/candidate_operator.py
|
||||||
|
tests/test_candidate_operator_quantity_entity_binding.py
|
||||||
|
tests/test_candidate_operator.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional, only if the PR explicitly includes the trivial replay target
|
||||||
|
allowlist:
|
||||||
|
|
||||||
|
```text
|
||||||
|
generate/replay_adapter.py
|
||||||
|
tests/test_replay_adapter_bound_attempt.py
|
||||||
|
```
|
||||||
|
|
||||||
|
New dataclasses:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GroundedQuantityEntityCue
|
||||||
|
```
|
||||||
|
|
||||||
|
New constants:
|
||||||
|
|
||||||
|
```text
|
||||||
|
QUANTITY_ENTITY_BINDING_CANDIDATE_OPERATOR_NAME
|
||||||
|
QUANTITY_ENTITY_BINDING_CANDIDATE_OPERATOR_VERSION
|
||||||
|
QUANTITY_ENTITY_BINDING_RESIDUAL_KIND
|
||||||
|
QUANTITY_ENTITY_BINDING_RESIDUAL_CODE
|
||||||
|
QUANTITY_ENTITY_BINDING_CANDIDATE_ORGAN
|
||||||
|
QUANTITY_ENTITY_BINDING_OPERATOR_POLICY
|
||||||
|
```
|
||||||
|
|
||||||
|
Policy versions:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CANDIDATE_OPERATOR_POLICY_VERSION remains "candidate_operator.v1"
|
||||||
|
CANDIDATE_OPERATOR_SET_VERSION becomes "candidate_operators.v2"
|
||||||
|
```
|
||||||
|
|
||||||
|
Candidate payload canonicalization:
|
||||||
|
|
||||||
|
- use tuple-pairs in fixed lexical/schema order;
|
||||||
|
- include `quantity_mention_id`, `entity_mention_id`, `quantity_kind`, and
|
||||||
|
`unit_mention_id`;
|
||||||
|
- include exact evidence spans in existing span-payload format;
|
||||||
|
- exclude explanation prose.
|
||||||
|
|
||||||
|
Input digest canonicalization:
|
||||||
|
|
||||||
|
- include operator name and version;
|
||||||
|
- include residual kind/code/organ;
|
||||||
|
- include gate, budget, run, and operator-set identities;
|
||||||
|
- include attempt index;
|
||||||
|
- include sorted unique schema and policy version pairs;
|
||||||
|
- exclude typed-cue prose and explanations.
|
||||||
|
|
||||||
|
Operator result digest canonicalization:
|
||||||
|
|
||||||
|
- reuse the existing `_operator_result_id_payload` shape;
|
||||||
|
- use the new organ/operator identity;
|
||||||
|
- include candidate and reconstruction digests;
|
||||||
|
- include exact evidence spans;
|
||||||
|
- keep reason codes empty on success.
|
||||||
|
|
||||||
|
Budget charge:
|
||||||
|
|
||||||
|
```text
|
||||||
|
BudgetCharge(candidates=1, steps=1)
|
||||||
|
depth = 1
|
||||||
|
max_parallelism = 1
|
||||||
|
max_attempts_per_run = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Static authority guards:
|
||||||
|
|
||||||
|
- public dataclasses expose no answer/proof/score/rank/selected/mutation fields;
|
||||||
|
- AST/static tests prove no imports of runtime, Workbench, teaching, packs,
|
||||||
|
random, time, os, pathlib discovery, network, subprocess, or model clients;
|
||||||
|
- no dynamic operator table construction;
|
||||||
|
- no filesystem/env/plugin loading;
|
||||||
|
- no solver or rate-reader calls;
|
||||||
|
- no hidden normalization;
|
||||||
|
- no updates outside the candidate/replay allowlist surfaces named above.
|
||||||
|
|
||||||
|
Focused validation commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run python -m pytest -q tests/test_candidate_operator.py
|
||||||
|
uv run python -m pytest -q tests/test_candidate_operator_quantity_entity_binding.py
|
||||||
|
uv run python -m pytest -q tests/test_run_attempt_binding.py
|
||||||
|
uv run python -m pytest -q tests/test_replay_adapter_bound_attempt.py
|
||||||
|
uv run python -m pytest -q tests/test_sealed_practice_trace_bound_episode.py
|
||||||
|
uv run ruff check generate/candidate_operator.py tests/test_candidate_operator*.py
|
||||||
|
uv run python -m compileall -q generate/candidate_operator.py tests/test_candidate_operator*.py
|
||||||
|
uv run python -m core.cli test --suite smoke -q
|
||||||
|
git diff --check
|
||||||
|
git diff --name-only origin/main...HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
If the replay allowlist is included in the same PR, also run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run ruff check generate/replay_adapter.py tests/test_replay_adapter_bound_attempt.py
|
||||||
|
uv run python -m compileall -q generate/replay_adapter.py tests/test_replay_adapter_bound_attempt.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. Merge Blockers For The Implementation PR
|
||||||
|
|
||||||
|
The implementation PR must not merge if:
|
||||||
|
|
||||||
|
- the operator-set table is dynamic or discovered from files;
|
||||||
|
- more than two operator rows appear in `candidate_operators.v2`;
|
||||||
|
- `missing_role_candidate.v1` behavior changes except for the expected
|
||||||
|
operator-set identity/version update;
|
||||||
|
- `quantity_entity_binding_candidate.v1` accepts any residual code other than
|
||||||
|
`local_binding_relation_unbound`;
|
||||||
|
- it produces more than one attempt per run;
|
||||||
|
- explanation text affects any digest;
|
||||||
|
- candidate output contains answer/proof/score/rank/selection/mutation fields;
|
||||||
|
- state-change, rate, currency-rate, percent, comparison, or prose-only
|
||||||
|
triggers produce a quantity-entity candidate;
|
||||||
|
- replay/binding/sealing behavior is weakened to accept malformed identity
|
||||||
|
chains;
|
||||||
|
- `ReplayAdapterRefusal` for unavailable quantity-entity replay is hidden or
|
||||||
|
coerced into success;
|
||||||
|
- any pack, teaching, policy, identity, runtime, Workbench, eval report, or
|
||||||
|
serving path is touched; or
|
||||||
|
- validation commands above fail.
|
||||||
|
|
||||||
|
## 9. Deferred Work
|
||||||
|
|
||||||
|
Deferred explicitly:
|
||||||
|
|
||||||
|
- `rate_application_candidate.v1`;
|
||||||
|
- `missing_rate_candidate.v1`;
|
||||||
|
- rate contract registry and rate residual taxonomy;
|
||||||
|
- replay execution for quantity-entity beyond the existing adapter shell;
|
||||||
|
- multi-operator orchestration or search fan-out;
|
||||||
|
- ranking, scoring, best-candidate selection, or answer production;
|
||||||
|
- any candidate promotion, learning, or pack mutation;
|
||||||
|
- dynamic operator discovery;
|
||||||
|
- filesystem, environment, network, time, random, runtime, or model-call
|
||||||
|
authority; and
|
||||||
|
- broad unit normalization or hidden conversion.
|
||||||
|
|
||||||
|
The natural next ADR after this one is a rate contract/residual authorization:
|
||||||
|
|
||||||
|
```text
|
||||||
|
rate_application contract
|
||||||
|
-> rate residual codes
|
||||||
|
-> eligible gate/budget rows
|
||||||
|
-> rate_application_candidate.v1
|
||||||
|
```
|
||||||
|
|
||||||
|
That sequence should reuse the evidence spine defined here rather than
|
||||||
|
shortcutting through the older reader/solver path.
|
||||||
Loading…
Reference in a new issue