Public recommendation contract: expose catalog reasoning_effort on serialized CandidateEvaluation #148
Labels
No labels
accessibility
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
P1
P2
P3
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Creatidy/scarcity-router#148
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Expose the reasoning effort belonging to every serialized recommendation candidate through the stable public recommendation contract, so independent consumers (e.g. Creatidy Kernel) can execute the exact recommended configuration without parsing opaque identifiers.
This closes the contract gap discovered by Creatidy Kernel K8 (Kernel issue #11): the Kernel public-contract gate failed because
/v1/selectcould not convey the exact execution configuration without forbidden inference. This is an upstream Scarcity Router contract correction, not a Kernel-specific compatibility hack.Required semantics
Extend the public
CandidateEvaluationserialization with:REASONING_EFFORTSvocabulary).nullmeans the candidate has no configured reasoning effort.nullis distinct from the real configured effort value"none".variant, model name, resource id, source id, display name or other opaque identity; no new mapping or inference rule is introduced.Because
SelectionDecisionreusesCandidateEvaluation, the same field and semantics must appear consistently inselected,alternatives,excluded,closest_candidatesandrecoverable_candidates. No specialselected.reasoning_effortrepresentation.Public surfaces (one-core parity)
The field must propagate consistently through all existing recommendation surfaces that serialize
SelectionDecision:/v1/selectscarcity_select/v1/selectNo selection logic in any transport adapter.
Versioning
Verified:
docs/machine-interfaces.md(Versioning section) states additive backwards-compatible domain fields may flow through machine-interface v1 as long as existing v1 clients remain valid (the D-039eligibilityprecedent). This change keeps envelope/schema version 1 and is documented as an additive-compatible extension. No version bump, no compatibility serializer.Provenance and source of truth
reasoning_effortmust come from the same catalog entry that participates in the authoritative selector ranking (the ranking already consumes the catalog effort as the D-032 effort tie-break). It must NOT be reconstructed after selection fromModelIdentity.variant. The serialized field reflects the exact candidate catalog configuration used by that decision.Required regressions
Discriminating tests proving at minimum:
null(key present);"none"serializes as"none", notnull;variantdiffers from its reasoning effort still serializes the catalog reasoning effort (no inference from variant);Scope constraints (do NOT)
main, release or deployment.This issue only makes an already-existing authoritative selection input visible in the public decision output. Documentation updates:
docs/machine-interfaces.md(additive v1 note),docs/selection-policy.md(records the D-032 deferral of public explicit effort output — superseded by this change),docs/architecture.md, and a decision record indocs/decisions.md(serialized public contract change requires an explicit decision per repository policy).Cross-project evidence
Motivating external-consumer proof: Creatidy Kernel K8 issue #11 — public-contract gate failed because
/v1/selectcould not convey the exact execution configuration without forbidden inference. Recorded as evidence only; the repositories are not coupled operationally.Workflow
Branch from
develop, PR targetsdevelop, PR left open (unmerged) for human review.ALREADY_SATISFIED — closed; current
develop(17f4918) fully implements this contract. Disposition pass 2026-09-30; no code changes made.The work landed via PR #149 (merged as
81b8e63, D-057 indocs/decisions.md) and is an ancestor of currentorigin/develop. Evidence against the acceptance criteria:Contract & provenance
CandidateEvaluation.reasoning_effort: str | Noneis a required construction argument (no default) —scarcity_router/selector.py:976— so a candidate cannot exist without its catalog provenance; the docstring pins the no-inference rule.policy_blackout, hard-constraint, capability, reservation — plus the eligible path) passesreasoning_effort=entry.reasoning_effortverbatim from the rankedModelCatalogEntry(selector.py:1247–1408). Nothing reconstructs it fromvariantpost-selection.to_dict()always emits the key (selector.py:1162): unconfigured serializes as explicitnull, configured"none"as"none"— materially distinct.REASONING_EFFORTSvocabulary (selection_types.py:73; enforced atselector.py:1007). BecauseSelectionDecisionreusesCandidateEvaluation,selected,alternatives,excluded,closest_candidatesandrecoverable_candidatesall carry it with one representation.Surfaces (one-core parity) —
tests/test_interfaces_guardrails.py:912test_select_reasoning_effort_parityasserts byte-equal payloads across direct core, CLI--json, loopback REST/v1/selectand MCPscarcity_select, walking all five candidate lists and comparing each value to the ranked catalog entry;tests/test_control_api.py:250covers the authenticated control/v1/select;test_remote_select_passes_reasoning_effort_through_verbatim(test_interfaces_guardrails.py:1336) proves the bridge neither strips nor invents the member.Discriminating regressions (issue points 1–9) — all present and passing:
test_decision_walk_serializes_catalog_effort_everywhere,test_alternatives_serialize_their_own_efforts_independently,test_capacity_excluded_and_recoverable_candidates_preserve_the_field,test_closest_candidates_preserve_the_field,test_unconfigured_effort_serializes_explicit_null,test_configured_none_serializes_as_none_not_null,test_effort_is_never_inferred_from_variant(variant"medium"/catalog"high", and variant"high"/unconfigured →null),test_ranking_still_consumes_the_catalog_effort(non-effort variant names, frozen tie-break unchanged),test_constructor_rejects_effort_outside_the_vocabulary, plus vocabulary round-trip/boundary tests intest_selection_types.py.Versioning & docs — envelope
schema_versionstays 1; additive v1 note indocs/machine-interfaces.md(~line 246); the D-032 deferral supersession recorded indocs/selection-policy.md(~line 138);docs/architecture.md(~line 213); decision record D-057 indocs/decisions.md.Commands run (all green):
uv run python -m unittest tests.test_selector -k effort(15 tests),tests.test_selection_types -k effort(3),tests.test_interfaces_guardrails -k reasoning_effort(2),tests.test_control_api -k reasoning_effort(1).