Release v0.75.0 — Post-audit hygiene + operational follow-through
Date: 2026-04-21
Tag: v0.75.0
Summary
v0.74.0 closed the 2026-04-18 audit’s verification-gap asks and removed the “do before new features” blockers. v0.75.0 lands the maintainability + operational follow-through that was explicitly deferred at planning time. Twelve commits since v0.74.0 across five cohesive workstreams: WIP-suite drain, HA Run 2 enablement, GHCR republish enablement, editor build polish, and a render-route preview-routes split.
No public-API surface changes. No breaking changes. The only YAML edit to release.yml corrects a workflow_dispatch tag-checkout bug; routes, schemas, status codes, and CLI shapes are untouched.
This release ships without CI signal. CI and Release workflows remain in disabled_manually from the prior billing block. The release commit carries [skip ci] so no Actions fire on the push or the tag. See docs/runbooks/ghcr-republish.md for the post-billing publish runbook — that runbook’s revision-label verification is also the end-to-end proof of the workflow_dispatch fix below.
What shipped
1. WIP-suite drain (2 of 3 entries promoted)
The file-mode WIP suite dropped from 3 entries to 1. The two fixes that landed were both the same root cause: tests reading lowercased response-header keys with a dash (x-pulp-engine-*) when Node’s HTTP layer lowercases the emitted X-PulpEngine-* headers to x-pulpengine-* (no dash). Test-only fixes — the shipped public header names are unchanged.
apps/api/src/__tests__/batch-async.test.tsHMAC signature read fixed; promoted to gatedtest:filesuite (commit6b673e5).apps/api/src/__tests__/template-labels.route.test.tsfour B.3 variant-resolution tests fixed; promoted (commit07d3fe4).apps/api/src/__tests__/plugin-integration/plugin-storage-activation.test.tsre-parked in WIP with an accurate diagnosis (commit36dfa35). Initial promotion was over-eager: the test passes in isolation and as part of theplugin-integration/subset, but fails the first test with/health/ready = 503in the full 74-file singleFork gated run. The original AUD-001 boot-ordering bug is fixed and verified — the seam that remains is a narrower cross-file singleFork state-pollution issue tracked in docs/initiatives/file-mode-wip-followups.md.
vitest.file.wip.config.ts now sets passWithNoTests: true so the empty-list case stays robust.
2. HA Run 2 enablement (scripts + playbook + override scaffold)
Run 1 (recorded in v0.74.0) closed 3 of 6 checks PASS, 2 deferred, 1 N/A. v0.75.0 lands reproducible drivers for the two deferred checks so Run 2 is operator-executable without re-deriving setup steps.
scripts/ha/check-2-schedule-fires-once.mjs+ thepnpm ha:check-2wrapper (commit984cc5a). API-only against a livedocker-compose.ha.ymlstack: seeds a temp template + per-minute schedule, pollsGET /schedules/:id/executionsfor ~3 ticks, asserts eachfireTimeappears exactly once, tears its own fixtures down in afinallyblock. No DB client /pgdep added. Bash + PowerShell invocation documented in the script header.- Check-6 API-key-rotation playbook in docs/ha-validation-report.md (commit
be300c6). End-to-end sequence matching the*_PREVIOUScontract: previous keys are verify-only for editor session tokens, cannot mint, cannot be used asX-Api-Key. Restart-window log evidence is captured BEFORE the four explicit 200/200/401/401 probes so background traffic isn’t conflated with deliberate negatives. PowerShell variants inline. docker-compose.ha.override.yml.examplewith two commented sections: port override (!override) for thelbservice when host port 3000 is held, and the rotation cutover env block.- Run 2 evidence template appended to
docs/ha-validation-report.mdfor operators to fill in post-run.
Operational execution remains operator-gated.
3. GHCR republish enablement (workflow fix + runbook)
Recon during planning uncovered a real bug in release.yml: while check-ci correctly resolved the tag SHA on workflow_dispatch, every downstream job (docker, scan, windows-installer, eval-bundle, release) called actions/checkout@v4 with no ref: and used ${{ github.sha }} in Docker build args + OCI revision labels. On workflow_dispatch, github.sha is the dispatch ref’s HEAD — so gh workflow run Release -f tag=vX.Y.Z from a main past the tag would have built main HEAD and labeled the image as vX.Y.Z.
- Fix in commit
19247ef:check-ciexportssha/tag/ownerjob outputs; every downstream job declaresneeds: [check-ci]and checks out withref: ${{ needs.check-ci.outputs.sha }}; every DockerGIT_SHAbuild arg +org.opencontainers.image.revisionlabel uses the resolved SHA. - docs/runbooks/ghcr-republish.md (commit
9afdf5d) — operator-gated runbook for republishing the three release images (pulp-engine{,-worker,-controller}) under their post-rename names. Includes a criticalorg.opencontainers.image.revision == git rev-parse vX.Y.Z^{}verification that doubles as the end-to-end proof of the workflow_dispatch fix. Appendix A gates the dangerous tag-delete-and-repush fallback behind explicit maintainer approval; Appendix B recommends leaving the stale pre-renamedocuforge*GHCR packages in place.
The first end-to-end proof of both lands when an operator runs the runbook post-billing-restoration.
4. Editor build polish (manualChunks + spawn modernization)
Two commits, both shipped under verification (full editor build green on Windows; pnpm --filter @pulp-engine/editor typecheck clean; 1313/1313 Vitest suite green).
apps/editor/scripts/build-all.mjsmodernization (commit720f9bb). Dropsshell: trueand the pnpm-as-parent-process pattern. Resolvesvite/bin/vite.jsandtypescript/bin/tscviacreateRequire+<pkg>/package.json(Vite 8’sexportsfield blocks the directvite/bin/vite.jspath) and invokes them viaspawn(process.execPath, [script, ...args]). Pure Node — no shell, nocmd.exe, no.cmdindirection. Manualnew Promise+ dualchild.on(close|error)handlers replaced withevents.once(child, 'exit'). The empirical “just usepnpm.cmdon Windows” alternative was rejected:spawn('pnpm.cmd', ...)withshell: falsefails withspawn EINVALon Windows.MSYS_NO_PATHCONV: '1'env shim removed.windowsHide: trueadded.apps/editor/vite.config.tsmanualChunks(commit3857220). First-pass vendor grouping:react-vendor(react, react-dom, scheduler),radix(@radix-ui/),tiptap(@tiptap/),dnd-kit(@dnd-kit/*), genericvendorcatch-all.idnormalized to forward slashes up front so Windows dev builds group identically to CI. Lib-mode wrapper configs (vite.config.embed.ts,vite.config.form.ts) intentionally untouched — embedders consume them as a single<script>. Measured effect: chunk count 96 → 87 (−9 dedupe wins; lazy boundaries no longer carry duplicate vendor copies); total size flat at 3.7 MB; vendor chunks stay cache-stable across releases that only touch app code. Named chunk sizes: react-vendor 178 KB, radix 115 KB, tiptap 311 KB, dnd-kit 60 KB, vendor 2 MB.
5. Render-route preview split (highest-ROI hotspot decomposition)
The user’s triage of three large apps/api/src/ files identified render.ts (3,200 lines) as the only one with a clean seam — the renderPreviewRoutes plugin export was already structurally separate from renderRoutes, registered separately in server.ts behind the PREVIEW_ROUTES_ENABLED gate. The split moves code, not logic.
apps/api/src/routes/render/render-preview.tsnew sibling (commitb734889) holdsrenderPreviewRoutes+PreviewBodySchema+PreviewOptionsSchema. Imports its 8 shared symbols from./render.js. Matches thebatch-shared.ts→batch-async.tssibling pattern.render.tsshrinks 3,200 → 2,302 lines (−898). Two helpers (makePptxAssetResolver,logPptxWarnings) gainedexportso the sibling can consume them.RenderConfigSchemaimport dropped (was preview-only). Top-of-file JSDoc added documenting the sibling layout.server.tssplits one import line into two; registration logic unchanged.- Public HTTP surface — every route’s path, schema, status codes, streaming semantics — unchanged byte-for-byte.
Verified: typecheck clean, lint clean, file-mode gated suite at the post-WIP-drain baseline (73 files / 1226 passed / 96 skipped), 28/28 preview tests pass.
6. Deferred (with rationale)
Two of the three named hotspot files stay untouched, by design:
apps/api/src/config.ts(1,575 lines) — organized around a singleenvSchemaZod object that parses into a frozenconfigsingleton. No clean seam to extract preview-config or any other domain into a sub-schema without forking validation atomicity. Splitting would add fragility without functional benefit.apps/api/src/server.ts(1,230 lines) — bootstrap orchestrator with order-sensitive plugin registration. The62eba04AUD-001 reorder is a recent reminder that the ordering is load-bearing. Splitting along any plausible seam (e.g. health routes intohealth.ts) fights the order constraint. Right move is a plugin-architecture rethink, not a line-count split.
Verification
The release commit ships under these signals (executed locally during the work; CI signal blocked by billing):
pnpm --filter @pulp-engine/api typecheck— clean.pnpm --filter @pulp-engine/api lint— clean.pnpm --filter @pulp-engine/api test:file— 73 passed | 8 skipped (81 files), 1226 passed | 96 skipped (1322 tests). Matches the post-WIP-drain baseline.pnpm --filter @pulp-engine/api exec dotenv -e ../../.env -- vitest run src/__tests__/render-preview.test.ts— 28/28 green.pnpm --filter @pulp-engine/editor typecheck— clean.pnpm --filter @pulp-engine/editor build— full three-stage build green on Windows; dist containsindex.html,embed.html,form.html,assets/*,pulp-engine-embed.{js,iife.js},pulp-engine-form.{js,iife.js}.pnpm --filter @pulp-engine/editor test— 95 files, 1313/1313 tests pass.
Operational status (carried forward)
Release and CI workflows remain disabled_manually per memory project_actions_billing_block.md. The first end-to-end proof of the release.yml workflow_dispatch fix happens when an operator follows docs/runbooks/ghcr-republish.md post-billing — the runbook’s org.opencontainers.image.revision verification is the proof point.
HA Run 2 scripts and playbook are ready; actual run-and-capture is operator-gated against a live docker-compose.ha.yml stack.
Two-axis status framing
Per the framing locked in this session (memory project_post_audit_status_framing.md):
- Codebase gating work is complete. The audit’s verification-gap asks closed in v0.74.0; v0.75.0 adds maintainability + scripts/runbooks on top.
- Operational publish/HA evidence work is prepared but still pending external prerequisites. The runbook + harness exist; their actual execution awaits billing restoration and a live HA-stack run.