@jimhoyd/urlcode 0.4.0-alpha.1 → 0.4.0-alpha.3
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 +36 -22
- package/.claude/skills/urlcode-operations/SKILL.md +16 -22
- package/.claude-plugin/marketplace.json +1 -1
- package/CONTRIBUTING.md +20 -16
- package/README.md +83 -80
- package/ROADMAP.md +61 -331
- package/SECURITY.md +23 -8
- package/dist/BUILD-MANIFEST.json +42 -47
- package/dist/agents-guide.js +18 -18
- package/dist/build-static.js +136 -0
- package/dist/capabilities.js +85 -31
- package/dist/capability-query.js +0 -1
- package/dist/cli.js +24 -32
- package/dist/compliance-rules/baseline.js +2 -10
- package/dist/compliance-rules/privacy.js +5 -16
- package/dist/compliance-rules/shared.js +0 -2
- package/dist/compliance.js +6 -8
- package/dist/config.js +15 -8
- package/dist/context.js +9 -10
- package/dist/examples.js +2 -2
- package/dist/explain-cli.js +7 -5
- package/dist/explain.js +10 -5
- package/dist/extensions.js +61 -2
- package/dist/function-sources.js +34 -2
- package/dist/function-worker.js +3 -1
- package/dist/functions.js +84 -13
- package/dist/guest-api.js +29 -3
- package/dist/index.js +3 -6
- package/dist/manifest.js +11 -7
- package/dist/mcp-authoring.js +2 -2
- package/dist/mcp.js +1 -1
- package/dist/observability.js +1 -21
- package/dist/policies/cache.js +8 -3
- package/dist/policies.js +3 -1
- package/dist/policy.js +28 -9
- package/dist/prerender.js +4 -0
- package/dist/project-tests.js +3 -3
- package/dist/readiness.js +35 -9
- package/dist/route-diff.js +12 -5
- package/dist/router.js +5 -7
- package/dist/runtime.js +77 -58
- package/dist/sandbox.js +48 -0
- package/dist/scaffold.js +0 -0
- package/dist/scripts/operational-drills.js +12 -54
- package/dist/server.js +3 -29
- package/dist/tooling.js +1 -1
- package/dist/trusted-functions.js +210 -0
- package/dist/types/build-static.d.ts +43 -0
- package/dist/types/capabilities.d.ts +14 -5
- package/dist/types/compliance-rules/shared.d.ts +0 -2
- package/dist/types/compliance.d.ts +0 -3
- package/dist/types/config.d.ts +2 -1
- package/dist/types/context.d.ts +2 -1
- package/dist/types/examples.d.ts +1 -1
- package/dist/types/explain.d.ts +6 -0
- package/dist/types/extensions.d.ts +55 -0
- package/dist/types/function-sources.d.ts +4 -0
- package/dist/types/functions.d.ts +48 -5
- package/dist/types/guest-api.d.ts +1 -0
- package/dist/types/index.d.ts +3 -6
- package/dist/types/manifest.d.ts +5 -3
- package/dist/types/observability.d.ts +1 -14
- package/dist/types/project-tests.d.ts +1 -2
- package/dist/types/readiness.d.ts +12 -3
- package/dist/types/router.d.ts +2 -1
- package/dist/types/runtime.d.ts +0 -27
- package/dist/types/sandbox.d.ts +12 -0
- package/dist/types/scaffold.d.ts +0 -2
- package/dist/types/server.d.ts +1 -4
- package/dist/types/tooling.d.ts +3 -3
- package/dist/types/trusted-functions.d.ts +29 -0
- package/dist/types/types.d.ts +22 -8
- package/dist/types/verify-deployment.d.ts +2 -2
- package/dist/types.js +25 -5
- package/dist/typescript-authoring.js +67 -18
- package/dist/verify-deployment.js +3 -3
- package/docs/AI-AUTHORING.md +109 -10
- package/docs/ASSETS.md +2 -1
- package/docs/AUTH-BACKUP.md +32 -0
- package/docs/AWS.md +4 -4
- package/docs/BEST-PRACTICES.md +17 -8
- package/docs/CAPABILITIES.md +30 -17
- package/docs/CAPACITY.md +128 -31
- package/docs/CI-FOLLOWUP-2026-09-19.md +97 -0
- package/docs/CI-RELEASE-AUDIT-2026-09-19.md +322 -0
- package/docs/CI.md +8 -3
- package/docs/CLOUDFLARE.md +1 -2
- package/docs/CODEBASE-AUDIT-2026-09-20.md +278 -0
- package/docs/COMPLIANCE.md +6 -9
- package/docs/DEPLOYMENT-CHECKS.md +1 -1
- package/docs/DEVELOPMENT-PIPELINE.md +181 -0
- package/docs/EXTENSIONS.md +193 -11
- package/docs/FRAMEWORK.md +53 -33
- package/docs/FUNCTION-SECURITY.md +173 -32
- package/docs/INSTALL.md +0 -5
- package/docs/LOAD-TESTING.md +4 -4
- package/docs/LOCAL-DEVELOPMENT.md +3 -0
- package/docs/MIDDLEWARE.md +27 -16
- package/docs/MONITORING.md +2 -19
- package/docs/OBSERVABILITY.md +7 -16
- package/docs/OPEN-DECISIONS.md +184 -0
- package/docs/OPERATIONAL-PROOF.md +26 -30
- package/docs/OPERATIONS.md +23 -32
- package/docs/POLICIES.md +27 -8
- package/docs/PRERENDER.md +25 -13
- package/docs/PROJECT-DIRECTION.md +35 -10
- package/docs/READINESS.md +6 -2
- package/docs/README.md +20 -41
- package/docs/RELEASE-0.4.0-alpha.3.md +50 -0
- package/docs/RELEASE-READINESS.md +50 -30
- package/docs/RELEASE-SECURITY.md +72 -86
- package/docs/RESILIENCE.md +16 -15
- package/docs/ROUTING.md +7 -10
- package/docs/SANDBOX-REVIEW.md +19 -6
- package/docs/SCAFFOLDING.md +0 -2
- package/docs/SECURITY-AUDIT.md +42 -2
- package/docs/SPECIFICATION.md +63 -27
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +288 -0
- package/docs/SPIKE-BUSINESS-SUITE.md +16 -0
- package/docs/SPIKE-CORE-LAYERING.md +368 -0
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +211 -0
- package/docs/SPIKE-LAMBDA-COMPILE.md +12 -2
- package/docs/STARTERS.md +1 -1
- package/docs/STATIC.md +105 -0
- package/docs/TOOLING.md +17 -12
- package/docs/TUNNELS.md +0 -3
- package/docs/TYPESCRIPT-AUTHORING.md +32 -12
- package/docs/TYPESCRIPT.md +25 -4
- package/docs/VERCEL.md +4 -5
- package/docs/VERSION-ALIGNMENT.md +46 -0
- package/docs/YAML-GUIDE.md +0 -3
- package/docs/YAML-REFERENCE.md +16 -6
- package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +68 -0
- package/docs/{MANAGEMENT-SECURITY.md → archive/2026-09-19/MANAGEMENT-SECURITY.md} +20 -0
- package/docs/{NEXT-PHASE-PLAN.md → archive/2026-09-19/NEXT-PHASE-PLAN.md} +26 -8
- package/docs/{NEXT-STEPS.md → archive/2026-09-19/NEXT-STEPS.md} +274 -43
- package/docs/archive/2026-09-19/OPEN-DECISIONS.md +277 -0
- package/docs/archive/2026-09-19/RELEASE-SECURITY.md +186 -0
- package/docs/archive/2026-09-19/ROADMAP.md +386 -0
- package/docs/{SPIKE-EXTENSION-MODEL.md → archive/2026-09-19/SPIKE-EXTENSION-MODEL.md} +12 -2
- package/docs/{SPIKE-EXTENSIONS.md → archive/2026-09-19/SPIKE-EXTENSIONS.md} +28 -12
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +776 -0
- package/docs/{USABILITY-REVIEW.md → archive/2026-09-19/USABILITY-REVIEW.md} +18 -2
- package/docs/archive/README.md +27 -0
- package/docs/policies/agents.md +1 -1
- package/docs/policies/cache.md +13 -0
- package/docs/policies/compression.md +3 -2
- package/docs/policies/security.md +3 -2
- package/examples/assets/example.yaml +2 -2
- package/examples/aws/example.yaml +1 -1
- package/examples/cloudflare/example.yaml +1 -1
- package/examples/conditions/example.yaml +1 -1
- package/examples/cookbook/example.yaml +1 -1
- package/examples/cookbook/middleware/bucket.mjs +12 -2
- package/examples/cookbook/middleware/locale.mjs +7 -3
- package/examples/cookbook/route-index.json +1 -1
- package/examples/cookbook/routes/middleware.yaml +1 -1
- package/examples/egress/example.yaml +1 -1
- package/examples/extensions/example.yaml +1 -1
- package/examples/prerender/README.md +14 -6
- package/examples/prerender/example.yaml +1 -1
- package/examples/prerender/functions/page.mjs +4 -2
- package/examples/prerender/middleware/template.mjs +1 -1
- package/examples/prerender/prerender.mjs +1 -1
- package/examples/prerender/urlcode.yaml +8 -4
- package/examples/provider-conformance/example.yaml +1 -1
- package/examples/vercel/example.yaml +1 -1
- package/llms-full.txt +686 -239
- package/llms.txt +27 -15
- package/package.json +32 -5
- package/packaging/claude-plugin/.claude-plugin/plugin.json +2 -2
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +36 -22
- package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +16 -22
- package/recipes/authenticated-json-api/README.md +4 -3
- package/recipes/authenticated-json-api/functions/profile.mjs +2 -1
- package/recipes/authenticated-json-api/recipe.yaml +2 -2
- package/recipes/contact-form/functions/contact.mjs +2 -1
- package/recipes/contact-form/recipe.yaml +3 -3
- package/recipes/cors-api/README.md +2 -2
- package/recipes/cors-api/recipe.yaml +2 -2
- package/recipes/health-page/README.md +1 -1
- package/recipes/health-page/recipe.yaml +1 -1
- package/recipes/json-api/README.md +1 -1
- package/recipes/json-api/recipe.yaml +4 -4
- package/recipes/middleware/README.md +8 -4
- package/recipes/middleware/middleware/bucket.mjs +12 -2
- package/recipes/middleware/middleware/locale.mjs +7 -3
- package/recipes/middleware/recipe.yaml +1 -1
- package/recipes/protected-download/README.md +1 -1
- package/recipes/protected-download/recipe.yaml +2 -2
- package/recipes/redirect/recipe.yaml +1 -1
- package/recipes/static-plus-api/README.md +2 -2
- package/recipes/static-plus-api/public/index.html +1 -1
- package/recipes/static-plus-api/recipe.yaml +2 -2
- package/recipes/static-plus-api/urlcode.yaml +1 -1
- package/recipes/typescript/README.md +2 -1
- package/recipes/typescript/recipe.yaml +5 -5
- package/recipes/webhook-receiver/README.md +5 -1
- package/recipes/webhook-receiver/recipe.yaml +2 -1
- package/recipes/webhook-receiver/urlcode.yaml +7 -0
- package/schemas/recipe.schema.json +4 -3
- package/schemas/urlcode.schema.json +14 -41
- package/skills/urlcode/SKILL.md +32 -11
- package/starters/default/AGENTS.md +18 -18
- package/starters/default/urlcode.yaml +0 -1
- package/dist/link-api.js +0 -136
- package/dist/link-cli.js +0 -141
- package/dist/link-events.js +0 -76
- package/dist/link-records.js +0 -31
- package/dist/link-store-worker.js +0 -150
- package/dist/link-store.js +0 -250
- package/dist/management-policy.js +0 -40
- package/dist/sqlite-version.js +0 -6
- package/dist/types/link-api.d.ts +0 -30
- package/dist/types/link-cli.d.ts +0 -37
- package/dist/types/link-events.d.ts +0 -27
- package/dist/types/link-records.d.ts +0 -11
- package/dist/types/link-store-worker.d.ts +0 -1
- package/dist/types/link-store.d.ts +0 -130
- package/dist/types/management-policy.d.ts +0 -8
- package/dist/types/sqlite-version.d.ts +0 -1
- package/docs/DYNAMIC-LINKS.md +0 -61
- package/docs/links/cli.md +0 -110
- package/docs/links/limits.md +0 -175
- package/docs/links/management-api.md +0 -80
- package/docs/links/pools.md +0 -75
- package/docs/links/setup.md +0 -135
- package/docs/yaml/links.md +0 -30
- package/examples/live-links/README.md +0 -11
- package/examples/live-links/example.yaml +0 -21
- package/examples/live-links/tests/requests.json +0 -6
- package/examples/live-links/urlcode.yaml +0 -16
package/docs/CI.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Checking a project on GitHub
|
|
2
2
|
|
|
3
|
+
For this repository's own PR checks and releases, see the
|
|
4
|
+
[development pipeline](DEVELOPMENT-PIPELINE.md). This page describes the action
|
|
5
|
+
used by applications built with URLCode.
|
|
6
|
+
|
|
3
7
|
`jimhoyd-com/urlcode/action` is a composite GitHub Action for a URLCode
|
|
4
8
|
*project*: a repository with a `urlcode.yaml`. It runs the same local checks
|
|
5
9
|
you run by hand and, on pull requests, keeps one comment up to date with the
|
|
@@ -81,8 +85,9 @@ On `pull_request` events the action fetches the base commit, checks it out into
|
|
|
81
85
|
a temporary git worktree, runs `urlcode routes` there and on the head with the
|
|
82
86
|
head's runtime, and renders `routes --compare` as Markdown: one table each for
|
|
83
87
|
added, removed and changed routes, or "No route changes". A changed route lists
|
|
84
|
-
each differing field (handler, methods, state,
|
|
85
|
-
generated marker or policy description) with its
|
|
88
|
+
each differing field (handler, methods, state, `sandbox`, `sandboxReason`,
|
|
89
|
+
middleware count, policies, generated marker or policy description) with its
|
|
90
|
+
before and after value.
|
|
86
91
|
|
|
87
92
|
`action/comment.mjs` then finds the pull request's comments for the marker
|
|
88
93
|
`<!-- urlcode-route-diff project="<project>" -->` and updates that comment,
|
|
@@ -135,7 +140,7 @@ urlcode routes --project . --compare /tmp/base.json --format markdown
|
|
|
135
140
|
|
|
136
141
|
Or `make validate`, `make test` and `make audit ARGS='--expect-routes 2'`
|
|
137
142
|
from the starter Makefile. The runtime repository exercises the action on
|
|
138
|
-
|
|
143
|
+
full-lane pull requests against `examples/cookbook` (`.github/workflows/ci.yml`,
|
|
139
144
|
job `action`) with the packed tarball as `runtime`, and `test/action.test.ts`
|
|
140
145
|
checks that `action.yml` is a composite action with the inputs above and that
|
|
141
146
|
every third-party action it or the starter workflow uses is pinned to a
|
package/docs/CLOUDFLARE.md
CHANGED
|
@@ -21,8 +21,7 @@ route pattern and the reason named:
|
|
|
21
21
|
|
|
22
22
|
| Handler | Why it is refused |
|
|
23
23
|
| --- | --- |
|
|
24
|
-
| `function`, `middleware` | need worker threads and the QuickJS WASM engine |
|
|
25
|
-
| `link` | needs a durable writable store the platform does not provide here |
|
|
24
|
+
| `function`, `middleware` | need the self-hosted Node lifecycle, and worker threads and the QuickJS WASM engine when a route declares `sandbox: true` |
|
|
26
25
|
| `page`, `static`, `download` | need a platform static-asset binding, not an inline copy |
|
|
27
26
|
| `env`, `secrets` | would have to be baked into the artifact |
|
|
28
27
|
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Codebase, tooling and documentation audit — 2026-09-20
|
|
2
|
+
|
|
3
|
+
Reviewed commit: `bca8ac7` (core 0.4.0-alpha.2, auth 0.1.0-alpha.5,
|
|
4
|
+
admin 0.1.0-alpha.4, UI 0.1.0-alpha.6). Local environment: macOS,
|
|
5
|
+
Node 26.8.2. Date is UTC. This report records findings; it does not change
|
|
6
|
+
runtime behavior or remove supported features.
|
|
7
|
+
|
|
8
|
+
The trusted default is implemented correctly in the dispatch paths inspected:
|
|
9
|
+
only a route declaring `sandbox: true` enters the QuickJS pool. Ordinary
|
|
10
|
+
project functions and middleware run in Node. Host access by trusted code is
|
|
11
|
+
intentional, not an audit finding. Binding grants scope injected context;
|
|
12
|
+
they do not confine trusted code's independent filesystem, environment or
|
|
13
|
+
network access. Opt-in sandbox isolation and provider refusals remain necessary.
|
|
14
|
+
|
|
15
|
+
## Scope and method
|
|
16
|
+
|
|
17
|
+
Inventoried 966 tracked files. Reviewed the implemented specification,
|
|
18
|
+
contribution/security instructions, runtime activation/dispatch, module loading,
|
|
19
|
+
TypeScript authoring, inspection/manifests/diffs, extension hooks, workspace
|
|
20
|
+
contracts, package/build/check scripts, CI/release configuration, starter,
|
|
21
|
+
example/recipe coverage and documentation indexes. Used targeted source review,
|
|
22
|
+
the complete local verification suite and synthetic reproductions. This is a
|
|
23
|
+
repository-wide engineering audit, not a claim that every line or execution
|
|
24
|
+
path received independent security review.
|
|
25
|
+
|
|
26
|
+
Scanned tracked non-archive/non-spike Markdown for local links and YAML fences:
|
|
27
|
+
64 YAML fences, 49 route/project-shaped candidates. Added only an omitted
|
|
28
|
+
format version or the documented route wrapper for schema checks. Classified
|
|
29
|
+
partial illustrations separately from complete examples. Semantic constraints,
|
|
30
|
+
external services and prose fragments require more than schema validation.
|
|
31
|
+
|
|
32
|
+
Searched existing repository issues before filing. Five new issues and new
|
|
33
|
+
evidence on existing issue 168 retain all actionable findings below.
|
|
34
|
+
|
|
35
|
+
## Findings, ordered by practical impact
|
|
36
|
+
|
|
37
|
+
### 1. Inspection hides middleware execution-mode changes — P2
|
|
38
|
+
|
|
39
|
+
[Issue 199](https://github.com/jimhoyd-com/urlcode/issues/199).
|
|
40
|
+
|
|
41
|
+
For a `respond` route with `middleware: [mw.mjs]`, switching `sandbox` from
|
|
42
|
+
true to false produces identical manifest route records and an empty route
|
|
43
|
+
diff. This changes execution from QuickJS to full Node without appearing in
|
|
44
|
+
those review surfaces. The overall manifest digest can change; it does not
|
|
45
|
+
explain the change to reviewers.
|
|
46
|
+
|
|
47
|
+
`src/explain.ts` adds sandbox information only inside the function-handler
|
|
48
|
+
case. `src/manifest.ts` carries that handler object forward, while
|
|
49
|
+
`src/readiness.ts` and `src/route-diff.ts` omit execution mode from inventory.
|
|
50
|
+
Context already exposes it at route level.
|
|
51
|
+
|
|
52
|
+
Reproduction: create the same middleware/native route in both modes, call
|
|
53
|
+
`buildManifest`, obtain `createRuntime(...).testPlan()`, then call `diffRoutes`.
|
|
54
|
+
Observed diff: `{"added":[],"removed":[],"changed":[]}`. Add route-level
|
|
55
|
+
execution mode/reason to explain, manifest and inventory, with compatibility
|
|
56
|
+
handling for older inventory files and regressions for both handler kinds.
|
|
57
|
+
|
|
58
|
+
### 2. Auth/admin hook reactivation serves stale entry code — P2
|
|
59
|
+
|
|
60
|
+
[Issue 198](https://github.com/jimhoyd-com/urlcode/issues/198).
|
|
61
|
+
|
|
62
|
+
Both `packages/auth/src/lifecycle-hooks.ts` and
|
|
63
|
+
`packages/admin/src/admin-hooks.ts` import an unchanged file URL. Loading a
|
|
64
|
+
hook, editing its entry file, then loading it again in the same process returns
|
|
65
|
+
the old hook. A synthetic decision changed on disk from allow/v1 to deny/v2;
|
|
66
|
+
both loaders still returned allow/v1 after reactivation.
|
|
67
|
+
|
|
68
|
+
Native trusted routes already give entry modules a new URL per activation.
|
|
69
|
+
Apply an explicit hook reload policy and test it, or clearly require process
|
|
70
|
+
restart for hook changes. This reproduction exercised the loaders directly,
|
|
71
|
+
not the end-to-end development watcher and account flow. Transitive Node module
|
|
72
|
+
caching is a separate, already documented limitation. Keep trusted execution
|
|
73
|
+
as the default and reject unsupported sandbox hooks explicitly.
|
|
74
|
+
|
|
75
|
+
### 3. TypeScript authoring rejects valid mixed-trust module reuse — P2
|
|
76
|
+
|
|
77
|
+
[Issue 197](https://github.com/jimhoyd-com/urlcode/issues/197).
|
|
78
|
+
|
|
79
|
+
`src/typescript-authoring.ts:34` assigns each source one trust mode and rejects
|
|
80
|
+
reuse from a different mode. Two routes sharing a pure `f.ts`, one trusted and
|
|
81
|
+
one sandboxed, fail with “Module is imported by both a sandboxed and a trusted
|
|
82
|
+
route”. The equivalent JavaScript project activates and both routes return
|
|
83
|
+
200/ok. Shared transitive helpers have the same restriction.
|
|
84
|
+
|
|
85
|
+
Separate emission deduplication from sandbox reachability validation. A helper
|
|
86
|
+
reachable by a sandboxed route must still satisfy all sandbox import/source
|
|
87
|
+
budgets; trusted execution must not be downgraded or unnecessarily restricted.
|
|
88
|
+
Also reconcile the documented unlimited trusted source size with the builder's
|
|
89
|
+
16 MiB per-file authoring cap.
|
|
90
|
+
|
|
91
|
+
### 4. Live guidance still contradicts trust by default — P2
|
|
92
|
+
|
|
93
|
+
[Issue 196](https://github.com/jimhoyd-com/urlcode/issues/196).
|
|
94
|
+
|
|
95
|
+
- `docs/yaml/functions.md:57` denies Node/npm/network/filesystem APIs immediately
|
|
96
|
+
after an ordinary trusted route example.
|
|
97
|
+
- `docs/yaml/middleware.md:26` describes a mandatory shared sandbox.
|
|
98
|
+
- `docs/MIDDLEWARE.md:49` applies guest clone/body restrictions without scoping
|
|
99
|
+
them to sandboxed execution. Reproduced: trusted middleware reads a native
|
|
100
|
+
response body and returns its uppercase text; the sandboxed version rejects
|
|
101
|
+
that operation.
|
|
102
|
+
- `docs/SPECIFICATION.md` says `.js` ESM behavior is independent of package
|
|
103
|
+
settings. Reproduced: ESM `f.js` under `type: commonjs` fails trusted activation
|
|
104
|
+
and works in the sandbox. Ordinary Node resolution is the intended default;
|
|
105
|
+
the documentation should explain `.mjs` and package type accurately.
|
|
106
|
+
- `README.md` and `docs/FRAMEWORK.md` overstate binding grants as the only way
|
|
107
|
+
secrets reach code. Scope that statement to injected bindings.
|
|
108
|
+
- `docs/READINESS.md` calls the activated runtime isolated; the first test in
|
|
109
|
+
`test/typescript-authoring.test.ts` claims QuickJS execution but omits sandbox.
|
|
110
|
+
|
|
111
|
+
The prose guards pass this revision despite these contradictions. Strengthen
|
|
112
|
+
specific regression fixtures and fix the guidance; do not impose old sandbox
|
|
113
|
+
restrictions on trusted code to make the prose true.
|
|
114
|
+
|
|
115
|
+
The POST/body-based sandbox advisory is a related cleanup candidate. It is
|
|
116
|
+
non-blocking and documented, but suggests isolation from request shape rather
|
|
117
|
+
than evidence about code trust. Review whether to remove or reword it while
|
|
118
|
+
retaining explicit `sandboxReason` information. This is a recommendation, not
|
|
119
|
+
a discovered isolation failure.
|
|
120
|
+
|
|
121
|
+
### 5. Copyable documentation examples fail validation — P2
|
|
122
|
+
|
|
123
|
+
[New evidence on issue 168](https://github.com/jimhoyd-com/urlcode/issues/168#issuecomment-5747024306).
|
|
124
|
+
|
|
125
|
+
- `docs/yaml/policies.md:18` uses `page.source`; the field is `page.file`.
|
|
126
|
+
- `docs/policies/compression.md:35` uses `secret: api-key`, rejected by the
|
|
127
|
+
external binding-name grammar; an identifier such as `api_key` is admitted.
|
|
128
|
+
- `docs/AI-AUTHORING.md:249` omits the leading slash on `webhooks/stripe`.
|
|
129
|
+
This also illustrates why schema checks alone cannot replace semantic route
|
|
130
|
+
validation. The same section still lists the retired `link` handler.
|
|
131
|
+
|
|
132
|
+
The first two are complete fenced projects and fail `validateDocument`.
|
|
133
|
+
The partial profile illustration in `docs/POLICIES.md:169` was excluded from
|
|
134
|
+
findings because it deliberately omits a handler. Add classified executable
|
|
135
|
+
snippets: complete projects, context-dependent fragments and intentionally
|
|
136
|
+
invalid examples. Existing cookbook tests do not validate every prose example.
|
|
137
|
+
|
|
138
|
+
### 6. Monorepo ownership, navigation and release prose need cleanup — P3
|
|
139
|
+
|
|
140
|
+
[Issue 200](https://github.com/jimhoyd-com/urlcode/issues/200).
|
|
141
|
+
|
|
142
|
+
- Package AGENTS files still direct findings to former package repositories.
|
|
143
|
+
- Framework/status pages retain former source homes, old alpha combinations,
|
|
144
|
+
private cross-repository credential instructions and peer-pin language.
|
|
145
|
+
- Two admin README links target nonexistent package-local release workflows.
|
|
146
|
+
- Documentation navigation duplicates cookbook entries with conflicting 25/40
|
|
147
|
+
counts; llms.txt repeats semantics and TypeScript entries.
|
|
148
|
+
- Hook comments claim core lacks a sandbox primitive although SandboxPool is
|
|
149
|
+
exported. The packages still explicitly reject sandboxed hooks; update the
|
|
150
|
+
reason and tracked work rather than claiming they already support them.
|
|
151
|
+
|
|
152
|
+
Use local links and one canonical version/ownership inventory for live material;
|
|
153
|
+
keep dated release evidence clearly historical. Consolidate the duplicate
|
|
154
|
+
SQLite preflight predicates through development tooling without removing auth's
|
|
155
|
+
runtime gate. Shorten repeated issue-history narration in implementation comments
|
|
156
|
+
where a contract explanation and issue link are sufficient.
|
|
157
|
+
|
|
158
|
+
Do not remove the supported UI primitive fallback, opt-in sandbox path, provider
|
|
159
|
+
compatibility refusals, licenses or regression fixtures. No dead production module
|
|
160
|
+
was established with enough evidence to recommend deleting it in this audit.
|
|
161
|
+
|
|
162
|
+
## Verification evidence
|
|
163
|
+
|
|
164
|
+
The final sequential `npm run verify` passed, including lint, strict typecheck,
|
|
165
|
+
syntax/JSON/catalog checks, generated-document checks, workspace links, release
|
|
166
|
+
checks, build, core tests and all workspace suites:
|
|
167
|
+
|
|
168
|
+
| Suite | Passed | Skipped | Failed/cancelled |
|
|
169
|
+
|---|---:|---:|---:|
|
|
170
|
+
| Core | 518 | 1 | 0 |
|
|
171
|
+
| UI | 57 | 0 | 0 |
|
|
172
|
+
| Auth | 207 | 0 | 0 |
|
|
173
|
+
| Admin | 68 | 0 | 0 |
|
|
174
|
+
| Workspace scaffold integration | 1 | 0 | 0 |
|
|
175
|
+
|
|
176
|
+
Total: 851 passed, one intentional HTTPS-deployment test skip. The integration
|
|
177
|
+
test checks scaffold composition; it is not a live provider deployment.
|
|
178
|
+
|
|
179
|
+
`npm run test:package` passed installation of the packed archive and
|
|
180
|
+
starter/cookbook/authoring-consumer checks. Separate CLI HTTP fixture runs passed
|
|
181
|
+
for the starter, assets, cookbook, Vercel, AWS, Cloudflare, conditions (with its
|
|
182
|
+
documented public origin) and prerender source. The cookbook count audit passed
|
|
183
|
+
with 40 routes. Extension, fake-egress, monitoring, provider-conformance and
|
|
184
|
+
tunnel examples are also exercised by their dedicated core tests.
|
|
185
|
+
|
|
186
|
+
`npm audit --omit=dev --json`
|
|
187
|
+
reported zero known runtime advisories at audit time. Neither substitutes for
|
|
188
|
+
source review or establishes absence of unknown vulnerabilities.
|
|
189
|
+
|
|
190
|
+
The initial restricted-environment run could not run local HTTP tests. A first
|
|
191
|
+
unrestricted run had five test-file cancellations; those files passed alone.
|
|
192
|
+
An overlapping build also caused transient missing-declaration errors in a
|
|
193
|
+
workspace run. The final sequential full run passed without cancellations.
|
|
194
|
+
These intermediate results are not reported as confirmed product defects.
|
|
195
|
+
|
|
196
|
+
## Remaining evidence boundaries
|
|
197
|
+
|
|
198
|
+
No provider account was deployed, no release published, no production system
|
|
199
|
+
probed, and no container/Windows/Linux/Node 22 or 24 run was performed locally.
|
|
200
|
+
Live email/OIDC/passkey services, accessibility/browser/device assessment,
|
|
201
|
+
operational recovery/soak evidence and independent sandbox review remain separate.
|
|
202
|
+
Existing issues 58, 173, 174 and 185 already retain relevant acceptance,
|
|
203
|
+
model-benchmark, schema-discovery and release/CI-evidence work; this audit does
|
|
204
|
+
not close those gates.
|
|
205
|
+
|
|
206
|
+
## Follow-up: dead-code reachability
|
|
207
|
+
|
|
208
|
+
[Issue 203](https://github.com/jimhoyd-com/urlcode/issues/203) records a dedicated
|
|
209
|
+
unused-code pass requested after the initial audit. Runtime source remains the
|
|
210
|
+
same as the reviewed revision; the intervening commit only added this report.
|
|
211
|
+
|
|
212
|
+
A conservative relative-reference graph rooted at package exports and CLI entry
|
|
213
|
+
points reached all 157 tracked production TypeScript modules. Every direct
|
|
214
|
+
runtime dependency has a production source reference, and every root script
|
|
215
|
+
has a named reference elsewhere in the repository. No whole production file,
|
|
216
|
+
runtime dependency or root script was established as removable.
|
|
217
|
+
|
|
218
|
+
A TypeScript symbol/reference pass excluded 501 symbols exposed by public
|
|
219
|
+
package entrypoints. Candidates were then checked with repository-wide search
|
|
220
|
+
and manual inspection, including local uses, CLI imports, namespace dispatch,
|
|
221
|
+
worker URLs and dynamically loaded agent-list code. The confirmed small removals
|
|
222
|
+
are:
|
|
223
|
+
|
|
224
|
+
| Declaration/plumbing | Evidence | Proposed cleanup |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| `src/capability-query.ts:47`, `capabilityNameList()` | Declaration only; no caller or public entry export | Delete the unused wrapper |
|
|
227
|
+
| `src/catalog.ts:39`, `metadataFiles` | Declaration only; no reader or public entry export | Delete the unused constant |
|
|
228
|
+
| `src/trusted-functions.ts`, `log` option/property | Assigned but never read | Remove this unused executor plumbing, preserving runtime observer/logging behavior |
|
|
229
|
+
| `src/mcp-authoring.ts:57`, `expandHandler(path, handler)` | `path` is never read | Remove the argument and update callers |
|
|
230
|
+
| `src/policies/cache.ts:201`, `revalidate(state, req, result)` | `state` is never read | Remove the argument and update callers |
|
|
231
|
+
|
|
232
|
+
Several live implementation helpers are unnecessarily exported: admin's
|
|
233
|
+
`activeKit`, core's `routeState`, `forbiddenHeaders`, `normalizeRoute`,
|
|
234
|
+
`manifestFileName`, and local scaffold/render/name helpers in `init-with.ts`.
|
|
235
|
+
These are candidates for removing export modifiers, not deleting their bodies.
|
|
236
|
+
Check declaration dependencies before changing exported types.
|
|
237
|
+
|
|
238
|
+
An additional TypeScript check with `--noUnusedLocals --noUnusedParameters`
|
|
239
|
+
reported six unused parameters: the two production helpers above and four test
|
|
240
|
+
callbacks. It reported no unused local declarations. This stricter exploratory
|
|
241
|
+
check is separate from the normal passing typecheck.
|
|
242
|
+
|
|
243
|
+
Public APIs with no internal callers, types used in public signatures, registry
|
|
244
|
+
policy hooks, dynamic imports, supported UI fallback rendering and opt-in sandbox
|
|
245
|
+
execution are not dead code. This reachability analysis does not prove that
|
|
246
|
+
every branch executes. No production code was removed by this follow-up.
|
|
247
|
+
|
|
248
|
+
## Follow-up: unnecessary files and distribution weight
|
|
249
|
+
|
|
250
|
+
[File-level evidence on issue 200](https://github.com/jimhoyd-com/urlcode/issues/200#issuecomment-5747116642).
|
|
251
|
+
|
|
252
|
+
One package-local script is obsolete: `packages/admin/scripts/peer-revisions.mjs`.
|
|
253
|
+
It has no workflow/package-script caller and reads the deleted admin `peers.json`;
|
|
254
|
+
executing it fails with ENOENT. Its only other named reference is historical
|
|
255
|
+
monorepo prose. Remove it; workspace linking replaced its cross-repository
|
|
256
|
+
revision-output mechanism. The earlier root-script scan did not cover this
|
|
257
|
+
package-local leftover.
|
|
258
|
+
|
|
259
|
+
The three package `CODE_OF_CONDUCT.md` files are byte-identical to the root copy
|
|
260
|
+
(1,062 bytes each). Consolidate their links to the root policy before deleting
|
|
261
|
+
the duplicates. Package governance also repeats repository-wide controls and
|
|
262
|
+
can link to the root while retaining any package-specific information. Package
|
|
263
|
+
licenses and attribution notices serve a different purpose and must stay.
|
|
264
|
+
|
|
265
|
+
A dry-run npm package inventory includes 11 archived documentation files
|
|
266
|
+
(179,666 bytes) and six design-spike files (184,792 bytes): about 356 KiB
|
|
267
|
+
uncompressed combined. The audit report also ships because package.json includes
|
|
268
|
+
all of docs. Consider excluding maintainer/history/design records from npm
|
|
269
|
+
while retaining them in Git and keeping their references navigable. These
|
|
270
|
+
figures are content sizes, not estimates of compressed download savings.
|
|
271
|
+
|
|
272
|
+
The duplicate Claude plugin skills are intentional, checked distribution copies;
|
|
273
|
+
cookbook/recipe copies make each project independently usable; upstream UI
|
|
274
|
+
snapshots preserve attribution/provenance; the benchmark baseline is consumed
|
|
275
|
+
by its gate. None is established as unwanted. No tracked build output,
|
|
276
|
+
node_modules, tarballs, logs, backup files or TypeScript build-info files were
|
|
277
|
+
found. Ignored local dependencies/build products are regenerable development
|
|
278
|
+
output, not tracked repository clutter. No files were deleted in this audit.
|
package/docs/COMPLIANCE.md
CHANGED
|
@@ -49,11 +49,11 @@ A **project** rule runs once with:
|
|
|
49
49
|
|---|---|
|
|
50
50
|
| `document` | The parsed and validated `urlcode.yaml` (includes are merged into `routes`) |
|
|
51
51
|
| `routes` | Route configuration by pattern, as written in YAML |
|
|
52
|
-
| `plan` | `testPlan()`: `inventory[]` (`path`, `handler`, `methods`, `middleware`, `policies`, `state`), `policies` (the per-route describe map)
|
|
52
|
+
| `plan` | `testPlan()`: `inventory[]` (`path`, `handler`, `methods`, `middleware`, `policies`, `state`), `policies` (the per-route describe map) |
|
|
53
53
|
| `policies` | `effectivePolicies(document, route)` by pattern: the merged configuration of every policy on each route |
|
|
54
54
|
| `origin` | The declared public origin, or `null` |
|
|
55
55
|
| `target` | `node` unless the caller states another |
|
|
56
|
-
| `host` | `{ requestLog
|
|
56
|
+
| `host` | `{ requestLog }` as declared for the deployment; `null` where undeclared |
|
|
57
57
|
|
|
58
58
|
A **route** rule runs once per inventory entry and additionally receives
|
|
59
59
|
`route` (the inventory entry), `config` (that route's YAML), `policy` (the
|
|
@@ -86,7 +86,6 @@ cache and compression secrets handling in `src/policies/cache.ts` and
|
|
|
86
86
|
| `rfc6585/throttle-functions` | [RFC 6585](https://www.rfc-editor.org/rfc/rfc6585) §4 | medium | Every active function or middleware route has an effective `policies.throttle` | Declare a throttle on the route or the project |
|
|
87
87
|
| `rfc9309/robots` | [RFC 9309](https://www.rfc-editor.org/rfc/rfc9309) | low | Some active route declares `policies.agents`, or an active `/robots.txt` `respond` route exists | Add a `/robots.txt` route or an agents deny list |
|
|
88
88
|
| `rfc9110/expired-routes` | [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110) §15.5.11 | info | Lists routes past `expires` that still answer 410 | Remove them once the 410 window has served its purpose |
|
|
89
|
-
| `ops/management-private` | [Management security](MANAGEMENT-SECURITY.md) | info | Reminder when `dynamicLinks: true`: the management API and `/_urlcode` probes belong on a private bind | Run `links api` privately with an auth file; keep probes internal |
|
|
90
89
|
|
|
91
90
|
### `strict` (baseline plus)
|
|
92
91
|
|
|
@@ -102,15 +101,13 @@ cache and compression secrets handling in `src/policies/cache.ts` and
|
|
|
102
101
|
|
|
103
102
|
These rules check deployment settings, so `audit` takes `--request-log` to
|
|
104
103
|
declare the level the deployment uses (the audit process itself always logs
|
|
105
|
-
nothing)
|
|
106
|
-
false`; embedders pass `host` themselves. The references are the runtime's
|
|
104
|
+
nothing); embedders pass `host` themselves. The references are the runtime's
|
|
107
105
|
own [logging guarantees](MONITORING.md): records carry no URL, query, header,
|
|
108
106
|
body or binding, and `detailed` adds only the method and route pattern.
|
|
109
107
|
|
|
110
108
|
| Rule | Standard | Severity | Checks | Remediation |
|
|
111
109
|
|---|---|---|---|---|
|
|
112
110
|
| `privacy/request-log-minimal` | [Monitoring](MONITORING.md), Log records | medium (`info` when undeclared) | `host.requestLog` is `minimal` | Use the default log unless per-route rates are required |
|
|
113
|
-
| `privacy/link-events-off` | [Monitoring](MONITORING.md), The link event channel | medium; `high` with `includeCode` (`info` when undeclared and `dynamicLinks` is on) | The link event channel is off, or on without `includeCode` | Enable it only for a declared purpose; never disclose codes |
|
|
114
111
|
| `privacy/detailed-log-parameters` | [Monitoring](MONITORING.md), Log records | low | With `detailed` logging, no active route takes parameters (records name the pattern and method, never values) | Keep `minimal` on parameterised deployments |
|
|
115
112
|
|
|
116
113
|
## Writing custom rules
|
|
@@ -159,9 +156,9 @@ Without any compliance flag the audit report is unchanged apart from
|
|
|
159
156
|
"findings": [{ "rule": "rfc6585/throttle-functions", "severity": "medium", "route": "/hello/{name}",
|
|
160
157
|
"message": "…", "remediation": "…", "standard": { "name": "RFC 6585 …", "reference": "…", "section": "…" } }],
|
|
161
158
|
"counts": { "high": 0, "medium": 4, "low": 11, "info": 1 }, "pass": true,
|
|
162
|
-
"evidence": { "routes": 21, "active": 19, "
|
|
159
|
+
"evidence": { "routes": 21, "active": 19, "policies": ["agents", "cache", "security", "throttle"],
|
|
163
160
|
"files": ["urlcode.yaml", "routes/code.yaml"], "origin": null, "target": "node",
|
|
164
|
-
"host": { "requestLog": "minimal"
|
|
161
|
+
"host": { "requestLog": "minimal" },
|
|
165
162
|
"scope": "declared configuration and runtime facts; not a deployment or certification" } }
|
|
166
163
|
```
|
|
167
164
|
|
|
@@ -190,7 +187,7 @@ const report = await runCompliance(runtime, {
|
|
|
190
187
|
rules: [], override: {}, disable: [], // as a rules module would export them
|
|
191
188
|
ignore: ['rfc9110/expired-routes'],
|
|
192
189
|
origin: 'https://links.example',
|
|
193
|
-
host: { requestLog: 'minimal'
|
|
190
|
+
host: { requestLog: 'minimal' }, // what the deployment is configured with
|
|
194
191
|
});
|
|
195
192
|
await runtime.close();
|
|
196
193
|
```
|
|
@@ -19,7 +19,7 @@ URLs for; defaults to the target), `--expect-routes N`, `--expect-metrics`
|
|
|
19
19
|
(per request, 100-120000, default 10000), `--fail-on high|medium|low|info|none`
|
|
20
20
|
(default `high`), the `audit` compliance flags (`--compliance`,
|
|
21
21
|
`--compliance-rules`, `--compliance-ignore`, `--compliance-warn`), and the
|
|
22
|
-
usual `--policy`
|
|
22
|
+
usual `--policy` binding the local snapshot needs to start.
|
|
23
23
|
|
|
24
24
|
## What it verifies
|
|
25
25
|
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Development and release pipeline
|
|
2
|
+
|
|
3
|
+
The repository uses npm workspaces, independent package versions, and Changesets
|
|
4
|
+
for UI, auth and admin. Core remains at the repository root and is explicitly
|
|
5
|
+
included in the shared release inventory. Moving it is not required to use the
|
|
6
|
+
same release checks and coordinator.
|
|
7
|
+
|
|
8
|
+
## Pull requests
|
|
9
|
+
|
|
10
|
+
Every PR starts `verify`. A complete Git diff selects one of two lanes:
|
|
11
|
+
|
|
12
|
+
- **Prose:** root project Markdown, `docs/**/*.md`, `llms.txt` and
|
|
13
|
+
`llms-full.txt` changes run guidance/generated-resource checks, runtime audit,
|
|
14
|
+
and the required container job. CodeQL retains its repository policy.
|
|
15
|
+
- **Full:** all other changes, mixed changes, empty/unavailable diffs and main
|
|
16
|
+
pushes run static checks once and core and workspace suites separately. Both
|
|
17
|
+
suites retain Linux on Node 22/24/26. Main adds Windows/macOS on Node 24.
|
|
18
|
+
PRs add those platform legs for runtime, CLI, SQLite, fixture, dependency,
|
|
19
|
+
workflow and unknown changes; known UI presentation-only changes omit them. Package, action,
|
|
20
|
+
cookbook, reproducibility and operational checks retain their coverage.
|
|
21
|
+
|
|
22
|
+
The prose allowlist is deliberately narrow. Package documentation, skills,
|
|
23
|
+
starters, examples, schemas, manifests and workflow changes select full checks.
|
|
24
|
+
A rename from source into docs also selects full checks. No required workflow
|
|
25
|
+
uses `paths-ignore`.
|
|
26
|
+
|
|
27
|
+
`verify-complete` accepts only the results specified by the successful plan.
|
|
28
|
+
Failed, canceled, missing or unexpectedly skipped work fails the gate. Required
|
|
29
|
+
check names (`verify-complete`, `container`) and CodeQL enforcement are preserved.
|
|
30
|
+
The repository ruleset currently does not require a branch to be up to date;
|
|
31
|
+
release publication separately requires verification of the exact main commit.
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm run check:docs # prose checks without the runtime suite
|
|
35
|
+
npm run ci:plan -- BASE_SHA HEAD_SHA
|
|
36
|
+
npm run ci:report -- RUN_ID # read GitHub job/step durations
|
|
37
|
+
npm run ci:history -- 100 2026-09-19 # group historical timing samples
|
|
38
|
+
npm run verify # full local validation remains available
|
|
39
|
+
npm run test:package # builds and installs a real archive
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
CI uses `test:package:built` only after building in that same job. Core tests and
|
|
43
|
+
workspace tests run in separate jobs to shorten their serial critical path;
|
|
44
|
+
this increases job setup overhead and needs monitoring for runner queue pressure.
|
|
45
|
+
After building all three extensions, the workspace job also runs the real
|
|
46
|
+
`init --with auth,admin,ui` scaffold integration. Missing workspace outputs fail
|
|
47
|
+
instead of silently skipping an absent external checkout.
|
|
48
|
+
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 first compact
|
|
50
|
+
main result and explain why the new lanes still need 20 organic runs each.
|
|
51
|
+
|
|
52
|
+
## Version and release ownership
|
|
53
|
+
|
|
54
|
+
A feature PR records release intent in a Changeset for a changed workspace
|
|
55
|
+
package. Review dependency/peer changes explicitly. Keep pre-mode enabled until
|
|
56
|
+
an explicit decision to leave alpha. Do not force all packages to one version.
|
|
57
|
+
Core version bumps remain explicit in the release PR, including its CLI banner.
|
|
58
|
+
`release:check` verifies every manifest against its lockfile entry and checks the
|
|
59
|
+
alpha-mode policy. Existing CLI tests catch core banner/version disagreement.
|
|
60
|
+
|
|
61
|
+
A release PR collects version/changelog and lockfile changes together. Ordinary
|
|
62
|
+
unreleased development does not move existing version tags or npm versions.
|
|
63
|
+
Use the exact release commit after its full platform checks have passed. Routine
|
|
64
|
+
main builds use five OS/Node combinations per suite (ten jobs total), rather
|
|
65
|
+
than the full nine per suite (eighteen). Nightly runs at 07:17 UTC and manual
|
|
66
|
+
runs retain all three operating systems on all three Node versions. Before a
|
|
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:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
npm run release:status # registry channels, peer compatibility, remote tag SHAs
|
|
75
|
+
npm run release:plan # read-only JSON inventory, including root core
|
|
76
|
+
npm run release:run # read-only ordered proposal at HEAD
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
All three commands require network access; none publish. `release:run` selects
|
|
80
|
+
unpublished manifest versions and releases already tagged at HEAD (for resuming
|
|
81
|
+
partial completion). Its order is core, UI, auth, admin, skipping other already
|
|
82
|
+
published versions. Plan fields come from package manifests, not copied versions
|
|
83
|
+
in another config file.
|
|
84
|
+
|
|
85
|
+
Once the release itself is authorized, from a clean checkout of that exact SHA:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
npm run release:run -- --execute
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This opt-in command creates missing tags through GitHub, refuses an existing tag
|
|
92
|
+
at a different commit, and waits for each package's tag-triggered release before
|
|
93
|
+
starting the next. It stops on failure or missing npm publication. Do not push
|
|
94
|
+
all release tags at once: publication workflows share one concurrency group and
|
|
95
|
+
GitHub may replace pending runs. The coordinator intentionally starts one at a
|
|
96
|
+
time. It never merges a PR, bypasses checks, force-pushes or publishes locally.
|
|
97
|
+
|
|
98
|
+
The coordinator needs `gh` authentication with repository Contents write and
|
|
99
|
+
Actions read; reading checks also needs Checks read. It is intended for an
|
|
100
|
+
authorized maintainer session or a repository-scoped GitHub App. A fine-grained
|
|
101
|
+
PAT can serve a short-lived maintainer script with those permissions, but must
|
|
102
|
+
have no ruleset bypass. The workflow `GITHUB_TOKEN` should not be used to create
|
|
103
|
+
these trigger tags: its push events do not start another ordinary push workflow.
|
|
104
|
+
For long-lived automation prefer a GitHub App; do not add a bypass to `main`.
|
|
105
|
+
[GitHub App guidance](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/deciding-when-to-build-a-github-app)
|
|
106
|
+
|
|
107
|
+
## Publication and recovery
|
|
108
|
+
|
|
109
|
+
The four workflow filenames remain unchanged because npm trusted publishing
|
|
110
|
+
names them. They call shared helpers for identity, preflight, peer installation,
|
|
111
|
+
retry handling and publication. npm authentication remains OIDC; no npm token
|
|
112
|
+
is introduced. Core candidate and release share `prepare-core-release.sh`.
|
|
113
|
+
For an authorized release, first dispatch the manual `candidate.yml` workflow on
|
|
114
|
+
the selected main commit. It extends the core candidate with UI/auth/admin
|
|
115
|
+
archives and verifies all four together in an isolated temporary consumer:
|
|
116
|
+
peer compatibility, installed versions, public imports and real scaffold
|
|
117
|
+
composition. Its signed `train.json` records the proposed archives and integrity.
|
|
118
|
+
A candidate does not publish, validate live providers or prove registry OIDC;
|
|
119
|
+
release workflows still prepare and retain their own immutable retry artifacts.
|
|
120
|
+
Extensions share `prepare-extension-release.sh` and test published peer floors.
|
|
121
|
+
Auth/admin build in the workspace for packaging, then build and run their suites
|
|
122
|
+
in a temporary copy outside the monorepo against exact registry peer floors.
|
|
123
|
+
This preserves #184’s isolation fix; npm `--prefix` is not an isolation boundary.
|
|
124
|
+
|
|
125
|
+
Preflight checks the checkout SHA, main ancestry, a successful exact-SHA full
|
|
126
|
+
`ci.yml` nightly or explicit manual run, CodeQL, remote tag SHA, npm
|
|
127
|
+
channel monotonicity and published peer floors. When a full run was canceled,
|
|
128
|
+
run `verify` manually at the selected tag/ref, then rerun the failed release;
|
|
129
|
+
never substitute another commit's passing run or move the tag.
|
|
130
|
+
|
|
131
|
+
Prepared artifacts are retained for 90 days before publication. A rerun of the
|
|
132
|
+
same workflow run restores those original bytes and skips preparation. npm
|
|
133
|
+
versions already present must have identical SHA-512 integrity; existing GitHub
|
|
134
|
+
assets must match byte for byte. Different bytes stop the release. An absent or
|
|
135
|
+
expired artifact requires reconstruction that still passes these comparisons;
|
|
136
|
+
if it cannot, diagnose and create a new version rather than overwrite history.
|
|
137
|
+
|
|
138
|
+
GitHub release classification follows the manifest's prerelease status. New
|
|
139
|
+
GitHub releases use `--latest=false`; stable latest promotion is a separate
|
|
140
|
+
maintainer decision, avoiding accidental promotion by a package-level release.
|
|
141
|
+
GHCR updates the derived channel (`alpha` for alphas, `latest` for stable) and
|
|
142
|
+
preserves existing version images only when their source label matches. An
|
|
143
|
+
existing image/channel without the required labels fails closed and needs a
|
|
144
|
+
reviewed migration; this change does not silently relabel old images.
|
|
145
|
+
|
|
146
|
+
Partial npm/GitHub/GHCR success is possible; those systems cannot be updated
|
|
147
|
+
atomically. Rerun the original failed run, check its summary and then rerun the
|
|
148
|
+
coordinator. A changed source commit requires a new version and tag. Failed
|
|
149
|
+
OIDC configuration needs correction on npm, not tag deletion. A green dry run
|
|
150
|
+
cannot prove registry-side OIDC trust; each package's first authorized publish
|
|
151
|
+
must verify it.
|
|
152
|
+
|
|
153
|
+
Historical tags, GitHub release flags and npm channels are not retroactively
|
|
154
|
+
rewritten by these scripts. Use `release:status` to inspect them. The active [Immutable release tags rule](https://github.com/jimhoyd-com/urlcode/rules/23712319)
|
|
155
|
+
prohibits update/deletion of `v*` and `@jimhoyd/urlcode-*@*` tags, permits
|
|
156
|
+
creation, and has no bypass actors. Its reviewed configuration is tracked in
|
|
157
|
+
`.github/rulesets/release-tags.json`. Main protection is unchanged.
|
|
158
|
+
|
|
159
|
+
During the September 19 alignment, GitHub releases `v0.4.0-alpha.1` and
|
|
160
|
+
`v0.4.0-alpha.2` were explicitly marked as prereleases and GitHub latest was
|
|
161
|
+
restored to `v0.3.0`, matching npm. Their tags and artifact bytes were unchanged.
|
|
162
|
+
Further performance and release-train validation is tracked in
|
|
163
|
+
[issue #185](https://github.com/jimhoyd-com/urlcode/issues/185).
|
|
164
|
+
|
|
165
|
+
### Windows fixture cleanup
|
|
166
|
+
|
|
167
|
+
Auth/admin tests register resources with their package-local `test/cleanup.ts`.
|
|
168
|
+
Cleanup runs in reverse acquisition order: close servers and SQLite services
|
|
169
|
+
before deleting temporary directories, including services reopened by a test.
|
|
170
|
+
Every registered callback is attempted even if another closer throws, and the
|
|
171
|
+
combined error fails the test. Register each closer as soon as its resource opens.
|
|
172
|
+
The suites use a five-minute test-file timeout so a stuck fixture is diagnosed
|
|
173
|
+
before the CI job limit. Node applies this limit to whole test files too; the
|
|
174
|
+
large auth-core file legitimately exceeds two minutes on Windows Node 22.
|
|
175
|
+
Windows regression coverage runs on Node 24 for platform-sensitive PRs and
|
|
176
|
+
main; nightly/manual runs cover Node 22/24/26.
|
|
177
|
+
|
|
178
|
+
Failed auth service initialization also waits for its SQLite worker to terminate
|
|
179
|
+
before rejecting. Callers can clean up or retry after a rejected open without
|
|
180
|
+
racing a remaining database handle; configuration identity failures still fail
|
|
181
|
+
closed with the same error code.
|