From 1d8dbf8c8fc00a0f56dffa54c0101cd7cdbf1dd7 Mon Sep 17 00:00:00 2001 From: Will Blair <85643015+williamjblair@users.noreply.github.com> Date: Thu, 27 Aug 2026 10:22:50 -0400 Subject: [PATCH] feat(problems): make projection reconstruction public --- AGENTS.md | 20 +++---- DESIGN.md | 5 +- PRODUCT.md | 6 +- README.md | 4 +- docs/WEB.md | 40 ++++++++++--- docs/design-system.md | 4 +- docs/projection-provenance.md | 4 +- docs/security/vela-web-threat-model.md | 2 +- .../activity-data/config/vela-schemas.v1.json | 6 +- .../tests/draft-submission.test.ts | 6 +- .../config/vela-release.v1.json | 18 +++--- .../config/vela-schemas.v1.json | 6 +- .../scripts/reconstruct-projection.mjs | 60 ++++++++++++------- .../src/source-adapters/artifact.ts | 6 +- .../src/source-adapters/reference.ts | 38 ++++++++---- .../tests/manifest-compatibility.test.ts | 4 +- .../tests/projection-builder.test.ts | 2 +- .../tests/reconstruct-projection.test.ts | 6 +- .../tests/source-adapters.test.ts | 23 +++++++ packages/ui/src/styles/editorial.css | 2 +- scripts/release-problems.mjs | 32 ++++++---- 21 files changed, 195 insertions(+), 99 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 550c710..8728f2c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,15 +2,13 @@ ## Product boundary -This private Bun workspace contains two deliberately separate Next.js -applications, one retained editorial source library, and four shared packages: +This public Bun workspace contains one Next.js application and four internal +packages: - `apps/problems`: the unified Vela product application at canonical `problems.science`, combining exact scientific State, non-authoritative Work, and advanced record inspection; -- `apps/www`: the static Vela application at canonical `vela.space`, owning the - front page, which is its only route; - `packages/brand`: framework-neutral tokens, fonts, marks, and licenses; - `packages/ui`: shared React UI source built from shadcn `base-nova` on Base UI, plus stable Vela presentation semantics; @@ -31,9 +29,9 @@ accounts remain separate from Vela actor identities. ## Sources of truth -- Retained paintings: `apps/www/src/assets/paintings`, with their custody record - beside them; Problems keeps its own copy of the one plate it reuses -- Vela editorial routes and shell: `apps/www/src/app` +- Reviewed Problems-local painting: `apps/problems/src/assets/hero.webp`, with + its custody record beside it; the separate `vela.space` surface owns its own + assets - Vela product routes and compositions: `apps/problems/src/app`, `apps/problems/src/components` - Brand contract: `packages/brand/vela.tokens.json` and its generated outputs @@ -77,7 +75,7 @@ when their authors wrote them. Do not rewrite them to match the current system. canvas, deep marine navigation, cobalt interaction, and semantic colors for real state. Earned editorial and identity moments may reuse the canonical sail, first-party watercolor, horizon, constellation, borrowed-light, and - long-handoff language on `apps/www`. Constellations must encode exact relationships; + long-handoff language on `vela.space`. Constellations must encode exact relationships; nautical motifs must clarify orientation or handoff. Never turn product controls into themed props or use literal galaxy wallpaper, glass, neon, route hero slabs, card soup, or decorative charts. @@ -110,10 +108,8 @@ when their authors wrote them. Do not rewrite them to match the current system. ## Editing workflow - Use Bun only. Do not add npm, pnpm, Yarn, Turborepo, or app-local lockfiles. -- Keep `apps/www` static and read-only. It may import current committed release - facts and shared brand/UI source; it must not read request state, connect to - the projection or activity databases, host identity, or interpret protocol - records. +- Keep `vela.space` outside this workspace. Problems may link to it but must not + grow a second editorial application or duplicate its source here. - Preserve exact-state semantics and the distinction between Verification and acceptance. - Preserve the canonical Vela sail unless an approved brand pass replaces it. diff --git a/DESIGN.md b/DESIGN.md index c9eeb44..30193bd 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -160,7 +160,7 @@ accessible behavior. None supplies Vela's ontology or visual brand. The registers are coordinated, not blended. `apps/problems` workspaces use Entire-like dominant objects, GitHub-like files and diffs, and Hugging Face-like discovery. -The separate `apps/www` landing and living essay own the full editorial register. +The separate `vela.space` surface owns the full editorial register. Problems Home, collection cover moments, meaningful empty states, graph orientation, and profile accents may inherit restrained first-party materials. Use the canonical sail and retained artwork; do not redraw the mark or generate @@ -368,7 +368,8 @@ About, and other explicit editorial moments rather than beside the product search. The one permitted ground is a masked geometric texture that no reader could mistake for data: a line here encodes nothing. -The full long-form composition lives in `apps/www`, outside the Problems shell. +The full long-form composition lives at `vela.space`, outside this repository +and the Problems shell. It preserves the historical continuous folio, margin notes, authored figures, deep links, and time-of-day atmosphere. Product pages may reuse a painting or quiet accent, but they do not import the essay layout. diff --git a/PRODUCT.md b/PRODUCT.md index f747399..a89a80c 100644 --- a/PRODUCT.md +++ b/PRODUCT.md @@ -56,9 +56,9 @@ restrained identity moments reused by Problems where they help orientation. These surfaces may use the retained paintings and editorial type. They have their own static shell and do not enter the Problems AppShell. -The front page source is `apps/www/src`, which holds its own retained paintings -and their custody record. Problems keeps its own copy of the one plate it -reuses. Neither app generates replacement art or redraws the sail. +The front page and its retained-painting custody live outside this repository. +Problems keeps its own reviewed copy of the one plate it reuses. Neither +surface generates replacement art or redraws the sail. The editorial origin is live. Problems links to `https://vela.space`; its `/about/endless-frontiers` address is retained only as a permanent cross-origin diff --git a/README.md b/README.md index d9878aa..437ef64 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ Git repository preserves exact Submissions and scoped Verification evidence. Neither production nor Verification changes Standing. Only an authorized, attributed Decision in that named Repository changes Standing; the performer may be human or agent, and deterministic replay -derives the successor state and current actions. Vela Web provides the +derives the successor state and current actions. Problems provides the read-only map, contribution handoff, and review surfaces. ```text @@ -190,7 +190,7 @@ npm, pnpm, Yarn, Turborepo, or per-application lockfiles. ```bash bun install --frozen-lockfile bun run dev:problems # http://127.0.0.1:4322 -bun run dev:www # configurable local editorial preview +bun run dev:demo # deterministic local Problems data, no credentials ``` ## Verify a release candidate diff --git a/docs/WEB.md b/docs/WEB.md index 85d51f1..44c4adf 100644 --- a/docs/WEB.md +++ b/docs/WEB.md @@ -10,9 +10,9 @@ Earlier design and migration plans live under `docs/history/`. readers across current change, direct contribution, communities, and the exact scientific record. Advanced records remain available in the same runtime. -- `vela.space` is the separate static origin compiled by `apps/www` from its - own `src`. It publishes one page. The *Endless Frontiers* essay was published - there at `/constellations` and was removed on 2026-08-21. +- `vela.space` is a separate static origin outside this repository. It + publishes one page. The *Endless Frontiers* essay was published there at + `/constellations` and was removed on 2026-08-21. - Hosted Vela is non-authoritative. The Problems reads a bounded SELECT-only projection from Neon. Work mode writes hosted research activity through `@vela/activity-data`. Canonical custody remains in Repository Git @@ -28,12 +28,10 @@ the named identity files. The package-direction check keeps `@vela/projection-data` independent of mutable activity and limits `@vela/activity-data` reuse to exact canonical and read contracts. -The repository is a Bun workspace with six maintained runtime boundaries and -one non-runnable content area: +The repository is a Bun workspace with five maintained runtime boundaries: ```text apps/problems Vela Problems product: Problem State, Work, and Records -apps/www static Vela front page, one route packages/brand governed identity, tokens, fonts, and delivery assets packages/ui shared shadcn/Base UI source and Vela presentation semantics packages/projection-data Git-to-Neon projection, validation, search, and manifests @@ -141,6 +139,33 @@ database in the `vela-problems-projection` Neon project: bun run release:problems ``` +The release transaction acquires every declared public source from the checked +locks, writes one content-addressed adapter artifact, and retains it as a public +release asset in `vela-science/problems`. New projection manifests use +`vela.projection-source-adapter-artifact-reference.v3`; its retrieval contract +is unauthenticated. Historical v2 manifests remain readable as history but are +never emitted by the current builder. + +A clean public checkout can prepare the same input class without access to +`vela-web`: + +```bash +bun run sources:refresh -- \ + --output /tmp/vela-source-adapters \ + --artifact-directory /tmp + +bun run projection:reconstruct -- \ + --repositories-root /path/containing/public-math-checkout \ + --vela /path/to/the-digest-verified-released-vela-0.977.6-binary \ + --source-adapter-artifact /tmp/vela-projection-source-adapters-.json \ + --production-parity skip \ + --output /tmp/vela-problems-clean-room.json +``` + +The skip is only for a noncanonical preactivation candidate. C1 qualification +must use the default required production comparison against the SELECT-only +reader; activation remains a separate operator-controlled stage. + Refresh refuses dirty or unpushed sources, wrong branches or remotes, Vela version or released-binary-byte drift, packet drift, missing decision evidence, incomplete reviews, root disagreement, and every ambient corpus-drop override. @@ -174,7 +199,8 @@ table roots, and source roots are identical. After activation, the same operator transaction stages any editorial snapshot, requalifies and reconstructs the clean local commit, publishes that exact commit, deploys it through Vercel's exact Git-SHA API, verifies production, and retains a qualification record. -The content-addressed source-adapter artifact is retained before activation. +The content-addressed source-adapter artifact is retained as an unauthenticated +public release asset in `vela-science/problems` before activation. An exact remote lock prevents two operators from interleaving those stages. Run this transaction immediately after an accepted canonical Math change and diff --git a/docs/design-system.md b/docs/design-system.md index bf989bf..68c8382 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -256,8 +256,8 @@ publication compositions stay app-local CSS modules. Shared controls, focus, tokens, motion limits, and type sources still come from `@vela/ui` and `@vela/brand`. -The front page source is `apps/www/src`, with painting provenance beside the -plates. The essay it once carried was removed on 2026-08-21. +The front page source and its painting provenance live outside this repository. +The essay it once carried was removed on 2026-08-21. Problems selectively consumes reviewed paintings and current passages in its brand register; the source does not become a second design system or runtime. diff --git a/docs/projection-provenance.md b/docs/projection-provenance.md index c196f9c..eac71b0 100644 --- a/docs/projection-provenance.md +++ b/docs/projection-provenance.md @@ -1,7 +1,7 @@ # Problems product provenance -`apps/problems` is a new implementation in the consolidated `vela-web` -history. The archived `vela-site` repository was studied at commit +`apps/problems` is the canonical implementation extracted from the historical +private `vela-web` line. The archived `vela-site` repository was studied at commit `34e3f20` for product anatomy: its inset shell, collapsible navigation, contextual header, dense ledgers, metadata rail, command palette, and stable deep-link behavior. diff --git a/docs/security/vela-web-threat-model.md b/docs/security/vela-web-threat-model.md index 639617e..e8860a9 100644 --- a/docs/security/vela-web-threat-model.md +++ b/docs/security/vela-web-threat-model.md @@ -257,7 +257,7 @@ flowchart LR | Public State routes | Anonymous HTTP GET | Internet -> exact reader | SELECT-only scientific projection | `apps/problems/src/app/p/[repository]/[problem]/page.tsx`; `AGENTS.md` | | AuthKit proxy and `currentAccount` | Browser session | Internet -> WorkOS -> app | Hosted identity only; validates redirect and cookie configuration | `apps/problems/src/proxy.ts`; `apps/problems/src/lib/auth.ts` | | GitHub connection adapter | WorkOS GitHub identity, signed App installation, selected repository metadata | Internet -> GitHub -> app -> Activity | HMAC-bound setup state; Octokit webhook verification; delivery-root deduplication; short-lived read-only installation tokens; no source-byte or token persistence | `apps/problems/src/lib/github-app.ts`; `apps/problems/src/app/api/github`; `packages/activity-data/schema/github-codebases.sql` | -| Core integration inspector | Temporary exact GitHub archive at an immutable commit | app -> pinned Vela 0.977.3 CLI | Bounded link-free extraction; closed authority-neutral JSON; temporary bytes removed after inspection | `apps/problems/src/lib/codebase-inspection.ts`; `packages/projection-data/src/core-integration.ts` | +| Core integration inspector | Temporary exact GitHub archive at an immutable commit | app -> pinned released Vela 0.977.6 CLI | Bounded link-free extraction; closed authority-neutral JSON; temporary bytes removed after inspection | `apps/problems/src/lib/codebase-inspection.ts`; `packages/projection-data/src/core-integration.ts` | | Workspace Server Actions | Authenticated form submit | Browser -> server | One declared action file; recomputes exact anchor | `apps/problems/src/app/actions/activity.ts` | | Draft export | Authenticated GET | Browser -> server -> activity API | Membership-required canonical bytes; private/no-store | `apps/problems/src/app/drafts/[id]/export/route.ts`; `activity_api.export_submission_draft` | | Activity SQL API | Parameterized SQL | server -> separate database | `SECURITY DEFINER`, fixed search path, membership and command allowlist | `packages/activity-data/src/activity.ts`; `packages/activity-data/migrations/20260811_activity_v1.sql` | diff --git a/packages/activity-data/config/vela-schemas.v1.json b/packages/activity-data/config/vela-schemas.v1.json index 5112717..8d620ce 100644 --- a/packages/activity-data/config/vela-schemas.v1.json +++ b/packages/activity-data/config/vela-schemas.v1.json @@ -1,8 +1,8 @@ { "schema": "site.vela-schemas.v1", - "vela_version": "0.977.3", - "vela_tag": "v0.977.3", - "vela_commit": "1c1abe8f365f16803fea889bf9280877992a6d02", + "vela_version": "0.977.6", + "vela_tag": "v0.977.6", + "vela_commit": "9ac8e7730bfb63a3b8eb1d2e1d91081c3e703c59", "files": [ { "file": "submission.schema.json", diff --git a/packages/activity-data/tests/draft-submission.test.ts b/packages/activity-data/tests/draft-submission.test.ts index c562192..4709e15 100644 --- a/packages/activity-data/tests/draft-submission.test.ts +++ b/packages/activity-data/tests/draft-submission.test.ts @@ -50,9 +50,9 @@ describe("vela.submission.v3 drafts", () => { .toBe("sha256:c931f3a454c2a6544ac08e40adb2e0eb77f71131b7593957de8ff88f748d7318"); expect(provenance.files[0].sha256).toBe(schemaRoot); expect(provenance).toMatchObject({ - vela_version: "0.977.3", - vela_tag: "v0.977.3", - vela_commit: "1c1abe8f365f16803fea889bf9280877992a6d02", + vela_version: "0.977.6", + vela_tag: "v0.977.6", + vela_commit: "9ac8e7730bfb63a3b8eb1d2e1d91081c3e703c59", }); const exported = createSubmissionDraftExport(fixture()); expect(exported.payload.schema).toBe("vela.submission.v3"); diff --git a/packages/projection-data/config/vela-release.v1.json b/packages/projection-data/config/vela-release.v1.json index 7044ebb..b8ec96a 100644 --- a/packages/projection-data/config/vela-release.v1.json +++ b/packages/projection-data/config/vela-release.v1.json @@ -1,12 +1,12 @@ { "schema": "vela.release-record.v1", - "version": "0.977.3", - "tag": "v0.977.3", - "commit": "1c1abe8f365f16803fea889bf9280877992a6d02", - "release_url": "https://github.com/vela-science/vela/releases/tag/v0.977.3", - "docs_url": "https://github.com/vela-science/vela/blob/1c1abe8f365f16803fea889bf9280877992a6d02/docs", - "generator_binary_sha256": "sha256:89e5f366db5480a011c722bdc7d3c7f09e07fe78c0cd2855d2e53d3a419520a0", - "macos_generator_binary_sha256": "sha256:3a1173918bdcb887155bab681411bf5e9ff64d925fe1b50369ac37ab020b94ad", - "linux_archive_sha256": "sha256:072af0182152ac4b4a8f04cec7e37f1dc3b5b7a42f49ef4066cce559e26835b3", - "macos_archive_sha256": "sha256:f1299f217985c1eecdfb20ef8750014bbf7b2fc6d3cc31ca33bdd433e2be8991" + "version": "0.977.6", + "tag": "v0.977.6", + "commit": "9ac8e7730bfb63a3b8eb1d2e1d91081c3e703c59", + "release_url": "https://github.com/vela-science/vela/releases/tag/v0.977.6", + "docs_url": "https://github.com/vela-science/vela/blob/9ac8e7730bfb63a3b8eb1d2e1d91081c3e703c59/docs", + "generator_binary_sha256": "sha256:e476ece52cb5f356519f890533f06c918fb10f3dd00268092d490701f7fd1b65", + "macos_generator_binary_sha256": "sha256:5b21415c98503b20518c0e68714b0b4f4b3c371525ea110563b89a53a0d3dbb3", + "linux_archive_sha256": "sha256:a8cb120a01211fbb40b5da6d697b0fc8e4a84b0d76e62cfa574a2518bdebb83e", + "macos_archive_sha256": "sha256:62ea9006e086b40f0431b2ce2cf74827518f37dc58e329353920083f50dad874" } diff --git a/packages/projection-data/config/vela-schemas.v1.json b/packages/projection-data/config/vela-schemas.v1.json index de712c5..50eb921 100644 --- a/packages/projection-data/config/vela-schemas.v1.json +++ b/packages/projection-data/config/vela-schemas.v1.json @@ -1,8 +1,8 @@ { "schema": "site.vela-schemas.v1", - "vela_version": "0.977.3", - "vela_tag": "v0.977.3", - "vela_commit": "1c1abe8f365f16803fea889bf9280877992a6d02", + "vela_version": "0.977.6", + "vela_tag": "v0.977.6", + "vela_commit": "9ac8e7730bfb63a3b8eb1d2e1d91081c3e703c59", "files": [ { "file": "vela-status-v4.schema.json", diff --git a/packages/projection-data/scripts/reconstruct-projection.mjs b/packages/projection-data/scripts/reconstruct-projection.mjs index cdc5c6f..d77b45e 100644 --- a/packages/projection-data/scripts/reconstruct-projection.mjs +++ b/packages/projection-data/scripts/reconstruct-projection.mjs @@ -152,6 +152,7 @@ export function parseArgs(argv) { "repositories-root", "vela", "source-adapter-artifact", + "production-parity", "output", ]); const values = new Map(); @@ -165,6 +166,11 @@ export function parseArgs(argv) { values.set(name, value); } const vela = values.get("vela") ?? process.env.VELA_BIN ?? "vela"; + const productionParity = values.get("production-parity") ?? "required"; + invariant( + productionParity === "required" || productionParity === "skip", + "--production-parity must be required or skip", + ); return { repositoriesRoot: resolve(values.get("repositories-root") ?? join(repositoryRoot, "..")), vela: vela.includes("/") ? resolve(vela) : vela, @@ -173,6 +179,7 @@ export function parseArgs(argv) { ?? process.env.VELA_SOURCE_ADAPTER_ARTIFACT ?? "", ), + productionParity, output: values.has("output") ? resolve(values.get("output")) : null, }; } @@ -520,22 +527,38 @@ export async function reconstructProjection(options) { invariant(canonicalJson(runs[0]) === canonicalJson({ ...runs[1], attempt: 1 }), "two database reconstructions disagree"); reportPhase("database_comparison_complete"); - const activeDatabaseUrl = process.env.VELA_PROJECTION_DATABASE_URL; - invariant(activeDatabaseUrl, "VELA_PROJECTION_DATABASE_URL is required for active projection parity"); - const active = new SQL(activeDatabaseUrl, { max: 1, prepare: false }); - let production; - try { - production = await currentStoredRelease(active); - } finally { - await active.close(); + let productionParity; + if (options.productionParity === "skip") { + productionParity = { + status: "skipped", + source: null, + release_root: null, + reason: "explicit noncanonical preactivation qualification", + }; + } else { + const activeDatabaseUrl = process.env.VELA_PROJECTION_DATABASE_URL; + invariant(activeDatabaseUrl, "VELA_PROJECTION_DATABASE_URL is required for active projection parity"); + const active = new SQL(activeDatabaseUrl, { max: 1, prepare: false }); + let production; + try { + production = await currentStoredRelease(active); + } finally { + await active.close(); + } + invariant(production, "active projection database has no current release"); + const parity = compareProductionProjection(candidates[0].manifest, production); + invariant(parity.manifest_schema_equal, "active projection manifest schema drift"); + invariant(parity.vela_version_equal, "active projection Vela version drift"); + invariant(parity.table_roots_equal, "active projection table-root drift"); + invariant(parity.source_repositories_equal, "active projection Repository input drift"); + invariant(parity.source_registry_equal, "active projection source-registry drift"); + productionParity = { + status: "pass", + source: "active_projection_database", + release_root: production.release_root, + ...parity, + }; } - invariant(production, "active projection database has no current release"); - const parity = compareProductionProjection(candidates[0].manifest, production); - invariant(parity.manifest_schema_equal, "active projection manifest schema drift"); - invariant(parity.vela_version_equal, "active projection Vela version drift"); - invariant(parity.table_roots_equal, "active projection table-root drift"); - invariant(parity.source_repositories_equal, "active projection Repository input drift"); - invariant(parity.source_registry_equal, "active projection source-registry drift"); const body = { schema: "vela.projection-clean-room-qualification.v1", @@ -561,12 +584,7 @@ export async function reconstructProjection(options) { byte_identical: true, reader_select_only: true, }, - production_parity: { - status: "pass", - source: "active_projection_database", - release_root: production.release_root, - ...parity, - }, + production_parity: productionParity, limitations: [ "The macOS reconstruction has a distinct release root when production was generated by the recorded Linux binary; exact table roots, Repository inputs, and source-registry roots must still match.", "The same frozen source-adapter artifact is used twice; this proves reconstruction, not future reacquisition from mutable upstream sources.", diff --git a/packages/projection-data/src/source-adapters/artifact.ts b/packages/projection-data/src/source-adapters/artifact.ts index afb694e..c75161b 100644 --- a/packages/projection-data/src/source-adapters/artifact.ts +++ b/packages/projection-data/src/source-adapters/artifact.ts @@ -132,15 +132,15 @@ function artifactReference( artifact: ProjectionSourceAdapterArtifact, ): ProjectionSourceAdapterArtifactReference { return projectionSourceAdapterArtifactReferenceSchema.parse({ - schema: "vela.projection-source-adapter-artifact-reference.v2", + schema: "vela.projection-source-adapter-artifact-reference.v3", set_root: artifact.set_root, artifact_root: artifact.artifact_root, retrieval: { type: "github_release_asset", - repository: "vela-science/vela-web", + repository: "vela-science/problems", release_tag: projectionSourceAdapterArtifactReleaseTag(artifact.set_root), filename: projectionSourceAdapterArtifactFilename(artifact.set_root), - authentication: "required", + authentication: "none", }, }); } diff --git a/packages/projection-data/src/source-adapters/reference.ts b/packages/projection-data/src/source-adapters/reference.ts index 762ae71..afee361 100644 --- a/packages/projection-data/src/source-adapters/reference.ts +++ b/packages/projection-data/src/source-adapters/reference.ts @@ -39,16 +39,34 @@ const projectionSourceAdapterArtifactReferenceV2Schema = z.object({ } }); -/* One shape, because only one was ever produced. - * - * `artifact.ts` hard-codes v2, every stored release manifest and the live site - * manifest report v2, and the v1 schema string appeared nowhere on disk except - * the arm that declared it. A union of one live shape and one that never - * existed is not compatibility, it is an unexercised branch — and it hid a - * real defect: `sources:verify` printed `reference.locator`, a field only v1 - * defined, so the value silently dropped out of its own output. */ -export const projectionSourceAdapterArtifactReferenceSchema = - projectionSourceAdapterArtifactReferenceV2Schema; +const projectionSourceAdapterArtifactReferenceV3Schema = z.object({ + schema: z.literal("vela.projection-source-adapter-artifact-reference.v3"), + set_root: hashRootSchema, + artifact_root: hashRootSchema, + retrieval: z.object({ + type: z.literal("github_release_asset"), + repository: z.literal("vela-science/problems"), + release_tag: z.string(), + filename: z.string(), + authentication: z.literal("none"), + }).strict(), +}).strict().superRefine((value, context) => { + if (value.retrieval.release_tag !== projectionSourceAdapterArtifactReleaseTag(value.set_root)) { + context.addIssue({ code: "custom", path: ["retrieval", "release_tag"], message: "release tag does not match the set root" }); + } + if (value.retrieval.filename !== projectionSourceAdapterArtifactFilename(value.set_root)) { + context.addIssue({ code: "custom", path: ["retrieval", "filename"], message: "filename does not match the set root" }); + } +}); + +/* v2 remains readable because activated historical releases bind its private + * `vela-web` locator. New artifacts are v3: the same content-addressed release + * asset contract under public Problems custody, with no authentication. This + * is history compatibility rather than a fallback: the writer emits only v3. */ +export const projectionSourceAdapterArtifactReferenceSchema = z.union([ + projectionSourceAdapterArtifactReferenceV3Schema, + projectionSourceAdapterArtifactReferenceV2Schema, +]); export type ProjectionSourceAdapterArtifactReference = z.infer< typeof projectionSourceAdapterArtifactReferenceSchema diff --git a/packages/projection-data/tests/manifest-compatibility.test.ts b/packages/projection-data/tests/manifest-compatibility.test.ts index 53e2381..f70fd24 100644 --- a/packages/projection-data/tests/manifest-compatibility.test.ts +++ b/packages/projection-data/tests/manifest-compatibility.test.ts @@ -34,7 +34,7 @@ function manifest(overrides: Record = {}) { describe("current projection compatibility", () => { test("loads the one exact supported Vela release", () => { - expect(normalizeProjectionManifest(manifest()).vela_version).toBe("vela 0.977.3"); + expect(normalizeProjectionManifest(manifest()).vela_version).toBe("vela 0.977.6"); }); test("keeps the frontier projection block additive and self-consistent", () => { @@ -73,7 +73,7 @@ describe("current projection compatibility", () => { } catch (error) { expect(projectionRefusal(error)).toBe("foreign_manifest"); expect(error).toBeInstanceOf(Error); - expect((error as Error).message).toContain("requires vela 0.977.3"); + expect((error as Error).message).toContain("requires vela 0.977.6"); expect((error as Error).message).toContain("serves vela 0.976.1"); expect((error as Error).message).toContain("SELECT-only exact supported projection"); expect((error as Error).message).not.toMatch(/postgres(?:ql)?:\/\//u); diff --git a/packages/projection-data/tests/projection-builder.test.ts b/packages/projection-data/tests/projection-builder.test.ts index b0accb2..ad80b62 100644 --- a/packages/projection-data/tests/projection-builder.test.ts +++ b/packages/projection-data/tests/projection-builder.test.ts @@ -152,7 +152,7 @@ function projection(overrides: Record = {}) { authority_effect: "none", projection_root: root("0"), projection_root_definition: "sha256 of RFC 8785 canonical JSON after removing only projection_root", - reader_version: "0.977.3", + reader_version: "0.977.6", repository: { repository_id: "123e4567-e89b-42d3-a456-426614174000", name: "Test repository", diff --git a/packages/projection-data/tests/reconstruct-projection.test.ts b/packages/projection-data/tests/reconstruct-projection.test.ts index fc728a4..aa6b02b 100644 --- a/packages/projection-data/tests/reconstruct-projection.test.ts +++ b/packages/projection-data/tests/reconstruct-projection.test.ts @@ -34,8 +34,10 @@ describe("clean-room production parity", () => { expect(() => parseArgs(["--production-manifest", "https://example.test/manifest.json"])).toThrow( "unsupported argument --production-manifest", ); - expect(() => parseArgs(["--production-parity", "skip"])).toThrow( - "unsupported argument --production-parity", + expect(parseArgs(["--production-parity", "skip"]).productionParity).toBe("skip"); + expect(parseArgs([]).productionParity).toBe("required"); + expect(() => parseArgs(["--production-parity", "maybe"])).toThrow( + "--production-parity must be required or skip", ); expect(() => parseArgs(["--unknown-input", "stale.json"])).toThrow( "unsupported argument --unknown-input", diff --git a/packages/projection-data/tests/source-adapters.test.ts b/packages/projection-data/tests/source-adapters.test.ts index 4729a6e..1ef9ce6 100644 --- a/packages/projection-data/tests/source-adapters.test.ts +++ b/packages/projection-data/tests/source-adapters.test.ts @@ -49,6 +49,7 @@ import { loadProjectionSourceAdapterArtifact, writeProjectionSourceAdapterArtifact, } from "../src/source-adapters/artifact"; +import { projectionSourceAdapterArtifactReferenceSchema } from "../src/source-adapters/reference"; import { mathSourceRegistry } from "../src/math-sources"; import { sha256 } from "../src/canonical"; @@ -1937,9 +1938,31 @@ describe("projection refresh source-adapter set", () => { expect(reconstructed.artifact.artifact_root).toBe( packed.artifact.artifact_root, ); + expect(reconstructed.reference).toMatchObject({ + schema: "vela.projection-source-adapter-artifact-reference.v3", + retrieval: { + repository: "vela-science/problems", + authentication: "none", + }, + }); expect(reconstructed.manifest.set_root).toBe(prepared.manifest.set_root); expect([...reconstructed.bundles.keys()].sort()).toEqual( [...loaded.bundles.keys()].sort(), ); }, 15_000); + + test("retains the historical private v2 reference as read-only history", () => { + expect(projectionSourceAdapterArtifactReferenceSchema.parse({ + schema: "vela.projection-source-adapter-artifact-reference.v2", + set_root: `sha256:${"1".repeat(64)}`, + artifact_root: `sha256:${"2".repeat(64)}`, + retrieval: { + type: "github_release_asset", + repository: "vela-science/vela-web", + release_tag: `source-adapter-set-${"1".repeat(64)}`, + filename: `vela-projection-source-adapters-${"1".repeat(64)}.json`, + authentication: "required", + }, + }).schema).toBe("vela.projection-source-adapter-artifact-reference.v2"); + }); }); diff --git a/packages/ui/src/styles/editorial.css b/packages/ui/src/styles/editorial.css index 06e001b..6e2a440 100644 --- a/packages/ui/src/styles/editorial.css +++ b/packages/ui/src/styles/editorial.css @@ -1,5 +1,5 @@ /* Editorial profile of the same Vela semantic system. Content-specific - * composition remains in apps/www; controls, focus, motion and state do not. */ + * composition remains on vela.space; controls, focus, motion and state do not. */ @import "./product.css"; @custom-variant dark (&:is(.dark *, [data-theme="dark"] *)); diff --git a/scripts/release-problems.mjs b/scripts/release-problems.mjs index c4f0af3..8d5c991 100644 --- a/scripts/release-problems.mjs +++ b/scripts/release-problems.mjs @@ -158,7 +158,16 @@ function deriveOperatorEnvironment(scoped) { } function vercelEnvironment(environment) { - return environmentFor(environment, ["VERCEL_TOKEN", "VERCEL_GLOBAL_CONFIG"]); + return environmentFor(environment, [ + "VERCEL_TOKEN", + "VERCEL_GLOBAL_CONFIG", + "VERCEL_TEAM_ID", + "VERCEL_PROJECT_ID", + "VERCEL_PROJECT_NAME", + "VERCEL_GIT_REPO_ID", + "VERCEL_GIT_REPO_REF", + "VELA_DEPLOY_REPOSITORY", + ]); } function run(command, args, { environment = process.env, cwd = root, quiet = false } = {}) { @@ -180,15 +189,18 @@ function git(args, cwd = root, environment = process.env) { return run("git", args, { cwd, environment, quiet: true }); } -function exactWebCheckout(environment) { +function exactProblemsCheckout(environment) { const safe = githubGitEnvironment(environment); - const expectedRemote = "https://github.com/vela-science/vela-web.git"; + const expectedRemotes = new Set([ + "https://github.com/vela-science/problems.git", + "git@github.com:vela-science/problems.git", + ]); const head = git(["rev-parse", "HEAD"], root, safe); if (git(["branch", "--show-current"], root, safe) !== "main") { throw new Error("release requires main"); } - if (git(["remote", "get-url", "origin"], root, safe) !== expectedRemote) { - throw new Error("release requires the canonical vela-science/vela-web origin"); + if (!expectedRemotes.has(git(["remote", "get-url", "origin"], root, safe))) { + throw new Error("release requires the canonical vela-science/problems origin"); } if (git(["status", "--porcelain"], root, safe)) { throw new Error("release requires a clean worktree"); @@ -346,7 +358,7 @@ export function releaseLookupState({ status, stdout = "", stderr = "" }) { function releaseLookup(environment, tag) { const safe = githubEnvironment(environment); const result = spawnSync(required(environment, "VELA_GITHUB_CLI"), [ - "api", `repos/vela-science/vela-web/releases/tags/${tag}`, "--include", + "api", `repos/vela-science/problems/releases/tags/${tag}`, "--include", ], { env: safe, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }); const state = releaseLookupState(result); if (state) return state; @@ -358,7 +370,7 @@ function retainExactFile(environment, { tag, path, target, title, notes }) { if (releaseLookup(environment, tag) === "missing") { run(required(environment, "VELA_GITHUB_CLI"), [ "release", "create", tag, path, - "--repo", "vela-science/vela-web", + "--repo", "vela-science/problems", "--target", target, "--title", title, "--notes", notes, @@ -369,7 +381,7 @@ function retainExactFile(environment, { tag, path, target, title, notes }) { const existing = join(dirname(path), `retained-${basename(path)}`); run(required(environment, "VELA_GITHUB_CLI"), [ "release", "download", tag, - "--repo", "vela-science/vela-web", + "--repo", "vela-science/problems", "--pattern", basename(path), "--output", existing, ], { environment: safe }); @@ -608,7 +620,7 @@ function deploy(environment, context, field = "deployment") { environment: { ...safe, VELA_SITE_COMMIT: context.siteCommit, - GITHUB_REPOSITORY: "vela-science/vela-web", + GITHUB_REPOSITORY: "vela-science/problems", GITHUB_REF: "refs/heads/main", }, }); @@ -770,7 +782,7 @@ function retainQualification(environment, context) { } const stageDefinitions = Object.freeze([ - ["exact_checkout", (environment, context) => { context.siteCommit = exactWebCheckout(environment); }], + ["exact_checkout", (environment, context) => { context.siteCommit = exactProblemsCheckout(environment); }], ["release_lock", (environment, context) => acquireReleaseLock(environment, context)], ["static_qualification", (environment) => runStaticQualification(environment)], ["neon_production_identity", (environment, context) => { context.neon = verifyNeonProductionBranch(environment); }],