Skip to content

fix(deploy): prune unused runtime schema history - #7496

Merged
bokelley merged 5 commits into
mainfrom
conductor/safe-fly-image-size-unblock
Oct 13, 2026
Merged

bokelley merged 5 commits into
mainfrom
conductor/safe-fly-image-size-unblock

Conversation

@bokelley

@bokelley bokelley commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

The runtime image carried schema releases that no current docs snapshot, hosted-compliance bundle or retained training checkpoint needs. This PR builds a disposable runtime schema copy and omits only that unused history. It keeps all48 published hosted-compliance pairs, all4 configured docs snapshots, discovery/latest/major/minor aliases, and both retained training anchors (3.2.0-beta.6 and 3.2.0-rc.7). Missing or noncanonical required anchors fail before pruning. Current closure retains51 of86 schema releases and omits35 from the image copy. Immutable Git/R2 releases, protocol tarballs, source schemas and compliance/docs history are untouched.

The Dockerfile preserves main's cache sanitization, compiled-asset guards and 7424 MiB application ceiling. Builder and final-image smokes use the actual pinned SDK15.2.0 to load/index every published compliance version and initialize every configured docs snapshot. CI path filters include the closure/smoke inputs. The new contents-read-only default-image workflow builds the exact reviewed head with actual Docker defaults, verifies every configured cache, records image/root/app/cache sizes and runs final-image smokes. It has no secrets, registry publication or deployment step; checkout credentials are not persisted. Its initial PR trigger is limited to the workflow path, with later exact-SHA manual dispatch supported.

Current head: c238b56188d7a4832bdc1c653b565268c15d259c. Production source is unchanged from qualified parent 3dd3e27; the two follow-ups only add/fix the footprint workflow. The original draft head remains in history; this updates the same PR/branch without a force push or duplicate PR.

Validation:

  • Production-source parent:8 closure tests +3 canonical CI-registration tests passed; normal hooks passed root1106, server748files/12512passed/31intentional skips after the canonical21-file/215-test serial retry, all guards and both complete typechecks. Standard pre-push passed, including49 docs-navigation checks. Parent service CI completed57success/1neutral/1expectedDraftskip; those are historical parent checks, not transferred to this head.
  • Each workflow-only follow-up: normal hooks passed root1106, guards and both typechecks; canonical server-unit gate correctly skipped because no server source/unit input changed. Standard pre-push passed. The first definition failed GitHub validation before any job because runner context was used in job-level env; the corrected definition passes actionlint and the actual runner proof below. That failed run is retained; redundant runs on the superseded intermediate head were cancelled.
  • Current-head default-image proof passed with no Docker build-argument overrides: 6003 MiB visible unpacked root, 5759 MiB application /7424 MiB ceiling, 69 MiB repository cache, all28 configured repositories present. Image sha256:6191a37d7bd13cc60f8b676cd24b37d081f9b0d75f9f5069f2b83b8a202490aa. Both builder and final-image docs4/SDK48 smokes passed. Exact source SHA, image inspection, sizes and smoke logs are retained in the workflow artifact.
  • Fresh general service CI on the current head is complete:58 successful checks, one neutral result and the expected draft AI-review skip. All8 storyboard shards and the aggregate passed. The documented local advisory storyboard skip leaves authoritative GitHub storyboard qualification required. Read-only Sonnet/Haiku reviews and primary-source checks supplement these gates; they do not provide human maintainer approval.

Direct Fly history narrows to the runtime closure. Read-only history verification now covers every omitted schema asset: all23,246 committed JSON files across all35 omitted schema versions returned HTTP200 with exact Git-verified bytes from the candidate Worker. All35 version indexes also matched committed bytes on both the public site and candidate. This proves candidate historical-schema content, not the active public route binding or complete compliance/protocol-tar cutover; confirm routing before deploying the pruned Fly image. No deployment or live route/settings change was performed.

Remain Draft; auto-merge off. Human maintainer/workflow review and deployment/CDN routing confirmation remain required. The full default-cache image size/smoke proof is complete.

Existing workspace.

@bokelley
bokelley requested a review from a team September 14, 2026 05:07

Copy link
Copy Markdown
Contributor Author

Exact-head terminal verification

Independent read-only review at head 72faa85dc6bdda36558244ece826ff55932cdc8e found no blocker.

  • Exact base/parent remains 8d50c0e3c303391c8bb35c5042248eb847b06bb9; one commit, six files, +512/-6, with no migration or versioned dist changes.
  • Exact CI is terminal: 56 passed, zero pending/failing, with only the expected Draft Ladon and Mintlify skips.
  • The CI production image is 5,558 MiB: 586 MiB below the repository budget and 1,866 MiB below Fly's limit.
  • Docker pruning retained 50 schema releases, pruned 41, and passed compiled docs plus all 47 manifest-derived hosted-compliance smokes.
  • CodeQL passed; Semgrep reported zero findings; all unit, integration, canonical, storyboard, migration, dependency, and docs jobs passed.
  • No review comments, conversation findings, or inline findings exist.

The PR remains intentionally Draft with auto-merge disabled. Human maintainer/CODEOWNER approval is still required before a ready transition, merge, or deployment.

@bokelley
bokelley marked this pull request as ready for review October 13, 2026 10:29
const { require: requireTypeScript } = require("tsx/cjs/api");
const sourcePath = join(repoRoot, "server", "src", "training-agent", "schema-compat.ts");
const { RETAINED_SCHEMA_BUNDLES: retained } = requireTypeScript(sourcePath, __filename);
if (!retained || typeof retained !== "object" || Array.isArray(retained)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MUST FIX: const { RETAINED_SCHEMA_BUNDLES: retained } = requireTypeScript(sourcePath, ...) reads an export that does not exist. At base/head, server/src/training-agent/schema-compat.ts exports only the singular RETAINED_SCHEMA_BUNDLE ('3.2.0-beta.6'); RETAINED_SCHEMA_BUNDLES appears nowhere in the repo, and this PR's 8 files do not add it. retained is therefore undefined and L67-69 throws must export a nonempty RETAINED_SCHEMA_BUNDLES map.

Two consequences, both blocking: the Dockerfile runtime-artifacts stage (RUN node scripts/prune-runtime-artifacts.cjs ...) exits non-zero, failing every image build on the deploy path; and npm run test:runtime-artifacts (build-check.yml) fails on its first test, since runtimeArtifactManifest(repoRoot) resolves retainedTrainingSchemaVersions against the real tree. The PR body's "retained training anchors 3.2.0-beta.6 and 3.2.0-rc.7" and passing-build proof describe a tree that carries this export — it is missing from the PR. Ship the schema-compat.ts change that exports RETAINED_SCHEMA_BUNDLES, or point the script at the existing RETAINED_SCHEMA_BUNDLE.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Request changes

Request changes — 1 blocking finding.

Blocking findings

  • scripts/prune-runtime-artifacts.cjs:66 — Prune script destructures RETAINED_SCHEMA_BUNDLES from schema-compat.ts, but that export does not exist (base/head only export the singular RETAINED_SCHEMA_BUNDLE), and none of the PR's 8 files add it. The prune step throws, breaking the Dockerfile runtime-artifacts stage and npm run test:runtime-artifacts. The dependency the PR body's passing-build proof assumes is absent from the submission.

Row 1 of the decision table fires: a high-severity finding is present, so the outcome is request-changes. Fix: either add the RETAINED_SCHEMA_BUNDLES export to schema-compat.ts or correct the destructure to the existing singular RETAINED_SCHEMA_BUNDLE.

Otherwise the pruning design is sound — fail-closed, well-tested, prunes only the in-image dist/schemas copy, leaves immutable git/R2 release records intact, and correctly requires no changeset for a deploy-only change.

Blocking findings

  • scripts/prune-runtime-artifacts.cjs:66 — Prune script reads nonexistent RETAINED_SCHEMA_BUNDLES export; breaks image build and CI

Comment thread Dockerfile
# and the training-agent checkpoint. Prune the builder copy so repository
# release records are never deleted or rewritten.
FROM builder AS runtime-artifacts
RUN node scripts/prune-runtime-artifacts.cjs \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MUST FIX: Pruning the 35 omitted schema versions from the Fly image changes the wire for pinned immutable URLs if the Node server still serves /schemas/. schemas-middleware.ts:462-493 does not 404 a missing pinned version — resolvePinnedFallback silently rewrites /schemas/3.0.5/... to the nearest lower kept version on the same major and serves different bytes with Cache-Control: public, no-cache, must-revalidate instead of the exact immutable bytes. On main the image carries all 86 versions, so these are exact hits. After this prune, every adopter who pinned one of the 35 omitted paths — exactly the population the immutability rule protects — gets silently substituted schema content. The PR body's "confirm routing before deploying" is procedural, not a guard. Fail-closed: do not deploy this image as the /schemas/ origin unless the public route is bound to the artifact-cdn worker (which carries all versions); the discovery surfaces already advertise only the kept subset, so the exposure is direct pinned requests.

}

function run() {
const repoRoot = resolve(process.argv[2] ?? join(__dirname, ".."));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: run() defaults repoRoot to the real checkout, and the only guard — assertAdcpRepoRoot — passes on the real repo rather than blocking it. So node scripts/prune-runtime-artifacts.cjs run anywhere in the working tree will rmSync the 35 omitted dist/schemas/<version> directories and rewrite dist/schemas/index.json in place — deleting committed, immutable released artifacts from the checkout. Reversible via git, but surprising for a script whose guard exists to confirm it's pointed at a real adcp checkout. Consider a --dry-run default, or require an explicit opt-in (env flag / /app sentinel) so the destructive path only runs inside the Docker build stage it's designed for.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Request changes

Request changes — 1 blocking finding.

Blocking findings

  • Dockerfile:24 — Pruned runtime image silently serves wrong schema bytes for pinned immutable URLs. After pruning the local dist/schemas tree to a 51-of-86 closure, the Node schemas-middleware does not 404 the 35 omitted pinned versions; it falls through to serving the nearest lower kept version with a no-cache header. This breaks the immutable-URL contract for pinning adopters unless the public /schemas/ origin is bound to the artifact-cdn worker. The PR's "confirm routing" note is procedural, not an in-code guard — the served-wrong-bytes behavior is a reproducible customer-contract break on a load-bearing path (high).

Medium findings

  • scripts/prune-runtime-artifacts.cjs:244 — prune script defaults to the real checkout and its guard enables rather than blocks destructive in-place deletion of released artifacts. Fail-closed verification (verify before/after prune) is the right shape and should be adopted.

Decision table: Row 1 fires (a high-severity finding is present) → request-changes. Note: gated_paths is true with review_decision=REVIEW_REQUIRED, which would independently force escalate under Row 2, but Row 1 wins top-down. Human/CODEOWNERS approval is still required on the .github/workflows/** changes before this can merge.

Blocking findings

  • Dockerfile:24 — Pruned image silently serves nearest-lower kept schema version for 35 omitted pinned immutable URLs instead of 404ing, breaking the immutable-URL pinning contract; the routing note is procedural, not an in-code guard

Medium findings

  • scripts/prune-runtime-artifacts.cjs:244 — prune script defaults to real checkout and deletes released artifacts in place

@bokelley
bokelley merged commit d89129e into main Oct 13, 2026
103 of 108 checks passed
@bokelley
bokelley deleted the conductor/safe-fly-image-size-unblock branch October 13, 2026 12:02
bokelley added a commit that referenced this pull request Oct 13, 2026
Keep both image-size fixes: hard-link dedup in the builder, then main's
runtime-artifacts prune stage (#7496).

Co-Authored-By: Claude Opus 5.5 (1M context) <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.

1 participant