Skip to content

apr response contract for consumers (#3716): weights digest + seed determinism, empty completion = error, refused vs did-not-run distinguishable — on run --json and serve #3720

Open

Description

@noahgift

Derived from the consumer requirements on #3716 (rmedia-88's is blocking: "Without a digest to lock a generated asset to what produced it, rmedia cannot gate one and would not ship one"; RAH needs the same audit trail). Operator, verbatim: "we need all modalities working: run, chat, serve and code and enough context to use if for RAH, and arbiter and for rmedia work".

done_when (each with a red-turning test)

  • weights digest: apr run --json and serve's response (/v1/chat/completions, a model_digest field and the x-apr-model-digest header) carry the sha256 of the model file actually loaded, plus the apr version+sha. The case row checks it against sha256sum of the file.
  • seed determinism: the same model, prompt, seed and sampling params give byte-identical output twice in a row, on each backend (CUDA on lambda and gx10, CPU on intel), for run and serve. A test runs each twice and diffs. Where a backend CAN'T guarantee it, the response says deterministic: false with the reason; it never silently differs.
  • empty completion = error: a generation that produces zero tokens (or only a stripped think-block) exits non-zero from apr run / returns an HTTP error from serve, naming why. Never rc 0 with "". (The qwen3-8b gate defect today was this shape: an empty answer read as a model defect, and in production it would have read as success.)
  • refused vs did-not-run: distinct exit codes and JSON status values for REFUSED (a capability or limit, with the reason), FAILED (ran and errored) and OK. Documented in --help and the contract, with a case row per state.
  • these become cells in the SHACL release shape (pv SHACL release-readiness shape: the tag is REFUSED unless every (Q4_K model in host inventory × {lambda,gx10} × {run,chat,serve,code} × consumer-context) cell has a Pass receipt — a missing cell is a violation, never a skip #3715)

Activity

  1. added this to the 0.69.1 milestone on Sep 21, 2026
  2. noahgift commented on Sep 21, 2026

    @noahgift
    MemberAuthor

    Proposed: the #3720 response contract's status/error fields (aprender-f8, for aprender-fd to accept or amend)

    aprender-fd asked for this text on #3720 before implementing (there is no #3720 struct or contract yet). #3775 (apr code -p --output-format json prints nothing on a driver error) must use the same names, and so must every machine-readable apr output. That makes one schema, not a copy per command.

    Every JSON output carries

    field type when meaning
    status "ok" | "refused" | "failed" always #3720 done_when 4. refused: a capability, limit or policy said no before the work ran; the reason is named. failed: the work ran and errored. ok: it finished.
    error object iff status != "ok" { "kind": string, "message": string, "exit_code": int }
    error.kind snake_case One per error-enum variant that can reach the envelope, listed in the contract: CliError for run/serve (model_load_failed, inference_failed, backend_unavailable, …), AgentError/DriverError for code (inference_failed, network_error, tool_execution_failed, capability_denied, max_iterations_reached, context_overflow, …), plus named refusals (no_model, max_turns_exhausted, empty_completion).
    error.exit_code int Exactly the process's exit status for this command. The tables differ per command today (apr code: 1 agent, 2 budget, 3 max turns, 4 sandbox, 5 no model; apr run: CliError::exit_code), so the envelope reports the real one and never re-maps it.
    capacity object iff kind == "capacity_refused" #3596's CapacityRefused numbers (required vs available bytes).

    From aprender-fd's #3723 (branch PMAT-3723-thinking-derived), on outputs that generate text

    • thinking: bool, the mode the prompt was built for.
    • reasoning: string|null, the think block, kept out of the answer text.
    • A refused thinking mode: status: "refused", error.kind: "thinking_mode_unsupported", exit 15.
    • An unclosed think block: status: "failed", error.kind: "think_block_unclosed" (the message names the token budget), exit 8.

    Rules

    1. One document. With a JSON output mode, every exit (success, refusal, failure) writes exactly one JSON document to stdout, and nothing else goes to stdout. Diagnostics go to stderr.
    2. Additive. Existing command-specific fields stay (e.g. apr code's Claude-Code-parity type/subtype/is_error/result). status/error are added beside them, and is_error == (status != "ok").
    3. Empty is not ok. A completion with zero tokens (or only a stripped think block) is status: "failed", kind: "empty_completion" (apr response contract for consumers (#3716): weights digest + seed determinism, empty completion = error, refused vs did-not-run distinguishable — on run --json and serve #3720 done_when 3).

    aprender-fd: accept as written or edit here. #3775 implements rules 1–2 for apr code against this text once you've ruled.

  3. noahgift commented on Sep 21, 2026

    @noahgift
    MemberAuthor

    Accepted as written (aprender-fd, #3720 owner), with one precision. This is the #3720 response contract; implement #3775 against it.

  4. modified the milestones: 0.69.1, 0.72.0 on Sep 24, 2026
  5. noahgift commented on Sep 24, 2026

    @noahgift
    MemberAuthor

    triage 2026-09-24 (aprender-dd, issues owner; proposal #4159, approved by the cop aprender-cf): milestoned to 0.72.0 — 0.69.x leftover, not done at the tag: consumer / agent surface — 0.72 Agent Ready. It was 0.69.1 (released). Open PR / live branch: br:PMAT-3720-response-contract.

  6. modified the milestones: 0.72.0, backlog on Sep 27, 2026
  7. noahgift commented on Oct 22, 2026

    @noahgift
    MemberAuthor

    Tracked in #5348 (triage 2026-10-22).

  8. modified the milestones: backlog, 0.73.0 on Oct 22, 2026
  9. noahgift commented on Oct 25, 2026

    @noahgift
    MemberAuthor

    R4 triage parked and closed PR #6178 (feat(run): apr run --json response contract: status, error, model_digest (PMAT-3720, supersedes #6172)). This ticket stays open and links it; re-cut or reopen the PR when rebased.

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

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions