Repository navigation
Commit 8323cad
test(dogfood): the anonymous door sweep — ADR-0138 criterion 2 as an in-repo conformance test (#23137)
Part of #22146
Clause-②: no (a test; no published accept set or public surface changes)
## Summary
The test half of ruling `6134095444`: ADR-0138 acceptance criterion 2 is
evidenced by a test in the tree, not by a one-off sweep. This PR adds
one dogfood suite and its classification table, nothing else:
- `packages/qa/dogfood/test/anonymous-door-sweep.dogfood.test.ts`
- `packages/qa/dogfood/test/anonymous-door-sweep.classification.ts`
No production code changes, and nothing under `docs/adr/**`. The
criterion-2 wording and criterion 4's two back-pointers are the next PR
of the ruling, after this test is green on `main`.
**The suite is red on this head. That is the result, not a defect in the
test.** The everything-else class does not answer 401 throughout: one
surface family outside D2's list answers an anonymous caller without the
401 floor (class-level result below). As the dispatch orders, the
assertion stays as D2 states it. It is not weakened and not marked
expected-to-fail, and the PM decides the next step.
## What the suite does
1. **Boot.** It boots the showcase the way a deployment serves it:
`bootShowcase`, plus two extras. `MetadataPlugin` runs over a
build-shaped artifact, because `createStandaloneStack` composes it and
declared endpoints only route through it. `MCPServerPlugin` is added
because `serve` mounts MCP by default.
2. **Population.** It reads the registered route table off the running
server, from three sources:
- the port, `getMountedRoutes()`;
- the framework's own table, `getRawApp().routes`, which also holds the
raw-app mounts;
- the declared ADR-0121 endpoints the metadata service matches.
Nothing in the population is listed by hand. The derivation is guarded
by a floor per source, by asserting the port table is a subset of the
framework table, by one anchor per source, and by checking that every
declared endpoint resolves through `matchEndpoint` to itself.
3. **One unauthenticated request per route.** Middleware is the
exception, because it serves no request of its own.
4. **A closed table.** Each answer is judged by the route's class: D2's
six door classes, the surfaces outside the guest model by construction,
middleware, and nothing else.
- A route the table does not claim is in the everything-else class. It
must answer 401 with an error envelope and nothing served, so a new
anonymous door nobody classified turns the suite red.
- A classification row that claims no route on the boot is red as stale.
5. **Every class on a real subject, admitted and refused.** A sweep
whose probes name nothing can only observe the refusal half of a door.
The subjects are the showcase's own or are created in-test while signed
in: the public form, the shareable brief, the public book, an uploaded
file. The showcase has no subject for three of the legs, so they are
declared inline beside it:
- an anonymous `object_operation` endpoint and an anonymous `flow`
endpoint, over a flow with no data step;
- an `org`-audience book;
- two docs.
The showcase's own two declared endpoints are `authRequired: true` and
of the same two types, so they serve as the negative controls of classes
4 and 5.
The assertions are class-level. A failure names the route pattern and
the status class it answered, never a body.
## Classification, for the ADR amendment to name
**Door classes (D2):**
| class | routes |
|:--|:--|
| 1 · the public form doors | resolve and submit |
| 2 · share links | the two token-addressed reads |
| 3 · the public book and doc reads | no route of its own: it rides the
shared `/meta` read routes, which the sweep holds to 401 for every other
subject |
| 4 · declared `authRequired: false` endpoints, `object_operation` | the
declared endpoints |
| 5 · declared `authRequired: false` endpoints, `flow` | the declared
endpoints |
| 6 · the `acl: 'public_read'` file download | both download routes |
**Outside the guest model by construction.** Each row carries its reason
in the table.
- D2's own list:
- the control-plane allowlist, by its own predicate
`isAuthGateAllowlisted`, never a copy of its paths;
- the ADR-0041 signed inbound hook, asserted 401 on a missing signature,
as D2 states;
- MCP, asserted 401, as D2 states.
- The surfaces the ruling ordered classified explicitly:
- the approvals plugin's token-addressed action pages (the ADR-0043
action token);
- the OAuth and OpenID well-known documents;
- `RuntimeConfigPlugin`'s bootstrap reads;
- the MCP skill document read;
- the dev-only metadata HMR stream, which this boot mounts because
`bootStack` runs the development posture.
- Two more that the population held and the round-5 list did not name:
- the discovery documents, at the two spellings `isAuthGateAllowlisted`
does not name literally (the REST root and the dispatcher's well-known
document);
- the local storage adapter's signed byte URLs, where the signed,
expiring token an authorized upload or download mints is the credential.
**Middleware:** the server-wide `*` registrations.
## Class-level result on this head
| class | matches D2 |
|:--|:--|
| 1 · public form doors | yes |
| 2 · share links | yes |
| 3 · public book and doc reads | yes |
| 4 · `authRequired: false` `object_operation` | yes (D3's empty state:
the guest holds no grant, so it is denied) |
| 5 · `authRequired: false` `flow` | yes (admitted at the door as the
guest; the flow's output only) |
| 6 · `acl: 'public_read'` file download | yes |
| everything else | **no**: one family |
The family, at function level only: the settings service's REST family
(`registerSettingsRoutes` in `@objectstack/service-settings`, mounted by
`SettingsServicePlugin`). It opens no handler with an anonymous floor,
so it answers an anonymous caller from its own authorization logic
rather than with 401. On this boot it served no settings value. The
status class is still not the one D2 states. The finding is in the
report on the card, for the seat to file.
## Proof the suite can fail
Two ablations, each through `scripts/ablation-replace.mjs` (anchor hit,
then restore proven by blob hash against `HEAD` and an empty `git diff
HEAD`):
- **A, the closed table.** One outside-family row (the runtime-config
bootstrap reads) was deleted from the classification.
- Mutated: the everything-else case named 4 → 6 routes, and every other
case was unchanged.
- Restored: blob `eb86b66c06` == `HEAD`.
- **B, a product floor, through `dist/`.** One dispatcher domain's
anonymous floor (`shouldDenyAnonymous` in the `/i18n` domain of
`@objectstack/runtime`) was bypassed in source, and runtime was rebuilt.
- Mutated: `ablation-dist-preflight` found the marker in 2 built files,
and the everything-else case named 4 → 7 routes.
- Restored: blob `d111be27d4` == `HEAD`, runtime rebuilt, the marker
absent from all 6 built files, the tree clean, and the everything-else
case back to the same 4.
Both moved in the predicted direction: more diagnostics, not a different
failure.
## Tests
Run on head `bf3ba9957`, which merges `origin/main` `dfb3ba2b2`:
- `pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2
test/anonymous-door-sweep.dogfood.test.ts
test/authz-conformance.test.ts`: 74 passed, 1 failed. The failing case
is the everything-else one, on the settings family. `authz-conformance`
is green; it scans every `*.dogfood.test.ts` for row attribution, and
the new file claims no row.
- `pnpm --filter @objectstack/dogfood typecheck` (`tsc --noEmit`): exit
0. `tsc --listFilesOnly` lists both new files in the program.
- eslint, narrowed to the 2 new files with `--no-inline-config --format
json`: 0 errors, 0 warnings over 2 files. The narrowing is safe because
`eslint.config.mjs` enables no type-aware linting (no
`parserOptions.project` or `projectService` in the printed config), so
no untouched file's result can change.
- Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derives 53 commands from the merge base. All
53 were run on this head and every one exits 0. The `--ran` reconcile
prints `53 derived famil(ies) accounted for — 53 run, 0 NOT-MEASURED (a
DERIVED zero — all 53 recorded an exit code and none of them is 3)`. Two
of them first exited 3 (PREREQUISITE NOT MET): one needed a pinned
commit the shallow clone lacked, and one needed 8 packages that had no
`dist/`. Each was re-run once its prerequisite was met, and both exit 0.
## Acceptance notes
- **Where it runs.** The suite lands in the `isolated` dogfood project
by its file name; `packages/qa/dogfood/vitest.config.ts` is untouched,
and PR #22962 edits it. It runs with the dogfood battery, which is part
of `main`'s full battery. PR and queue runs carry the fast set, so this
draft's own CI does not run it. Landing it while the settings family is
open makes the dogfood battery red on `main`.
- **Two codes for one refusal.** The everything-else 401s carry two
codes: `UNAUTHENTICATED`, and `AUTH_REQUIRED` on the storage family's
refusals. D2's text names `UNAUTHENTICATED`, while criterion 2 judges
the status class and what is served. The suite asserts the status and
the error envelope and does not pin the code. Noted for the ADR wording
PR.
- **What a failure prints.** A failure message names the route pattern
and the status class. On `main` that text lands in CI logs. It never
carries a body.
- **No changeset.** The dogfood package is private, so this PR carries
`skip-changeset`.
## Unparked (written by the seat, `domain:spec` seat 2)
- **Was parked** in draft on #23142: the settings REST family answered
anonymous callers without the 401 floor, and this suite's
everything-else case was red on that family alone.
- **Unlocked:** #23142 landed on `main` as `20949e7a7` (PR #23171).
`main` was merged into this branch through the relay's base sync (head
`87a0f748a`).
- **Re-run on `87a0f748a`** (the dev's run through `os-verify-lock`):
`Tests 75 passed (75)` across this suite and `authz-conformance`, and
the dogfood typecheck exits 0. Classes 1 to 6 match D2, and the
everything-else class answers 401 throughout (the suite's floor of more
than 100 routes judged held).
- The dogfood battery is not in PR or queue CI; it runs on `main`, where
this suite now runs with it.
- The criterion-2 wording PR on `docs/adr/**` (Tier H) follows once this
suite is green on `main`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01BRQ9Mp1yH7R1az6MaUM18H)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com>1 parent d4aaeaf commit 8323cad
2 files changed
Lines changed: 997 additions & 0 deletions
File tree
- packages/qa/dogfood/test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
0 commit comments