Skip to content

ci(publish): real-publish-equivalent preflight (no more half-releases)#141

Merged
rickylabs merged 1 commit into
mainfrom
ci/jsr-publish-preflight
Jun 26, 2026
Merged

ci(publish): real-publish-equivalent preflight (no more half-releases)#141
rickylabs merged 1 commit into
mainfrom
ci/jsr-publish-preflight

Conversation

@rickylabs

Copy link
Copy Markdown
Owner

Real-publish-equivalent preflight — never half-release again

deno publish --dry-run is a confirmed blind spot: it can pass while the real publish module-graph build rejects a module. That is exactly how alpha.5 half-published — the text-import graph-build rejection surfaced only at real publish (after dry-run was green), so a partial member set uploaded before the run errored.

This adds a preflight that exercises the real publish code path but cannot upload.

How it works

A new preflight mode in publish-workspace.ts runs the actual deno publish (not --dry-run) with a deliberately invalid token (--token … --no-provenance). deno publish builds the full combined module graph for every workspace member first, then hits auth — so an invalid token lets the graph build run for real and stops at the auth boundary before any upload.

Classification:

Outcome Meaning Verdict
Reached auth boundary (invalidBearerToken) full graph built for every member PASS
Exit 0 (all already published, registry skip) nothing to build PASS (no-op)
Any other nonzero (graph/check error) a real publish would also fail FAIL (would half-publish)

CI wiring

publish.yml gains a Publish preflight (real graph build, no upload) step between Publish dry-run and Publish. A real graph/check failure now reds the job before the real Publish step runs, so a release can never half-publish. The step never uploads (the token is invalid by design); it adds no secret dependency.

Validation (local, Deno 2.9.0)

All three classification branches exercised end-to-end:

  • Already-published (alpha.5 current) → PASS no-op, exit 0.
  • Unpublished version (forced real graph build) → real graph built for all 31 members incl. @netscript/fresh-ui, stopped at the auth boundary → PASS, exit 0.
  • Injected missing-module graph error → FAILED, exit 1 (throws before the auth boundary, nothing uploaded).

deno check ✓ · deno fmt --check ✓ · deno lint ✓ (tools clean).

Closes the deno publish --dry-run blind spot recorded in the JSR publish dry-run blind-spots note.

🤖 Generated with Claude Code

`deno publish --dry-run` is a confirmed blind spot: it can pass while the
REAL publish module-graph build rejects a module. That is exactly how the
alpha.5 release half-published (text-import graph-build rejection surfaced
only at real publish, after dry-run was green), uploading a partial set.

Add a `preflight` publish mode that runs the actual `deno publish` (not
--dry-run) with a deliberately invalid token, so it builds every member's
publish graph in the real code path and then stops at the auth boundary
before any upload. Classification:
  - reached auth boundary (invalidBearerToken)  -> PASS (full graph built)
  - exit 0 (all already published, registry skip) -> PASS (no-op)
  - any other nonzero (graph/check error)        -> FAIL (would half-publish)

Wire a "Publish preflight" CI step between dry-run and Publish, so a real
graph/check failure reds the job before the real Publish step runs. The
preflight never uploads (the token is invalid by design).

Validated locally across all three branches: already-published no-op PASS,
unpublished-version real-graph-build -> auth-boundary PASS (all 31 members
incl. fresh-ui), and an injected missing-module graph error -> FAIL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
@rickylabs
rickylabs merged commit 8c45620 into main Jun 26, 2026
6 checks passed
@rickylabs
rickylabs deleted the ci/jsr-publish-preflight branch June 26, 2026 18:51
rickylabs added a commit that referenced this pull request Jun 26, 2026
…to 0.0.1-alpha.6 (#142)

Revert the #139 string-const asset embedding and return to `with { type: 'text' }`
imports in the generated barrels (cli/fresh-ui/plugin) — text imports keep the
asset content in the module graph so it resolves over https from JSR, and are the
cleaner mechanism. They are runtime-stable in Deno 2.8+ and the real publish-graph
build accepts them (verified locally and in CI on Deno 2.9.0).

The #141 publish preflight is kept: it runs the real `deno publish` graph build
(no upload) before the Publish step, so if the text-import graph build is ever
rejected the job reds before any upload — no half-publish. The alpha.5 text-import
rejection was not reproducible locally or in CI on the same Deno 2.9.0.

Bump 0.0.1-alpha.5 -> 0.0.1-alpha.6 across all workspace members, the root
deno.json, and the deno.lock @netscript range mirror.

Gates: check:assets-barrel (deterministic regen) PASS; local publish preflight
(real graph build over all 31 members incl. fresh-ui + plugin text imports) PASS.


Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
rickylabs added a commit that referenced this pull request Jun 27, 2026
Composes the four alpha.11 fix-train slices: A (root --version + write-free --dry-run via DryRunFileSystemAdapter), B (scaffold type-soundness), C (--cache/--cache-backend + interactive init via PromptPort), E (plain GET /health + service-health e2e probe). Init conflict resolved keeping both A+C behaviors. Composed scaffold.runtime e2e 48/0. IMPL-EVAL PASS (OpenHands qwen3.7-max, separate session): no userland leak, no unauthorized casts, minimal lock churn. Integrates #156 #157 #158 #159. Refs #141 #153.
rickylabs added a commit that referenced this pull request Jun 27, 2026
Replace scaffold-emitted TypeScript AppHost instructions that still said `aspire run` with the valid `aspire start` command, including generated README content, init next steps, template comments, embedded asset content, and assertions.

Refs #141 #153

Co-Authored-By: OpenAI Codex <codex@openai.com>
rickylabs added a commit that referenced this pull request Jun 27, 2026
Replace scaffold-emitted TypeScript AppHost instructions that still said `aspire run` with the valid `aspire start` command, including generated README content, init next steps, template comments, embedded asset content, and assertions.

Refs #141 #153

Co-authored-by: OpenAI Codex <codex@openai.com>
rickylabs added a commit that referenced this pull request Jun 27, 2026
* docs(harness): alpha.11 fix-train plan + research (Storefront Ch.1 blockers)

PLAN-EVAL deliverable for the alpha.11 fix-train (#141) addressing the
Storefront Ch.1 eye-test blockers (#153). Six slices: A CLI-core (F-3/F-4),
B scaffold type-soundness (F-15), C interactive-init + cache feature
(F-11, new public surface), D doc-truth alignment driven by the e2e
(F-6/F-8/F-12), E service health + e2e :3001 coverage (F-14/F-13),
F install pin + alpha banner (F-1).

Grounded by two code-truth scouts: F-6 is a doc fix not a code reorder
(db commands self-provision Aspire via DbOperationRunner); F-13 is
diagnosed by a new e2e service-health probe; interactive init reuses the
dormant PromptPort/CliffyPrompt adapter (init = first consumer).

Plan artifacts only; no framework source. Implementation follows on
PLAN-EVAL PASS via WSL Codex daemon-attached slices.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct

* alpha.11 fix-train: PLAN-EVAL verdict — PASS (with advisories)

- Slice decomposition, dependency order, lane split, gate set, debt list all sound.
- F-6 disposition (db self-provisions Aspire via DbOperationRunner.executeDetached)
  confirmed; doc fix only, not code reorder.
- F-14 advisory: defineService().withHealth() is already wired (current main);
  Slice E's template work is verify-only — only the new :3001 e2e probe is required.
- F-15a/b: template-rewrite preferred over SDK widening (getQueryState exists
  on the underlying @tanstack/query-core 5.101.0).
- F-15c: publish-only drift disposition is correct; self-resolves on republish.
- Slice C public surface (--cache/--cache-backend) is contract-first and
  doctrine-conformant; --ci/--yes escape hatches preserve CI non-interactive.
- Gate set: scoped check/lint/fmt + deno task test + deno doc --lint on changed
  public exports + full e2e:cli scaffold.runtime with new :3001 probe.

Co-authored-by: openhands <openhands@all-hands.dev>

* alpha.11 fix-train: PLAN-EVAL trace metadata (run-28284573816-1, verdict=PASS)

Co-authored-by: openhands <openhands@all-hands.dev>

* chore(openhands): record run trace 28284573816-1

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: openhands <openhands@all-hands.dev>
rickylabs added a commit that referenced this pull request Jun 27, 2026
…realignment (#162)

* docs(cli-reference): document alpha.11 init surface — cache + interactive + version

Lane D (doc-truth align) commit 1: add the new `netscript init` surface introduced by the
alpha.11 fix-train (PR #160) to the scaffold reference.

- `--cache` / `--no-cache` (default on) + `--cache-backend redis|garnet|deno-kv` (default redis;
  redis/garnet are Aspire container resources, deno-kv is app-level/no container).
- Interactive-vs-non-interactive callout: TTY prompts for omitted options incl. the two cache
  questions; `--yes`/`--ci` skip prompts; defaults = Postgres-ready workspace + Redis cache.
- `netscript --version` prints the installed CLI version.

Grounded against integration/alpha11-fixtrain init-command.ts + cache-backend.ts.
Docs-only; lands as a separate docs PR. Refs #141 #153.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct

* docs(alpha11): replace invalid `aspire run` with `aspire start` across published docs

`aspire run` is not a valid Aspire CLI command (the CLI has `aspire start`);
Run-1 eye-test blocker F-12 confirmed it orphans the scaffolded AppHost on
Windows. Sweeps all 61 published pages (tutorials, capabilities, how-to,
explanation, landing, cli-reference) to `aspire start`. Internal `_plan/`
worklogs left untouched. 1:1 string swap, no other content change.

Refs #141 #153

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct

* docs(alpha11): pin CLI install/invoke to the alpha release train (F-1)

Bare `jsr:@netscript/cli` resolves to JSR `latest`, which is null while the
package is prerelease-only — so every unpinned `deno install`/`deno x` in the
docs dead-ends at "no matching version" (Run-1 eye-test blocker F-1). Pin all
20 occurrences to the dynamic `{{ releaseSpecifier }}` (= `@<releaseVersion>`,
sourced from packages/cli/deno.json), so published docs always show the exact
current alpha pin and self-advance on each release bump.

Plain fences/prose use `{{ releaseSpecifier }}`; comp-tag JS string args use
`" + releaseSpecifier + "` concatenation to avoid nested-`{{}}` parse breaks.
Lume build verified green (306 files, exit 0).

Refs #141 #153

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct

* docs(alpha11): realign cache docs to multi-backend capability (redis default)

Present the scaffold cache as a multi-backend capability across the site:
`redis` is the default backend everywhere; `garnet` and `deno-kv` are
first-class alternatives selectable via `--cache-backend`. Garnet is kept
only where shown as an explicit alternative backend choice, and real symbols
(createGarnetListTransport, Cache.<backend> keys, GARNET_URI/REDIS_URI) are
preserved.

- 45 docs files realigned (concepts, cli-reference, glossary, explanation/*,
  capabilities/*, how-to/*, tutorials/*, index/why/quickstart).
- aspire-resource-graph diagram: add the default Redis cache resource node and
  wiring; SVG regenerated (byte-clean, single-diagram delta).
- quickstart bare `netscript init` now passes `--db postgres` to match the
  tutorial stack.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct

* docs(alpha11): present the database as polyglot (postgres default; mysql/mssql/sqlite via --db)

Mirror the cache multi-backend realignment for the database. NetScript scaffolds
a Prisma-backed database for four engines chosen at scaffold time with `--db`:
postgres | mysql | mssql | sqlite. Postgres stays the recommended default (every
tutorial uses `--db postgres`); mysql/mssql/sqlite are first-class alternatives.
postgres/mysql/mssql provision an Aspire container resource; sqlite is file-backed
with no container.

This is enrichment, not replacement: wherever the docs presented Postgres as the
sole app database (monolithic framing), add concise polyglot framing or — on the
anchor pages (database, cli-reference, glossary, use-a-second-database,
database-migration) — a richer backend/--db passage with the sqlite no-container
caveat. Tutorials keep every `--db postgres` command verbatim and gain one
swap-engine note near the first scaffold step.

Rule-C kept untouched (engine-specific by design, not the app-DB-engine choice):
Postgres-as-subsystem backends (queue provider FOR UPDATE SKIP LOCKED / advisory
locks, saga/durable stores), connection-string mechanics (DATABASE_URL /
POSTGRES_URI), Prisma provider literals, engine-specific paths, and the runtime
second-datasource adapter barrel (which ships postgres/mssql/mysql only — no
sqlite adapter was fabricated).

- 27 docs files realigned (capabilities/*, cli-reference, glossary, explanation/*,
  how-to/*, tutorials/*, concepts/index/why/quickstart).
- Lume build: green (306 files). No diagram sources changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct

* docs(alpha11): note Prisma-backed subsystems follow the scaffolded DB engine (not Postgres-only)

A file-grounded subsystem audit showed the saga `prisma` store and the
better-auth backend are NOT Postgres-specific — both persist through the
project's Prisma client, so they follow whichever engine was scaffolded with
`--db` (Postgres by default; mysql / mssql / sqlite all work). The docs
presented them as "Postgres" stores, implying a hard coupling that does not
exist.

Add concise polyglot framing at the definitional points only:
- capabilities/durable-sagas.md — `prisma` store backend + trait-matrix row.
- explanation/durability-model.md — `prisma` store row + "choosing kv vs prisma".
- capabilities/auth.md — better-auth tables provisioning note.
- how-to/add-authentication.md — auth.prisma aggregation line + table caption.

Deliberately left unchanged (genuinely engine-specific, per the same audit):
- The queue PostgreSQL provider — hard-tied to Postgres (`FOR UPDATE SKIP
  LOCKED`, `JSONB`, `TIMESTAMPTZ`, raw `$N` SQL); a polyglot queue is real
  engineering (SQLite infeasible) and is tracked as backlog, not a doc framing.
- The durable-streams service's Postgres/Redis dependency line (service infra,
  accurate by default).

Lume build: green (306 files). No diagram sources changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct

* chore(openhands): record run trace 28300391133-1

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: OpenHands Bot <openhands@all-hands.dev>
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