Repository navigation
feat: stable machine-facing API — operations, idempotency, cancellation, shared audit path - #13
Merged
Merged
Conversation
5 tasks done
iamgp
force-pushed
the
feat/agent-api
branch
from
September 14, 2026 08:55
6e38468 to
a636fbf
Compare
iamgp
force-pushed
the
feat/agent-api
branch
2 times, most recently
from
September 14, 2026 09:53
e24e729 to
e8d21dc
Compare
iamgp
force-pushed
the
feat/agent-api
branch
2 times, most recently
from
September 14, 2026 10:36
2e1b2b0 to
e9b7ad0
Compare
iamgp
force-pushed
the
feat/agent-api
branch
from
September 14, 2026 12:19
e9b7ad0 to
b45da76
Compare
iamgp
force-pushed
the
feat/agent-api
branch
from
September 14, 2026 13:25
b45da76 to
ef2e275
Compare
iamgp
force-pushed
the
feat/agent-api
branch
2 times, most recently
from
September 14, 2026 15:49
573b1b9 to
2bb5fb0
Compare
iamgp
force-pushed
the
feat/agent-api
branch
from
September 14, 2026 16:26
2bb5fb0 to
45db1b2
Compare
iamgp
force-pushed
the
feat/agent-api
branch
from
September 14, 2026 17:57
45db1b2 to
98c9fcc
Compare
…on, shared audit path Daemon gains a two-plane v1 API: reads (plan, lineage document, branch and lineage diffs, state inspection) and operations (run/test/promote/reload) with a stable queued→running→succeeded|failed|cancelled lifecycle, live progress read back from the state store, cooperative cancellation via CancelHandle, per-key idempotent replay, a single-mutating-operation gate, strict param schemas, and structured API0xx errors. The promotion audit path (audited_diff, environment artifacts, contract_breaking_changes, catalog naming) moves into phlo-transform-engine::audit so the CLI and the daemon apply identical evidence rules.
iamgp
force-pushed
the
feat/agent-api
branch
3 times, most recently
from
September 14, 2026 21:45
e085ae5 to
9d1fc61
Compare
…ncy, durable journal, auth
Review fixes on the agent API bundle:
- run/resume/retry_failed provision the candidate Nessie branch and its
branch-scoped catalog, then recompile the workspace retargeted at it —
the same orchestration the CLI's --ref path runs; a passed op binds to
the environment's post-run head. Without Nessie the environment stays a
state label only; with Nessie configured but no adapter or catalog-facing
URI, a non-base environment fails closed with API007 before execution
rather than running on the default target and binding evidence to the
candidate's head.
- Candidate catalog identity is collision-safe: names are
phlo_<sanitised-ref>_<8-hex-sha256>, so punctuation-equivalent refs can
never share a physical catalog. ensure_catalog reports CatalogStatus
(Created/Unverified/Unmanaged) — an existing catalog is never adopted on
name alone since its bound Nessie ref cannot be read back over SQL;
unverified catalogs are accepted only via the self-naming convention or a
recorded binding, and a catalog another candidate's artifacts claim is
refused outright. Cleanup drops only catalogs phlo provably created.
- plan and run share one environment resolver (EnvironmentContext::resolve):
Ensure provisions then compiles retargeted; ReadOnly — used by
GET /v1/plan?environment=&base= — computes the same physical catalog
(override → recorded binding → generated name) and a retargeted compile
without creating branches or writing evidence, so the previewed targets
are exactly what a run executes.
- resume keeps the interrupted run's id and reuses its passed work;
retry_failed links a new run via continued_from; the stored run's
environment is authoritative — continuations retarget it, not a
requested one.
- /v1/diff/lineage runs the shared engine LineageDiffContext — identical
merge-base/ref→ref orchestration, provenance artifact, and candidate
lineage fingerprint as the CLI, so the API can no longer drift.
- Idempotency keys are global to POST /v1/operations and bound to a sha256
of the canonical {kind, params} request: same key with different params
or a different kind is 409 API016, and the reservation is journaled
before the operation is acknowledged — a restart can never execute a
keyed submission twice.
- Full read surface: /v1/impact (model, column, selection blast radius),
/v1/state/runs/{id}/failed, /v1/diff/branch contract changes.
- Daemon e2e: an environment-targeted run over real Nessie + Trino is
physically branch-isolated and its plan names identical targets (ignored
by default; runs in CI).
iamgp
force-pushed
the
feat/agent-api
branch
from
September 14, 2026 23:22
9d1fc61 to
2482c1f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bundle 9 — stable machine-facing API. Stacked on
feat/lineage-diff(PR #12); do not merge before it.Shared audit path (CLI ↔ API)
phlo_transform_engine::auditmodule: the promotion-evidence helpers moved verbatim out of the CLI —audited_diff(branch_diff.json preferred, single-model diff.json fallback, both-side staleness, post-diff materialisation, self-comparison rejection,--fullrequirement),read_environment/read_environment_for/environment_artifact_name/write_environment_artifacts/remove_environment_artifacts,contract_breaking_changes(live desired-vs-recorded contract diff), pluscatalog_nameandcompiled_catalog. The CLI keeps thin delegating wrappers — one evidence path, no drift.Daemon — read plane (never mutates)
GET /v1/plan?select=&environment=&force=— plan DTO (needs adapter)GET /v1/lineage— full lineage document (lineage --format graph)GET /v1/diff/lineage?base=<git-ref>— semantic lineage diff, offline, read-only tree extractionGET /v1/diff/branch?from=&to=&full=— branch diff (needs adapter), persistsbranch_diff.jsonlike the CLI so promote audits itGET /v1/state/runs[?environment=],/v1/state/runs/{id|prefix},/v1/state/models/{id}[?environment=],/v1/state/promotionsPOST /v1/reload— synchronous snapshot recompileDaemon — operation plane (mutations, long-running)
POST /v1/operations{kind, idempotency_key?, params?}→ handle; kindsrun,test,promote,reloadqueued → running → succeeded|failed|cancelled;GET /v1/operations[/{id}]polls status;POST .../{id}/cancelcancels via the engineCancelHandlerunops expose liveprogressread back from the state store (the runner persists per-model transitions up front)promoteruns the full gated path: ref resolution, run history, audited diff artifact, live contract breaks, provisioning-base target-hash pin, merge check, promotion record + optional cleanup — identical topromoteon the CLIidempotency_key(orIdempotency-Keyheader) replays the existing handle under one lock — no duplicate effects on retry, including across daemon restarts.phlo/transform/operations.jsonland folded back at startup; an op in flight at shutdown restores asfailed/interruptedrather than silently re-runningrun/promoteserialize (409 API008);test/reloadungateddeny_unknown_fields);?select=accepts single or repeated keysAuth —
daemon --token <secret>requiresAuthorization: Beareron every endpoint except/status; missing/wrong credentials answer401 API015. No token = unauthenticated loopback mode.Errors —
{"error":{code,message}}with stable codesAPI001–API015documented indocs/daemon.md; capability gaps answer503 API007; op records carry the same codes pluscancelledandinterrupted.Wiring —
phlo-transform daemonbuildsServiceConfigfrom the global CLI flags (adapter/state/nessie/environment/catalog/schema/token), degrading toAPI007per missing capability;/v1/statusreportscapabilities.Results reuse CLI DTOs:
run→ RunResult (run.json),test→{tests,failed},promote→ gates + PromotionRecord, diffs/state → the exact--jsonshapes.Test plan
cargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo test --workspace— all suites greenrunop end-to-end on real DuckDB + sqlite state (succeeded, run persisted, state endpoints serve it, idempotent replay)promoteend-to-end over HTTP — real DuckDB + sqlite state + recorded provisioning evidence + deep branch-diff artifact + in-memory Nessie:--checkgates without merging, real promote merges and persists the promotion record, unaudited candidate failsrungate withok:falsetestop,GET /v1/planselector narrowing +API006, lineage document + self-diff,API010on bogus refAPI007/008/009/012/015, conflict gate, cancel,POST /v1/reloadinterrupted--ignored)