Skip to content

Quote holds for the program, not only the given options - #53

Closed
rawwerks wants to merge 2 commits into
fix/hold-quote-parametersfrom
feat/program-aware-quote
Closed

rawwerks wants to merge 2 commits into
fix/hold-quote-parametersfrom
feat/program-aware-quote

Conversation

@rawwerks

@rawwerks rawwerks commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

I'm an AI agent (Claude Code) opening this PR on behalf of Ray (@rawwerks). He asked for this change and approved opening the PR, but he did not write the text below.

Stacked on #51. It targets #51's branch and should be retargeted to main once #51 merges.

Why

#51 made quotes send the options the user gave, but a quote still could not include a program's own run: settings or its declared tools, because the CLI never parses program text. The hosted service now prices a program itself, in two ways:

  • GET /run/quote?program_ref=OWNER/SLUG[@REV]:
    • It prices a saved program.
    • Explicit model, reasoning_effort, environment and repositories override the program's values.
    • Public programs need no key, and private ones need the owner's.
  • POST /run/quote: it takes the same body and query as POST /run.
  • Both: the responses add basis, which gives each input (model, reasoning_effort, environment, tools, repositories) as {value, source}, with source one of request, program, job_default (the default for a job's type) or default.

What changes

Requests.

  • run submit / run create plan: sends its exact submission body to POST /run/quote, with the POST /run query minus live and session, using the key. The quote now covers that run exactly: content or the pinned program_ref, the model, effort and repositories. It stays advisory.
  • run quote FILE|-: sends POST /run/quote with the program, built as run submit builds it but without inputs. It sends the key when one is configured, or goes anonymous otherwise. It applies run submit's option checks, including the offered environment and runtime from the /health it already reads.
  • run quote --from REF: sends GET /run/quote?program_ref=.
    • OWNER/SLUG and OWNER/SLUG@REV are sent as given.
    • A bare SLUG, an @N, or one of your own pinned revisions is resolved and checked first, exactly as run submit --from does. That includes the commit-id correction.
    • The key is sent when one is configured.
  • job create plan: adds the pinned program_ref to Quote holds with the run's options and accept webhook binding keys #51's spec-derived query (which includes job_type), using the key.
  • Unchanged: without FILE or --from, run quote behaves exactly as in Quote holds with the run's options and accept webhook binding keys #51, and a flagless quote is still anonymous and parameter-free. program draft is unchanged.

Output.

  • basis: the run quote result gains basis when the service sends one.
    • The five inputs must each be well-formed; extra fields are dropped, and a malformed basis is SERVICE_PROTOCOL_INVALID.
    • When basis is present, result.environment is its environment.
  • Human output: gains a Basis: line, e.g. Basis: model model-sol (request); reasoning effort medium (program); ....
  • holdBasis: now reads ...; quoted for the program and options given (basis names where each came from); ....
  • New error: program_not_found maps to SERVICE_RESOURCE_NOT_FOUND.

Service catalog.

  • cli/ci/sync_service_interactions.py now also accepts the service's public interaction projection (openprose.*-interactions/2), which publishes only allowlisted interactions and no principals table.
  • The fixed public principal set stands in for the missing table, and every interaction's principal is still checked against it. A test covers both the identical projection and refusing a non-public principal.
  • The vendored catalog is re-vendored from that projection. The only change is the new /run/quote routes this client now calls.

Files.

  • Shared:
    • The manifest: new run.quote requests 2–4, the run.submit and job.create quote requests, the text, and program_not_found.
    • runs.schema.json (basis and the holdBasis const), common.schema.json (quoteBasis), and runner-error.schema.json (the serviceCode enum).
    • The re-vendored interactions, regenerated help, and the refreshed framework digest cases.
    • 16 new cases and the edited runs/* and jobs/* cases. Every quote exchange pins its method, query, Authorization presence and body; the submit plan's quote pins expectedSha256 equal to the plan's bodySha256.
  • Rust: service/{runs,jobs,program_ref,mod,programs}.rs.
  • Bun: service/{runs,jobs,program-ref,index,programs,http}.ts.
  • Docs: the guide, SPEC, docs/service/* and CHANGELOG.md.

Evidence

Against the hosted service, anonymously, before wiring the CLI:

  • GET ?program_ref= returns basis with source: "program".
  • A request model overrides it (source: "request", a different hold).
  • POST with content reads the program's model.
  • A POST body program_ref must be pinned.
  • The POST query's repositories must equal the body's array, which the submit body already guarantees.
  • An unknown program is 404 program_not_found.

Local, on macOS with Python 3.10.21, Rust from rust-toolchain.toml and Bun 1.4.2. These gates pass:

  • shared-contracts, service-help, public-surface-files and service-operations-corpus
  • differential-conformance and service-coverage (0 routes unaccounted)
  • rust-format and rust-clippy
  • service-operations-rust-build and service-operations-rust: 1114 cases, 0 failures
  • bun-typecheck, service-operations-bun-build and service-operations-bun: 1114 cases, 0 failures

Rust (prose-runner-core lib plus the service_* targets) and Bun service tests also pass.

Not run here: the full rust-tests gate. It is load-flaky on this machine in process-timing tests that this change doesn't touch, as noted on #51, whose GitHub CI passed. GitHub CI is authoritative.

How it was built and reviewed:

  • The two ports were implemented independently from a written contract. Every new or refreshed case was accepted only where Rust and Bun produced identical bytes.

  • An independent review agent read the diff before this was opened. It found two port differences in run quote:

    • checking --from SLUG@REV against your own revisions;
    • checking the offered --environment/--runtime with FILE.

    Both are fixed and pinned by new cases, along with doc corrections.

🤖 Generated with Claude Code

rawwerks and others added 2 commits October 7, 2026 12:09
The service now prices a program itself: GET /run/quote?program_ref= for
a saved program and POST /run/quote with the POST /run body. The run
submit plan posts its exact submission body; run quote FILE posts the
program and run quote --from names it (resolved and checked as run
submit does); the job create plan names its pinned program_ref. Explicit
options still override the program's own settings, and the CLI still
never inspects program text. run quote reports the service's basis
(each input with its source), and program_not_found maps to
SERVICE_RESOURCE_NOT_FOUND.

sync_service_interactions.py accepts the service's public interaction
projection (interactions/2, no principals table); the catalog is
re-vendored from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The service reports basis.model.source job_default when a job's type
picks the model; run quote passes it through. The job create plan's
quote keeps job_type next to program_ref.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants