@jimhoyd/urlcode 0.4.1 → 0.4.6
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 +60 -7
- package/.claude/skills/urlcode-operations/SKILL.md +4 -0
- package/README.md +19 -15
- package/SECURITY.md +5 -3
- package/dist/BUILD-MANIFEST.json +31 -28
- package/dist/agent-context.js +82 -0
- package/dist/agents-guide.js +42 -42
- package/dist/authoring.js +12 -2
- package/dist/body-schema.js +159 -0
- package/dist/build-cloudflare.js +2 -0
- package/dist/capabilities.js +1 -1
- package/dist/cli.js +29 -12
- package/dist/config.js +74 -6
- package/dist/context.js +4 -6
- package/dist/errors.js +3 -1
- package/dist/examples.js +1 -1
- package/dist/extensions.js +138 -3
- package/dist/http-policy.js +19 -4
- package/dist/http-response.js +2 -2
- package/dist/init-with.js +71 -9
- package/dist/mcp.js +15 -2
- package/dist/pattern-guard.js +32 -0
- package/dist/policies/security.js +0 -0
- package/dist/policy.js +16 -0
- package/dist/project-tests.js +35 -11
- package/dist/readiness.js +188 -32
- package/dist/recipes.js +1 -1
- package/dist/router.js +17 -0
- package/dist/runtime.js +26 -3
- package/dist/scaffold.js +0 -0
- package/dist/server.js +26 -3
- package/dist/site.js +0 -0
- package/dist/tooling.js +3 -1
- package/dist/types/agent-context.d.ts +44 -0
- package/dist/types/authoring.d.ts +3 -1
- package/dist/types/body-schema.d.ts +58 -0
- package/dist/types/config.d.ts +10 -2
- package/dist/types/context.d.ts +1 -1
- package/dist/types/errors.d.ts +9 -1
- package/dist/types/examples.d.ts +1 -1
- package/dist/types/extensions.d.ts +105 -2
- package/dist/types/http-policy.d.ts +3 -0
- package/dist/types/init-with.d.ts +10 -1
- package/dist/types/pattern-guard.d.ts +10 -0
- package/dist/types/project-tests.d.ts +9 -0
- package/dist/types/readiness.d.ts +71 -0
- package/dist/types/recipes.d.ts +1 -1
- package/dist/types/runtime.d.ts +4 -0
- package/dist/types/server.d.ts +9 -1
- package/dist/types/tooling.d.ts +2 -0
- package/dist/types/types.d.ts +13 -0
- package/dist/types.js +10 -2
- package/dist/typescript-authoring.js +5 -3
- package/dist/verify-deployment.js +16 -10
- package/examples/body-validation/README.md +16 -0
- package/examples/body-validation/example.yaml +17 -0
- package/examples/body-validation/tests/requests.json +15 -0
- package/examples/body-validation/urlcode.yaml +34 -0
- package/examples/coverage-waiver/README.md +8 -0
- package/examples/coverage-waiver/example.yaml +16 -0
- package/examples/coverage-waiver/functions/notes.mjs +2 -0
- package/examples/coverage-waiver/tests/requests.json +3 -0
- package/examples/coverage-waiver/urlcode.yaml +10 -0
- package/examples/data-dir/README.md +39 -0
- package/examples/data-dir/data/welcome.txt +1 -0
- package/examples/data-dir/example.yaml +22 -0
- package/examples/data-dir/functions/note.mjs +18 -0
- package/examples/data-dir/tests/requests.json +6 -0
- package/examples/data-dir/urlcode.yaml +6 -0
- package/examples/lifecycle/README.md +15 -0
- package/examples/lifecycle/example.yaml +19 -0
- package/examples/lifecycle/functions/notes.mjs +29 -0
- package/examples/lifecycle/tests/requests.json +12 -0
- package/examples/lifecycle/urlcode.yaml +29 -0
- package/examples/not-found/README.md +10 -0
- package/examples/not-found/example.yaml +17 -0
- package/examples/not-found/public/404.html +3 -0
- package/examples/not-found/public/index.html +3 -0
- package/examples/not-found/tests/requests.json +7 -0
- package/examples/not-found/urlcode.yaml +7 -0
- package/examples/shared-blocks/README.md +11 -0
- package/examples/shared-blocks/example.yaml +17 -0
- package/examples/shared-blocks/tests/requests.json +7 -0
- package/examples/shared-blocks/urlcode.yaml +37 -0
- package/llms-full.txt +436 -149
- package/llms.txt +44 -6
- package/package.json +15 -22
- package/recipes/static-page/README.md +9 -0
- package/recipes/static-page/public/index.html +11 -0
- package/recipes/static-page/recipe.yaml +21 -0
- package/recipes/static-page/tests/requests.json +22 -0
- package/recipes/static-page/urlcode.yaml +7 -0
- package/recipes/static-plus-api/README.md +6 -0
- package/recipes/static-plus-api/urlcode.yaml +4 -0
- package/recipes/store-crud/README.md +53 -0
- package/recipes/store-crud/recipe.yaml +31 -0
- package/recipes/store-crud/tests/requests.json +18 -0
- package/recipes/store-crud/urlcode.yaml +18 -0
- package/schemas/urlcode.schema.json +125 -60
- package/skills/urlcode/SKILL.md +53 -26
- package/starters/default/AGENTS.md +43 -43
- package/starters/page/README.md +14 -0
- package/starters/page/public/index.html +12 -0
- package/starters/page/tests/requests.json +17 -0
- package/starters/page/urlcode.yaml +6 -0
- package/.claude-plugin/marketplace.json +0 -18
- package/CONTRIBUTING.md +0 -112
- package/ROADMAP.md +0 -66
- package/docs/AI-AUTHORING.md +0 -338
- package/docs/ASSETS.md +0 -107
- package/docs/AUTH-BACKUP.md +0 -32
- package/docs/AWS.md +0 -86
- package/docs/BEST-PRACTICES.md +0 -276
- package/docs/BULK.md +0 -79
- package/docs/CAPABILITIES.md +0 -192
- package/docs/CAPACITY.md +0 -305
- package/docs/CI-FOLLOWUP-2026-09-19.md +0 -97
- package/docs/CI-RELEASE-AUDIT-2026-09-19.md +0 -322
- package/docs/CI.md +0 -147
- package/docs/CLOUDFLARE.md +0 -109
- package/docs/CODEBASE-AUDIT-2026-09-20.md +0 -284
- package/docs/COMPLIANCE.md +0 -239
- package/docs/COMPOSING-A-SITE.md +0 -278
- package/docs/CONDITIONS.md +0 -74
- package/docs/DEPLOYMENT-CHECKS.md +0 -108
- package/docs/DEVELOPMENT-PIPELINE.md +0 -270
- package/docs/EGRESS.md +0 -125
- package/docs/EXTENSIONS.md +0 -438
- package/docs/FRAMEWORK.md +0 -217
- package/docs/FUNCTION-SECURITY.md +0 -254
- package/docs/HTTP.md +0 -129
- package/docs/INSTALL.md +0 -128
- package/docs/INTERCHANGE.md +0 -134
- package/docs/LOAD-TESTING.md +0 -91
- package/docs/LOCAL-DEVELOPMENT.md +0 -102
- package/docs/MIDDLEWARE-EXAMPLES.md +0 -75
- package/docs/MIDDLEWARE.md +0 -102
- package/docs/MONITORING.md +0 -115
- package/docs/OBSERVABILITY.md +0 -222
- package/docs/OPEN-DECISIONS.md +0 -224
- package/docs/OPERATIONAL-PROOF.md +0 -41
- package/docs/OPERATIONS.md +0 -201
- package/docs/ORGANIZATION.md +0 -135
- package/docs/PERFORMANCE.md +0 -72
- package/docs/PLUGINS.md +0 -271
- package/docs/POLICIES.md +0 -211
- package/docs/PRERENDER.md +0 -245
- package/docs/PROJECT-DIRECTION.md +0 -118
- package/docs/PROVIDER-VERIFICATION.md +0 -84
- package/docs/READINESS.md +0 -150
- package/docs/README.md +0 -87
- package/docs/RECIPES.md +0 -99
- package/docs/RELEASE-0.4.0-alpha.3.md +0 -50
- package/docs/RELEASE-0.4.1.md +0 -73
- package/docs/RELEASE-READINESS.md +0 -117
- package/docs/RELEASE-SECURITY.md +0 -96
- package/docs/RESILIENCE.md +0 -161
- package/docs/ROUTING.md +0 -92
- package/docs/SANDBOX-REVIEW.md +0 -72
- package/docs/SCAFFOLDING.md +0 -70
- package/docs/SECURITY-AUDIT.md +0 -164
- package/docs/SITE.md +0 -150
- package/docs/SPECIFICATION.md +0 -359
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +0 -288
- package/docs/SPIKE-BUSINESS-SUITE.md +0 -1029
- package/docs/SPIKE-CORE-LAYERING.md +0 -368
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +0 -211
- package/docs/STANDARDS.md +0 -311
- package/docs/STARTERS.md +0 -83
- package/docs/STATIC.md +0 -105
- package/docs/TOOLING.md +0 -298
- package/docs/TUNNELS.md +0 -72
- package/docs/TYPESCRIPT-AUTHORING.md +0 -87
- package/docs/TYPESCRIPT.md +0 -123
- package/docs/VERCEL.md +0 -114
- package/docs/VERSION-ALIGNMENT.md +0 -80
- package/docs/YAML-GUIDE.md +0 -57
- package/docs/YAML-REFERENCE.md +0 -449
- package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +0 -68
- package/docs/archive/2026-09-19/MANAGEMENT-SECURITY.md +0 -102
- package/docs/archive/2026-09-19/NEXT-PHASE-PLAN.md +0 -108
- package/docs/archive/2026-09-19/NEXT-STEPS.md +0 -646
- package/docs/archive/2026-09-19/OPEN-DECISIONS.md +0 -277
- package/docs/archive/2026-09-19/RELEASE-SECURITY.md +0 -186
- package/docs/archive/2026-09-19/ROADMAP.md +0 -387
- package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +0 -430
- package/docs/archive/2026-09-19/SPIKE-EXTENSIONS.md +0 -492
- package/docs/archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md +0 -365
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +0 -778
- package/docs/archive/2026-09-19/USABILITY-REVIEW.md +0 -139
- package/docs/archive/README.md +0 -28
- package/docs/policies/agents.md +0 -182
- package/docs/policies/cache.md +0 -152
- package/docs/policies/compression.md +0 -169
- package/docs/policies/contract.md +0 -52
- package/docs/policies/hardened.md +0 -56
- package/docs/policies/interoperability.md +0 -169
- package/docs/policies/operations.md +0 -45
- package/docs/policies/security.md +0 -161
- package/docs/policies/throttle.md +0 -103
- package/docs/yaml/assets.md +0 -36
- package/docs/yaml/conditions.md +0 -20
- package/docs/yaml/functions.md +0 -168
- package/docs/yaml/middleware.md +0 -31
- package/docs/yaml/organization.md +0 -74
- package/docs/yaml/policies.md +0 -37
- package/docs/yaml/redirects.md +0 -64
- package/docs/yaml/responses.md +0 -57
- package/docs/yaml/site.md +0 -24
- package/packaging/claude-plugin/.claude-plugin/plugin.json +0 -19
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +0 -120
- package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +0 -108
package/docs/OBSERVABILITY.md
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
# Observability
|
|
2
|
-
|
|
3
|
-
URLCode reports what it does through three interfaces built on one stream of
|
|
4
|
-
events: the JSON log on stdout, operator **observers** that receive the same
|
|
5
|
-
events in process, and a **metrics snapshot** of counters derived from them,
|
|
6
|
-
optionally served in Prometheus format. Observers mirror [host plugins](PLUGINS.md):
|
|
7
|
-
they are JavaScript an operator passes to `startServer` or `createRuntime`,
|
|
8
|
-
never something a project's YAML can name, and they run with the host's
|
|
9
|
-
privileges. [Monitoring](MONITORING.md) is the operator's guide to probes,
|
|
10
|
-
recipes and alerts; this page is the contract.
|
|
11
|
-
|
|
12
|
-
```js
|
|
13
|
-
import { startServer } from '@jimhoyd/urlcode';
|
|
14
|
-
|
|
15
|
-
await startServer({
|
|
16
|
-
project: './site',
|
|
17
|
-
observers: [myObserver], // in-process event and metrics sinks
|
|
18
|
-
metrics: true, // GET /_urlcode/metrics, Prometheus text format
|
|
19
|
-
});
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Event catalogue
|
|
23
|
-
|
|
24
|
-
`events` in `@jimhoyd/urlcode/observability` is a frozen object mapping each event name
|
|
25
|
-
to the complete list of fields it may carry. A test runs a real server and
|
|
26
|
-
holds every record to it, so a field or event that is not in the table below
|
|
27
|
-
does not ship. Fields marked *optional* are present only in the situations
|
|
28
|
-
named.
|
|
29
|
-
|
|
30
|
-
| Event | Fields | Emitted when |
|
|
31
|
-
|---|---|---|
|
|
32
|
-
| `request` | `requestId` string, `status` integer, `durationMs` number; `method` string and `route` string or `null` with `--request-log detailed` | Every response the server wrote, including probes and shed 503s. `route` is the configured pattern (`/u/{id}`) or the probe path, never the requested path. |
|
|
33
|
-
| `reload` | `status` `ok`/`rejected`; `version` string and `routes` integer on `ok` | `app.reload()` or the development watcher swapped, or refused to swap, the snapshot. |
|
|
34
|
-
| `watch` | `status` `failed` | The development watcher could not fingerprint the project. |
|
|
35
|
-
| `function_worker` | `status` `started`/`restarting`, `slot` integer; `attempt` and `delayMs` integers on `restarting` | A function worker became ready or is scheduled for replacement. |
|
|
36
|
-
| `signal` | `outcome` (`accepted`, `delivered`, `failed`, `dropped`), positive `count` | Best-effort webhook totals; no destination, request data or secrets. |
|
|
37
|
-
| `logs_dropped` | `count` integer | The JSON logger shed records because stdout was not writable. Written by the logger itself, so observers do not see it. |
|
|
38
|
-
| `observer` | `status` `failed`, `name` string | An observer hook threw or rejected. Written to the default log only, never to observers. |
|
|
39
|
-
| `throttle` | `route`, `outcome` `allowed`/`exceeded`, `remaining` integer | A throttle decision. `allowed` is logged only in `mode: report`; enforce mode logs refusals. |
|
|
40
|
-
| `agents` | `route`, `list` string, `outcome` `denied`/`reported` | A User-Agent matched a list. The list name is logged, never the header. |
|
|
41
|
-
| `cache` | `route`, `outcome` `hit`/`stale`/`miss`/`store` | A cache lookup or store. |
|
|
42
|
-
| `listening` | `address`, `port`, `mode`, `origin` | Printed once by the CLI at startup, not emitted by the server. |
|
|
43
|
-
|
|
44
|
-
Every event carries `event` (its name). Numbers are JSON numbers, never
|
|
45
|
-
strings.
|
|
46
|
-
|
|
47
|
-
### Privacy guarantees
|
|
48
|
-
|
|
49
|
-
No event, snapshot or exposition carries a request URL, path, query string,
|
|
50
|
-
header, body, client address, User-Agent string, binding, secret or user
|
|
51
|
-
exception text. `route` is always a configured pattern
|
|
52
|
-
from reviewed YAML. `requestId` is server-generated unless
|
|
53
|
-
`--trust-request-id` accepts one from a trusted proxy. An observer
|
|
54
|
-
that logs should keep the same rule; nothing in an event lets it break it.
|
|
55
|
-
|
|
56
|
-
## Observers
|
|
57
|
-
|
|
58
|
-
```js
|
|
59
|
-
const myObserver = {
|
|
60
|
-
name: 'forwarder', // ^[a-z][a-z0-9-]{0,63}$, unique per server
|
|
61
|
-
version: '1.0.0', // any string up to 64 characters
|
|
62
|
-
onEvent(event) {}, // every record the JSON logger writes, in order
|
|
63
|
-
onMetrics(snapshot) {}, // the metrics snapshot, on the interval and at close
|
|
64
|
-
async onClose() {}, // release resources; reverse order
|
|
65
|
-
};
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
In TypeScript the contract is `Observer` from `@jimhoyd/urlcode/observability` (also
|
|
69
|
-
exported from `urlcode`), with `ObserverEvent` for a record and
|
|
70
|
-
`MetricsSnapshot` for what `onMetrics` receives; the declarations ship with the
|
|
71
|
-
package:
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
import type { Observer, ObserverEvent, MetricsSnapshot } from '@jimhoyd/urlcode/observability';
|
|
75
|
-
|
|
76
|
-
const myObserver: Observer = {
|
|
77
|
-
name: 'forwarder',
|
|
78
|
-
version: '1.0.0',
|
|
79
|
-
onEvent(event: ObserverEvent) { queue.push(event); },
|
|
80
|
-
onMetrics(snapshot: MetricsSnapshot) { gauge.set(snapshot.requests.inFlight); },
|
|
81
|
-
};
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Validation (`validateObservers`) matches plugins: at most 32 observers, each
|
|
85
|
-
an object with a kebab-case `name` no other observer uses, a `version` string,
|
|
86
|
-
every declared hook a function and at least one present. It runs before the
|
|
87
|
-
listener starts, so a bad observer fails startup rather than a request.
|
|
88
|
-
|
|
89
|
-
`onEvent` receives the same object the logger serialised, after the logger,
|
|
90
|
-
observers in array order. Do not mutate it. It runs on the request path, so
|
|
91
|
-
keep it cheap: buffer and flush on a timer rather than awaiting a network
|
|
92
|
-
call. A hook that throws or returns a rejecting promise is isolated: the
|
|
93
|
-
request is unaffected, the next observer still runs, `observers.errors` in
|
|
94
|
-
the snapshot increments and one `observer` record goes to the default log.
|
|
95
|
-
Nothing is retried; an observer that needs delivery guarantees owns its own
|
|
96
|
-
queue.
|
|
97
|
-
|
|
98
|
-
`onMetrics` receives a fresh snapshot every `metricsIntervalMs`
|
|
99
|
-
(`startServer` option, `0` off by default, 1 s to 1 h) and once at `close()`.
|
|
100
|
-
`onClose` runs in reverse order after the runtime has closed.
|
|
101
|
-
`app.observers` lists the `{ name, version }` pairs.
|
|
102
|
-
|
|
103
|
-
`createRuntime(project, { observers })` takes the same array for embedding
|
|
104
|
-
without the server: the runtime's own sink and counters are then yours, and
|
|
105
|
-
`runtime.metrics()` returns its snapshot. `startServer` never passes its
|
|
106
|
-
observers down to the runtimes it creates, so a reload does not re-register
|
|
107
|
-
them and counters survive reloads.
|
|
108
|
-
|
|
109
|
-
`createObserverSink(observers, fallbackLog)` is the fan-out itself, exported
|
|
110
|
-
for tests and custom hosts: it returns a `log(event)` function with `.metrics`,
|
|
111
|
-
`.publish(snapshot)` and `.close()`.
|
|
112
|
-
|
|
113
|
-
## Metrics snapshot
|
|
114
|
-
|
|
115
|
-
`app.metrics()` and `runtime.metrics()` return a plain object, safe to
|
|
116
|
-
`JSON.stringify`, of counters since the process started serving. Numbers
|
|
117
|
-
only; the one keyed table is `requests.byRoute`, keyed by configured pattern
|
|
118
|
-
and capped at 10 000 keys.
|
|
119
|
-
|
|
120
|
-
| Field | Type | Meaning |
|
|
121
|
-
|---|---|---|
|
|
122
|
-
| `version` | gauge | Snapshot shape version, currently `2`. |
|
|
123
|
-
| `uptimeSeconds`, `rssBytes` | gauge | Process facts. |
|
|
124
|
-
| `requests.total`, `requests.byStatusClass.{2xx,3xx,4xx,5xx}` | counter | Application responses, including shed 503s. |
|
|
125
|
-
| `requests.inFlight` | gauge | Requests holding application admission now. |
|
|
126
|
-
| `requests.byRoute[pattern]` | counter | Responses per matched route. A shed or unmatched request has no route. |
|
|
127
|
-
| `health.total`, `health.byStatusClass`, `health.inFlight` | counter, gauge | The probe budget: `/_urlcode/health`, `/_urlcode/ready` and `/_urlcode/metrics`. |
|
|
128
|
-
| `shed.requests`, `shed.health` | counter | 503s answered because an admission budget was full. |
|
|
129
|
-
| `reloads.ok`, `reloads.rejected` | counter | Snapshot swaps. |
|
|
130
|
-
| `watch.failed` | counter | Development watcher failures. |
|
|
131
|
-
| `functionWorkers.started`, `functionWorkers.restarts` | counter | Worker starts and scheduled replacements. |
|
|
132
|
-
| `functionWorkers.healthySlots`, `functionWorkers.slots` | gauge | Ready slots and configured slots of the serving runtime. |
|
|
133
|
-
| `policies.throttle.{allowed,exceeded}` | counter | Throttle decisions (see the catalogue for what enforce mode logs). |
|
|
134
|
-
| `policies.agents.{denied,reported}` | counter | Agents decisions. |
|
|
135
|
-
| `policies.cache.{hit,stale,miss,store}` | counter | Cache outcomes. |
|
|
136
|
-
| `signals.{accepted,delivered,failed,dropped}` | counter | Best-effort webhook outcomes; exposed as `signals_total` with outcome labels. |
|
|
137
|
-
| `logsDropped` | counter | Records the JSON logger shed. |
|
|
138
|
-
| `observers.errors` | counter | Observer hooks that threw or rejected. |
|
|
139
|
-
|
|
140
|
-
Policy counters are derived from the `throttle`, `agents` and `cache` events
|
|
141
|
-
as they pass through the sink, so the policies themselves have no metrics
|
|
142
|
-
code. Runtime facts that never become events (admission, shedding, slot
|
|
143
|
-
health) are recorded by the server directly. Counters are per process;
|
|
144
|
-
aggregation across replicas is the scraper's job.
|
|
145
|
-
|
|
146
|
-
## Prometheus exposition
|
|
147
|
-
|
|
148
|
-
`startServer({ metrics: true })`, or `urlcode serve --metrics` on the command
|
|
149
|
-
line, serves `GET /_urlcode/metrics` as `text/plain; version=0.0.4`, rendered from the same snapshot by
|
|
150
|
-
`renderPrometheus(snapshot)`, a pure function you can also call yourself.
|
|
151
|
-
Every metric is prefixed `urlcode_`; counters end in `_total`; the only labels
|
|
152
|
-
are `status_class`, `route` and `outcome`.
|
|
153
|
-
|
|
154
|
-
```
|
|
155
|
-
# HELP urlcode_requests_total Application requests answered since start, by status class.
|
|
156
|
-
# TYPE urlcode_requests_total counter
|
|
157
|
-
urlcode_requests_total{status_class="2xx"} 1042
|
|
158
|
-
urlcode_route_requests_total{route="/u/{id}"} 977
|
|
159
|
-
urlcode_requests_in_flight 3
|
|
160
|
-
urlcode_shed_total{outcome="requests"} 0
|
|
161
|
-
urlcode_reloads_total{outcome="ok"} 2
|
|
162
|
-
urlcode_function_worker_restarts_total 0
|
|
163
|
-
urlcode_function_worker_healthy_slots 2
|
|
164
|
-
urlcode_throttle_total{outcome="exceeded"} 14
|
|
165
|
-
urlcode_cache_total{outcome="hit"} 511
|
|
166
|
-
urlcode_logs_dropped_total 0
|
|
167
|
-
urlcode_observer_errors_total 0
|
|
168
|
-
urlcode_uptime_seconds 86400
|
|
169
|
-
urlcode_process_rss_bytes 71303168
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
The endpoint is **off by default**. It shares the probes' admission budget
|
|
173
|
-
(`--max-in-flight-health`) and the same bind host, which is `127.0.0.1`
|
|
174
|
-
unless `--host` says otherwise. Like the probes it is unauthenticated and
|
|
175
|
-
discloses route patterns and traffic shape, so **do not expose it publicly**:
|
|
176
|
-
keep it on an internal interface or restrict it at the ingress. A scrape
|
|
177
|
-
counts under `health`, not under application requests. See
|
|
178
|
-
[`examples/monitoring/prometheus-scrape.yaml`](../examples/monitoring/prometheus-scrape.yaml).
|
|
179
|
-
|
|
180
|
-
## OpenTelemetry sketch
|
|
181
|
-
|
|
182
|
-
The runtime has no OpenTelemetry dependency. An observer can forward events
|
|
183
|
-
to an OTLP exporter and map the snapshot onto instruments; this is a sketch,
|
|
184
|
-
not shipped code, and omits batching, resource attributes and error handling.
|
|
185
|
-
|
|
186
|
-
```js
|
|
187
|
-
// Sketch. `logs` and `meter` come from the OpenTelemetry SDK the operator
|
|
188
|
-
// configures; the runtime knows nothing about them.
|
|
189
|
-
export function otelObserver({ logger, meter }) {
|
|
190
|
-
const requests = meter.createCounter('urlcode.requests', { unit: '{request}' });
|
|
191
|
-
const inFlight = meter.createObservableGauge('urlcode.requests.in_flight');
|
|
192
|
-
let last;
|
|
193
|
-
inFlight.addCallback(result => { if (last) result.observe(last.requests.inFlight); });
|
|
194
|
-
return {
|
|
195
|
-
name: 'otel', version: '0.1.0',
|
|
196
|
-
onEvent(event) {
|
|
197
|
-
// Every field is already safe to attach as an attribute.
|
|
198
|
-
logger.emit({ body: event.event, attributes: event });
|
|
199
|
-
if (event.event === 'request') requests.add(1, { status_class: `${Math.floor(event.status / 100)}xx`, route: event.route ?? '' });
|
|
200
|
-
},
|
|
201
|
-
onMetrics(snapshot) { last = snapshot; }, // the gauge reads the latest snapshot
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
Counters in the snapshot are cumulative, so they map to OpenTelemetry
|
|
207
|
-
`Counter` instruments read through an observable callback, or to a
|
|
208
|
-
Prometheus receiver scraping `/_urlcode/metrics` directly. `durationMs` on
|
|
209
|
-
`request` is the input for a `Histogram`; the runtime does not bucket it.
|
|
210
|
-
|
|
211
|
-
## What is not provided
|
|
212
|
-
|
|
213
|
-
- **Tracing.** There are no spans and no context propagation; `requestId` is
|
|
214
|
-
the only correlation key, and a trusted proxy can supply it.
|
|
215
|
-
- **Sampling.** Every event is delivered to every observer, or shed by the
|
|
216
|
-
logger under back-pressure and reported as `logs_dropped`.
|
|
217
|
-
- **Persistence and aggregation.** Counters live in process memory, reset on
|
|
218
|
-
restart, and describe one process. Retention and cross-replica sums belong
|
|
219
|
-
to the collector.
|
|
220
|
-
- **Per-URL analytics.** By design; see the privacy guarantees.
|
|
221
|
-
- **Adapters.** Vercel, Lambda and Cloudflare handlers emit through the
|
|
222
|
-
platform's own logging and do not take observers.
|
package/docs/OPEN-DECISIONS.md
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
# Decisions to align
|
|
2
|
-
|
|
3
|
-
Reviewed 2026-09-19 against core `db375bf` and the current public package sources.
|
|
4
|
-
This is the maintainer's decision list, not a second implementation backlog.
|
|
5
|
-
The [roadmap](../ROADMAP.md) gives sequence and the [archive](archive/README.md)
|
|
6
|
-
keeps earlier discussions. Recommendations below are not accepted decisions.
|
|
7
|
-
|
|
8
|
-
## Principles already settled, in plain language
|
|
9
|
-
|
|
10
|
-
- **Describe first, code only when needed.** Use a supported YAML feature or
|
|
11
|
-
extension before writing plumbing. Custom application code is still welcome.
|
|
12
|
-
- **Your application code runs like normal Node code.** Functions and middleware
|
|
13
|
-
are trusted by default. `sandbox: true` deliberately restricts a route's whole
|
|
14
|
-
function/middleware chain. Request data still needs validation in either mode.
|
|
15
|
-
- **A grant controls what URLCode supplies, not what trusted code can access.**
|
|
16
|
-
Trusted code can independently read the host environment, files and network.
|
|
17
|
-
Opt-in sandboxing retains its existing isolation and revision-pinned grants.
|
|
18
|
-
- **Portable does not mean every host supports every feature.** Keep infrastructure
|
|
19
|
-
out of route YAML and reject unsupported targets before activation.
|
|
20
|
-
- **Core works alone; optional packages add accounts, admin and presentation.**
|
|
21
|
-
Core never imports those implementations. Shared UI belongs in `urlcode-ui`.
|
|
22
|
-
- **Documentation stays beside the owning code.** Core guides live here;
|
|
23
|
-
extension contracts and implementation status live in their repositories.
|
|
24
|
-
`urlcode-docs` is deleted. `urlcode-short` and `urlcode-dynamic-link` are retired.
|
|
25
|
-
- **Passing tests proves the tested behavior.** It does not prove deployment,
|
|
26
|
-
accessibility, hostile tenant isolation or independent security assessment.
|
|
27
|
-
- **Keep the free runtime useful.** Apache-2.0 remains unchanged; no mandatory
|
|
28
|
-
hosted account, paid capability gate or provider lock-in belongs in core.
|
|
29
|
-
|
|
30
|
-
## Decisions still needed
|
|
31
|
-
|
|
32
|
-
| Decision | What the code says today | Recommendation and consequence |
|
|
33
|
-
|---|---|---|
|
|
34
|
-
| 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
|
-
| 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
|
-
| 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
|
-
|
|
39
|
-
The broader [AI benchmark proposal](SPIKE-AI-FRAMEWORK-BENCHMARK.md) also needs a
|
|
40
|
-
chosen application, model-run budget and execution authorization. The existing
|
|
41
|
-
small-task harness can supply evidence without committing to that larger study.
|
|
42
|
-
|
|
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
|
-
## Accepted: one Node deployment per project
|
|
62
|
-
|
|
63
|
-
**Decided 2026-09-19.** Projects that use `function` or `middleware` deploy as
|
|
64
|
-
**one trusted Node process** — a container or a VM running the project as it
|
|
65
|
-
runs locally. That is the supported execution model, and it needs no new work:
|
|
66
|
-
it is what the runtime already does.
|
|
67
|
-
|
|
68
|
-
**Per-route Lambda compilation is not pursued.** The alternative on the table
|
|
69
|
-
was a build step emitting one Lambda per `function` route
|
|
70
|
-
([the proposal](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md)). It is declined for now, on three
|
|
71
|
-
grounds the proposal itself states:
|
|
72
|
-
|
|
73
|
-
1. It would replace the sandbox guarantee rather than preserve it, and lose the
|
|
74
|
-
fresh-per-invocation state that `sandbox: true` currently guarantees.
|
|
75
|
-
2. It would make this project the author of generated IAM roles — a
|
|
76
|
-
security-critical output it has never owned.
|
|
77
|
-
3. It would trade an honest refusal for a larger claim nobody has deployed.
|
|
78
|
-
|
|
79
|
-
Against that, a single Node deployment supports every route type today with no
|
|
80
|
-
compiler, no generated infrastructure and no second isolation story to document.
|
|
81
|
-
|
|
82
|
-
**What follows from this decision:**
|
|
83
|
-
|
|
84
|
-
- AWS and Vercel continue to refuse `function` and `middleware` at activation,
|
|
85
|
-
naming the route (`src/capabilities.ts`, `activateNativeOnly` in
|
|
86
|
-
`src/adapters.ts`). That refusal is now a **deliberate position**, not a gap
|
|
87
|
-
awaiting an adapter. Documentation should say so rather than implying the
|
|
88
|
-
support is coming.
|
|
89
|
-
- Serverless targets remain first-class for the declarative route types they can
|
|
90
|
-
actually serve; nothing about static or native-only deployment changes.
|
|
91
|
-
- [SPIKE-LAMBDA-COMPILE.md](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md) is kept as the analysis
|
|
92
|
-
behind this decision, not as a plan. Reopen it only on evidence of real demand
|
|
93
|
-
for URLCode `function` routes specifically on AWS serverless — the proposal's
|
|
94
|
-
own §6 already scopes what a first attempt would be.
|
|
95
|
-
|
|
96
|
-
This decision is about the *execution model*, not about AWS. Deploying the Node
|
|
97
|
-
process to AWS (ECS, EC2, App Runner) is an operator choice this fully supports.
|
|
98
|
-
|
|
99
|
-
## Accepted: per-package release tags
|
|
100
|
-
|
|
101
|
-
**Decided 2026-09-19.** Workspace packages under `packages/` release on
|
|
102
|
-
Changesets' own `<package name>@<version>` form — for example
|
|
103
|
-
`@jimhoyd/urlcode-ui@0.1.0-alpha.6`. Core keeps bare `v*`.
|
|
104
|
-
|
|
105
|
-
**The problem.** Core and all three extensions arrived here triggering on
|
|
106
|
-
`tags: ['v*']`, and their alpha tags overlap outright: ui shipped
|
|
107
|
-
`v0.1.0-alpha.2` through `-alpha.5`, admin `v0.1.0-alpha.1` and `-alpha.3`,
|
|
108
|
-
auth `v0.1.0-alpha.1` through `-alpha.3`. Across four repositories that was
|
|
109
|
-
fine. In one repository a single bare tag push starts more than one release
|
|
110
|
-
workflow. Each one fails closed on its own tag-matches-manifest check, so
|
|
111
|
-
nothing can mis-publish — but "two workflows race and one errors on every
|
|
112
|
-
release" is not a release process, and the failure is confusing rather than
|
|
113
|
-
informative.
|
|
114
|
-
|
|
115
|
-
**Why Changesets' form rather than a prefix like `ui-v0.1.0-alpha.6`.** Both
|
|
116
|
-
work and both are valid ref names. The deciding factor is that Changesets is
|
|
117
|
-
already the chosen release flow, and `changeset tag` emits the
|
|
118
|
-
`<name>@<version>` form natively. Picking anything else means writing and
|
|
119
|
-
maintaining a translation layer between the tool that computes the version and
|
|
120
|
-
the tag that triggers the publish — new code whose only job is to disagree
|
|
121
|
-
with a default. The spike chose Changesets partly because it is "cheap and
|
|
122
|
-
low-risk for an agent or a human to generate correctly"; hand-rolling the tag
|
|
123
|
-
shape undercuts exactly that.
|
|
124
|
-
|
|
125
|
-
**Why the two schemes cannot collide.** A scoped package name begins with `@`,
|
|
126
|
-
and GitHub's `v*` filter requires a leading `v`, so no tag can match both.
|
|
127
|
-
Verified rather than assumed, including that `*` does not match `/` in a filter
|
|
128
|
-
pattern, so `@jimhoyd/urlcode-ui@*` matches the version segment only.
|
|
129
|
-
|
|
130
|
-
**Core's asymmetry is forced, not preferred.** Under layout A core is the
|
|
131
|
-
repository root rather than a workspace member, so Changesets does not manage
|
|
132
|
-
it and `changeset version` will not bump it. Core therefore keeps the tag
|
|
133
|
-
scheme and release workflow it already had.
|
|
134
|
-
|
|
135
|
-
[`scripts/check-release-tags.ts`](../scripts/check-release-tags.ts) enforces
|
|
136
|
-
this in `npm run check`: it rejects a workspace package workflow that does not
|
|
137
|
-
trigger on its own `<name>@*`, rejects any workflow other than core's claiming
|
|
138
|
-
`v*`, and independently asserts that no two filters can match the same tag. The
|
|
139
|
-
reasoning above is the kind of prose that rots as soon as `auth` and `admin`
|
|
140
|
-
arrive, which is the whole argument this repository makes for enforcing checks
|
|
141
|
-
over documented intent.
|
|
142
|
-
|
|
143
|
-
## Done: the monorepo migration is complete
|
|
144
|
-
|
|
145
|
-
**Closed 2026-09-19.** `urlcode-ui`, `urlcode-auth` and `urlcode-admin` are
|
|
146
|
-
workspace packages under `packages/`, and all three have been released from
|
|
147
|
-
this repository — `@jimhoyd/urlcode-ui@0.1.0-alpha.6`,
|
|
148
|
-
`@jimhoyd/urlcode-auth@0.1.0-alpha.6`, `@jimhoyd/urlcode-admin@0.1.0-alpha.4`,
|
|
149
|
-
each on `alpha` with `latest` deliberately held behind. Core's dist-tags are
|
|
150
|
-
unchanged. The three source repositories are gone; their history survives only
|
|
151
|
-
as verified `git bundle`s, because the repository allows squash merges only and
|
|
152
|
-
the imported commits did not survive onto `main`.
|
|
153
|
-
|
|
154
|
-
The operational runbook is [DEVELOPMENT-PIPELINE.md](DEVELOPMENT-PIPELINE.md)
|
|
155
|
-
and [RELEASE-SECURITY.md](RELEASE-SECURITY.md). The plan itself is archived at
|
|
156
|
-
[archive/2026-09-19/SPIKE-MONOREPO.md](archive/2026-09-19/SPIKE-MONOREPO.md),
|
|
157
|
-
whose closing note records what the plan got wrong — chiefly that its
|
|
158
|
-
strongest argument, the reach of the enforcing checks, only became true after
|
|
159
|
-
both checkers were changed to discover workspace packages.
|
|
160
|
-
|
|
161
|
-
The section below is kept for the middleware decision it records, which is
|
|
162
|
-
still the reason there is no `packages/middleware`.
|
|
163
|
-
|
|
164
|
-
## Accepted: monorepo first — middleware withdrawn rather than consolidated
|
|
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.
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# Operational evidence and deployment acceptance
|
|
2
|
-
|
|
3
|
-
`node scripts/operational-drills.ts` creates and deletes its own temporary
|
|
4
|
-
project. It runs mixed real HTTP requests against native redirects and
|
|
5
|
-
`function` routes; asserts responses; rejects a bad reload; and activates and
|
|
6
|
-
rolls back a configuration. `URLCODE_SOAK_SECONDS=60` selects a longer run
|
|
7
|
-
(1–3600 seconds, default 5). Output is JSON with request count, batch p99
|
|
8
|
-
duration and RSS. Batch latency is not per-request p99 or a capacity promise.
|
|
9
|
-
CI's `verify` job runs the short drill across a 3-Node (22/24/26) × 3-OS
|
|
10
|
-
(Linux/macOS/Windows) matrix, nine combinations, but only on a push to `main`.
|
|
11
|
-
A pull request runs the same drill across all three Node versions on Linux
|
|
12
|
-
only (3 of the 9 combinations); the macOS/Windows legs only run once a PR
|
|
13
|
-
merges, per `.github/workflows/ci.yml`'s matrix.
|
|
14
|
-
|
|
15
|
-
Core has no durable store of its own, so this drill has no backup/restore or
|
|
16
|
-
disk-exhaustion exercise: an extension package owning durable state is
|
|
17
|
-
responsible for its own persistence proof. No such package ships today — the
|
|
18
|
-
`urlcode-dynamic-link` package that did has been retired.
|
|
19
|
-
|
|
20
|
-
## Required proof on the intended deployment
|
|
21
|
-
|
|
22
|
-
Local/CI passes do not close these gates. `urlcode verify-deployment --target`
|
|
23
|
-
([deployment checks](DEPLOYMENT-CHECKS.md)) records that the deployed responses
|
|
24
|
-
match the project and is the first step of the rollback drill below, not a
|
|
25
|
-
substitute for it. The deployment owner must record:
|
|
26
|
-
|
|
27
|
-
| Exercise | Acceptance evidence |
|
|
28
|
-
|---|---|
|
|
29
|
-
| Soak | At least 24 hours at expected peak and burst load through actual TLS/ingress; native/function mix, slow clients, response correctness, p50/p95/p99, throughput, error/rejection rate, RSS plateau, CPU and FD growth. Define numerical SLOs before starting |
|
|
30
|
-
| Kill/restart | Kill the server with outstanding requests. Reconcile uncertain mutations by request ID, restore readiness, and prove no duplicate side effects from application code |
|
|
31
|
-
| Rollback | Deploy candidate by exact digest beside last-good, run route assertions, switch ingress, drain, then switch back. Verify configuration/policy compatibility |
|
|
32
|
-
| Monitoring | Deliver test alerts for missing logs, sustained errors, pool rejection/failure, low disk, restart storms and failed readiness to a named on-call owner |
|
|
33
|
-
|
|
34
|
-
If your deployment adds a durable-state extension, add that extension's own
|
|
35
|
-
backup/restore, logical export/import and disk/log exhaustion exercises to
|
|
36
|
-
this table; core's proof above does not cover them.
|
|
37
|
-
|
|
38
|
-
Record date, operator, source/app/policy/image digests, topology, hardware/limits,
|
|
39
|
-
commands, duration, synthetic dataset size, raw metrics/log locations, result
|
|
40
|
-
and unresolved findings. A reviewer signs the acceptance record; a blank
|
|
41
|
-
record is not a pass.
|