@jimhoyd/urlcode 0.4.1 → 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/ROADMAP.md +16 -12
- package/dist/BUILD-MANIFEST.json +6 -6
- package/dist/cli.js +2 -1
- package/dist/extensions.js +78 -1
- package/dist/mcp.js +1 -1
- package/dist/policy.js +16 -0
- package/dist/readiness.js +5 -1
- package/dist/tooling.js +2 -1
- package/dist/types/extensions.d.ts +58 -0
- package/dist/types/readiness.d.ts +3 -0
- package/dist/types/tooling.d.ts +1 -0
- package/docs/AI-AUTHORING.md +5 -4
- package/docs/COMPOSING-A-SITE.md +15 -6
- package/docs/CONTAINER-PROMOTION.md +74 -0
- package/docs/DEVELOPMENT-PIPELINE.md +39 -5
- package/docs/EXTENSIONS.md +53 -88
- package/docs/FUNCTION-SECURITY.md +5 -8
- package/docs/OPEN-DECISIONS.md +27 -102
- package/docs/READINESS.md +4 -1
- package/docs/README.md +16 -10
- package/docs/RELEASE-0.4.2.md +30 -0
- package/docs/RELEASE-READINESS.md +40 -11
- package/docs/RELEASE-SECURITY.md +6 -2
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +9 -13
- package/docs/TOOLING.md +1 -1
- package/docs/VERSION-ALIGNMENT.md +8 -0
- package/docs/archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md +116 -0
- package/docs/archive/README.md +1 -0
- package/llms-full.txt +79 -107
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/packaging/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +10 -0
- package/skills/urlcode/SKILL.md +8 -1
|
@@ -82,7 +82,8 @@ and current sample sizes.
|
|
|
82
82
|
Core remains at the repository root. Independent extension versions remain
|
|
83
83
|
supported; a coordinated version is an explicit maintainer choice, not a
|
|
84
84
|
permanent fixed-version policy. An explicitly selected stable version exits
|
|
85
|
-
Changesets alpha pre-mode
|
|
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
|
|
86
87
|
Changesets; core release notes remain an explicit maintainer responsibility.
|
|
87
88
|
|
|
88
89
|
`release:check` checks manifest/lock versions and peer ranges, CLI and MCP
|
|
@@ -101,12 +102,41 @@ npm run release:prepare -- --version 0.4.1 --consume-changesets --execute
|
|
|
101
102
|
|
|
102
103
|
An optional `--notes PATH` adds reviewed maintainer notes. Dry runs do not change
|
|
103
104
|
files. Preparation rejects downgrades, reused local tags, dirty checkouts and
|
|
104
|
-
stale plans. A stable target removes `.changeset/pre.json
|
|
105
|
-
`latest`, and leaves the historical `alpha`
|
|
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
|
|
106
108
|
require existing alpha mode; the helper never silently re-enters prerelease mode.
|
|
107
109
|
It does not invoke a permanent Changesets fixed-version policy.
|
|
108
110
|
|
|
109
|
-
##
|
|
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
|
|
110
140
|
|
|
111
141
|
Inspect without writing:
|
|
112
142
|
|
|
@@ -115,12 +145,14 @@ npm run release:status # registry channels, peer compatibility, tag SHAs
|
|
|
115
145
|
npm run release:plan # manifest-derived inventory
|
|
116
146
|
npm run release:run # ordered states at this checkout: pending/resume/unchanged
|
|
117
147
|
npm run release:run -- --version 0.4.1 --consume-changesets
|
|
148
|
+
npm run release:run -- --version 0.4.2 --package auth --consume-changesets
|
|
118
149
|
```
|
|
119
150
|
|
|
120
151
|
For an explicitly authorized coordinated release:
|
|
121
152
|
|
|
122
153
|
```sh
|
|
123
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
|
|
124
156
|
```
|
|
125
157
|
|
|
126
158
|
`--execute` authorizes the entire sequence: create the release branch/PR, wait
|
|
@@ -241,7 +273,9 @@ npm uses OIDC with pinned npm 11.5.1. Alpha versions use npm/GHCR `alpha`, and
|
|
|
241
273
|
GitHub prerelease classification with `--latest=false`. Existing `latest`
|
|
242
274
|
pointers are not promoted by this flow. Core GHCR publication remains conditional
|
|
243
275
|
on `PUBLISH_CONTAINER=true`; its existing version/channel identity guards remain.
|
|
244
|
-
Historical GHCR verification is still a separate follow-up.
|
|
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.
|
|
245
279
|
|
|
246
280
|
The [Immutable release tags rule](https://github.com/jimhoyd-com/urlcode/rules/23712319)
|
|
247
281
|
blocks updates/deletions of `v*` and `@jimhoyd/urlcode-*@*`, permits creation, and
|
package/docs/EXTENSIONS.md
CHANGED
|
@@ -183,29 +183,18 @@ Node adapter conformance is not a live-provider deployment claim.
|
|
|
183
183
|
|
|
184
184
|
## Project-level lifecycle hooks
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
**The pattern.** An extension with lifecycle events an author judges worth
|
|
200
|
-
exposing should let the project name its own function in the extension's own
|
|
201
|
-
`config`, using the same source shape `function`/`middleware` routes already
|
|
202
|
-
use (a string path, or `{source, export, args}` — `schemas/urlcode.schema.json`),
|
|
203
|
-
and add its own `sandbox` boolean next to it (below). The extension's own
|
|
204
|
-
`activate()` reads that config, and its own runtime dispatch — not a new core
|
|
205
|
-
primitive, an ordinary call the extension package makes with the request
|
|
206
|
-
context it already has — invokes the named function at the lifecycle point
|
|
207
|
-
it defines, with a typed input and a typed verdict the extension's own
|
|
208
|
-
schema documents. For example, an auth-style extension might declare:
|
|
186
|
+
Extensions expose project customization points through the core hook primitive.
|
|
187
|
+
Each registration publishes `hooks`, a machine-readable list containing the
|
|
188
|
+
hook name, whether it is a value-transforming `filter` or side-effect `action`,
|
|
189
|
+
its description and its input/output JSON Schemas. The extension embeds
|
|
190
|
+
`extensionHooksSchema(contracts)` in its configuration schema and calls
|
|
191
|
+
`loadExtensionHooks(config.hooks, contracts, context)` during activation.
|
|
192
|
+
Core then enforces the common source/export shape, project-root confinement,
|
|
193
|
+
known names, eager module/export validation, input/output schemas and reload
|
|
194
|
+
cache busting. Hook entry bytes participate in the project revision, so editing
|
|
195
|
+
a hook invalidates the operator's extension pin.
|
|
196
|
+
|
|
197
|
+
Projects select those declared hooks in the extension's own configuration:
|
|
209
198
|
|
|
210
199
|
```yaml
|
|
211
200
|
extensions:
|
|
@@ -221,76 +210,51 @@ extensions:
|
|
|
221
210
|
```
|
|
222
211
|
|
|
223
212
|
with `beforeRegister` called before an account is created, given a typed
|
|
224
|
-
`{email,
|
|
213
|
+
`{email, profile?}` input and returning a typed verdict (`{allow: true}`
|
|
225
214
|
or `{allow: false, reason}`), and `onSignUp` called after, for side effects
|
|
226
|
-
such as provisioning a workspace.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
project code
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
unchanged file URL makes a second activation in the same process keep
|
|
265
|
-
serving the hook code that was on disk at the first one
|
|
266
|
-
(jimhoyd-com/urlcode#198). Only the hook's **entry** module is refreshed
|
|
267
|
-
this way — modules the hook itself imports stay on Node's module cache,
|
|
268
|
-
the same limitation the trusted route path has, so a change to a hook's
|
|
269
|
-
own dependency still needs a process restart.
|
|
270
|
-
- **Sandboxed (`sandbox: true`).** `@jimhoyd/urlcode/sandbox` exports
|
|
271
|
-
`SandboxPool`, the same QuickJS/worker-thread engine that backs a
|
|
272
|
-
sandboxed `function`/`middleware` route — the identical module-allowlist
|
|
273
|
-
walk, memory/stack limits, two-layer deadline enforcement, `maxBytes` and
|
|
274
|
-
response-shape validation, with no separate or weaker engine for
|
|
275
|
-
extensions. It takes an explicit list of `{source, export}` entries
|
|
276
|
-
(resolve a hook's `source` string with the re-exported `functionFile()`,
|
|
277
|
-
the same resolution/validation a native route's `source` gets) instead of
|
|
278
|
-
anything route/YAML-shaped, and `execute({entry, chain}, request, context)`
|
|
279
|
-
in place of a `FunctionRoute`. There is no "trusted" mode exported
|
|
280
|
-
alongside it — `SandboxPool` is only ever the isolated path; see
|
|
281
|
-
[FUNCTION-SECURITY.md](FUNCTION-SECURITY.md) and
|
|
282
|
-
[TYPESCRIPT.md](TYPESCRIPT.md) for the full contract.
|
|
283
|
-
|
|
284
|
-
An extension honoring a hook's `sandbox: true` is expected to actually
|
|
285
|
-
isolate that invocation through `SandboxPool` now that the primitive exists
|
|
286
|
-
(or document plainly that it does not yet, rather than accepting the field
|
|
287
|
-
and silently running it trusted) — say which, in the extension's own docs,
|
|
288
|
-
so an author reading them is not misled about what opt-in exists.
|
|
215
|
+
such as provisioning a workspace. Hook names and lifecycle timing remain the
|
|
216
|
+
extension's domain, while their declaration, loading and discovery are shared.
|
|
217
|
+
|
|
218
|
+
Hooks are first-party project code and run trusted in-process by default, with
|
|
219
|
+
full Node access, like trusted `function` and `middleware` routes. Contract v1
|
|
220
|
+
does not define an arbitrary-value sandbox hook protocol. A hook reference with
|
|
221
|
+
`sandbox: true` is rejected during activation rather than silently run trusted.
|
|
222
|
+
Only the entry module is refreshed during reactivation; its imported dependencies
|
|
223
|
+
remain in Node's module cache until restart.
|
|
224
|
+
|
|
225
|
+
The UI extension exposes `transformView`, a synchronous filter called before a
|
|
226
|
+
named kit template renders. It receives `{template, view}` and returns the view
|
|
227
|
+
model to render. Use copy, templates, theme and CSS for ordinary presentation
|
|
228
|
+
changes; use this hook for project-specific computed view data that those
|
|
229
|
+
declarative layers cannot express.
|
|
230
|
+
|
|
231
|
+
## Building an extension
|
|
232
|
+
|
|
233
|
+
An extension package should export a registration factory and, when it supports
|
|
234
|
+
`urlcode init --with`, a side-effect-free `scaffold` function. The registration:
|
|
235
|
+
|
|
236
|
+
1. Declares its logical name, contract version, supported targets, exact project
|
|
237
|
+
revision pin and strict configuration/policy schemas.
|
|
238
|
+
2. Publishes every project hook through `hooks` and reuses
|
|
239
|
+
`extensionHooksSchema` plus `loadExtensionHooks`; it does not implement its
|
|
240
|
+
own path resolver or dynamic-import cache.
|
|
241
|
+
3. Activates all configuration, files, services and hooks before serving a
|
|
242
|
+
request. Invalid or stale configuration fails activation.
|
|
243
|
+
4. Returns `handle` for mounts and optionally `authorize`/`middleware` for route
|
|
244
|
+
policies. It closes resources it owns.
|
|
245
|
+
5. Keeps credentials, storage and provider setup in the operator host. Project
|
|
246
|
+
YAML contains logical configuration and project-relative hook references.
|
|
247
|
+
|
|
248
|
+
Consumers install the package, declare its YAML block and mounts/policies, and
|
|
249
|
+
register it in `host.mjs`. They modify it through declared configuration,
|
|
250
|
+
presentation layers and hooks. A fork is reserved for changing behavior the
|
|
251
|
+
extension has not exposed; that is evidence for a new declarative field or hook.
|
|
252
|
+
See [Composing a site](COMPOSING-A-SITE.md) for the complete ui/auth/admin example.
|
|
289
253
|
|
|
290
254
|
## Discovering schemas
|
|
291
255
|
|
|
292
256
|
Each registration carries the JSON Schemas that validate its `config` block and
|
|
293
|
-
its per-route policy requirements. `urlcode extensions` prints them together with
|
|
257
|
+
its per-route policy requirements, plus its hook contracts. `urlcode extensions` prints them together with
|
|
294
258
|
the project's own declarations so an author can see what a mount accepts:
|
|
295
259
|
|
|
296
260
|
```sh
|
|
@@ -299,6 +263,7 @@ urlcode extensions --project ./site --host-file /absolute/operator/host.mjs [--j
|
|
|
299
263
|
|
|
300
264
|
For every registration in the host file it reports the name, contract version,
|
|
301
265
|
targets, credential headers, configuration schema, policy schema (if any),
|
|
266
|
+
declared hook names, kinds, descriptions and input/output schemas,
|
|
302
267
|
whether the project declares it, whether its `projectSha256` matches the current
|
|
303
268
|
revision, the routes that mount it and the routes whose policies require it.
|
|
304
269
|
Declared names the host does not register are listed as unregistered. The command
|
|
@@ -93,14 +93,11 @@ machinery either way.
|
|
|
93
93
|
|
|
94
94
|
This engine — worker spawning, the module-allowlist walk, the two-layer
|
|
95
95
|
deadline, `maxBytes` and response-shape validation — is one implementation
|
|
96
|
-
shared by route dispatch and by `@jimhoyd/urlcode/sandbox`'s `SandboxPool
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
`sandbox: true`
|
|
100
|
-
|
|
101
|
-
through that entry point; there is no separate, weaker sandbox for extensions
|
|
102
|
-
to reach for, and no "trusted" mode exported there — trusted execution needs
|
|
103
|
-
no primitive at all (docs/SPIKE-DEFAULT-TRUST-MODEL.md).
|
|
96
|
+
shared by route dispatch and by `@jimhoyd/urlcode/sandbox`'s `SandboxPool`.
|
|
97
|
+
That public HTTP-shaped primitive remains available to extension authors, but
|
|
98
|
+
project extension hooks use arbitrary typed values and contract v1 runs them
|
|
99
|
+
trusted in-process; it rejects `sandbox: true` rather than claiming HTTP sandbox
|
|
100
|
+
semantics apply to them. See [extensions](EXTENSIONS.md#project-level-lifecycle-hooks).
|
|
104
101
|
|
|
105
102
|
## What the trusted default can and can't do
|
|
106
103
|
|
package/docs/OPEN-DECISIONS.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Decisions to align
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Last reconciled 2026-09-20 against open and closed issues (#242); the source
|
|
4
|
+
review it began from is dated 2026-09-19 (core `db375bf`, now archived).
|
|
4
5
|
This is the maintainer's decision list, not a second implementation backlog.
|
|
5
6
|
The [roadmap](../ROADMAP.md) gives sequence and the [archive](archive/README.md)
|
|
6
7
|
keeps earlier discussions. Recommendations below are not accepted decisions.
|
|
@@ -33,31 +34,14 @@ keeps earlier discussions. Recommendations below are not accepted decisions.
|
|
|
33
34
|
|---|---|---|
|
|
34
35
|
| Where does work status live? | Several old plans repeated issues and continued calling delivered work unfinished. | Issues for actionable status, this short roadmap for sequence, archive for completed proposals. Preserve evidence gaps when archiving. |
|
|
35
36
|
| Expand into business applications now? | No collection handler or proposed business suite is implemented; the model-backed benchmark evidence is missing. | Measure existing tasks and record repeated application plumbing before selecting a collection/CMS/forms project. Retired short-link products stay retired. [Proposal](SPIKE-BUSINESS-SUITE.md). |
|
|
36
|
-
| Fold extension schemas into retrieved context? | `urlcode extensions` and the MCP `get_extensions` query return the registered configuration and policy schemas, but `src/context.ts:113` reports `extensions` as names only, so an author writing `extensions.<name>.config` or `policies.extensions.<name>` must run the separate operator-authorized query first. | Decide from retrieval and task evidence, not preference: the existing small-task harness can measure whether folding schemas into bounded context improves authoring. Keep the token budget bounded and never auto-load a project-selected host file. This is a discovery improvement, not a defect in the existing query. |
|
|
37
|
+
| Fold extension schemas into retrieved context? ([#174](https://github.com/jimhoyd-com/urlcode/issues/174), open) | `urlcode extensions` and the MCP `get_extensions` query return the registered configuration and policy schemas, but `src/context.ts:113` reports `extensions` as names only, so an author writing `extensions.<name>.config` or `policies.extensions.<name>` must run the separate operator-authorized query first. | Decide from retrieval and task evidence, not preference: the existing small-task harness can measure whether folding schemas into bounded context improves authoring. Keep the token budget bounded and never auto-load a project-selected host file. This is a discovery improvement, not a defect in the existing query. |
|
|
38
|
+
| Build the tested-image promotion path? ([#233](https://github.com/jimhoyd-com/urlcode/issues/233), open) | [Design and an inert invariant helper](CONTAINER-PROMOTION.md) are merged. `release.yml` still rebuilds from source when `PUBLISH_CONTAINER` is true, and a retry accepts an existing `:VERSION` on its `revision` label alone. Live GHCR state and whether GHCR preserves an OCI digest through `skopeo`/`crane` are unverified. | The reason is integrity (tested bytes are the shipped bytes), not speed: CI's image build took about 12 seconds, so a cross-run build cache is not justified and should not be added. Recommendation: do not implement while publication is off and GHCR is uninspected. First grant `read:packages` and inspect historical labels and digests; then land a candidate-side image build behind an operator input, inspect one real candidate, and only then change the publisher. Not changed here. |
|
|
37
39
|
| Keep the POST-plus-`request.body` sandbox advisory? | `src/readiness.ts` nudges any code-running route that accepts POST with a declared `request.body` and declares neither `sandbox: true` nor `sandboxReason`. It is advisory only: never fails `audit`, never changes `ready`. | The nudge keys on request *shape* while [AI authoring](AI-AUTHORING.md) tells authors to decide on *code* trust, so it can read as "untrusted input implies sandbox" -- the reasoning that guidance explicitly rejects. It still has value as a prompt to record a decision. Recommendation: keep the trigger, restate the message as a request to record the trust decision (`sandbox: true` or `sandboxReason`) rather than as a suggestion that this route may need isolation. Not changed here; #196 was a docs/tooling alignment pass. |
|
|
38
40
|
|
|
39
41
|
The broader [AI benchmark proposal](SPIKE-AI-FRAMEWORK-BENCHMARK.md) also needs a
|
|
40
42
|
chosen application, model-run budget and execution authorization. The existing
|
|
41
43
|
small-task harness can supply evidence without committing to that larger study.
|
|
42
44
|
|
|
43
|
-
## Closed questions removed from the active list
|
|
44
|
-
|
|
45
|
-
- Publishing convention is recorded in [version alignment](VERSION-ALIGNMENT.md):
|
|
46
|
-
publishable manifests on main, releases through reviewed tags/workflows.
|
|
47
|
-
- Core `0.4.0-alpha.2` and current extension releases exist; publishing that
|
|
48
|
-
already-shipped version is not a next step.
|
|
49
|
-
- Auth/admin kit adoption and shared form helpers are implemented in their code.
|
|
50
|
-
- The UI primitive fallback is retired, which settles the question this table
|
|
51
|
-
carried. Auth and admin now render every screen through the kit and refuse
|
|
52
|
-
activation without it (`packages/auth/src/auth.ts`: "there is no
|
|
53
|
-
shared-primitive fallback"), so "keep both" no longer describes the code.
|
|
54
|
-
- The template pins `0.4.0-alpha.2`. Its skill differences were read against that
|
|
55
|
-
pin: omitted handlers and advice about the removed management API are stale,
|
|
56
|
-
not intentional older-version behavior.
|
|
57
|
-
- The guidance checks run through `npm run check` inside `verify`; a regex check
|
|
58
|
-
is not a schema validator for every example. Extending its coverage is tracked
|
|
59
|
-
separately, not a reason to weaken review or bypass required checks.
|
|
60
|
-
|
|
61
45
|
## Accepted: one Node deployment per project
|
|
62
46
|
|
|
63
47
|
**Decided 2026-09-19.** Projects that use `function` or `middleware` deploy as
|
|
@@ -140,85 +124,26 @@ reasoning above is the kind of prose that rots as soon as `auth` and `admin`
|
|
|
140
124
|
arrive, which is the whole argument this repository makes for enforcing checks
|
|
141
125
|
over documented intent.
|
|
142
126
|
|
|
143
|
-
##
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
workspace packages
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
The maintainer confirmed that monorepo work is starting now. The earlier
|
|
167
|
-
recommendation to keep repositories separate for now is superseded.
|
|
168
|
-
|
|
169
|
-
**Reversed 2026-09-19: the middleware half of this decision no longer applies.**
|
|
170
|
-
This section used to say "move middleware into the monorepo as its own package
|
|
171
|
-
first, preserving its existing API and behavior," and explicitly: "do not
|
|
172
|
-
unpublish or retire the middleware package as part of the initial move."
|
|
173
|
-
That instruction was overtaken. `@jimhoyd/urlcode-middleware` has been
|
|
174
|
-
**unpublished** from npm at `0.1.0-alpha.2` and `jimhoyd-com/urlcode-middleware`
|
|
175
|
-
**deleted** — the package was withdrawn outright instead of migrated, so there
|
|
176
|
-
is no `packages/middleware` to create and no subsequent fold-into-core step.
|
|
177
|
-
The reversal is recorded here rather than deleted because the instruction it
|
|
178
|
-
replaces was explicit, and a reader who remembers it should be able to see that
|
|
179
|
-
it was changed deliberately and not simply forgotten.
|
|
180
|
-
|
|
181
|
-
Nothing was lost in capability terms: per-route middleware is **native to
|
|
182
|
-
core** via the `middleware:` array ([MIDDLEWARE.md](MIDDLEWARE.md)), and the
|
|
183
|
-
deleted package only ever offered the same behavior through the extension
|
|
184
|
-
seam. Trust remains the default and explicit sandbox choices keep their
|
|
185
|
-
meaning. The generic extension wrapping hook (`ExtensionInstance.middleware`,
|
|
186
|
-
`RuntimeExtension.cacheSensitive`) stays in core's contract for other
|
|
187
|
-
extensions — it is no longer exercised by any shipped package, which is worth
|
|
188
|
-
knowing before it is assumed to be covered. Static targets continue rejecting
|
|
189
|
-
request-time middleware because there is no server to run it.
|
|
190
|
-
|
|
191
|
-
Migration scope is therefore **core, auth, admin and UI**. Template and the
|
|
192
|
-
distribution tap stay outside that package move. The observed stale peer pins
|
|
193
|
-
and checkout-limited guidance checks strengthen the case for shared
|
|
194
|
-
verification: carry those checks across the new package paths, since merely
|
|
195
|
-
moving files does not prove every generated skill is covered. The cleanup PRs
|
|
196
|
-
that superseded the earlier zero-open-PR survey have since merged, and a fresh
|
|
197
|
-
survey again reports zero open pull requests across all four in-scope
|
|
198
|
-
repositories — re-run it per repository immediately before that repository
|
|
199
|
-
moves rather than trusting this line.
|
|
200
|
-
|
|
201
|
-
The [archived monorepo plan](archive/2026-09-19/SPIKE-MONOREPO.md) records
|
|
202
|
-
migration context.
|
|
203
|
-
[Issue 172](https://github.com/jimhoyd-com/urlcode/issues/172), which tracked
|
|
204
|
-
"consolidate middleware into core after moving it into the monorepo," was
|
|
205
|
-
**closed on 2026-09-19** as moot — there was nothing left to consolidate.
|
|
206
|
-
Migration starting is not a claim that it has landed.
|
|
207
|
-
|
|
208
|
-
## Source review baseline
|
|
209
|
-
|
|
210
|
-
| Repository | Reviewed commit | Code checked |
|
|
211
|
-
|---|---|---|
|
|
212
|
-
| core | `db375bf` | Runtime dispatch, schema normalization, capabilities, static compiler, prerender, MCP and resource generators |
|
|
213
|
-
| auth | `71957dd` | Lifecycle hooks, UI rendering and shared helper imports |
|
|
214
|
-
| admin | `f3b4882` | UI rendering, auth-service integration and shared helper imports |
|
|
215
|
-
| UI | `0e96f7f` | Shared forms, kit/host exports and copied core contract |
|
|
216
|
-
| ~~middleware~~ | `f201f4b` | Extension wrapping, per-entry sandbox dispatch and scaffolding — **repository deleted 2026-09-19; this baseline is unreachable except through the local `urlcode-middleware.bundle`** |
|
|
217
|
-
| template | `4e09e50` | Exact core pin, generated guide and both vendored skills |
|
|
218
|
-
|
|
219
|
-
The Homebrew tap (`73eaaef`) still selects stable core `0.3.0`; its old trust
|
|
220
|
-
behavior belongs to that pin and must not be rewritten as alpha.2 behavior.
|
|
221
|
-
The other organization tap and Scoop bucket contain Gitroll, not URLCode.
|
|
222
|
-
The deleted documentation repository is historical context, not a second source
|
|
223
|
-
of current contracts; its former GitHub links no longer resolve. This review is targeted source inspection, not an audit of
|
|
224
|
-
every execution path or an independent security assessment.
|
|
127
|
+
## Completed work (archived)
|
|
128
|
+
|
|
129
|
+
Closed questions, the completed monorepo migration (ui, auth and admin are
|
|
130
|
+
workspace packages; released from this repository) and the 2026-09-19 source
|
|
131
|
+
review baseline live in
|
|
132
|
+
[archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md](archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md).
|
|
133
|
+
The migration plan is [archived](archive/2026-09-19/SPIKE-MONOREPO.md). Versions
|
|
134
|
+
and channels are in [version alignment](VERSION-ALIGNMENT.md) and
|
|
135
|
+
`npm run release:status`, not here.
|
|
136
|
+
|
|
137
|
+
## Accepted: middleware withdrawn rather than consolidated
|
|
138
|
+
|
|
139
|
+
**Decided 2026-09-19.** `@jimhoyd/urlcode-middleware` was unpublished and its
|
|
140
|
+
repository deleted; there is no `packages/middleware` and nothing to fold into
|
|
141
|
+
core afterward (#172, closed as moot). Per-route middleware is native to core
|
|
142
|
+
via the `middleware:` array ([MIDDLEWARE.md](MIDDLEWARE.md)). The generic
|
|
143
|
+
extension wrapping hook (`ExtensionInstance.middleware`,
|
|
144
|
+
`RuntimeExtension.cacheSensitive`) stays in core's contract for other extensions
|
|
145
|
+
but is no longer exercised by any shipped package, so do not assume it is
|
|
146
|
+
covered. Static targets keep rejecting request-time middleware. The earlier
|
|
147
|
+
instruction to move middleware into the monorepo first was overtaken; its full
|
|
148
|
+
text is retained in the archived record.
|
|
149
|
+
|
package/docs/READINESS.md
CHANGED
|
@@ -82,7 +82,10 @@ parameter patterns still need explicit negative fixtures to exercise them.
|
|
|
82
82
|
|
|
83
83
|
`ready: true` requires a nonempty active project, matching expected count (when
|
|
84
84
|
supplied), zero failed checks and no uncovered active route/method combinations.
|
|
85
|
-
|
|
85
|
+
When `ready` is false, `notReadyReasons` lists each failed condition:
|
|
86
|
+
`no-active-routes`, `route-count-mismatch`, `failed-checks` and
|
|
87
|
+
`uncovered-route-methods` (see `uncovered` for the pairs). `unassertedCases` never
|
|
88
|
+
affects `ready`. It means this local gate passed, not that all branches, parameter values or assets
|
|
86
89
|
have independent business assertions. Function routes intentionally serving only
|
|
87
90
|
errors cannot satisfy normal-response coverage in this release. Time-dependent
|
|
88
91
|
expiry is evaluated at audit start; avoid running a gate exactly at expiry.
|
package/docs/README.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
# URLCode documentation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Choose a starting point, then use the topic directory below when you need detail.
|
|
4
|
+
Use documentation from the same pinned revision as your runtime.
|
|
5
|
+
|
|
6
|
+
| I want to… | Start here |
|
|
7
|
+
|---|---|
|
|
8
|
+
| Understand what URLCode does | [Framework](FRAMEWORK.md) |
|
|
9
|
+
| Build my first project | [Installation](INSTALL.md), then [YAML guide](YAML-GUIDE.md) |
|
|
10
|
+
| Build a site with UI, accounts and admin | [Composing a site](COMPOSING-A-SITE.md) |
|
|
11
|
+
| Have an AI author a project | [AI authoring](AI-AUTHORING.md), [agent index](../llms.txt) |
|
|
12
|
+
| Deploy and operate a project | [Operations](OPERATIONS.md) |
|
|
13
|
+
| Contribute to URLCode | [Contributing](../CONTRIBUTING.md), [local development](LOCAL-DEVELOPMENT.md) |
|
|
14
|
+
|
|
15
|
+
The [specification](SPECIFICATION.md) owns implemented semantics; the
|
|
16
|
+
[generated field reference](YAML-REFERENCE.md) lists accepted fields.
|
|
17
|
+
[Project direction](PROJECT-DIRECTION.md) explains the product boundary.
|
|
8
18
|
|
|
9
19
|
## Author a project
|
|
10
20
|
|
|
@@ -13,11 +23,9 @@ boundary and the license. Use documentation pinned to your runtime revision;
|
|
|
13
23
|
| Install the CLI | [Installation](INSTALL.md) |
|
|
14
24
|
| Write YAML with examples | [YAML guide and recipes](YAML-GUIDE.md) |
|
|
15
25
|
| Look up every accepted field | [Generated field reference](YAML-REFERENCE.md), [JSON Schema](../schemas/urlcode.schema.json) |
|
|
16
|
-
| Let an AI build routes | [AI authoring guide](AI-AUTHORING.md), [llms.txt](../llms.txt) |
|
|
17
26
|
| Load authoring/operations rules into an agent | [Authoring skill](../.claude/skills/urlcode-authoring/SKILL.md), [operations skill](../.claude/skills/urlcode-operations/SKILL.md), [how they are distributed](AI-AUTHORING.md#agent-skills) |
|
|
18
|
-
| Run examples | [25-route cookbook](../examples/cookbook/README.md), [prerender recipe](../examples/prerender/README.md), [small starter](STARTERS.md) |
|
|
19
27
|
| Understand exact behavior | [Specification](SPECIFICATION.md), [routing](ROUTING.md), [HTTP](HTTP.md) |
|
|
20
|
-
| Run examples | [
|
|
28
|
+
| Run examples | [Executable cookbook](../examples/cookbook/README.md), [prerender recipe](../examples/prerender/README.md), [small starter](STARTERS.md) |
|
|
21
29
|
| Let an AI build routes | [The framework](FRAMEWORK.md), [AI authoring guide](AI-AUTHORING.md), [llms.txt](../llms.txt), [SDK and read-only MCP](TOOLING.md) |
|
|
22
30
|
| Reuse code around routes | [Middleware](MIDDLEWARE.md), [middleware examples](MIDDLEWARE-EXAMPLES.md) |
|
|
23
31
|
| Handle secrets and decide what to sandbox | [Function security](FUNCTION-SECURITY.md) |
|
|
@@ -43,7 +51,6 @@ boundary and the license. Use documentation pinned to your runtime revision;
|
|
|
43
51
|
| Manage users, sessions, roles and audit | [urlcode-admin](../packages/admin#readme) |
|
|
44
52
|
| Restyle every extension page and translate copy | [urlcode-ui](../packages/ui#readme), [ui contract](../packages/ui/CONTRACT.md) |
|
|
45
53
|
| Write or install a versioned extension | [Extensions](EXTENSIONS.md), [example fixture](../examples/extensions/README.md) |
|
|
46
|
-
| Follow implementation of the auth, admin and UI extensions | [Extension implementation sequence](archive/2026-09-19/EXTENSION-IMPLEMENTATION.md) |
|
|
47
54
|
| Know which core version an extension package supports, and how it says so | [Core version alignment](VERSION-ALIGNMENT.md) |
|
|
48
55
|
| Add host behavior in operator code | [Plugins](PLUGINS.md) |
|
|
49
56
|
| Use the API from TypeScript | [TypeScript: shipped declarations, exports, build and fidelity](TYPESCRIPT.md) |
|
|
@@ -59,7 +66,6 @@ boundary and the license. Use documentation pinned to your runtime revision;
|
|
|
59
66
|
| Review security findings and gaps | [Internal security audit](SECURITY-AUDIT.md) |
|
|
60
67
|
| Assess release readiness | [Evidence and open gates](RELEASE-READINESS.md) |
|
|
61
68
|
| See unfinished work | [Roadmap](../ROADMAP.md) |
|
|
62
|
-
| Read why per-route Lambda compilation was declined | [Lambda compile spike (archived)](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md) |
|
|
63
69
|
| Verify a running deployment matches the project | [Deployment checks](DEPLOYMENT-CHECKS.md) |
|
|
64
70
|
| Inspect target support | [Capabilities and normalized representation](CAPABILITIES.md) |
|
|
65
71
|
| Deploy to Vercel, AWS Lambda or Cloudflare Workers | [Vercel](VERCEL.md), [AWS](AWS.md), [Cloudflare](CLOUDFLARE.md), [provider verification evidence](PROVIDER-VERIFICATION.md) |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# URLCode 0.4.2
|
|
2
|
+
|
|
3
|
+
Core, UI, auth and admin share this explicitly selected stable version. Independent package versioning remains enabled.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install --save-exact @jimhoyd/urlcode@0.4.2 @jimhoyd/urlcode-ui@0.4.2 @jimhoyd/urlcode-auth@0.4.2 @jimhoyd/urlcode-admin@0.4.2
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
### admin-copy-project-override.md
|
|
10
|
+
|
|
11
|
+
Project translations of `adminUi.*` ids in `ui/copy/<locale>.json` now reach the admin console.
|
|
12
|
+
|
|
13
|
+
In a composed site the console copy source is built with `createAdminPresentation({ base: kit.presentation })`, and the admin ids were resolved only from the bundled English, so a project's `adminUi.*` entries were silently ignored. When a base presentation is given, an admin id the base resolves for the request's locale now wins; otherwise the bundled admin English still answers. Hosts that pass no `base` and hosts that supply their own presentation are unchanged.
|
|
14
|
+
|
|
15
|
+
### ui-styles-once-per-verify.md
|
|
16
|
+
|
|
17
|
+
Compile Tailwind once per `npm run verify`.
|
|
18
|
+
|
|
19
|
+
No API change and no change to the generated stylesheet. `verify` used to run
|
|
20
|
+
`typecheck` then `build`, and each of those runs `styles`, so
|
|
21
|
+
`scripts/build-styles.mjs` compiled the same minified CSS twice per
|
|
22
|
+
verification. `verify` now runs `styles` once and then the compiler-only
|
|
23
|
+
`typecheck:tsc` and `build:tsc` scripts. `typecheck` and `build` are unchanged
|
|
24
|
+
from a caller's point of view: each still runs `styles` first, so either one
|
|
25
|
+
works on its own from a fresh checkout.
|
|
26
|
+
|
|
27
|
+
Nothing is cached and nothing is skipped because an output already exists; the
|
|
28
|
+
single run is unconditional, so a source change is still picked up.
|
|
29
|
+
|
|
30
|
+
Publish to the npm `latest` channel only after exact-commit CI and candidate verification. Existing tags and the `alpha` channel stay unchanged. Update the standalone starter after core registry installability is verified. This preparation is not evidence of publication or an independent security assessment.
|