@jimhoyd/urlcode 0.4.0-alpha.3 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/urlcode-authoring/SKILL.md +10 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/CONTRIBUTING.md +36 -0
- package/README.md +20 -15
- package/ROADMAP.md +25 -16
- package/dist/BUILD-MANIFEST.json +20 -19
- package/dist/authoring.js +15 -1
- package/dist/capability-query.js +0 -1
- package/dist/catalog.js +0 -1
- package/dist/cli.js +24 -7
- package/dist/config.js +1 -1
- package/dist/explain.js +1 -1
- package/dist/extensions.js +78 -1
- package/dist/http-response.js +1 -1
- package/dist/index.js +1 -0
- package/dist/init-with.js +36 -11
- package/dist/manifest.js +1 -1
- package/dist/mcp-authoring.js +2 -2
- package/dist/mcp.js +1 -1
- package/dist/policies/cache.js +2 -2
- package/dist/policy.js +16 -0
- package/dist/project-dependencies.js +305 -0
- package/dist/readiness.js +5 -1
- package/dist/runtime.js +1 -1
- package/dist/tooling.js +2 -1
- package/dist/trusted-functions.js +4 -5
- package/dist/types/authoring.d.ts +9 -1
- package/dist/types/capability-query.d.ts +0 -1
- package/dist/types/catalog.d.ts +0 -4
- package/dist/types/config.d.ts +1 -9
- package/dist/types/explain.d.ts +0 -1
- package/dist/types/extensions.d.ts +58 -0
- package/dist/types/http-response.d.ts +0 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/init-with.d.ts +7 -13
- package/dist/types/manifest.d.ts +0 -1
- package/dist/types/project-dependencies.d.ts +78 -0
- package/dist/types/readiness.d.ts +3 -0
- package/dist/types/tooling.d.ts +1 -0
- package/dist/types/trusted-functions.d.ts +1 -4
- package/docs/AI-AUTHORING.md +10 -5
- package/docs/AWS.md +9 -0
- package/docs/CI-FOLLOWUP-2026-09-19.md +1 -1
- package/docs/CODEBASE-AUDIT-2026-09-20.md +6 -0
- package/docs/COMPOSING-A-SITE.md +287 -0
- package/docs/CONTAINER-PROMOTION.md +74 -0
- package/docs/DEVELOPMENT-PIPELINE.md +242 -119
- package/docs/EXTENSIONS.md +88 -93
- package/docs/FRAMEWORK.md +45 -30
- package/docs/FUNCTION-SECURITY.md +5 -8
- package/docs/INSTALL.md +13 -8
- package/docs/MIDDLEWARE.md +10 -4
- package/docs/OPEN-DECISIONS.md +64 -99
- package/docs/READINESS.md +8 -4
- package/docs/README.md +18 -13
- package/docs/RELEASE-0.4.1.md +73 -0
- package/docs/RELEASE-0.4.2.md +30 -0
- package/docs/RELEASE-READINESS.md +40 -11
- package/docs/RELEASE-SECURITY.md +33 -14
- package/docs/SPECIFICATION.md +5 -1
- package/docs/SPIKE-CORE-LAYERING.md +1 -1
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +9 -13
- package/docs/STARTERS.md +17 -5
- package/docs/TOOLING.md +7 -5
- package/docs/VERCEL.md +10 -2
- package/docs/VERSION-ALIGNMENT.md +50 -8
- package/docs/archive/2026-09-19/ROADMAP.md +1 -0
- package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +1 -0
- package/docs/{SPIKE-LAMBDA-COMPILE.md → archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md} +168 -12
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +2 -0
- package/docs/archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md +116 -0
- package/docs/archive/README.md +2 -0
- package/docs/yaml/functions.md +10 -2
- package/docs/yaml/middleware.md +5 -3
- package/examples/cookbook/middleware/envelope.mjs +4 -2
- package/llms-full.txt +458 -143
- package/llms.txt +2 -1
- package/package.json +8 -5
- package/packaging/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +10 -0
- package/recipes/middleware/middleware/envelope.mjs +4 -2
- package/skills/urlcode/SKILL.md +8 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Container image promotion (design, issue #233)
|
|
2
|
+
|
|
3
|
+
Status: design plus an inert helper. Nothing described under "Proposed" is
|
|
4
|
+
implemented, and no workflow was changed. This does not authorize enabling
|
|
5
|
+
`PUBLISH_CONTAINER`, publishing, or changing any existing tag. The point is
|
|
6
|
+
supply-chain integrity (tested bytes are shipped bytes), not speed: the sampled
|
|
7
|
+
CI image build took about 12 seconds, so build caching is deliberately out of scope.
|
|
8
|
+
|
|
9
|
+
## Current behavior (read from the workflows at 67a6996)
|
|
10
|
+
|
|
11
|
+
- `ci.yml` job `container` (a required check) runs `docker build -t urlcode:test .`
|
|
12
|
+
and smoke-tests it. That image is discarded; no digest is recorded.
|
|
13
|
+
- `candidate.yml` builds and signs the npm archives, SBOM and manifests. It builds no
|
|
14
|
+
image. `manifest.json` and the exact-file-set check in `validateCandidate` know nothing
|
|
15
|
+
about images.
|
|
16
|
+
- `release.yml`, only when `vars.PUBLISH_CONTAINER == 'true'`, runs a fresh
|
|
17
|
+
`docker build` of the tagged commit with `revision`/`version` labels and pushes
|
|
18
|
+
`:VERSION` and the channel tag.
|
|
19
|
+
- On retry, if `:VERSION` already exists it is accepted after checking only the
|
|
20
|
+
`revision` label. A label is asserted by whoever built the image, so a differently
|
|
21
|
+
built image with the same label passes. Bytes are not compared.
|
|
22
|
+
- Channel monotonicity is enforced by reading the `version` label of the existing
|
|
23
|
+
channel tag (semver `gte`). That guard is sound and should be kept.
|
|
24
|
+
- A rebuild is not byte-reproducible (it runs `npm ci`, embeds timestamps), so the image
|
|
25
|
+
the release publishes is never the image CI tested, even for the same commit.
|
|
26
|
+
|
|
27
|
+
Not verified: live GHCR contents, package visibility/permissions, whether
|
|
28
|
+
`PUBLISH_CONTAINER` is set, and whether any image was ever pushed.
|
|
29
|
+
|
|
30
|
+
## Proposed path (not implemented)
|
|
31
|
+
|
|
32
|
+
1. Candidate build. In `candidate.yml`, build the image once from the exact commit
|
|
33
|
+
(`docker buildx build --output type=oci,dest=candidate/image.oci.tar` with the
|
|
34
|
+
revision/version labels), run the same three smoke tests as `ci.yml` against that
|
|
35
|
+
archive, and record `image: { digest, sourceCommit, version }` in `manifest.json`.
|
|
36
|
+
Add `image.oci.tar` to the attested subjects so the existing `attestation verify` and
|
|
37
|
+
the manifest SHA-256 pinned in the annotated tag cover the image as well.
|
|
38
|
+
2. Manifest schema. Add the archive to the expected file set in `validateCandidate`
|
|
39
|
+
and validate the record with `candidateImage()` from `scripts/release-image.ts`.
|
|
40
|
+
Because the tag pins the manifest hash, the digest is immutable once tagged.
|
|
41
|
+
3. Promotion. `release.yml` verifies the bundle (already done by `restore`), then copies
|
|
42
|
+
the archive to the registry preserving the manifest digest (`skopeo copy` or
|
|
43
|
+
`crane push` of the OCI layout; not `docker load` then `docker push`, which can
|
|
44
|
+
change digests under some storage drivers) and pushes `:VERSION` and the channel tag
|
|
45
|
+
as pointers to that digest. It never runs `docker build`.
|
|
46
|
+
4. Retry identity. If `:VERSION` exists, resolve its digest with
|
|
47
|
+
`docker buildx imagetools inspect` and require `assertPromotedImage()` to pass. A
|
|
48
|
+
rebuilt image with matching labels but different bytes is refused. Channel tags keep
|
|
49
|
+
the existing `assertChannel` check via `assertImageChannel()`.
|
|
50
|
+
5. Attestation. Optionally attest the image digest with `actions/attest` using
|
|
51
|
+
`push-to-registry`; this needs `packages: write` and its GHCR behavior is unverified.
|
|
52
|
+
6. Self-hosting. The `Dockerfile` and `docker build` remain the documented way to build
|
|
53
|
+
from source. CI's `container` check stays as is to preserve the required check name.
|
|
54
|
+
|
|
55
|
+
## What the inert helper proves
|
|
56
|
+
|
|
57
|
+
`scripts/release-image.ts` and `test/release-image.test.ts` are pure functions and unit
|
|
58
|
+
tests. They pin the invariant (promoted digest equals tested digest; labels match;
|
|
59
|
+
channel never regresses; a same-label different-bytes image is rejected). No workflow
|
|
60
|
+
imports them, so the release path is unchanged.
|
|
61
|
+
|
|
62
|
+
## Could not establish
|
|
63
|
+
|
|
64
|
+
- Whether GHCR preserves the OCI digest through `skopeo`/`crane` from the runner, and
|
|
65
|
+
whether the org's package settings permit it.
|
|
66
|
+
- Behavior of image attestation and OIDC for GHCR.
|
|
67
|
+
- Whether a candidate-built OCI archive fits comfortably within the artifact size and
|
|
68
|
+
90-day retention model; recovery from GitHub release assets would need the archive
|
|
69
|
+
attached there too.
|
|
70
|
+
- Any real run: none was dispatched.
|
|
71
|
+
|
|
72
|
+
A first implementation should be a separate reviewed PR that lands the candidate-side
|
|
73
|
+
build behind an operator-controlled input, and leaves the publisher change until one
|
|
74
|
+
candidate has been produced and inspected.
|
|
@@ -5,24 +5,51 @@ for UI, auth and admin. Core remains at the repository root and is explicitly
|
|
|
5
5
|
included in the shared release inventory. Moving it is not required to use the
|
|
6
6
|
same release checks and coordinator.
|
|
7
7
|
|
|
8
|
-
## Pull requests
|
|
8
|
+
## Pull requests and main pushes
|
|
9
9
|
|
|
10
|
-
Every PR starts `verify`. A complete Git diff selects one
|
|
10
|
+
Every PR and every push to main starts `verify`. A complete Git diff selects one
|
|
11
|
+
of two lanes:
|
|
11
12
|
|
|
12
|
-
- **Prose:** root project Markdown, `docs/**/*.md`, `llms.txt`
|
|
13
|
-
`
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
- **Prose:** root project Markdown, `docs/**/*.md`, `llms.txt`, `llms-full.txt`,
|
|
14
|
+
`benchmarks/agent/README.md`, `benchmarks/results/README.md` and each
|
|
15
|
+
package's `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` and `GOVERNANCE.md` run
|
|
16
|
+
guidance/generated-resource checks, runtime audit, and the required container
|
|
17
|
+
job. CodeQL retains its repository policy.
|
|
18
|
+
- **Full:** all other changes, mixed changes, and empty, unclassifiable or
|
|
19
|
+
unavailable diffs run static checks once and core and workspace suites
|
|
20
|
+
separately. Both suites retain Linux on Node 22/24/26. Main adds
|
|
21
|
+
Windows/macOS on Node 24. PRs add those platform legs for runtime, CLI,
|
|
22
|
+
SQLite, fixture, dependency, workflow and unknown changes; known UI
|
|
23
|
+
presentation-only changes omit them. Package, action, cookbook,
|
|
24
|
+
reproducibility and operational checks retain their coverage.
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
A pull request is classified against its merge base; a push to main is
|
|
27
|
+
classified tip to tip from the event's `before`/`after` SHAs, so a force-push or
|
|
28
|
+
rewritten history is measured by what actually moved. Classification fails
|
|
29
|
+
closed: a missing, malformed or all-zero SHA (branch creation or deletion), and
|
|
30
|
+
history this checkout cannot read, select full verification. Scheduled and
|
|
31
|
+
manually dispatched runs are never classified from paths at all, so exact-SHA
|
|
32
|
+
release coverage cannot silently become a docs-only run.
|
|
33
|
+
|
|
34
|
+
The prose allowlist is deliberately narrow, and it is a list of reviewed,
|
|
35
|
+
non-executable contributor prose rather than "every Markdown file". Skills,
|
|
36
|
+
starters, recipes, examples, schemas, manifests, workflows, benchmark prompts,
|
|
37
|
+
tasks, answers and acceptance notes, and any package document that ships inside
|
|
38
|
+
a published tarball or is read by an agent surface (`README.md`, `SECURITY.md`,
|
|
39
|
+
`CONTRACT.md`, `THREAT-MODEL.md`, `IMPLEMENTATION-STATUS.md`, `AGENTS.md`,
|
|
40
|
+
`CHANGELOG.md`) select full checks. Anything feeding a generator stays in the
|
|
41
|
+
code lane. A rename from source into docs also selects full checks, because the
|
|
42
|
+
diff is read without rename detection and shows both paths. Every prose path is
|
|
43
|
+
still covered by the always-run `docs` job, which walks all authored Markdown.
|
|
44
|
+
No required workflow uses `paths-ignore`.
|
|
45
|
+
|
|
46
|
+
The always-run `docs` job runs `npm run check:docs`; in the full lane the
|
|
47
|
+
`static` job runs `npm run check:code`, which is the rest of `npm run check`.
|
|
48
|
+
The two together are exactly `npm run check`, which stays complete for local
|
|
49
|
+
use. This removes a duplicated dependency install plus seven repeated checks on
|
|
50
|
+
the same commit, not meaningful wall time: the sampled documentation checking
|
|
51
|
+
was about two seconds. Job names, lane selection and `verify-complete`
|
|
52
|
+
dependencies are unchanged.
|
|
26
53
|
|
|
27
54
|
`verify-complete` accepts only the results specified by the successful plan.
|
|
28
55
|
Failed, canceled, missing or unexpectedly skipped work fails the gate. Required
|
|
@@ -32,7 +59,8 @@ release publication separately requires verification of the exact main commit.
|
|
|
32
59
|
|
|
33
60
|
```sh
|
|
34
61
|
npm run check:docs # prose checks without the runtime suite
|
|
35
|
-
npm run
|
|
62
|
+
npm run check:code # everything in `check` except the prose checks
|
|
63
|
+
npm run ci:plan -- BASE_SHA HEAD_SHA # previews as a pull request outside Actions
|
|
36
64
|
npm run ci:report -- RUN_ID # read GitHub job/step durations
|
|
37
65
|
npm run ci:history -- 100 2026-09-19 # group historical timing samples
|
|
38
66
|
npm run verify # full local validation remains available
|
|
@@ -43,123 +71,218 @@ CI uses `test:package:built` only after building in that same job. Core tests an
|
|
|
43
71
|
workspace tests run in separate jobs to shorten their serial critical path;
|
|
44
72
|
this increases job setup overhead and needs monitoring for runner queue pressure.
|
|
45
73
|
After building all three extensions, the workspace job also runs the real
|
|
46
|
-
`init --with auth,admin
|
|
74
|
+
`init --with ui,auth,admin` scaffold integration. Missing workspace outputs fail
|
|
47
75
|
instead of silently skipping an absent external checkout.
|
|
48
76
|
The [audit](CI-RELEASE-AUDIT-2026-09-19.md) records the previous timings.
|
|
49
|
-
The [follow-up measurements](CI-FOLLOWUP-2026-09-19.md) record the
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
## Version and release ownership
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
release, run `gh workflow run ci.yml --ref main` and wait for that exact commit's
|
|
68
|
-
full run to succeed. A successful compact main run alone cannot authorize a
|
|
69
|
-
release. Main pushes do not cancel scheduled/manual verification.
|
|
70
|
-
|
|
71
|
-
Inspect release state:
|
|
77
|
+
The [follow-up measurements](CI-FOLLOWUP-2026-09-19.md) record early compact-main observations; issue #185 contains the later decision
|
|
78
|
+
and current sample sizes.
|
|
79
|
+
|
|
80
|
+
## Version preparation and release ownership
|
|
81
|
+
|
|
82
|
+
Core remains at the repository root. Independent extension versions remain
|
|
83
|
+
supported; a coordinated version is an explicit maintainer choice, not a
|
|
84
|
+
permanent fixed-version policy. An explicitly selected stable version exits
|
|
85
|
+
Changesets alpha pre-mode when no package remains on alpha; subsequent stable
|
|
86
|
+
patches stay out of pre-mode. Feature PRs record workspace release intent in
|
|
87
|
+
Changesets; core release notes remain an explicit maintainer responsibility.
|
|
88
|
+
|
|
89
|
+
`release:check` checks manifest/lock versions and peer ranges, CLI and MCP
|
|
90
|
+
versions, generated plugin metadata, local peer compatibility, and channel policy.
|
|
91
|
+
The preparation helper updates these together, adds release notes and extension
|
|
92
|
+
changelogs, and records the version decision. Pending Changesets must be
|
|
93
|
+
explicitly consumed; they are archived under `.changeset/pre/` and their summaries
|
|
94
|
+
included in the release notes. Review the resulting diff and peer minimums.
|
|
72
95
|
|
|
73
96
|
```sh
|
|
74
|
-
|
|
75
|
-
npm run release:
|
|
76
|
-
|
|
97
|
+
# Example only: choose the next intended version before executing.
|
|
98
|
+
npm run release:prepare -- --version 0.4.1 --consume-changesets
|
|
99
|
+
# Apply local edits on a clean non-main branch; no remote writes or publication:
|
|
100
|
+
npm run release:prepare -- --version 0.4.1 --consume-changesets --execute
|
|
77
101
|
```
|
|
78
102
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
103
|
+
An optional `--notes PATH` adds reviewed maintainer notes. Dry runs do not change
|
|
104
|
+
files. Preparation rejects downgrades, reused local tags, dirty checkouts and
|
|
105
|
+
stale plans. A stable target removes `.changeset/pre.json` once no package
|
|
106
|
+
remains on alpha, publishes to npm `latest`, and leaves the historical `alpha`
|
|
107
|
+
pointer unchanged. Alpha targets
|
|
108
|
+
require existing alpha mode; the helper never silently re-enters prerelease mode.
|
|
109
|
+
It does not invoke a permanent Changesets fixed-version policy.
|
|
110
|
+
|
|
111
|
+
## GitHub Actions release buttons
|
|
112
|
+
|
|
113
|
+
The Actions page exposes `release core`, `release UI`, `release auth`, `release
|
|
114
|
+
admin` and `release all packages`. Each manual workflow accepts an exact stable
|
|
115
|
+
or alpha version and whether to consume relevant pending Changesets. The four
|
|
116
|
+
package workflows update and publish only their selected package. `release all
|
|
117
|
+
packages` aligns and publishes the complete train in core → UI → auth → admin
|
|
118
|
+
order. A Changeset spanning selected and unselected packages is rejected; use
|
|
119
|
+
the all-packages workflow or split the change intentionally.
|
|
120
|
+
|
|
121
|
+
All five buttons call the same serialized reusable workflow. It creates a
|
|
122
|
+
release PR, waits for normal required checks, merges without bypass, runs the
|
|
123
|
+
exact-commit full matrix and signed candidate, publishes the selected immutable
|
|
124
|
+
tag, checks registry installability, and verifies the current four-package
|
|
125
|
+
consumer combination. A core release also updates the standalone starter. The
|
|
126
|
+
workflow is resumable: rerun the same button and version after repairing a
|
|
127
|
+
failure. It reuses matching PRs, tags, candidates and successful publishers.
|
|
128
|
+
|
|
129
|
+
Configure `RELEASE_AUTOMATION_TOKEN` as a repository Actions secret. Prefer a
|
|
130
|
+
repository-scoped GitHub App token when available. A fine-grained PAT is also
|
|
131
|
+
supported when it is limited to `urlcode` and `urlcode-template` with Contents,
|
|
132
|
+
Pull requests and Actions read/write. The repositories are public, so the
|
|
133
|
+
coordinator can inspect their check runs without an additional token
|
|
134
|
+
permission. The token owner needs ordinary write access. Do not grant ruleset
|
|
135
|
+
bypass, administration, approval or package-registry credentials; npm
|
|
136
|
+
publishers continue to use their workflow OIDC identities. Dispatch from
|
|
137
|
+
`main`.
|
|
138
|
+
|
|
139
|
+
## One-command local release and resume
|
|
84
140
|
|
|
85
|
-
|
|
141
|
+
Inspect without writing:
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
npm run release:status # registry channels, peer compatibility, tag SHAs
|
|
145
|
+
npm run release:plan # manifest-derived inventory
|
|
146
|
+
npm run release:run # ordered states at this checkout: pending/resume/unchanged
|
|
147
|
+
npm run release:run -- --version 0.4.1 --consume-changesets
|
|
148
|
+
npm run release:run -- --version 0.4.2 --package auth --consume-changesets
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
For an explicitly authorized coordinated release:
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
npm run release:run -- --version 0.4.1 --consume-changesets --execute
|
|
155
|
+
npm run release:run -- --version 0.4.2 --package auth --consume-changesets --execute
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`--execute` authorizes the entire sequence: create the release branch/PR, wait
|
|
159
|
+
for checks and merge, run the release gates, create version tags, publish, verify
|
|
160
|
+
an installed consumer, and create/check/merge the starter update. It never
|
|
161
|
+
approves a review or bypasses a required check. A required human review still
|
|
162
|
+
blocks merging. No write or publication occurs without `--execute`.
|
|
163
|
+
|
|
164
|
+
The coordinator works in a temporary clone and prints its location. It prepares
|
|
165
|
+
`codex/release-VERSION`, or resumes the existing PR/branch after checking its
|
|
166
|
+
versions and receipt. It checks out the actual merged SHA and reinstalls that
|
|
167
|
+
commit's locked dependencies. Repeating the command discovers existing PRs,
|
|
168
|
+
gates, tags and workflow state rather than creating another version. Temporary
|
|
169
|
+
release clones are retained for diagnosis and can be removed after completion.
|
|
170
|
+
|
|
171
|
+
For an independently prepared release PR already merged to main, use a clean
|
|
172
|
+
checkout of its exact commit:
|
|
86
173
|
|
|
87
174
|
```sh
|
|
88
175
|
npm run release:run -- --execute
|
|
89
176
|
```
|
|
90
177
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
178
|
+
The coordinator creates `codex/release-validation/SHA` at the already-merged
|
|
179
|
+
commit when gates are missing. An existing validation branch must name that
|
|
180
|
+
exact SHA; it is never moved. This lets main advance without changing the release
|
|
181
|
+
being tested. The branches remain as audit/resume references. It dispatches full
|
|
182
|
+
`ci.yml` and `candidate.yml`, reuses existing successful runs, and waits for
|
|
183
|
+
running ones. A failed gate stops with its run ID; diagnose it and rerun that
|
|
184
|
+
exact run before resuming. Compact PR/main checks cannot replace the full
|
|
185
|
+
OS/Node matrix or CodeQL on the selected commit.
|
|
186
|
+
|
|
187
|
+
Before creating any version tags, it downloads and verifies the candidate bundle.
|
|
188
|
+
A green run with missing artifacts does not authorize tags. New version tags are
|
|
189
|
+
annotated with the source commit, chosen candidate run ID and the SHA256 of its
|
|
190
|
+
signed manifest. The manifest binds every package and supporting asset by hash. Every package in a
|
|
191
|
+
resumed train must select that same candidate. Neither a later candidate of the
|
|
192
|
+
same source nor a newer main commit can silently replace the chosen bytes.
|
|
193
|
+
A successful rerun of the same candidate ID cannot substitute a changed bundle: its
|
|
194
|
+
manifest must still match the immutable checksum in the release tag.
|
|
195
|
+
After tags exist, rerun package publishers rather than the pinned candidate run.
|
|
196
|
+
If a later attempt of that candidate run fails, the coordinator stops even when
|
|
197
|
+
an earlier attempt succeeded; it does not infer which attempt should be trusted.
|
|
198
|
+
|
|
199
|
+
Publication is sequential: core → UI → auth → admin, skipping unchanged published
|
|
200
|
+
versions. Each publisher must succeed and its version must be readable through
|
|
201
|
+
npm's abbreviated install metadata, with a downloadable SHA512-verified tarball,
|
|
202
|
+
before dependents begin. Bounded retries handle propagation, transport failures,
|
|
203
|
+
429 and server errors; authentication and integrity failures stop immediately.
|
|
204
|
+
Afterward, an external consumer with a fresh npm cache installs the four exact
|
|
205
|
+
registry versions, checks its peer tree and imports, and generates the combined
|
|
206
|
+
extension scaffold in dependency order (`ui,auth,admin`). The candidate archive
|
|
207
|
+
smoke uses the same scaffold check before any package is published.
|
|
208
|
+
|
|
209
|
+
The standalone starter helper updates the exact core pin, lockfile, matching
|
|
210
|
+
schema/docs links and guide from the installed published core package, then runs
|
|
211
|
+
validation/tests/audit/benchmark,
|
|
212
|
+
and opens a resumable PR. The coordinator waits for checks and merges it, checking
|
|
213
|
+
for a newer template pin immediately before merge. `--skip-template` explicitly
|
|
214
|
+
leaves this follow-up to the maintainer. To run only that follow-up:
|
|
215
|
+
|
|
216
|
+
```sh
|
|
217
|
+
npm run release:template -- --version 0.4.1 --execute
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
That standalone helper opens a PR but does not merge it. All helpers stop on
|
|
221
|
+
errors; rerun after diagnosis. A failed publisher is retried at most once per
|
|
222
|
+
coordinator invocation and must pass the original-byte recovery checks below.
|
|
223
|
+
|
|
224
|
+
The maintainer identity needs repository Contents, Actions and Pull requests
|
|
225
|
+
write, plus Checks read, on the affected repositories. GitHub App installations
|
|
226
|
+
should be scoped to URLCode and its template. No ruleset bypass or long-lived
|
|
227
|
+
npm token is needed. The workflow `GITHUB_TOKEN` must not create the triggering
|
|
228
|
+
version tags because its push events do not start ordinary push workflows.
|
|
105
229
|
[GitHub App guidance](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/deciding-when-to-build-a-github-app)
|
|
106
230
|
|
|
107
|
-
##
|
|
108
|
-
|
|
109
|
-
The four
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
Further performance and release-train validation is tracked in
|
|
231
|
+
## Build once, publish verified bytes
|
|
232
|
+
|
|
233
|
+
The four publisher filenames remain unchanged for npm trusted-publisher identity.
|
|
234
|
+
The candidate builds in the digest-pinned environment, runs verification,
|
|
235
|
+
packaging and local operational checks, packs all four packages, and tests an
|
|
236
|
+
isolated combined consumer. The signed bundle contains all four archives,
|
|
237
|
+
SBOM, Homebrew formula, source/build manifest, train identity and checksums.
|
|
238
|
+
The manifest binds it to the candidate run as well as the commit.
|
|
239
|
+
|
|
240
|
+
Publishers verify the selected candidate's workflow provenance, exact source SHA,
|
|
241
|
+
run identity, manifest/package identities and hashes. They publish the selected
|
|
242
|
+
package's existing archive without rebuilding it. Auth/admin still run isolated
|
|
243
|
+
compatibility tests against their actual published peer floors; temporary test
|
|
244
|
+
builds do not replace the promoted archive. This preserves the distinction
|
|
245
|
+
between workspace compatibility and registry compatibility.
|
|
246
|
+
|
|
247
|
+
Each package's GitHub release stores the complete signed bundle for durable
|
|
248
|
+
recovery. Supporting sibling archives are candidate evidence: an independent
|
|
249
|
+
package release does not imply every sibling archive was published to npm.
|
|
250
|
+
Candidate and release Actions artifacts retain 90 days; retention is not an
|
|
251
|
+
archival guarantee. Keep independent last-good copies for deployment rollback.
|
|
252
|
+
|
|
253
|
+
## Recovery, immutable tags and channels
|
|
254
|
+
|
|
255
|
+
A retry restores the original retained bundle, or recovers the complete verified
|
|
256
|
+
bundle from that package's GitHub release. Missing, incomplete or unverifiable
|
|
257
|
+
originals stop the retry. It never rebuilds archives or substitutes a new
|
|
258
|
+
candidate. If publication stopped before a complete durable release existed and
|
|
259
|
+
the retained artifact is gone, a new version may be required.
|
|
260
|
+
|
|
261
|
+
Existing npm versions must match SHA512 integrity; existing GitHub assets must
|
|
262
|
+
match byte for byte. Partial npm/GitHub/GHCR success is possible and cannot be
|
|
263
|
+
made atomic. Fix registry identity/settings where appropriate and resume the
|
|
264
|
+
original run. A source change requires a new version and tag. Never delete,
|
|
265
|
+
recreate, move or force-push version tags.
|
|
266
|
+
|
|
267
|
+
These recovery changes apply to releases made with the new workflows. They cannot
|
|
268
|
+
change the immutable workflow source at `0.4.0-alpha.3` or repair that historical
|
|
269
|
+
run by rerunning it. The missing-artifact behavior observed there is recorded in
|
|
270
|
+
[issue #223](https://github.com/jimhoyd-com/urlcode/issues/223).
|
|
271
|
+
|
|
272
|
+
npm uses OIDC with pinned npm 11.5.1. Alpha versions use npm/GHCR `alpha`, and
|
|
273
|
+
GitHub prerelease classification with `--latest=false`. Existing `latest`
|
|
274
|
+
pointers are not promoted by this flow. Core GHCR publication remains conditional
|
|
275
|
+
on `PUBLISH_CONTAINER=true`; its existing version/channel identity guards remain.
|
|
276
|
+
Historical GHCR verification is still a separate follow-up. Digest promotion of a
|
|
277
|
+
tested image is design only, in [CONTAINER-PROMOTION.md](CONTAINER-PROMOTION.md);
|
|
278
|
+
the publisher still builds from source.
|
|
279
|
+
|
|
280
|
+
The [Immutable release tags rule](https://github.com/jimhoyd-com/urlcode/rules/23712319)
|
|
281
|
+
blocks updates/deletions of `v*` and `@jimhoyd/urlcode-*@*`, permits creation, and
|
|
282
|
+
has no bypass actors. Main protection is unchanged. Source and artifact checks
|
|
283
|
+
are not an independent security assessment, provider deployment or recovery
|
|
284
|
+
proof. The next explicitly authorized new release must exercise the complete
|
|
285
|
+
new promotion/recovery path. Progress and remaining work are recorded in
|
|
163
286
|
[issue #185](https://github.com/jimhoyd-com/urlcode/issues/185).
|
|
164
287
|
|
|
165
288
|
### Windows fixture cleanup
|