Skip to content

feat: add structured push result contract - #369

Merged
abrichr merged 7 commits into
mainfrom
codex/push-json-contract
Aug 19, 2026
Merged

feat: add structured push result contract#369
abrichr merged 7 commits into
mainfrom
codex/push-json-contract

Conversation

@abrichr

@abrichr abrichr commented Aug 18, 2026

Copy link
Copy Markdown
Member

Summary

  • add openadapt-flow push --json with the versioned openadapt.push-result/v1 controller contract
  • distinguish paused_for_review, accepted_for_ingest, failed, and delivery_uncertain
  • bind successful bundle output to the exact approved archive, local runtime attestation, certification evidence, retained server bundle version, tenant, workflow, and server-owned artifact ingest/runtime-validation ids
  • require the complete accepted-ingest identity chain inside hosted.push() itself, so the human-readable command gets the same protection as --json
  • keep the existing human-readable push output when --json is absent

Safety contract

  • exit 0 only for paused_for_review and fully bound accepted_for_ingest
  • a 201 status code alone is never proof. Flow also requires the server ingest id, artifact kind and hash, a governed next action for a recording, and, for a bundle, status: accepted plus a retained version that exactly binds organization, workflow, artifact hash, version number, resolved run, and runtime-validation id
  • never synthesize workflow_id or artifact_ingest_id
  • bind the accepted workflow to the requested --workflow-id, and the retained promoted_from_run_id to --resolves-run-id
  • upload a verified private snapshot of the approved archive, so a path swap between approval and egress cannot ship unapproved bytes
  • classify transport failures and incomplete or ambiguous post-upload responses as delivery_uncertain; require reconciliation before retry. The human-readable mode prints the artifact hash and an explicit do-not-retry instruction, and leaks no server text
  • replace the review command string with the typed review_sanitized action plus a separate original_path, so a controller never builds a shell command from a local path
  • pin accepted runtime-validation attestation schemas to v1, v2, and v3, and require the governed authorization template exactly on v3
  • accept canonical RFC 9562 UUID versions 1-8 for server-owned ids, matching the hosted control plane's own check
  • keep JSON error text bounded and exclude raw server bodies, tokens, and local source paths
  • construct dashboard URLs only from the validated destination origin

Cross-repository verification

Verified read-only against openadapt-cloud origin/main: /api/ingest returns
ingest.artifact_ingest_id for accepted, deduplicated, and recording responses
(src/lib/artifactIngestResponse.ts), and an accepted bundle response carries
the retained version record with id, org_id, workflow_id, version,
artifact_sha256, runtime_validation_id, and promoted_from_run_id. Cloud
mints server ids as RFC 9562 version-5 UUIDs in mock mode, which is why Flow
accepts versions 1-8 rather than 1-5.

Validation

  • pytest -q tests/test_hosted.py tests/test_runtime_validation.py tests/test_sanitized_artifact.py — 294 passed, 1 skipped
  • fast unit suite as CI runs it (pytest -q --ignore=tests/e2e --ignore=tests/test_install_playwright_browser.py) — pass
  • ruff check openadapt_flow, ruff format --check openadapt_flow tests, mypy — pass
  • python scripts/check_consistency.py, python scripts/check_release_consistency.py — pass
  • built wheel and sdist; clean-venv install, py.typed, and source-boundary carve checks pass
  • the wheel ships the exact reviewed schemas/push-result-v1.json, and public-artifacts.json pins its SHA-256

🤖 Generated with Claude Code

A 201 status code alone did not prove the hosted control plane retained the
intended artifact. Require the complete server-owned identity chain in
hosted.push() itself, so the human-readable command gets the same protection
as --json: the server ingest id, the artifact kind and hash, a governed next
action for a recording, and, for a bundle, the accepted status plus the exact
retained version binding (id, org, workflow, artifact hash, resolved run,
runtime-validation id, version number). Anything short of that is an uncertain
delivery, never a success.

Also:

- Upload a verified private snapshot of the approved archive so a path swap
  between approval and egress cannot ship unapproved bytes.
- Bind the accepted workflow to the requested --workflow-id, and the retained
  version's promoted_from_run_id to --resolves-run-id.
- Replace the review "command" string with a typed review_sanitized action plus
  a separate original_path, so a controller never builds a shell command from a
  local path. The human-readable output still prints the review command.
- Pin the accepted runtime-validation attestation schemas to v1, v2 and v3, and
  require the governed authorization template exactly on v3.
- Accept canonical RFC 9562 UUID versions 1-8 for server-owned identifiers,
  matching the hosted control plane's own check and Flow's report-run regex.
- Collapse the obsolete invalid_ingest_response failure code into
  delivery_uncertain, and carry the local reconciliation binding into it.
- Report an uncertain delivery in human-readable mode with the artifact hash
  and an explicit do-not-retry instruction, without leaking server text.

Verified against openadapt-cloud origin/main: /api/ingest returns
ingest.artifact_ingest_id for accepted, deduplicated, and recording responses,
and the accepted bundle response carries the retained version record.
@abrichr
abrichr force-pushed the codex/push-json-contract branch from 37a8ba7 to 763079f Compare August 18, 2026 23:02
@abrichr
abrichr marked this pull request as ready for review August 19, 2026 21:37
@abrichr
abrichr merged commit 663550f into main Aug 19, 2026
16 checks passed
@abrichr
abrichr deleted the codex/push-json-contract branch August 19, 2026 22:05
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.

1 participant