Skip to content

feat(craft): outputs reconciler wired into every terminal branch - #14327

Open
nmgarza5 wants to merge 2 commits into
nikg/craft-artifact-classifierfrom
nikg/craft-outputs-reconciler
Open

feat(craft): outputs reconciler wired into every terminal branch#14327
nmgarza5 wants to merge 2 commits into
nikg/craft-artifact-classifierfrom
nikg/craft-outputs-reconciler

Conversation

@nmgarza5

@nmgarza5 nmgarza5 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Description

Why. Schema (#14319), manifest (#14325), and classifier (#14326) are inert until something runs them at the right moment. The design's requirement is specific: reconcile at turn end, while the turn still holds its prompt slot, so the next turn never starts against a half-written index, and idempotently, so a run skipped by a hard crash self-heals at the next turn end. This PR is that wiring. Stacked on #14326; the diff also carries #14325's commits (the manifest it consumes) until that merges — review this PR's own commits: outputs_reconciler.py, db/artifact.py, packets.py, session/streaming.py, and both executors.

What.

  • outputs_reconciler.py: fetch the manifest, adapt entries into the classifier, diff derived artifacts against the rows. Upserts only rows whose content or type moved or whose path came back, so turn_index keeps naming the turn that last changed each artifact; vanished paths flag deleted in one bulk statement. Incomplete manifests — truncated or partially unreadable — are never reduced, because the delete pass would otherwise flag rows the walk merely failed to see; the rows stay the baseline and the next turn end self-heals.
  • Wired into every owned terminal branch: the interactive executor (interrupt, sandbox error, deadline, no-final-response, cancel, success, owned exception) and the scheduled-task executor (awaiting-approval, budget timeout, failure, success, owned exception). Best-effort: a reconcile failure never blocks the turn's terminal handling.
  • Changed rows announce over Redis (capped at 50, the turn-end index refetch is the completeness guarantee) into the live SSE merge as a new artifact packet. No shipped client parses the type yet; clients ignore unknown packet types by design and the consumer lands with the output panel frontend.
  • The docker manifest exec is bounded by timeout(1) at 30s, matching the kubernetes RPC bound, so a pathological walk cannot hang terminal handling (exec_run has no timeout of its own).

How Has This Been Tested?

  • 16 external-dependency-unit tests (test_outputs_reconciler.py, real Postgres + Redis, sandbox stubbed at the manager seam): first reconcile creates rows and packets, unchanged rows stay untouched (turn_index preserved), content change bumps version, vanish/resurrect round-trip without a spurious bump, truncated and partially-unreadable manifests leave rows alone, manifest failures (RuntimeError family and ValidationError) skip cleanly, seeded type drift is corrected, webapp→directory transition bumps, file→directory at one path keeps one row, hidden churn produces zero packets, sessions are isolated, and the announce round-trips in FIFO order.
  • Crash recovery is the same mechanism as the idempotence tests: rows are the baseline, a re-run converges.
  • The timeout-wrapped exec verified live in the real sandbox image (exit 0, correct JSON, /usr/bin/timeout present and root-owned).
  • Full craft external-dependency suite and unit suite green alongside.

Additional Options

  • [Optional] Please cherry-pick this PR to the latest release version.
  • [Optional] Override Linear Check

@nmgarza5
nmgarza5 requested a review from a team as a code owner August 27, 2026 03:50
@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR reconciles sandbox output manifests with persisted artifact rows at terminal turn branches.

  • Adds transactional artifact upserts and bulk deletion marking.
  • Publishes changed artifact rows into the live session stream.
  • Adds bounded Docker and Kubernetes manifest retrieval.
  • Covers reconciliation and manifest behavior with focused tests.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains.

No blocking failure remains.

Important Files Changed

Filename Overview
backend/onyx/server/features/build/outputs_reconciler.py Adds manifest-to-artifact reconciliation, incomplete-manifest safeguards, and post-commit announcements.
backend/onyx/server/features/build/db/artifact.py Adds a bulk update that marks vanished live artifacts deleted and returns changed rows.
backend/onyx/server/features/build/interactive_turns/executor.py Runs best-effort artifact reconciliation across owned terminal branches.
backend/onyx/server/features/build/scheduled_tasks/executor.py Integrates output reconciliation into scheduled-task terminal handling.
backend/onyx/server/features/build/session/streaming.py Merges artifact announcements into the live build event stream.
backend/onyx/server/features/build/sandbox/image/sandbox_daemon/manifest.py Builds bounded, symlink-safe output manifests for reconciliation.

Sequence Diagram

sequenceDiagram
    participant E as Turn executor
    participant S as Sandbox manager
    participant R as Output reconciler
    participant DB as PostgreSQL
    participant C as Cache/SSE
    E->>S: get_outputs_manifest()
    S-->>E: complete manifest
    E->>R: reconcile_session_outputs()
    R->>DB: load artifact baseline
    R->>DB: upsert changed rows
    R->>DB: mark vanished paths deleted
    E->>DB: commit
    E->>C: announce changed artifacts
    C-->>E: merge packets into live stream
Loading

Reviews (2): Last reviewed commit: "docs(craft): state the packet rollout an..." | Re-trigger Greptile

Comment thread backend/onyx/server/features/build/packets.py
@nmgarza5

Copy link
Copy Markdown
Contributor Author

@greptile

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Full-stack Preview (frontend + backend)

Status Preview Commit Updated
https://e1f9523-onyx.preview.onyxcorp.dev/ e1f9523 2026-08-27 21:41:14 UTC

Diffs the sandbox outputs manifest against the artifact rows at turn end,
while the turn still holds its prompt slot, so the next turn never starts
against a half-written index. Upserts only rows whose content or type moved,
keeping turn_index the turn that last changed each artifact, flags vanished
paths in one statement, and announces changed rows over Redis so an attached
stream can render artifact packets promptly. Incomplete manifests, truncated
or partially unreadable, are never reduced: the rows stay the baseline and
the next turn end self-heals. Wired into the interactive executor's owned
terminal branches and every owned scheduled-task terminal status, with the
docker manifest exec bounded by timeout(1) to match the kubernetes RPC
bound.
Clients ignore unknown packet types by design, so the artifact packet ships
before its consumer, and the sandbox daemon cannot import onyx.* so its
routes raise HTTPException rather than OnyxError. Both were review questions
twice over, the code now answers them itself.
@nmgarza5
nmgarza5 force-pushed the nikg/craft-outputs-reconciler branch from 4f3a77f to e1f9523 Compare August 27, 2026 21:37
@nmgarza5
nmgarza5 force-pushed the nikg/craft-artifact-classifier branch from 2735d38 to 7c972f3 Compare August 27, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant