Skip to content

[P1] Enforce model call, token, context, latency, retry, failover, and cancellation budgets #179

Closed

Description

@wsdt

Summary

Define and enforce the operational policy around live model invocation. Every call, retry, failover, streamed byte, context token, output token, elapsed deadline, and cost reservation must be bounded and reflected in evidence.

Why this matters competitively

Neam exposes budgets and guards; production orchestration products expose routing and retry. Semaprax can outperform them by making these policies deterministic intersections of source ceilings, deployment ceilings, and invocation budgets with explicit uncertainty semantics.

Current state at the audit baseline

  • Agent deployments and typed effect runs already intersect several call/byte/turn ceilings.
  • Per-operation checkpoints and uncertain-effect handling exist for tool effects.
  • Live model provider calls and provider billing are not yet integrated.
  • Cancellation cannot be assumed to cancel remote provider work.

Audit baseline: ae25c6a49dc09ec4613c7a6f52a27daa69dcd3f3 (2026-09-11). Recheck main, this issue, and the owning specifications before changing code; later commits may have superseded individual details.

Required outcome

Introduce a provider-neutral model policy that reserves budgets before dispatch, tracks attempted and accepted usage, applies bounded retry/failover only for explicitly safe failure classes, and never treats cancellation or transport failure as proof of non-occurrence.

In scope

  • Maximum calls, retries, providers, context bytes/tokens, output bytes/tokens, aggregate tokens, latency/deadline, and estimated cost.
  • Source/deployment/invocation ceiling intersection.
  • Retryability classification and idempotency/uncertainty rules.
  • Provider failover with exact deployment policy.
  • Cooperative cancellation and terminal uncertainty.
  • Checkpoint behavior before and after model dispatch.
  • Usage accounting on malformed or rejected output.

Explicitly out of scope

  • Unlimited automatic retry.
  • Retrying an uncertain billable call as though nothing happened.
  • Using provider-reported token counts as unverified language truth.
  • Selecting providers dynamically outside the deployment policy.
  • Refunding budgets automatically on provider or decoder failure.

Starting points in the repository

  • AgentDeployment and Direct Runtime v2
  • AGENT-OPERATION-CHECKPOINT-V2.md
  • typed effects budget code
  • model.invoke contract
  • execution/evidence root modules

Use rg to find the current owners of the named schema, diagnostic, operation, or test before editing. Do not assume a path is still authoritative merely because it is listed here.

Implementation sequence

  1. Specify ModelBudget v1, ModelRetryPolicy v1, and a closed terminal/failure classification. Separate requested, reserved, attempted, provider-reported, locally measured, accepted, and reconciled usage.
  2. Compute effective limits as the minimum of source Agent ceilings, deployment policy, and invocation budget. Reject contradictory or zero-impossible policies before handler access.
  3. Reserve one call and worst-case or configured cost/token capacity before dispatch. Persist intent in durable mode before calling the provider.
  4. Classify failures into definitely-not-dispatched, rejected-before-processing, completed-with-response, uncertain, and provider-reported retryable. Automatic retry is allowed only for classes explicitly proven safe by the adapter contract.
  5. Implement bounded backoff as deterministic policy data. Clock waiting is a host effect; policy computation itself stays pure.
  6. For failover, bind the ordered provider/model alternatives and maximum attempts into DeploymentRoot. Every attempt receives a unique ordinal and evidence record.
  7. Check cancellation before dispatch, during streaming, and after provider completion. A cancelled wait may yield uncertain remote status and must retain reserved/attempted usage.
  8. Stop decoding and further attempts when any aggregate ceiling is reached. Record the exact limit that selected the terminal result.

Failure and security cases that must be considered

  • Provider timeout can occur after the provider completed and billed the call.
  • Retrying structured-output rejection may amplify cost unexpectedly.
  • Provider tokenizers differ; local estimates and provider usage must remain distinct.
  • Backoff can exceed the invocation deadline.
  • Failover can unintentionally expose data to a provider not authorized for the task.

Required tests and evidence

  • Exact boundary and maximum-plus-one tests for every budget.
  • No dispatch on missing capability, invalid policy, exhausted reservation, stale deployment, or pre-cancellation.
  • Retry occurs only for declared safe classes and never for uncertain delivery.
  • Failover follows the exact ordered deployment policy and confidentiality classification.
  • Cancellation at every boundary produces deterministic local state and honest remote-status uncertainty.
  • Malformed/rejected output still charges attempted bytes/tokens/calls as specified.
  • Durable replay performs no repeated provider call for recorded completion.

Acceptance criteria

  • Every model attempt is bounded before dispatch.
  • Retry/failover behavior is deterministic, deployment-bound, and evidence-bearing.
  • Cancellation never falsely asserts provider non-occurrence or no billing.
  • Usage fields distinguish estimates, local measurements, provider reports, and reconciliation.
  • No automatic budget refund or authority widening occurs.

Dependencies and sequencing

  • [P0] Add a provider-independent model.invoke effect to the source-native Agent runtime
  • [P0] Derive streaming structured-output decoding from the checked Proposal type

Rules for the implementing agent

  1. Read AGENTS.md, docs/COMPLETION-MATRIX.md, docs/ROADMAP.md, docs/QUALITY-GATES.md, and every owning versioned specification named above before editing.
  2. Prefer extending the existing semantic object, operation table, diagnostic catalogue, profile dispatcher, or evidence harness. Do not create a parallel source of truth.
  3. Preserve canonical source, persistent identities, revision binding, fail-closed replay, deterministic rendering, and the separation between evidence and authority.
  4. A local test is local evidence. A hosted run is hosted evidence. A generated artifact is not a support or publication decision. Record exactly what ran and do not widen claims.
  5. Any new wire format, ABI, descriptor, carrier, evidence document, or compatibility surface requires an explicit versioned schema, independent decoder/replay, hostile-input tests, bounds, and nonclaims.
  6. Never grant ambient filesystem, process, network, model, signing, payment, commit, publication, or registry authority merely because a declaration, receipt, test, or proof exists.
  7. Keep changes reviewable. Split implementation into commits that each preserve the existing test suite, and explain any known-answer update field by field.
  8. Do not weaken or delete a failing regression to obtain green CI. Fix the cause or narrow the admitted feature with a stable diagnostic.

Definition of done

The implementation, tests, documentation, status wording, diagnostics, and evidence agree on one exact scope. Targeted tests pass, the applicable repository quality gate passes, hostile cases fail with the documented stable reason, and no unrelated public surface or authority is widened.

Activity

  1. wsdt commented on Sep 12, 2026

    @wsdt
    MemberAuthor

    Landed (3afacaf4) — and this is not a duplicate of #113

    src/model_budget_policy/ (classification, limits, provider_policy,
    ledger) plus docs/MODEL-BUDGET-POLICY-V1.md. 38 selected, 38 passed in
    cargo test --locked -p semaprax --lib model_budget_policy. Coordinator re-ran
    fmt and clippy --all-targets -D warnings centrally, both exit 0.

    What #113 actually covers, verified rather than assumed

    Read 171a8751 and the current files, and grepped for enforcement code:
    one monetary ceiling and one absolute deadline (nonrefundable by construction in
    live_invocation::budget::CumulativeBudgetLedger), crash-safe resume by folding
    a journal prefix rather than a second durable counter, five cancellation
    checkpoints, and budget/deadline/cancellation kept as three distinct refusal
    reasons.

    The decisive finding: approved_providers exists only as an identity-binding
    field
    — order matters because it feeds the derived LiveInvocationId — and
    there is no failover mechanism, no retry loop, no retry classification, and no
    call/retry/provider ceiling anywhere in live_invocation
    . "retry" and
    "failover" appear only in doc comments; zero enforcement code.

    model_invoke.rs's own doc names the InvocationBudgetHook seam for "issues
    #113/#179". The two are complementary halves of it.

    dimension #113 this module
    money, deadline, 5 cancellation checkpoints done not duplicated
    max calls absent ModelBudgetLimits::max_calls
    max retries + outcome classification absent AttemptOutcomeClass / retry_is_permitted
    max providers, ordered confidentiality-checked failover identity field only ProviderPolicy::admit_failover
    context / output / aggregate token ceilings absent (money is opaque units) three ceilings in ModelBudgetLimits
    effective-limit intersection across source × deployment × invocation absent limits::intersect
    failover accounted distinctly, never a replay absent unique strictly-increasing ordinal + AttemptKind per reservation

    Nonrefundability, proven with numbers

    a_failed_attempt_then_its_retry_both_spend_and_neither_is_ever_refunded
    asserts cost_committed_micros moves 0 → 30 → 30 (recording the failure does
    not refund) → 31 (the retry adds its own cost), and
    aggregate_tokens_committed 0 → 20 → 22, with distinct ordinals. Remaining
    capacity decreases twice and is never restored.

    a_self_reported_zero_cost_outcome_never_reopens_spent_capacity closes the
    authority hole: a recorded outcome claiming zero cost cannot reopen an exhausted
    ceiling. Model data carries no authority.

    Every budget driven to its exact boundary

    • deadline: one_millisecond_before_the_deadline_is_admitted / exactly_the_deadline_instant_is_refused
    • max_calls: zero rejected at construction; the_call_exactly_at_max_calls_is_admitted_and_the_next_is_refused
    • max_retries: exact-then-refused, gated by an_uncertain_prior_outcome_never_permits_a_retry and a_completed_response_never_permits_a_retry_either
    • max_providers: exact-then-refused, plus failover_out_of_the_deployments_exact_order_is_refused, failover_to_an_unauthorized_provider_is_refused_even_if_it_is_next_in_order, and failover_is_refused_when_the_prior_outcome_was_uncertain
    • context / output tokens: exactly-at admitted, one-over refused
    • aggregate tokens: aggregate_tokens_exactly_exhaust_at_the_ceiling_across_two_attempts_the_third_is_refused
    • cost: exact exhaustion, plus a_negative_estimated_cost_is_refused_rather_than_treated_as_a_credit

    The intersection tests carry the control that matters most:
    max_retries_or_max_providers_at_zero_is_a_legal_restrictive_policy_not_a_rejection,
    paired with
    an_intersection_collapsing_max_calls_to_zero_is_refused_as_zero_impossible.
    Without that pair, "zero is rejected" would be indistinguishable from "zero is
    always rejected", and a legitimately restrictive policy would be unusable.

    Uncertainty is respected throughout: an uncertain prior outcome permits
    neither a retry nor a failover — the call may have happened, so treating it as a
    free escape hatch would double-spend a real request.

    Open

    ModelPolicyLedger is not wired into the live kernel loop or
    AgentDeployment — that downstream integration against the seam is #178–#181's,
    matching what live_invocation's own docs describe. No live network, provider or
    key; the existing public InvocationClock/StepClock fixture is reused rather
    than a second clock seam invented.

    Evidence class: local, offline fixtures. Not hosted.

  2. wsdt commented on Sep 12, 2026

    @wsdt
    MemberAuthor

    Audited against main at cc03e2e8. Not closing. The title says "Enforce", and nothing in the live system enforces anything yet.

    src/model_budget_policy/ is merged into main and is correct in isolation — but grep -rl "model_budget_policy" src returns only its own module. Nothing in src/live_invocation/kernel.rs calls ModelPolicyLedger::reserve_attempt. docs/MODEL-BUDGET-POLICY-V1.md says so itself under "Out of scope": wiring the ledger into the kernel loop "is downstream work." So today no dispatch — not even a fixture dispatch — is actually bounded by this policy.

    Criterion Status Evidence
    Every model attempt bounded before dispatch logic met, not integrated limits::intersect, ledger::reserve_attempt — exact-boundary and limit-plus-one tests for every named dimension. No caller.
    Retry/failover deterministic, deployment-bound, evidence-bearing logic met, not integrated provider_policy::admit_failover, classification::retry_is_permitted. The doc states the kernel has "no retry or failover primitive anywhere" today.
    Cancellation never falsely asserts non-occurrence or zero billing met at the classification level uncertain_is_never_retry_permitted; a self-reported zero-cost outcome never reopens spent capacity. This is a proof about the taxonomy, not about live cancellation — that machinery belongs to #113.
    Usage distinguishes estimate / local / provider / reconciled met record_outcome is evidence-only and never adjusts committed counters (a_failed_attempt_then_its_retry_both_spend_and_neither_is_ever_refunded)
    No automatic refund or authority widening met as above; ModelBudgetLimits::validate refuses a zero or negative policy before a ledger can be constructed

    Evidence class: local re-runnable, offline fixtures only. No live provider, credential, network call, or billing record exists anywhere in this code — that half is HUMAN_BLOCKED and cannot be produced by this run.

    Remaining for closure: one caller. Until live_invocation::kernel consults the ledger on the attempt path, "enforce" is not a claim this issue can make.

  3. wsdt commented on Sep 13, 2026

    @wsdt
    MemberAuthor

    Landed live budget-hook integration on main at 852aa90.

    LiveModelPolicyHook composes the existing model-policy ledger with the actual generic kernel work-budget seam. Explicit host quotes bind the exact request digest and declare context/output token and cost upper estimates. Stale/negative/overflowing quotes refuse before reservation. Intersected call/token/cost ceilings now stop actual dispatch; original-start latency becomes one checked absolute deadline, and cancellation/deadline are checked at existing kernel boundaries. Conservative model reservations remain charged if the later work gate refuses, and an altered effective work amount refuses rather than breaking quote identity.

    The actual two-turn kernel test dispatches once, refuses the second call, preserves all call/token/cost charges, and emits a compatible journal receipt. The streaming bridge test also uses this hook, so budget, compiled streaming decode and receipts are connected. Byte observations are never invented as actual token usage or actual cost.

    Remaining: this hook retains in-process state only. Generic v1 journal bytes cannot restore token/cost reservations after a crash; source priced V4/V5 recovery remains separate. Generic turns are fresh attempts on an explicitly selected provider, not a durable retry/failover scheduler. Cross-route coverage and real usage/accounting remain open.

    Local focused evidence: 56 selected library tests passed (one serial build), including the new integration paths, model-policy preservation tests, and receipt projection. Module-size, format and diff checks passed. No full quality suite, hosted CI, live provider or billing claim.

  4. wsdt commented on Sep 13, 2026

    @wsdt
    MemberAuthor

    Pushed implementation batch 6b6d691 to main (starting at 02287bd).

    The public RetryFailoverScheduler now drives actual SDK adapters through explicit factories, reserving each unique attempt before factory/start. It retries only declared safe classifications, keeps partial/uncertain/completed outcomes terminal, follows ordered authorized provider slots, preserves every reservation/result in a bounded in-process trail, and exposes host backoff without ambient clocks or waiting. The compiled route derives the existing exact adapter prompt from the retained checked schema and logical request, and rejects malformed final output. Cancellation/deadline checks cover factory/start/poll/settlement boundaries; both existing streaming bridges also recheck after a settlement poll returns.

    Fixed standalone ledger provider checks for fresh/retry attempts and checked token addition. No reservation is refunded. Partial provider usage remains partial rather than becoming invented zero billing. Focused local evidence: 48 policy tests and 65 SDK tests passed, including actual compiled success, stale grammar/no-factory, malformed completion, safe retry, ordered failover and uncertain no-retry controls. No full quality or hosted gates were run, per maintainer speed instruction.

    Still open: this is an in-process host scheduler, not durable generic journal recovery or a new authenticated DeploymentRoot policy route. Source/deployment/invocation cross-route enforcement and durable attempt restoration remain. No live-provider or billing claim. Logs: .agent-logs/0913-policy-corrected.log and .agent-logs/0913-provider-corrected.log in the private integration checkout.

  5. wsdt commented on Sep 13, 2026

    @wsdt
    MemberAuthor

    Implemented and pushed 70e3ce9821facc9d6d37f7b5c425a572f95c2f9a (from efa95897ca8be308ef054c0b083e8436dfab2dfb).

    The additive generic durable policy v2 now checkpoints intent before adapter construction and settlement afterward; unresolved intent never redispatches. Recovery validates canonical request/quote bytes, sequence, nonrefundable reservations, safe failure classes, streamed-byte contradictions, and exact capability requirements. Retained execution, source Proposal schema, task/budget, deployment provider/model selections, and seven source/deployment budget dimensions are checked before dispatch. The constructed adapter must declare the exact selected provider/model/capabilities before start. This declaration is not remote identity or billing proof.

    Focused local evidence on the combined build: 6 journal tests, 4 durable kernel tests, 6 existing scheduler regressions, and 2 public retained-execution binding/dispatch tests passed (18 relevant tests). Commands used the existing library and agent_runtime_v1 harnesses with selectors live_invocation::policy_journal::tests, live_invocation::policy_kernel::tests, model_budget_policy::retry::tests, and execution_revision::durable_policy. Logs: .agent-logs/0913-combined-core-final.log, .agent-logs/0913-combined-agent-final.log. The latter also selected an unrelated #116 repair regression that failed; both #179 tests passed. No full/hosted/live-provider claim.

    Keeping this issue open. Remaining functional work includes durable cumulative request/response byte ceilings, complete malformed-output usage retention, and explicit treatment of aggregate-token authority where the retained source profile has no matching token dimension. A post-failed-poll cancellation check and an additive byte-accounted profile are in progress; v2 wire bytes remain frozen.

  6. wsdt commented on Sep 13, 2026

    @wsdt
    MemberAuthor

    Accepted bbf9a95 → c02b49e on main (implementation a644d37; preserved concurrent README-only b33a8af).

    Added an executable durable V3 byte-accounting route over the existing retry scheduler. It derives exact serialized SDK envelope sizes through the scheduler projector, intersects retained source/deployment byte ceilings with narrower invocation limits, and persists nonrefundable per-attempt and cumulative reservations before factory creation. The outer checkpoint preserves frozen canonical V2 bytes; replay reconstructs accounting, rejects altered/noncanonical bytes, and never redispatches an unresolved intent. Failed or settled output above its reservation refuses the outcome and leaves the intent uncertain. Atomic append controls prevent partially updated byte ledgers.

    Post-poll cancellation is checked even when the poll returns an otherwise safe zero-byte failure. The regression proves one charged factory attempt and no retry/fallback.

    Local focused evidence: 4 V3 journal tests, 4 executable V3 route tests, 3 byte-ledger/binding tests, 1 post-poll cancellation test, and 2 retained execution/schema/model binding tests passed. Logs .agent-logs/0913-v3-{journal,route,bytes,cancel,retained}.log. An independent review checked envelope equality, retained-key compatibility, recovery and ACK order. No full/hosted/provider-billing claim.

    #179 remains open: this does not establish remote provider cancellation or actual reported-usage completeness after malformed schema settlement; broader end-to-end budget evidence remains distinct from these local durable guarantees.

  7. wsdt commented on Sep 18, 2026

    @wsdt
    MemberAuthor

    Already implemented — verified against code and executing tests, closing

    This issue's audit baseline is ae25c6a4 (2026-09-11) and it warns that later
    commits may have superseded individual details. They have. A full re-audit of
    the eight implementation steps and all seven required-evidence items against
    current main finds every one satisfied by code with a passing executable test.
    No code was changed to close this.

    Implementation sequence

    Step Status Evidence
    1. ModelBudget v1 / ModelRetryPolicy v1, closed classification, usage-stage separation SATISFIED AttemptOutcomeClass (src/model_budget_policy/classification.rs:30-61) is the closed 5-way set; AttemptRequest/AttemptReservation/AttemptUsage (ledger.rs:64-148) separate requested/reserved/attempted/accepted; TokenAccountingSource (src/provider_adapter_sdk/capability.rs:53-63) + BillingReconciler::reconcile_provider_reported (model_call_receipt/reconciliation.rs:333-432) add provider-reported vs reconciled
    2. Effective limits = min(source, deployment, invocation); reject contradictory/zero-impossible before handler access SATISFIED intersect (limits.rs:157-167) elementwise-mins all three then validate()?. EffectiveModelBudget (limits.rs:143) has no bare public constructor and I confirmed zero in-crate bare constructions outside limits.rs, so an unvalidated budget cannot reach a handler
    3. Reserve call + worst-case capacity before dispatch; persist intent durably first SATISFIED ModelPolicyLedger::reserve_attempt (ledger.rs:308-434) commits nonrefundably before provider contact; RetryAttemptJournal::intent at retry.rs:616 precedes adapter.start() at retry.rs:852
    4. Retry only for adapter-contract-proven-safe classes SATISFIED retry_is_permitted (classification.rs:86-93) admits only NotDispatched, RejectedBeforeProcessing, ProviderReportedRetryable; retry.rs:1009-1018 additionally intersects that with the adapter's own declared capabilities().retryable_failure_classes
    5. Bounded backoff as pure policy data; clock-wait is a host effect SATISFIED RetryBackoff (retry.rs:88-90) is an injected pure callback; the scheduler never reads a clock or sleeps
    6. Failover bound to ordered deployment alternatives, unique ordinal + evidence per attempt SATISFIED ProviderPolicy::admit_failover (provider_policy.rs:108-129) enforces exact order and confidentiality authorization; DurablePolicyBinding::bind (durable.rs:73-286) binds to ExecutionRevision/DeploymentRoot; AttemptReservation.ordinal strictly increasing (ledger.rs:84-93)
    7. Cancellation at every boundary; cancelled wait retains usage, never proves non-occurrence SATISFIED checked at four points in retry.rs::drive (:783, :836, :865, :894), always mapping to Uncertain/NotDispatched, never a false "did not happen"
    8. Stop on any aggregate ceiling, record the exact selecting limit SATISFIED AttemptRefusal (ledger.rs:98-137) names the dimension and the numbers (CallsExhausted, AggregateTokensExhausted{requested,remaining}, CostExhausted{...}) and propagates via SchedulerRefusal::Policy

    Required tests and evidence

    • Exact boundary and max-plus-one for every budget — ledger.rs carries the exhaustive _exactly_at_the_ceiling..._next_is_refused family across calls, retries, providers, context tokens, output tokens, aggregate tokens, cost and deadline.
    • No dispatch on missing capability / invalid policy / exhausted reservation / stale deployment / pre-cancellation — negotiate() gate (retry.rs:828-835), limits.rs rejection tests, public_compiled_route_refuses_stale_grammar_before_factory, a_pre_cancelled_attempt_is_refused_and_commits_nothing.
    • Retry only for declared safe classes, never for uncertain delivery — an_uncertain_prior_outcome_never_permits_a_retry, a_completed_response_never_permits_a_retry_either, an_uncertain_post_start_failure_never_constructs_a_fallback_adapter.
    • Failover follows exact order and confidentiality classification — an_unauthorized_next_in_order_provider_is_refused_and_never_silently_skipped, failover_to_an_unauthorized_provider_is_refused_even_if_it_is_next_in_order, failover_out_of_the_deployments_exact_order_is_refused.
    • Cancellation yields deterministic local state and honest remote uncertainty — retry/tests/cancellation_tests.rs:34 cancellation_after_poll_prevents_a_zero_byte_safe_failure_retry asserts the factory ran exactly once and run.attempts.len() == 1 with the comment "the pre-dispatch reservation remains charged". I read this test body directly to confirm it proves retention rather than merely naming it.
    • Malformed/rejected output still charges — retry.rs:1448 malformed_completed_response_is_not_reported_as_public_success classifies CompletedWithResponse (charged), not a silent retry.
    • Durable replay performs no repeated provider call — src/live_invocation/policy_kernel/tests/v3_tests.rs:47 v3_settles_then_terminal_replay_constructs_zero_adapters, asserting replay_calls == 0 at :111.

    Verification run

    cargo test --locked -p semaprax --lib -- model_budget_policy:: model_call_receipt::
    test result: ok. 124 passed; 0 failed; 0 ignored; 0 measured; 3483 filtered out
    

    Nonclaims and residual scope

    • All evidence here is local and simulated — ScriptedAdapter, StepClock, fixture factories. Nothing in this module has contacted a live provider, by design: the capability invariant forbids ambient network authority, and credentials belong to the host via the adapter seam. This is not hosted or production evidence and is not described as such. A live-provider run belongs to the adapter integrations ([P1] Create a provider adapter SDK and deterministic model-provider conformance suite #181), not here.
    • Minor, not a gap in behavior: DurablePolicyBindingRefusal has 14 variants and roughly 5 have direct per-variant coverage. The uncovered ones (EmptyProviderPolicy, ProviderOrderMismatch, ProviderIdTooLong, DeadlineOverflow, SchemaMismatch, ProviderNotDeploymentSelected, ContextLimitExceedsDeployment, AdapterRequestBytesExceeded) are structurally simple guards. The issue's required-evidence list does not demand per-variant exhaustiveness, so this is noted rather than treated as blocking.
    • EffectiveModelBudget's inner field is pub(crate). No in-crate code bypasses intersect today (verified by grep), but the type does not structurally prevent a future in-crate bypass. Worth a follow-up only if that becomes a real risk.

    Closing as already satisfied.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions