[Auth] Expose OpenAI subscription execution eligibility to selection (M4.1) #82

Closed
opened 2026-09-15 23:36:05 +00:00 by kilo · 1 comment
Member

Goal

Extend the normalized contract with a versioned, provider-generic execution eligibility report so selection can distinguish, before routing:

  • OpenAI subscription execution safe now (eligible);
  • included allowance exhausted/unavailable (allowance_unavailable);
  • purchased-credit state present (policy_blocked — forbidden for autonomy by owner policy);
  • telemetry/auth unknown (unknown — fail closed).

The selector must exclude non-eligible providers through a first-class exclusion stage so the router selects the least-scarce capable and executable provider (e.g. Z.ai when OpenAI is unsafe) without any caller-side fallback.

Why

Owner policy change for M4.1 (Infrastructure/creatidy-autonomy): subscription allowance is allowed; purchased-credit usage is forbidden; API PAYG is forbidden. Today the Codex collector validates credits/spend-control/ordinary-usage telemetry but withholds it from normalized output (D-019 "validated-but-unrepresented"), and a credits.hasCredits=true account still produces a healthy status="ok" snapshot — the router would select OpenAI for an account state the owner forbids. Unsafe OpenAI must be excluded BEFORE routing instead of failing after selection.

Evidence re-verified 2026-09-16 (first-party): included usage is consumed first, then usage draws from the credit balance; no documented opt-out exists for draw-down; auto-reload is a UI setting; mid-task continuation can cross the allowance/credit boundary; balances can go negative (help.openai.com articles 12642688, 11369540).

Scope

  • New pure contract ExecutionEligibility (own schema_version: 1; closed state + reason-code vocabularies; strict serialization, unknown keys rejected; omitted-never-null), separate from CapacitySnapshot v3 — never collapsed.
  • Pure OpenAI eligibility parser over the same decoded account/rateLimits/read result (credits present/unknown, ordinaryUsageAllowed generation-aware, spendControlReached, rateLimitReachedType, individualLimit exhausted, upsell present, 100% main windows). Unknown == unsafe.
  • Acquisition: one app-server session produces snapshot + eligibility together; expected operational failures map to unknown eligibility with safe reason codes. Optional explicit binary_path override (production uses a pinned standalone install; VS Code extension discovery stays default).
  • Selector: new first-class execution exclusion stage + reason codes (execution_allowance_unavailable, execution_policy_blocked, execution_unverified), per-provider eligibility gate in the candidate pipeline, eligibility_reports input with at-most-one-per-provider discipline; decision D-039 in docs/decisions.md.
  • Status surface: REST/MCP /v1/status envelope gains an additive optional eligibility array (machine-interface v1 additive-domain-field rule); CLI --json snapshot-array shape unchanged.
  • Fixture/contract tests pinning serialization/deserialization both ways; parity preserved.

Non-scope

  • No billing logic in capability metadata; no new capacity schema version; no catalog/rating changes; no execution capability (router recommends, never executes); no new providers.

Acceptance criteria

  1. credits.hasCredits=true → OpenAI candidates excluded with execution_policy_blocked even when capacity windows are healthy.
  2. hasCredits missing/unreadable, ordinaryUsageAllowed not explicitly true, or spendControlReached unknown → execution_unverified (fail closed).
  3. Blockers/exhaustion (reached type, spend control, individual limit, upsell, 100% main windows, ordinaryUsageAllowed=false) → execution_allowance_unavailable.
  4. Collector/telemetry failure → snapshot unchanged semantics + unknown eligibility; safe Z.ai candidates remain selectable; no eligibility report for Z.ai (gate is a no-op there).
  5. Eligible report + healthy snapshot → selection behavior identical to today.
  6. Existing v1 clients stay valid; new fields are additive; CLI status --json byte-shape unchanged.
  7. uv sync --only-dev && make check && uv run basedpyright && git diff --check green; tests deterministic and quota-free.

Constraints

  • Security invariants (docs/security.md): no account ids, balances, tokens or raw payload text in any output; redacted fixtures only.
  • Closed vocabulary additions land as explicit decision D-039 with provenance and human-reviewable diff.
  • Purity boundaries hold: selector stays provider-independent; eligibility reports are typed inputs like snapshots.
  • Autonomy milestone: Infrastructure/creatidy-autonomy (M4.1 issue)
  • Production packaging: Infrastructure/creatidy-onprem (M4.1 runtime issue)
  • Supersedes-in-part D-019's "validated-but-unrepresented" outcome for execution gating; D-019 parser semantics otherwise unchanged.
## Goal Extend the normalized contract with a versioned, provider-generic **execution eligibility** report so selection can distinguish, before routing: - OpenAI subscription execution safe now (`eligible`); - included allowance exhausted/unavailable (`allowance_unavailable`); - purchased-credit state present (`policy_blocked` — forbidden for autonomy by owner policy); - telemetry/auth unknown (`unknown` — fail closed). The selector must exclude non-eligible providers through a first-class exclusion stage so the router selects the least-scarce **capable and executable** provider (e.g. Z.ai when OpenAI is unsafe) without any caller-side fallback. ## Why Owner policy change for M4.1 (Infrastructure/creatidy-autonomy): subscription allowance is allowed; purchased-credit usage is forbidden; API PAYG is forbidden. Today the Codex collector validates credits/spend-control/ordinary-usage telemetry but withholds it from normalized output (D-019 "validated-but-unrepresented"), and a `credits.hasCredits=true` account still produces a healthy `status="ok"` snapshot — the router would select OpenAI for an account state the owner forbids. Unsafe OpenAI must be excluded BEFORE routing instead of failing after selection. Evidence re-verified 2026-09-16 (first-party): included usage is consumed first, then usage draws from the credit balance; no documented opt-out exists for draw-down; auto-reload is a UI setting; mid-task continuation can cross the allowance/credit boundary; balances can go negative (help.openai.com articles 12642688, 11369540). ## Scope - New pure contract `ExecutionEligibility` (own `schema_version: 1`; closed state + reason-code vocabularies; strict serialization, unknown keys rejected; omitted-never-null), separate from CapacitySnapshot v3 — never collapsed. - Pure OpenAI eligibility parser over the same decoded `account/rateLimits/read` result (credits present/unknown, ordinaryUsageAllowed generation-aware, spendControlReached, rateLimitReachedType, individualLimit exhausted, upsell present, 100% main windows). Unknown == unsafe. - Acquisition: one app-server session produces snapshot + eligibility together; expected operational failures map to `unknown` eligibility with safe reason codes. Optional explicit `binary_path` override (production uses a pinned standalone install; VS Code extension discovery stays default). - Selector: new first-class `execution` exclusion stage + reason codes (`execution_allowance_unavailable`, `execution_policy_blocked`, `execution_unverified`), per-provider eligibility gate in the candidate pipeline, `eligibility_reports` input with at-most-one-per-provider discipline; decision D-039 in docs/decisions.md. - Status surface: REST/MCP `/v1/status` envelope gains an additive optional `eligibility` array (machine-interface v1 additive-domain-field rule); CLI `--json` snapshot-array shape unchanged. - Fixture/contract tests pinning serialization/deserialization both ways; parity preserved. ## Non-scope - No billing logic in capability metadata; no new capacity schema version; no catalog/rating changes; no execution capability (router recommends, never executes); no new providers. ## Acceptance criteria 1. `credits.hasCredits=true` → OpenAI candidates excluded with `execution_policy_blocked` even when capacity windows are healthy. 2. `hasCredits` missing/unreadable, `ordinaryUsageAllowed` not explicitly `true`, or `spendControlReached` unknown → `execution_unverified` (fail closed). 3. Blockers/exhaustion (reached type, spend control, individual limit, upsell, 100% main windows, `ordinaryUsageAllowed=false`) → `execution_allowance_unavailable`. 4. Collector/telemetry failure → snapshot unchanged semantics + `unknown` eligibility; safe Z.ai candidates remain selectable; no eligibility report for Z.ai (gate is a no-op there). 5. Eligible report + healthy snapshot → selection behavior identical to today. 6. Existing v1 clients stay valid; new fields are additive; CLI `status --json` byte-shape unchanged. 7. `uv sync --only-dev && make check && uv run basedpyright && git diff --check` green; tests deterministic and quota-free. ## Constraints - Security invariants (docs/security.md): no account ids, balances, tokens or raw payload text in any output; redacted fixtures only. - Closed vocabulary additions land as explicit decision D-039 with provenance and human-reviewable diff. - Purity boundaries hold: selector stays provider-independent; eligibility reports are typed inputs like snapshots. ## Links - Autonomy milestone: Infrastructure/creatidy-autonomy (M4.1 issue) - Production packaging: Infrastructure/creatidy-onprem (M4.1 runtime issue) - Supersedes-in-part D-019's "validated-but-unrepresented" outcome for execution gating; D-019 parser semantics otherwise unchanged.
Author
Member

Closeout (v0.1.0 release-readiness reconciliation, 2026-09-21). The D-039 execution-eligibility work this issue describes is implemented, merged and pinned by tests; A0 (#85) already recorded that "its scope is complete and it remains a separate open issue for its own closeout" — this is that closeout.

  • Contract: ExecutionEligibility v1 (eligibility.py, own schema_version, closed state/reason vocabularies, strict two-way serialization) and the pure OpenAI eligibility parser (providers/openai_eligibility.py) — 23 contract tests in tests/test_eligibility.py, 89 acquisition/parser tests in tests/test_openai_codex_acquisition.py.
  • Selector integration: first-class execution exclusion stage with the acceptance criteria mapped to named tests in tests/test_selector_eligibility.py: test_policy_blocked_excludes_openai_and_zai_is_selected (criterion 1), test_unverified_excludes_openai_fail_closed (criterion 2), test_allowance_unavailable_excludes_openai (criterion 3), collector-failure → unknown with Z.ai unaffected and no-report baseline behavior (criterion 4/5, test_no_reports_matches_documented_baseline, test_openai_still_selected_when_eligible_and_zai_lacks_capacity), additive envelope parity with unchanged CLI status --json shape (criterion 6, M08 guardrails).
  • Decision D-039 recorded in docs/decisions.md with provenance; the later -32603 auth-failure classification fix (#101) landed separately and merged 2026-09-20.
  • Validation: full gate green at develop @ 9d6192e (1868 tests, basedpyright clean), Forgejo CI green (run #25).

Closing as complete.

**Closeout (v0.1.0 release-readiness reconciliation, 2026-09-21).** The D-039 execution-eligibility work this issue describes is implemented, merged and pinned by tests; A0 (#85) already recorded that "its scope is complete and it remains a separate open issue for its own closeout" — this is that closeout. - Contract: `ExecutionEligibility` v1 (`eligibility.py`, own `schema_version`, closed state/reason vocabularies, strict two-way serialization) and the pure OpenAI eligibility parser (`providers/openai_eligibility.py`) — 23 contract tests in `tests/test_eligibility.py`, 89 acquisition/parser tests in `tests/test_openai_codex_acquisition.py`. - Selector integration: first-class `execution` exclusion stage with the acceptance criteria mapped to named tests in `tests/test_selector_eligibility.py`: `test_policy_blocked_excludes_openai_and_zai_is_selected` (criterion 1), `test_unverified_excludes_openai_fail_closed` (criterion 2), `test_allowance_unavailable_excludes_openai` (criterion 3), collector-failure → `unknown` with Z.ai unaffected and no-report baseline behavior (criterion 4/5, `test_no_reports_matches_documented_baseline`, `test_openai_still_selected_when_eligible_and_zai_lacks_capacity`), additive envelope parity with unchanged CLI `status --json` shape (criterion 6, M08 guardrails). - Decision D-039 recorded in `docs/decisions.md` with provenance; the later `-32603` auth-failure classification fix (#101) landed separately and merged 2026-09-20. - Validation: full gate green at `develop` @ `9d6192e` (1868 tests, basedpyright clean), Forgejo CI green (run #25). Closing as complete.
kilo closed this issue 2026-09-21 17:07:05 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Creatidy/scarcity-router#82
No description provided.