@jimhoyd/urlcode 0.3.0 → 0.4.0-alpha.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.
Files changed (380) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +122 -0
  2. package/.claude/skills/urlcode-operations/SKILL.md +108 -0
  3. package/.claude-plugin/marketplace.json +18 -0
  4. package/CONTRIBUTING.md +30 -2
  5. package/README.md +195 -255
  6. package/ROADMAP.md +143 -15
  7. package/SECURITY.md +31 -9
  8. package/dist/BUILD-MANIFEST.json +72 -47
  9. package/dist/adapters.js +4 -23
  10. package/dist/agent-lists.js +1 -1
  11. package/dist/agents-guide.js +113 -0
  12. package/dist/authoring-files.js +60 -0
  13. package/dist/authoring.js +11 -1
  14. package/dist/aws.js +4 -3
  15. package/dist/build-cloudflare.js +11 -24
  16. package/dist/build-static.js +134 -0
  17. package/dist/bulk.js +37 -0
  18. package/dist/capabilities.js +262 -0
  19. package/dist/capability-query.js +71 -0
  20. package/dist/catalog.js +105 -0
  21. package/dist/cli.js +165 -34
  22. package/dist/client-address.js +1 -1
  23. package/dist/compliance-rules/baseline.js +9 -17
  24. package/dist/compliance-rules/privacy.js +7 -18
  25. package/dist/compliance-rules/shared.js +0 -2
  26. package/dist/compliance-rules/strict.js +5 -5
  27. package/dist/compliance.js +6 -8
  28. package/dist/conditions.js +88 -0
  29. package/dist/config.js +69 -6
  30. package/dist/context.js +155 -0
  31. package/dist/ecosystem-cli.js +88 -0
  32. package/dist/egress.js +98 -0
  33. package/dist/examples.js +92 -0
  34. package/dist/explain-cli.js +64 -0
  35. package/dist/explain.js +131 -0
  36. package/dist/extensions.js +231 -0
  37. package/dist/function-sources.js +49 -5
  38. package/dist/function-worker.js +3 -1
  39. package/dist/functions.js +84 -13
  40. package/dist/guest-api.js +29 -3
  41. package/dist/index.js +40 -6
  42. package/dist/init-with.js +165 -0
  43. package/dist/interchange-cli.js +42 -0
  44. package/dist/interchange.js +189 -0
  45. package/dist/manifest.js +109 -0
  46. package/dist/match.js +2 -2
  47. package/dist/mcp-authoring.js +147 -0
  48. package/dist/mcp.js +97 -0
  49. package/dist/observability.js +7 -21
  50. package/dist/operator-host.js +29 -0
  51. package/dist/plugins.js +12 -0
  52. package/dist/policies/agents.js +2 -2
  53. package/dist/policies/cache.js +8 -3
  54. package/dist/policies/compression.js +2 -1
  55. package/dist/policies/security.js +0 -0
  56. package/dist/policies.js +1 -1
  57. package/dist/policy.js +56 -15
  58. package/dist/prerender.js +100 -41
  59. package/dist/project-tests.js +3 -3
  60. package/dist/provider-verification.js +92 -0
  61. package/dist/proxy.js +44 -0
  62. package/dist/readiness.js +34 -11
  63. package/dist/recipes.js +41 -0
  64. package/dist/route-diff.js +106 -0
  65. package/dist/router.js +45 -7
  66. package/dist/runtime.js +164 -64
  67. package/dist/sandbox.js +48 -0
  68. package/dist/scaffold.js +0 -0
  69. package/dist/schema-query.js +62 -0
  70. package/dist/scripts/operational-drills.js +12 -54
  71. package/dist/server.js +3 -29
  72. package/dist/signals.js +24 -0
  73. package/dist/site.js +0 -0
  74. package/dist/tooling.js +96 -0
  75. package/dist/trusted-functions.js +210 -0
  76. package/dist/types/adapters.d.ts +7 -4
  77. package/dist/types/agent-lists.d.ts +0 -1
  78. package/dist/types/agents-guide.d.ts +17 -0
  79. package/dist/types/authoring-files.d.ts +10 -0
  80. package/dist/types/aws.d.ts +3 -1
  81. package/dist/types/build-cloudflare.d.ts +1 -0
  82. package/dist/types/build-static.d.ts +43 -0
  83. package/dist/types/bulk.d.ts +27 -0
  84. package/dist/types/capabilities.d.ts +64 -0
  85. package/dist/types/capability-query.d.ts +24 -0
  86. package/dist/types/catalog.d.ts +65 -0
  87. package/dist/types/client-address.d.ts +0 -1
  88. package/dist/types/compliance-rules/baseline.d.ts +1 -9
  89. package/dist/types/compliance-rules/privacy.d.ts +1 -4
  90. package/dist/types/compliance-rules/shared.d.ts +0 -2
  91. package/dist/types/compliance-rules/strict.d.ts +0 -5
  92. package/dist/types/compliance.d.ts +0 -3
  93. package/dist/types/conditions.d.ts +19 -0
  94. package/dist/types/config.d.ts +21 -2
  95. package/dist/types/context.d.ts +66 -0
  96. package/dist/types/ecosystem-cli.d.ts +17 -0
  97. package/dist/types/egress.d.ts +46 -0
  98. package/dist/types/examples.d.ts +50 -0
  99. package/dist/types/explain-cli.d.ts +11 -0
  100. package/dist/types/explain.d.ts +95 -0
  101. package/dist/types/extensions.d.ts +177 -0
  102. package/dist/types/function-sources.d.ts +9 -0
  103. package/dist/types/functions.d.ts +48 -5
  104. package/dist/types/guest-api.d.ts +1 -0
  105. package/dist/types/index.d.ts +36 -6
  106. package/dist/types/init-with.d.ts +30 -0
  107. package/dist/types/interchange-cli.d.ts +16 -0
  108. package/dist/types/interchange.d.ts +42 -0
  109. package/dist/types/manifest.d.ts +79 -0
  110. package/dist/types/match.d.ts +1 -0
  111. package/dist/types/mcp-authoring.d.ts +92 -0
  112. package/dist/types/mcp.d.ts +12 -0
  113. package/dist/types/observability.d.ts +3 -14
  114. package/dist/types/operator-host.d.ts +8 -0
  115. package/dist/types/plugins.d.ts +2 -0
  116. package/dist/types/policies/agents.d.ts +0 -2
  117. package/dist/types/policies/compression.d.ts +2 -0
  118. package/dist/types/policies/security.d.ts +0 -1
  119. package/dist/types/policy.d.ts +15 -4
  120. package/dist/types/project-tests.d.ts +3 -2
  121. package/dist/types/provider-verification.d.ts +53 -0
  122. package/dist/types/proxy.d.ts +21 -0
  123. package/dist/types/readiness.d.ts +10 -3
  124. package/dist/types/recipes.d.ts +30 -0
  125. package/dist/types/route-diff.d.ts +27 -0
  126. package/dist/types/router.d.ts +2 -1
  127. package/dist/types/runtime.d.ts +11 -27
  128. package/dist/types/sandbox.d.ts +12 -0
  129. package/dist/types/scaffold.d.ts +0 -2
  130. package/dist/types/schema-query.d.ts +12 -0
  131. package/dist/types/server.d.ts +1 -4
  132. package/dist/types/signals.d.ts +25 -0
  133. package/dist/types/site.d.ts +0 -1
  134. package/dist/types/tooling.d.ts +115 -0
  135. package/dist/types/trusted-functions.d.ts +29 -0
  136. package/dist/types/types.d.ts +71 -7
  137. package/dist/types/typescript-authoring.d.ts +12 -0
  138. package/dist/types/vercel.d.ts +3 -1
  139. package/dist/types/verify-deployment.d.ts +47 -0
  140. package/dist/types.js +37 -5
  141. package/dist/typescript-authoring.js +142 -0
  142. package/dist/vercel.js +4 -3
  143. package/dist/verify-deployment.js +270 -0
  144. package/docs/AI-AUTHORING.md +232 -15
  145. package/docs/AWS.md +4 -4
  146. package/docs/BEST-PRACTICES.md +3 -2
  147. package/docs/BULK.md +79 -0
  148. package/docs/CAPABILITIES.md +192 -0
  149. package/docs/CAPACITY.md +129 -32
  150. package/docs/CI.md +142 -0
  151. package/docs/CLOUDFLARE.md +1 -2
  152. package/docs/COMPLIANCE.md +6 -9
  153. package/docs/CONDITIONS.md +74 -0
  154. package/docs/DEPLOYMENT-CHECKS.md +108 -0
  155. package/docs/EGRESS.md +125 -0
  156. package/docs/EXTENSIONS.md +398 -0
  157. package/docs/FRAMEWORK.md +198 -0
  158. package/docs/FUNCTION-SECURITY.md +129 -32
  159. package/docs/INSTALL.md +45 -12
  160. package/docs/INTERCHANGE.md +134 -0
  161. package/docs/LOAD-TESTING.md +4 -4
  162. package/docs/MIDDLEWARE-EXAMPLES.md +75 -0
  163. package/docs/MIDDLEWARE.md +29 -16
  164. package/docs/MONITORING.md +2 -19
  165. package/docs/NEXT-PHASE-PLAN.md +98 -0
  166. package/docs/NEXT-STEPS.md +634 -0
  167. package/docs/OBSERVABILITY.md +11 -18
  168. package/docs/OPEN-DECISIONS.md +212 -0
  169. package/docs/OPERATIONAL-PROOF.md +30 -31
  170. package/docs/OPERATIONS.md +29 -35
  171. package/docs/PLUGINS.md +37 -0
  172. package/docs/POLICIES.md +23 -309
  173. package/docs/PRERENDER.md +41 -1
  174. package/docs/PROJECT-DIRECTION.md +75 -8
  175. package/docs/PROVIDER-VERIFICATION.md +84 -0
  176. package/docs/READINESS.md +21 -1
  177. package/docs/README.md +87 -34
  178. package/docs/RECIPES.md +99 -0
  179. package/docs/RELEASE-READINESS.md +57 -35
  180. package/docs/RELEASE-SECURITY.md +116 -7
  181. package/docs/RESILIENCE.md +16 -15
  182. package/docs/ROUTING.md +8 -10
  183. package/docs/SANDBOX-REVIEW.md +19 -6
  184. package/docs/SCAFFOLDING.md +0 -2
  185. package/docs/SECURITY-AUDIT.md +41 -1
  186. package/docs/SPECIFICATION.md +150 -29
  187. package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +287 -0
  188. package/docs/SPIKE-BUSINESS-SUITE.md +1021 -0
  189. package/docs/SPIKE-CORE-LAYERING.md +337 -0
  190. package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +209 -0
  191. package/docs/SPIKE-EXTENSION-MODEL.md +419 -0
  192. package/docs/SPIKE-EXTENSIONS.md +6 -0
  193. package/docs/SPIKE-LAMBDA-COMPILE.md +201 -0
  194. package/docs/SPIKE-MONOREPO.md +322 -0
  195. package/docs/STANDARDS.md +150 -142
  196. package/docs/STARTERS.md +21 -1
  197. package/docs/STATIC.md +94 -0
  198. package/docs/TOOLING.md +295 -0
  199. package/docs/TUNNELS.md +0 -3
  200. package/docs/TYPESCRIPT-AUTHORING.md +82 -0
  201. package/docs/TYPESCRIPT.md +25 -4
  202. package/docs/USABILITY-REVIEW.md +129 -0
  203. package/docs/VERCEL.md +4 -5
  204. package/docs/VERSION-ALIGNMENT.md +205 -0
  205. package/docs/YAML-GUIDE.md +15 -479
  206. package/docs/YAML-REFERENCE.md +143 -22
  207. package/docs/policies/agents.md +1 -1
  208. package/docs/policies/cache.md +13 -0
  209. package/docs/policies/contract.md +52 -0
  210. package/docs/policies/hardened.md +56 -0
  211. package/docs/policies/interoperability.md +169 -0
  212. package/docs/policies/operations.md +45 -0
  213. package/docs/yaml/assets.md +36 -0
  214. package/docs/yaml/conditions.md +20 -0
  215. package/docs/yaml/functions.md +160 -0
  216. package/docs/yaml/middleware.md +29 -0
  217. package/docs/yaml/organization.md +74 -0
  218. package/docs/yaml/policies.md +37 -0
  219. package/docs/yaml/redirects.md +64 -0
  220. package/docs/yaml/responses.md +57 -0
  221. package/docs/yaml/site.md +24 -0
  222. package/examples/assets/example.yaml +17 -0
  223. package/examples/aws/example.yaml +20 -0
  224. package/examples/cloudflare/example.yaml +19 -0
  225. package/examples/compliance/example.yaml +11 -0
  226. package/examples/conditions/README.md +12 -0
  227. package/examples/conditions/example.yaml +19 -0
  228. package/examples/conditions/tests/requests.json +13 -0
  229. package/examples/conditions/urlcode.yaml +24 -0
  230. package/examples/cookbook/README.md +8 -4
  231. package/examples/cookbook/example.yaml +17 -0
  232. package/examples/cookbook/functions/catalog.mjs +3 -0
  233. package/examples/cookbook/functions/fail.mjs +4 -0
  234. package/examples/cookbook/functions/items.mjs +3 -0
  235. package/examples/cookbook/functions/profile.mjs +3 -0
  236. package/examples/cookbook/functions/resource.mjs +3 -0
  237. package/examples/cookbook/functions/status.mjs +3 -0
  238. package/examples/cookbook/middleware/auth.mjs +48 -0
  239. package/examples/cookbook/middleware/body.mjs +15 -0
  240. package/examples/cookbook/middleware/bucket.mjs +29 -0
  241. package/examples/cookbook/middleware/cors.mjs +21 -0
  242. package/examples/cookbook/middleware/debug.mjs +13 -0
  243. package/examples/cookbook/middleware/envelope.mjs +11 -0
  244. package/examples/cookbook/middleware/errors.mjs +11 -0
  245. package/examples/cookbook/middleware/etag.mjs +18 -0
  246. package/examples/cookbook/middleware/locale.mjs +20 -0
  247. package/examples/cookbook/middleware/maintenance.mjs +10 -0
  248. package/examples/cookbook/middleware/methods.mjs +15 -0
  249. package/examples/cookbook/middleware/negotiate.mjs +20 -0
  250. package/examples/cookbook/middleware/referer.mjs +12 -0
  251. package/examples/cookbook/middleware/request-id.mjs +16 -0
  252. package/examples/cookbook/route-index.json +676 -0
  253. package/examples/cookbook/routes/middleware.yaml +126 -0
  254. package/examples/cookbook/tests/requests.json +526 -0
  255. package/examples/cookbook/urlcode.yaml +1 -0
  256. package/examples/egress/README.md +22 -0
  257. package/examples/egress/example.yaml +19 -0
  258. package/examples/egress/urlcode.yaml +19 -0
  259. package/examples/extensions/README.md +7 -0
  260. package/examples/extensions/example.yaml +21 -0
  261. package/examples/extensions/urlcode.yaml +25 -0
  262. package/examples/monitoring/example.yaml +8 -0
  263. package/examples/prerender/README.md +2 -2
  264. package/examples/prerender/example.yaml +16 -0
  265. package/examples/provider-conformance/README.md +12 -0
  266. package/examples/provider-conformance/example.yaml +14 -0
  267. package/examples/provider-conformance/urlcode.yaml +34 -0
  268. package/examples/tunnel/example.yaml +8 -0
  269. package/examples/vercel/example.yaml +19 -0
  270. package/llms-full.txt +3084 -0
  271. package/llms.txt +61 -21
  272. package/package.json +36 -7
  273. package/packaging/claude-plugin/.claude-plugin/plugin.json +19 -0
  274. package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +122 -0
  275. package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +108 -0
  276. package/recipes/authenticated-json-api/README.md +51 -0
  277. package/recipes/authenticated-json-api/functions/profile.mjs +5 -0
  278. package/recipes/authenticated-json-api/recipe.yaml +34 -0
  279. package/recipes/authenticated-json-api/tests/requests.json +39 -0
  280. package/recipes/authenticated-json-api/urlcode.yaml +12 -0
  281. package/recipes/contact-form/README.md +25 -0
  282. package/recipes/contact-form/functions/contact.mjs +17 -0
  283. package/recipes/contact-form/recipe.yaml +33 -0
  284. package/recipes/contact-form/tests/requests.json +47 -0
  285. package/recipes/contact-form/urlcode.yaml +18 -0
  286. package/recipes/cors-api/README.md +16 -0
  287. package/recipes/cors-api/functions/items.mjs +3 -0
  288. package/recipes/cors-api/middleware/cors.mjs +21 -0
  289. package/recipes/cors-api/recipe.yaml +26 -0
  290. package/recipes/cors-api/tests/requests.json +65 -0
  291. package/recipes/cors-api/urlcode.yaml +12 -0
  292. package/recipes/health-page/README.md +13 -0
  293. package/recipes/health-page/recipe.yaml +23 -0
  294. package/recipes/health-page/tests/requests.json +36 -0
  295. package/recipes/health-page/urlcode.yaml +19 -0
  296. package/recipes/json-api/README.md +6 -0
  297. package/recipes/json-api/functions/echo.mjs +3 -0
  298. package/recipes/json-api/recipe.yaml +25 -0
  299. package/recipes/json-api/tests/requests.json +34 -0
  300. package/recipes/json-api/urlcode.yaml +12 -0
  301. package/recipes/middleware/README.md +34 -0
  302. package/recipes/middleware/functions/catalog.mjs +3 -0
  303. package/recipes/middleware/functions/fail.mjs +4 -0
  304. package/recipes/middleware/functions/items.mjs +3 -0
  305. package/recipes/middleware/functions/profile.mjs +3 -0
  306. package/recipes/middleware/functions/resource.mjs +3 -0
  307. package/recipes/middleware/functions/status.mjs +3 -0
  308. package/recipes/middleware/middleware/auth.mjs +48 -0
  309. package/recipes/middleware/middleware/body.mjs +15 -0
  310. package/recipes/middleware/middleware/bucket.mjs +29 -0
  311. package/recipes/middleware/middleware/cors.mjs +21 -0
  312. package/recipes/middleware/middleware/debug.mjs +13 -0
  313. package/recipes/middleware/middleware/envelope.mjs +11 -0
  314. package/recipes/middleware/middleware/errors.mjs +11 -0
  315. package/recipes/middleware/middleware/etag.mjs +18 -0
  316. package/recipes/middleware/middleware/locale.mjs +20 -0
  317. package/recipes/middleware/middleware/maintenance.mjs +10 -0
  318. package/recipes/middleware/middleware/methods.mjs +15 -0
  319. package/recipes/middleware/middleware/negotiate.mjs +20 -0
  320. package/recipes/middleware/middleware/referer.mjs +12 -0
  321. package/recipes/middleware/middleware/request-id.mjs +16 -0
  322. package/recipes/middleware/public/guide.txt +1 -0
  323. package/recipes/middleware/recipe.yaml +50 -0
  324. package/recipes/middleware/tests/requests.json +528 -0
  325. package/recipes/middleware/urlcode.yaml +127 -0
  326. package/recipes/protected-download/README.md +22 -0
  327. package/recipes/protected-download/files/report.txt +1 -0
  328. package/recipes/protected-download/recipe.yaml +31 -0
  329. package/recipes/protected-download/tests/requests.json +32 -0
  330. package/recipes/protected-download/urlcode.yaml +15 -0
  331. package/recipes/redirect/README.md +7 -0
  332. package/recipes/redirect/recipe.yaml +25 -0
  333. package/recipes/redirect/tests/requests.json +19 -0
  334. package/recipes/redirect/urlcode.yaml +9 -0
  335. package/recipes/static-plus-api/README.md +15 -0
  336. package/recipes/static-plus-api/functions/info.mjs +3 -0
  337. package/recipes/static-plus-api/public/assets/index.html +3 -0
  338. package/recipes/static-plus-api/public/assets/site.css +1 -0
  339. package/recipes/static-plus-api/public/index.html +8 -0
  340. package/recipes/static-plus-api/recipe.yaml +29 -0
  341. package/recipes/static-plus-api/tests/requests.json +56 -0
  342. package/recipes/static-plus-api/urlcode.yaml +23 -0
  343. package/recipes/typescript/README.md +8 -0
  344. package/recipes/typescript/functions/hello.ts +5 -0
  345. package/recipes/typescript/recipe.yaml +23 -0
  346. package/recipes/typescript/tests/requests.json +18 -0
  347. package/recipes/typescript/urlcode.yaml +5 -0
  348. package/recipes/webhook-receiver/README.md +20 -0
  349. package/recipes/webhook-receiver/functions/receive.mjs +16 -0
  350. package/recipes/webhook-receiver/recipe.yaml +27 -0
  351. package/recipes/webhook-receiver/tests/requests.json +59 -0
  352. package/recipes/webhook-receiver/urlcode.yaml +23 -0
  353. package/schemas/recipe.schema.json +139 -0
  354. package/schemas/urlcode.schema.json +659 -110
  355. package/skills/urlcode/SKILL.md +119 -0
  356. package/starters/default/.github/workflows/urlcode.yml +23 -0
  357. package/starters/default/.mcp.json +12 -0
  358. package/starters/default/AGENTS.md +79 -0
  359. package/starters/default/urlcode.yaml +0 -1
  360. package/dist/link-api.js +0 -136
  361. package/dist/link-cli.js +0 -141
  362. package/dist/link-events.js +0 -76
  363. package/dist/link-records.js +0 -31
  364. package/dist/link-store-worker.js +0 -150
  365. package/dist/link-store.js +0 -250
  366. package/dist/management-policy.js +0 -41
  367. package/dist/sqlite-version.js +0 -6
  368. package/dist/types/link-api.d.ts +0 -30
  369. package/dist/types/link-cli.d.ts +0 -36
  370. package/dist/types/link-events.d.ts +0 -27
  371. package/dist/types/link-records.d.ts +0 -11
  372. package/dist/types/link-store-worker.d.ts +0 -1
  373. package/dist/types/link-store.d.ts +0 -130
  374. package/dist/types/management-policy.d.ts +0 -9
  375. package/dist/types/sqlite-version.d.ts +0 -1
  376. package/docs/DYNAMIC-LINKS.md +0 -561
  377. package/docs/MANAGEMENT-SECURITY.md +0 -82
  378. package/examples/live-links/README.md +0 -11
  379. package/examples/live-links/tests/requests.json +0 -6
  380. package/examples/live-links/urlcode.yaml +0 -16
@@ -0,0 +1,634 @@
1
+ # Next steps: closing the gaps
2
+
3
+ Status: plan written 2026-09-18 from the cleanup, the
4
+ [usability review](USABILITY-REVIEW.md) and the AI-first framework brief.
5
+ Each item says what it fixes, where the work is, how it is proven, and its
6
+ size (S: a day, M: a week, L: more). Phases can overlap; order inside a phase
7
+ is the recommended sequence. The [roadmap](../ROADMAP.md) owns what ships;
8
+ this page owns how the gaps close.
9
+
10
+ ## The rule everything below serves
11
+
12
+ > Your AI should build your application, not your framework.
13
+
14
+ Agents rebuild the same routing, validation, middleware, auth plumbing,
15
+ policies, admin patterns and deployment glue on every project, and the person
16
+ ends up owning it. URLCode's job is a small, deterministic, portable vocabulary
17
+ in readable YAML, so generated code goes to the part that is the application.
18
+ The agent describes what; the runtime owns how. The three tests that decide
19
+ what gets built are in [project direction](PROJECT-DIRECTION.md#why-your-ai-should-build-your-application-not-your-framework):
20
+ the boundary test (do agents generate this across unrelated projects?), the
21
+ feature test (does it reduce what the agent must know, generate, debug or
22
+ maintain?) and the evidence test (measured repetition, not a feature list).
23
+
24
+ The order below follows from that. **Prove the thesis before building on
25
+ it.** Phase 0 is the benchmark; if it shows a large saving, the rest is worth
26
+ the work, and if it shows a small one, the abstraction is not doing enough yet
27
+ and the next phases change. Nothing here weakens the security model: a
28
+ `sandbox: true` route's isolation stays exactly as strict, grants stay
29
+ operator-owned regardless of a route's trust setting, agents cannot
30
+ self-authorize, unsupported behavior fails with the route named, and
31
+ inspection tooling never becomes a privilege escalation path.
32
+
33
+ **Status 2026-09-19 (sequence).** Most of Phases 1, 3 and 4 landed before the
34
+ Phase 0 benchmark produced a single model-backed run. The instrument is built
35
+ and tested (`benchmarks/agent/harness.ts`, `benchmarks/agent/count-lines.ts`,
36
+ `benchmarks/agent/adapters/anthropic.ts`), but `benchmarks/agent/runs/` holds
37
+ only `baseline.json`, a stub record, so the application-specific code ratio is
38
+ still unmeasured and the re-prioritization 0.1 describes has never been
39
+ exercised. This is an observation about the order the work happened in, not a
40
+ change of priorities: Phase 0 still owns the evidence, and Phase 5 is still how
41
+ each shipped phase gets measured. Items below are marked from the source at this
42
+ revision.
43
+
44
+ ## Phase 0: prove the thesis (M, core `benchmarks/agent/`, before anything else)
45
+
46
+ ### 0.1 The agent benchmark — partly done
47
+
48
+ Done 2026-09-19, the instrument only: the runner, the counting rule, the tasks
49
+ and the storage format exist and are tested — `benchmarks/agent/harness.ts`
50
+ (`runArm`, `summarize`, `securityChecklist`, `writeRun`),
51
+ `benchmarks/agent/count-lines.ts` (`classify`, `codeRatio`),
52
+ `benchmarks/agent/adapters.ts` (`selectAdapter`, `stubAdapter`) with
53
+ `benchmarks/agent/adapters/anthropic.ts`, ten task directories under
54
+ `benchmarks/agent/tasks/`, both arm preambles under `benchmarks/agent/prompts/`,
55
+ `npm run benchmark:agent`, and `test/agent-benchmark.test.ts` ("the counting
56
+ rule: functions and declared modules are the idea, everything else is
57
+ plumbing"). The measurement is not done: `benchmarks/agent/runs/` holds only
58
+ `baseline.json`, whose `model` is `stub`, and `benchmarks/agent/README.md`
59
+ states that no number there is evidence until a stored run with a real model
60
+ adapter backs it. Running the tasks in both arms against a real model, storing
61
+ the runs and publishing only what they support remains the work below.
62
+
63
+ Fixes: there is no evidence that URLCode saves agent effort, and the whole
64
+ plan depends on it.
65
+
66
+ - Work: ten representative tasks first, twenty later: redirect service, URL
67
+ shortener, webhook receiver, small JSON API, static site plus API,
68
+ OAuth-protected internal app, CRUD backend, admin backend, file and download
69
+ service, API proxy, contact form, authenticated endpoint. Each task has a
70
+ natural-language prompt, an acceptance test suite that is the same for both
71
+ arms, and two harness configurations: conventional (the agent picks its own
72
+ stack) and URLCode (the agent has the skill, `urlcode context` and the
73
+ recipes). Run each arm several times with the same model. Capture input,
74
+ output and total tokens, generated lines and files, agent turns, retries,
75
+ failures, wall time, tests passed, and a checklist of obvious security
76
+ mistakes. Store raw runs under `benchmarks/agent/runs/` with model, date and
77
+ harness version.
78
+ - The headline metric is the **application-specific code ratio**: generated
79
+ lines that are the idea versus generated lines that are plumbing, counted
80
+ by a documented rule (files under `functions/` and the application's own
81
+ modules count as the idea; routing, auth, sessions, middleware, validation,
82
+ headers, static serving, deployment and test scaffolding count as plumbing).
83
+ Report it beside tokens and turns.
84
+ - Proof: a reproducible runner; a README that states exactly what the
85
+ numbers are and are not. Publish only what the stored runs support.
86
+ A result like "same application, 65 percent fewer generated lines and half
87
+ the tokens" is the story; a result like 8 percent means the vocabulary is
88
+ too small or too hard to discover, and Phases 1, 3 and 4 are re-prioritized
89
+ from what the runs show the agent still had to write.
90
+
91
+ ### 0.2 Authoring regression evals (S, once 0.1 exists) — done
92
+
93
+ Done 2026-09-19: the five prompts are `benchmarks/agent/evals/*.yaml`
94
+ (`add-redirect`, `add-authenticated-endpoint`, `serve-directory`,
95
+ `add-middleware`, `create-webhook-endpoint`), scored against the eight criteria
96
+ by `scoreEval` and `summarizeEvals` in `benchmarks/agent/harness.ts`, gated
97
+ against `benchmarks/agent/runs/baseline.json` by `benchmarks/agent/gate.ts`, and
98
+ run weekly by `.github/workflows/evals.yml`. Caveat: the committed baseline is a
99
+ stub record and the scheduled job skips cleanly when no model key is configured,
100
+ so the recorded pass rate proves the pipeline, not a model's behavior.
101
+
102
+ Prompts for common requests ("add a redirect", "add an authenticated
103
+ endpoint", "serve this directory", "add middleware", "create a webhook
104
+ endpoint") scored on: native functionality chosen, valid YAML, no unsupported
105
+ fields, no unnecessary JavaScript, no boundary violations, tests written,
106
+ validation run, provider limits respected. Run on a schedule; a new feature
107
+ must not lower the pass rate.
108
+
109
+ ## Phase 1: agent discovery (S each, no contract changes)
110
+
111
+ What exists: `llms.txt`, `AGENTS.md` (for working on the runtime),
112
+ `docs/AI-AUTHORING.md`, `docs/FRAMEWORK.md`, the JSON Schema, the generated
113
+ field reference, the cookbook, recipes, `validate`/`test`/`audit`, MCP
114
+ read-side tools (`inspectProject`, `validateProject`, `explainRoute`,
115
+ `getCapabilities`, `previewImport`/`previewExport`, `listRecipes`/`showRecipe`).
116
+ Keep all of it. The gap is that an agent still has to read documents to find
117
+ facts the runtime already knows.
118
+
119
+ ### 1.1 Application-level `AGENTS.md` from `urlcode init` (S, core) — done
120
+
121
+ Done 2026-09-19: `renderAgentsGuide` in `src/agents-guide.ts` generates the file
122
+ from the installed capability catalog, `initProject` in `src/authoring.ts`
123
+ writes it, and `initProjectWith` in `src/init-with.ts` calls `initProject`, so
124
+ `init --with` writes it into the project directory too; `renderMcpConfig` writes
125
+ `.mcp.json` beside it. `test/cli.test.ts` asserts the commands, the packaged
126
+ skill path and the MCP tool names the file lists, its length bound, and that the
127
+ committed `starters/default/AGENTS.md` equals what `init` generates from this
128
+ runtime. Caveat: the public `urlcode-template` copy is outside this repository
129
+ and is not checked here; `npm run check:downstream-skills` stays advisory.
130
+
131
+ Fixes: a project made with URLCode is not self-describing to Claude Code,
132
+ Codex or any repository-aware agent. Today's `AGENTS.md` explains how to work
133
+ on the runtime, not on an application.
134
+
135
+ - Work: `urlcode init` (and `init --with`, 2.3 below) writes `AGENTS.md` into
136
+ the project: inspect `urlcode.yaml` first; run `urlcode context` (1.3);
137
+ check capabilities and search recipes before writing code; prefer native
138
+ handlers; never recreate routing, validation, middleware, policies or
139
+ authentication the runtime provides; validate, test, audit; report
140
+ unsupported requirements instead of inventing fields; never create or
141
+ approve grants. Generated from the installed runtime's capability list so
142
+ it names only what that version has.
143
+ - Proof: starter test asserts the file exists and lists the commands; the
144
+ public `urlcode-template` gets the same file.
145
+
146
+ ### 1.2 `llms-full.txt` and clean Markdown for the docs site (S, core; S, urlcode-docs) — partly done
147
+
148
+ Done 2026-09-19, the core side: `scripts/build-llms-full.ts` generates
149
+ `llms-full.txt` from 15 authoring documents in reading order with a table of
150
+ contents and a token estimate in the file header, `npm run check` runs it with
151
+ `--check`, and `test/llms-full.test.ts` covers determinism, rejection of a stale
152
+ copy and the conservative link rewriting. The docs-site half is not done here:
153
+ serving `/docs/<page>.md` beside `/docs/<page>` belongs to `urlcode-docs` and is
154
+ not verifiable from this repository.
155
+
156
+ Fixes: `llms.txt` must stay a compact index, but an agent that wants complete
157
+ context has to fetch forty files.
158
+
159
+ - Work: `scripts/build-llms-full.ts` concatenates the authoring documents
160
+ (framework, AI authoring, YAML guide, field reference, specification,
161
+ routing, HTTP, middleware, assets, policies, extensions) in
162
+ reading order with a table of contents; checked in `npm run check` for
163
+ staleness like the field reference. The docs site serves `/docs/<page>.md`
164
+ beside `/docs/<page>` so agents never parse HTML. Do not adopt `agents.txt`
165
+ or similar until a convention settles.
166
+ - Proof: `npm run check` fails on a stale `llms-full.txt`; token estimate
167
+ recorded in the file header.
168
+
169
+ ### 1.3 `urlcode context` with a token budget (M, core) — done
170
+
171
+ Done 2026-09-19: `urlcode context [--project] [--budget] [--json] [--stats]` in
172
+ `src/cli.ts` over `buildContext`, `renderContext`, `estimateTokens` and
173
+ `documentationTokens` in `src/context.ts`, derived from the compiled project and
174
+ the capability catalog; `--stats` writes the estimate to stderr so stdout stays
175
+ parseable. `test/context.test.ts` covers the cookbook and starter summaries,
176
+ byte-identical output across runs, the fixed budget drop order with the estimate
177
+ never exceeded, the CLI's YAML and JSON forms, and MCP `get_context`
178
+ (`src/mcp.ts`) returning the same data read-only.
179
+
180
+ Fixes: the central gap. An agent needs a handful of facts about this project
181
+ and this runtime and spends tens of thousands of tokens reading documentation
182
+ to get them.
183
+
184
+ - Work: `urlcode context [--project DIR] [--budget N] [--json] [--stats]`
185
+ emits deterministic YAML (or JSON): runtime and schema version; project
186
+ summary (route count, handlers used, extensions declared, policies in
187
+ effect, custom functions and middleware files, bindings requested);
188
+ constraints that matter for generation (no guest network or Node APIs, no
189
+ regex routes, path shape, one handler per route, no interpolation); target
190
+ support for the project's features; and the exact `validate`/`test`/`audit`
191
+ commands with the intentional route count filled in. `--budget` drops
192
+ sections in a fixed order (per-route detail, then target table, then
193
+ constraints prose) until the estimate fits; estimation is a documented
194
+ characters-per-token approximation, no tokenizer dependency. `--stats`
195
+ prints the estimated size of the documentation corpus versus the emitted
196
+ context, labeled as estimates. Derived from the compiled project and the
197
+ capability catalog, never from prose.
198
+ - Proof: snapshot tests for the cookbook and the starter; a test that the
199
+ same project yields byte-identical output twice; budget test that output
200
+ never exceeds the estimate; MCP tool `get_context` returns the same data.
201
+
202
+ ### 1.4 Capability and schema fragment queries (M, core) — done
203
+
204
+ Done 2026-09-19: `urlcode capabilities <name>` and `urlcode schema <path>` in
205
+ `src/cli.ts` over `getCapability` in `src/capability-query.ts` and
206
+ `getSchemaFragment`/`schemaPathNames` in `src/schema-query.ts`, both re-exported
207
+ from `src/tooling.ts` and served as MCP `get_capability` and `get_schema`
208
+ (`src/mcp.ts`). `test/capability-query.test.ts` asserts that every catalog name
209
+ resolves with a valid, size-bounded fragment, that every schema path yields a
210
+ valid inline fragment and an unknown path lists the valid names, that entries
211
+ report bundled usage, grants and refusals from existing data, and that the CLI
212
+ fails closed on unknown names.
213
+
214
+ Fixes: `urlcode capabilities` reports target support per handler; an agent
215
+ cannot ask "what does `throttle` accept, where does it run, which recipe
216
+ shows it" or "give me only the schema for `redirect`".
217
+
218
+ - Work: `urlcode capabilities <name> [--json]` extends the existing catalog
219
+ entry with the schema fragment, constraints, required grants, per-target
220
+ support, known unsupported behavior and related recipes and examples.
221
+ `urlcode schema <path>` (`route`, `redirect`, `middleware`, `policies.cache`,
222
+ `extensions`) returns only that fragment of `schemas/urlcode.schema.json`,
223
+ resolving `$ref`s. Both derive from the schema and the catalog; nothing is
224
+ hand-maintained. MCP gains `get_capability` and `get_schema`.
225
+ - Proof: a test that every capability name resolves and every fragment is
226
+ valid JSON Schema; a size test that no fragment exceeds a fixed byte cap.
227
+
228
+ ### 1.5 The URLCode agent skill (S, core `skills/urlcode/`) — done
229
+
230
+ Done 2026-09-19: `skills/urlcode/SKILL.md` is 104 lines, ships in the package,
231
+ and is named by the generated `AGENTS.md` through `skillPath` in
232
+ `src/agents-guide.ts`; `test/cli.test.ts` asserts the reference and
233
+ `test/release.test.ts` asserts the packaged starter copy. Caveat: the
234
+ with-and-without comparison in the proof line waits on 0.1 — the URLCode arm
235
+ preamble (`benchmarks/agent/prompts/urlcode.md`) assumes the skill, and no model
236
+ run has been stored to compare against.
237
+
238
+ Fixes: agents that support skills have no packaged instruction for URLCode.
239
+
240
+ - Work: a small skill (under 150 lines) that teaches the loop: recognize a
241
+ project by `urlcode.yaml`; run `urlcode context`; query a capability;
242
+ search recipes and examples; prefer YAML; write minimal functions; validate,
243
+ test, audit; respect grants and never approve one; report unsupported
244
+ requirements. It tells the agent how to retrieve the minimum, and links
245
+ nothing else. Ship in the package under `skills/` and reference it from
246
+ the generated `AGENTS.md`.
247
+ - Proof: the authoring evals in Phase 5 run with and without the skill.
248
+
249
+ ## Phase 2: make the ladder real (no contract changes)
250
+
251
+ ### 2.1 Publish the three extension packages (decision, S) — done
252
+
253
+ Done 2026-09-18: `@jimhoyd/urlcode-ui`, `-auth` and `-admin` are on npm as
254
+ `0.1.0-alpha.x` against core `0.4.0-alpha.1`. The alpha caveat stays: source
255
+ complete, independent review, deployment evidence and accessibility
256
+ assessment pending ([issue 58](https://github.com/jimhoyd-com/urlcode/issues/58)).
257
+
258
+ Fixed: every install step in the "add accounts" row of the usability review
259
+ except the revision pin. Before this, a person or an agent cloned three private
260
+ repositories, ran `pack-sources.mjs` with four paths and a SHA, and installed
261
+ four tarballs.
262
+
263
+ - Decide: publish `@jimhoyd/urlcode-ui`, `-auth`, `-admin` as `0.1.0-alpha.N`
264
+ to npm with provenance, from tags on `main`, keeping the "private until
265
+ reviewed" caveats in each README and status file. An alpha on npm is a
266
+ distribution channel, not an endorsement.
267
+ - Work: copy core's `release.yml` shape into each repo (candidate build, npm
268
+ audit, `npm pack`, attest, publish behind a repository variable). Drop
269
+ `"private": true` only in the release commit. Set real peer ranges.
270
+ - Proof: a clean directory installs core and auth from the registry and runs
271
+ `urlcode-auth init`, `bootstrap`, `serve`; admin's
272
+ `scripts/clean-project-acceptance.mjs` runs against the published tarballs.
273
+
274
+ ### 2.2 One place for peer revisions (S)
275
+
276
+ Fixes: three disagreeing lists of verified peer commits (the CI workflows,
277
+ `ACCEPTANCE.md` in auth and admin, the pack script's core-revision check).
278
+
279
+ - Work: `peers.json` in auth and admin; the workflows read it; the pack
280
+ script defaults from it; `ACCEPTANCE.md` links to it. 2.1 has shipped, so
281
+ the published versions (`@jimhoyd/urlcode@0.4.0-alpha.1`,
282
+ `@jimhoyd/urlcode-ui@0.1.0-alpha.4`) replace the SHAs and the file can go.
283
+
284
+ ### 2.3 `urlcode init --with auth,admin,ui` (M, core plus each extension) — done
285
+
286
+ Done 2026-09-19: `--with` is parsed in `src/cli.ts` and implemented by
287
+ `parseWithNames`, `loadScaffold` and `initProjectWith` in `src/init-with.ts`,
288
+ which resolves `@jimhoyd/urlcode-<name>` from the invoking directory, refuses a
289
+ missing package or one without a `scaffold` export before writing anything,
290
+ merges the fragments into the starter through a last include, and writes one
291
+ `host.mjs` (`renderHost`), one `README.md` (`renderReadme`), `.mcp.json` and the
292
+ `AGENTS.md` from 1.1, printing the `inspectExtensionRevision` digest for
293
+ pinning. `test/init-with.test.ts` uses a fake `@jimhoyd/urlcode-<name>` package
294
+ in a temporary `node_modules` and, when companion checkouts are present,
295
+ composes the real auth and admin scaffolds. Caveat: each extension's own
296
+ `scaffold` export and admin's clean-project acceptance live in those
297
+ repositories and are not verified here.
298
+
299
+ Fixes: three initializers with three directory conventions; no single command
300
+ produces the layered project the framework page describes.
301
+
302
+ - Work in core: `--with a,b,c` resolves the installed
303
+ `@jimhoyd/urlcode-<name>` from the invoking directory and calls its
304
+ `scaffold` export (a small documented contract returning a YAML fragment,
305
+ host imports and entries, and a README section). Core merges fragments into
306
+ `urlcode.yaml`, writes one `host.mjs`, one `README.md`, the `AGENTS.md`
307
+ from 1.1, and prints the `inspectExtensionRevision` SHA. A missing package
308
+ refuses with the install command; core never imports the packages at build
309
+ time. Each extension exports `scaffold` built from its existing `init`.
310
+ - Proof: a core test with a fake `@jimhoyd/urlcode-demo` package in a temp
311
+ `node_modules`; each extension tests that its `scaffold` output validates
312
+ with core; admin's clean-project acceptance uses the new command.
313
+
314
+ ### 2.4 Print extension schemas: `urlcode extensions` (M, core) — partly done
315
+
316
+ Done 2026-09-19, the command and the MCP tool: `urlcode extensions
317
+ [--host-file] [--json]` in `src/cli.ts` over `describeExtensions` in
318
+ `src/tooling.ts` prints each registration's name, version, targets,
319
+ configuration schema, policy schema, mounts, policy routes and revision-pin
320
+ verdict, and without a host file names the declared extensions and says schemas
321
+ need one. The same function is exported from `src/index.ts` and served as MCP
322
+ `get_extensions`, offered only when the operator started the server with a host
323
+ file, which `test/extensions.test.ts` asserts against the
324
+ `examples/extensions` registry. Not done: the fold-in. `urlcode context
325
+ --host-file` reports host extension names and a plugin count rather than their
326
+ schemas (`buildContext` in `src/context.ts`), and `urlcode capabilities auth`
327
+ takes no host file, so registered contracts are not part of either view.
328
+
329
+ Fixes: an agent cannot discover what `extensions.auth.config` accepts without
330
+ reading auth's source; `urlcode mcp` cannot serve it.
331
+
332
+ - Work: `urlcode extensions --host-file … [--json]` loads the host file as
333
+ `validate` does and prints each registration's name, contract version,
334
+ targets, configuration schema and policy schema; the same data through the
335
+ SDK and an MCP tool. Folds into `urlcode context` and `capabilities auth`
336
+ when a host file is given.
337
+ - Proof: test against the `examples/extensions` demo registry.
338
+
339
+ ## Phase 3: retrieval instead of reading (M each, core)
340
+
341
+ ### 3.1 Recipes as the vocabulary of common behavior — done
342
+
343
+ Done 2026-09-19: every bundled recipe carries `recipe.yaml`, and the catalog is
344
+ now ten — `authenticated-json-api`, `contact-form`, `cors-api`, `health-page`,
345
+ `json-api`, `middleware`, `protected-download`, `static-plus-api`, `typescript`
346
+ and `webhook-receiver` — which covers the seven this item names.
347
+ `urlcode recipes search|show|add` runs through `src/ecosystem-cli.ts` over
348
+ `searchRecipes` and `showRecipe` in `src/recipes.ts`, matching id, description,
349
+ tags and capabilities locally with no service, and MCP gained `search_recipes`.
350
+ `npm run check` validates the metadata and requires its derived fields to equal
351
+ the capability preflight (`checkCatalog` and `derivedDifferences` in
352
+ `scripts/check.ts`), and `test/recipes.test.ts` asserts a search hit per recipe
353
+ and that every recipe validates, passes its fixtures and audits with its
354
+ declared route count. Growing the catalog from the Phase 6 repetition log has
355
+ not started; the current ten are the seed list above.
356
+
357
+ Fixes: four bundled recipes with a README each and no metadata; an agent
358
+ cannot search them, and nothing tells it to look before generating.
359
+
360
+ - Work: every recipe gains `recipe.yaml`: `id`, `description`,
361
+ `capabilities`, `tags`, `complexity`, required external services, grants,
362
+ configuration inputs, files, target compatibility, tests and expected
363
+ behavior. `urlcode recipes search <text>` matches id, description, tags and
364
+ capabilities locally (no AI service); `show` prints the metadata first.
365
+ MCP gains `search_recipes`. Grow the catalog from observed repetition (see
366
+ Phase 6), starting with: authenticated JSON endpoint, webhook receiver with
367
+ signature check via `proxy`/signals, contact form to a signal, protected
368
+ download, health and readiness page, CORS API, static site with API.
369
+ - Proof: schema for `recipe.yaml` checked in `npm run check`; a search test
370
+ per recipe; every recipe still validates, tests and audits.
371
+
372
+ ### 3.2 Examples become searchable the same way — done
373
+
374
+ Done 2026-09-19: each example carries `example.yaml` in the recipe metadata
375
+ shape, `urlcode examples search <text>` runs through `src/ecosystem-cli.ts` over
376
+ `searchExamples` in `src/examples.ts` and names the smallest runnable match with
377
+ its route, and the cookbook's per-route tags are generated into
378
+ `examples/cookbook/route-index.json` by `scripts/build-cookbook-index.ts`,
379
+ checked by `npm run check`. `scripts/check.ts` holds example metadata to the
380
+ same preflight as recipes, and `test/recipes.test.ts` ("examples carry the same
381
+ metadata shape and search returns the smallest runnable match with its route")
382
+ is the search test.
383
+
384
+ - Work: `examples/*/example.yaml` with the same metadata shape; `urlcode
385
+ examples search <text>` returns the smallest matching runnable example and
386
+ its route. The cookbook's forty routes get per-route tags in one index file.
387
+ - Proof: search test; count audit unchanged.
388
+
389
+ ### 3.3 `urlcode explain` from compiled semantics — done
390
+
391
+ Done 2026-09-19: `urlcode explain [/route] [--json]` in `src/cli.ts` over
392
+ `runExplainCommand` in `src/explain-cli.ts` and `explainRoute`/`explainProject`
393
+ in `src/explain.ts`, derived from the compiled IR, including extension policy
394
+ requirements when a host file is supplied. `test/explain.test.ts` covers the
395
+ project table and route detail through the CLI, the sandbox boolean, the
396
+ extension-protected route with a host registry, the nearest-route miss that
397
+ never carries binding values, and that explain agrees with the runtime on
398
+ methods and policies for every route.
399
+
400
+ Fixes: `explainRoute` exists in the SDK and MCP; there is no CLI, and the
401
+ output repeats matching rather than effective behavior.
402
+
403
+ - Work: `urlcode explain [/route] [--json]` prints, per route, the effective
404
+ methods, handler, middleware chain, validated inputs, policies in effect
405
+ (including `extensions.auth` requirements when a host file is given),
406
+ cache and no-store outcome, bindings and target support, derived from the
407
+ compiled IR. Whole-project form lists every route in one screen.
408
+ - Proof: snapshot tests on the cookbook; a test that `explain` and the
409
+ runtime agree on methods and policies for every route.
410
+
411
+ ### 3.4 A generated semantic manifest — done
412
+
413
+ Done 2026-09-19: `urlcode manifest [--json]` shares the `src/explain-cli.ts`
414
+ entry and runs `buildManifest` in `src/manifest.ts`; `build` writes
415
+ `manifest.json` beside the artifact and MCP offers `get_manifest`.
416
+ `test/manifest.test.ts` asserts determinism, that the revision equals the
417
+ extension revision digest, that external requirements and recipe provenance are
418
+ listed by name and never by value, and that the CLI prints the bytes the build
419
+ writes.
420
+
421
+ - Work: `urlcode manifest [--json]` (also written by `build` and offered by
422
+ MCP as `get_manifest`) emits routes, capabilities, recipe provenance,
423
+ external requirements, custom functions, requested bindings, target
424
+ compatibility and the revision digest. Generated only; never checked in as
425
+ a source of truth. `context` is a budgeted view of the same data.
426
+ - Proof: manifest equals `inspectProject` output for the same project;
427
+ digest equals `inspectExtensionRevision`.
428
+
429
+ ### 3.5 MCP authoring layer, separately authorized (M, core) — done
430
+
431
+ Done 2026-09-19: `src/mcp-authoring.ts` adds `create_route`, `add_recipe` and
432
+ `scaffold_feature` plus the `run_validate`, `run_test` and `run_audit` runners,
433
+ enabled only by `--allow-authoring` on the operator's command line, which
434
+ `src/cli.ts` refuses for any other command and which no tool argument or
435
+ environment variable can set (`src/mcp.ts`). Every write is validated before it
436
+ lands and returns the verdict. `test/mcp-authoring.test.ts` asserts the tools
437
+ are absent without the flag, that absolute, parent, symlinked, dotenv, git and
438
+ operator paths are refused, and that a create is followed by validation in one
439
+ call.
440
+
441
+ Fixes: MCP is read-only by design; an agent that wants to add a recipe or a
442
+ route still has to write files by hand.
443
+
444
+ - Work: keep the read side as is and add the new read tools above. Add an
445
+ authoring server mode enabled only by an explicit flag on the operator's
446
+ command line (`urlcode mcp --allow-authoring --project DIR`): `add_recipe`,
447
+ `create_route`, `scaffold_feature` (writes YAML and placeholder files
448
+ through the existing scaffold path), and `validate`/`test`/`audit` runners.
449
+ Every write is confined to the selected project, refuses paths outside it,
450
+ never touches operator files, grants, policies or host files, and returns
451
+ the validation verdict. Nothing in either mode reads secrets, creates
452
+ grants, deploys, or changes operator security policy.
453
+ - Proof: tests that authoring tools are absent without the flag; path
454
+ confinement tests; a write followed by `validate` in one call.
455
+
456
+ ## Phase 4: fewer lines for the common case (schema additions, `version: "1"` stays valid)
457
+
458
+ ### 4.1 Short form for function routes (M, core) — done
459
+
460
+ Done 2026-09-19: `normalizeRoute` in `src/config.ts` expands
461
+ `function: functions/x.mjs` into `{source, args}` with an argument per `{param}`
462
+ and a required bounded path parameter for any the route does not declare itself
463
+ (`SHORT_FORM_PATH_SCHEMA`), and a string `middleware` entry into `{source}`; the
464
+ long form stays the IR. Both shapes are in
465
+ `schemas/urlcode.schema.json` (`$defs.route.properties.function` and
466
+ `.middleware`) and the generated field reference lists them as options.
467
+ `test/config.test.ts` ("function and middleware short forms normalize to the
468
+ long form the long form compiles to") asserts identical output, that a declared
469
+ parameter keeps its schema, and that a bad short-form string is refused with the
470
+ route named; the cookbook uses the short form in
471
+ `examples/cookbook/routes/middleware.yaml`.
472
+
473
+ Fixes: the smallest function route is ten lines.
474
+
475
+ - Work: `function: functions/hello.mjs` as a string expands every `{param}`
476
+ to a required bounded string parameter and matching `args` entry; same for
477
+ `middleware: [functions/x.mjs]`. The long form stays the canonical IR;
478
+ `routes`, `audit`, `explain` and the field reference show the expansion.
479
+ - Proof: generated reference updated; cookbook gains a short-form route with
480
+ fixtures; a test that short and long forms compile to identical IR.
481
+
482
+ ### 4.2 Route-level `auth` as the semantic form (M, core plus auth) — done
483
+
484
+ Done 2026-09-19: the route-level `auth` key is in the schema
485
+ (`$defs.route.properties.auth` over `$defs.routeAuth` in
486
+ `schemas/urlcode.schema.json`) and expanded by `normalizeRouteAuth` in
487
+ `src/config.ts`, which refuses a route that declares `auth` without an
488
+ `extensions.auth` declaration, alongside `policies.extensions.auth`, or with
489
+ `policies.extensions: false`; `required: false` documents intent and emits
490
+ nothing. The type is documented in `src/types.ts`, and `test/recipes.test.ts`
491
+ ("the authenticated recipes use the auth short form and never let credentials
492
+ reach the guest") covers both `auth: true` and a role requirement. The `cache`
493
+ short form this item defers to later shipped with it:
494
+ `$defs.route.properties.cache` and the cache branch of `normalizeRoute`, refused
495
+ alongside `policies.cache`. Caveat: the auth repository's HTTP tests are outside
496
+ this repository and were not run here.
497
+
498
+ Fixes: protecting a route today is `policies: { extensions: { auth: {} } }`,
499
+ which is the mechanism, not the intent. The form an agent should write is
500
+ `auth: { required: true, roles: [admin] }`.
501
+
502
+ - Work: a route-level `auth` key that expands to the `policies.extensions.auth`
503
+ requirement the auth extension validates; `roles` maps to the extension's
504
+ policy schema. Valid only when an `auth` extension is declared, refused
505
+ with the route named otherwise. The same pattern applies later to `cache:
506
+ { strategy: public, maxAge: 3600 }` over the cache policy where the policy
507
+ form is more verbose than the intent.
508
+ - Proof: a test that the short form compiles to the identical requirement;
509
+ `explain` shows both; the auth repo's HTTP tests pass unchanged.
510
+
511
+ ### 4.3 Semantic over implementation configuration (rule, ongoing)
512
+
513
+ Every new field describes intended behavior (`auth: required`), never a
514
+ provider or framework knob. Review new YAML fields against this in PR
515
+ templates and the AI authoring matrix.
516
+
517
+ ## Phase 5: keep measuring
518
+
519
+ Phase 0 runs again after each of Phases 1 to 4 lands, on the same tasks and
520
+ model, so every feature shows its effect on tokens, turns and the
521
+ application-specific code ratio. The repetition log in Phase 6 is fed from
522
+ the benchmark runs: every plumbing line the agent still wrote in the URLCode
523
+ arm is an entry.
524
+
525
+ ## Phase 6: grow from observed repetition (rule, ongoing)
526
+
527
+ When building an application with URLCode, record every place an agent still
528
+ generated commodity infrastructure and classify it: missing primitive,
529
+ policy, recipe, capability, documentation, example, integration, or
530
+ legitimately application-specific. Keep the log in `docs/REPETITION-LOG.md`
531
+ with the project, the code that was written and the classification. Candidate
532
+ areas the brief lists (CRUD, webhooks, email, uploads, jobs, pagination, API
533
+ envelopes) are built only when the log shows them repeating. The application
534
+ data question is the first entry:
535
+
536
+ ### 6.1 Spike: a declared `collection` handler with an admin view (M to write, L to build)
537
+
538
+ "Full-fledged application" today means "site with accounts". Write
539
+ `docs/SPIKE-COLLECTIONS.md` against the same principles as links: YAML
540
+ declares a collection with a JSON Schema for records, exact bounded query and
541
+ mutation routes, operator-owned SQLite, no guest queries; admin registers a
542
+ generic records screen; functions receive records as validated `args`. It
543
+ must answer portability to serverless targets, limits, schema migration and
544
+ whether links become a collection. Decide after review; build nothing first.
545
+
546
+ ## Phase 7: one presentation story (extension repos)
547
+
548
+ ### 7.1 Shared markup helpers into urlcode-ui (S)
549
+
550
+ `hiddenField`, `postForm`, `withDeadline` in the ui main entry; auth and
551
+ admin drop their copies (`admin-markup.ts`, `admin-deadline.ts`).
552
+
553
+ ### 7.2 Auth and admin render through the kit (L) — partly done
554
+
555
+ Done 2026-09-19, the core prerequisite only: the immutable-cache exception for
556
+ hashed extension assets shipped. `ExtensionImmutableAssets` and
557
+ `validateAssetPrefix` in `src/extensions.ts` bound a declared prefix to literal
558
+ segments under the extension's mounts, and `immutableAssetResponse` is what
559
+ lets such a response carry an asset cache-control instead of `no-store`. The
560
+ rendering work itself is in the auth, admin and ui repositories and is not
561
+ verified from here.
562
+
563
+ Auth exports its catalogue and templates with view models, takes an optional
564
+ `ui` from the host, and renders with `ui.kit.page` when present; admin the
565
+ same. Needs the runtime change first: an immutable-cache exception for hashed
566
+ extension assets (core, S). Proof: `urlcode-ui doctor` shows full coverage;
567
+ existing HTTP tests pass under both render paths; a themed browser
568
+ walkthrough. Retire the `presentation` option one minor version later.
569
+
570
+ ## Phase 8: evidence still missing (unchanged from issue 58)
571
+
572
+ | Gap | Plan | Owner and size |
573
+ |---|---|---|
574
+ | Browser and device WebAuthn coverage | Playwright with a virtual authenticator in auth CI; one manual pass on Safari and Android | auth, M |
575
+ | Accessibility assessment | Automated axe pass in the browser walkthroughs plus one manual screen-reader and forced-colors pass | ui, auth, admin, M |
576
+ | Soak, backup and recovery on a deployment | `operational-drills` with `URLCODE_SOAK_SECONDS=3600` on a real host; auth's recovery drill against a restored snapshot | core and auth, M |
577
+ | Provider deployments | Deploy `examples/provider-conformance` to one Vercel, AWS and Cloudflare account; run `verify-provider` | core, M, needs accounts |
578
+ | Live Google, Apple and SES | Deferred by the owner; synthetic signed fixtures stay | auth, later |
579
+ | Independent security review | `SANDBOX-REVIEW.md` is the package; commission one reviewer before any non-alpha auth release | all, L, external |
580
+
581
+ ## Phase 9: hardening left from the audit (core, S each)
582
+
583
+ - Direct tests for the sandbox pool, worker crash recovery and timeout kill
584
+ path (`src/functions.ts`, `src/function-worker.ts`, `src/guest-api.ts`) —
585
+ done 2026-09-19: `test/sandbox-pool.test.ts` covers load shedding with slot
586
+ reuse, parallel workers with per-slot heaps, the 250 ms-to-30 s respawn
587
+ backoff, the deadline kill with slot replacement, an abrupt worker exit
588
+ failing the in-flight invocation, startup failure and close, and the guest
589
+ `Request`/`Response`/context surface.
590
+ - Re-verify the remaining line-number rows in `STANDARDS.md`; cite symbols —
591
+ done 2026-09-19: `docs/STANDARDS.md` records that every row was re-verified
592
+ at `8d7dd01` and now cites the implementing function or constant rather than
593
+ a line number, names the test that proves it, and says "unverified at
594
+ 8d7dd01" where a row could not be re-checked; no line-number citation
595
+ remains in the file.
596
+ - Split the longest reference documents (YAML guide, policies) into task pages
597
+ under 200 lines, so retrieval returns one page — done 2026-09-19:
598
+ `docs/yaml/` holds nine pages (largest `functions.md`, 160 lines) and
599
+ `docs/policies/` nine (largest `agents.md`, 182 lines), with
600
+ `docs/YAML-GUIDE.md` down to 57 lines. Caveat: `docs/POLICIES.md` is still
601
+ 203 lines as the index page.
602
+ - A pre-test check in the extension repos that fails fast with the SQLite
603
+ requirement named. Not verifiable from this repository.
604
+
605
+ ## Sequence at a glance
606
+
607
+ What remains, as of 2026-09-19:
608
+
609
+ ```
610
+ Phase 0 instrument built and tested; no model-backed run stored, so the
611
+ code-ratio evidence is still missing. Evals (0.2) run weekly.
612
+ Phase 1 done: AGENTS.md from init, llms-full, urlcode context,
613
+ capability/schema queries, the skill
614
+ left: the docs site's /docs/<page>.md (urlcode-docs)
615
+ Phase 2 done: published alphas, init --with, urlcode extensions + get_extensions
616
+ left: peers.json (auth, admin); extension schemas folded into
617
+ urlcode context and capabilities <name>
618
+ Phase 3 done: recipe metadata + search, examples search, explain, manifest,
619
+ MCP authoring behind --allow-authoring
620
+ Phase 4 done: short-form function and middleware routes, route-level auth,
621
+ the cache short form; 4.3 stays a standing rule for new fields
622
+ Phase 5 waits on a Phase 0 run with a real model
623
+ Phase 6 not started: no docs/REPETITION-LOG.md, no docs/SPIKE-COLLECTIONS.md
624
+ Phase 7 extension repos; the core immutable-asset exception 7.2 needs is done
625
+ Phase 8 proof gaps, in parallel, as people and environments allow
626
+ Phase 9 done: sandbox pool tests, STANDARDS symbol citations, split reference
627
+ pages; left: the extension repos' SQLite pre-test check
628
+ ```
629
+
630
+ Phase 0 decides whether the rest is worth doing and in what order. Phases 1
631
+ and 3 are what an agent meets first; the rest makes that cheaper or proves it.
632
+ Because Phases 1, 3 and 4 landed ahead of the first model-backed run, that run
633
+ now measures what shipped rather than choosing it; Phase 5's re-runs are where
634
+ the choosing role returns.