Commit 6f23667
* fix(spec,cli): config.storage was never a stack key — stop reading it, and report undeclared top-level keys (#4167)
`os serve` read `config.storage` and forwarded it to StorageServicePlugin. It
could almost never arrive: `ObjectStackDefinitionSchema` does not declare
`storage` and is not `.strict()`, so `defineStack` — which every documented
authoring path and every compiled artifact goes through — strips the key before
serve runs. The one combination that reached the branch (bare-object config on
the config-boot path) then carried the `driver`/`root` spelling the plugin does
not read either.
So one authoring key worked on a single unreachable-in-practice path and
vanished silently everywhere else. A host writing `storage: { driver: 's3', … }`
believed it had configured S3 and got local disk.
- serve no longer reads it. `resolveStorageCapabilityArg` takes only the env
root, and the production warning names the two channels that work
(OS_STORAGE_*, Setup → Settings) instead of advertising the one that does not.
- `lintUnknownAuthoringKeys` now covers TOP-LEVEL stack keys, not just object
and field keys. `storage` gets a prescriptive entry naming both channels and
why a stack definition is the wrong home for a credential — it would commit it
to git and to any published artifact. An ordinary misspelling still gets the
edit-distance suggestion (`datasource` → `datasources`), and the rule now runs
on a stack with no `objects` at all, which previously exited early.
- `os migrate files-to-references` shares the resolver. It built the same dead
`{ driver: 'local', root }`, so its adapter used `./storage` while the server
writes under `.objectstack/data/uploads` since #4096 — and that command
reconciles what records claim against what storage holds, so a disagreeing
root reconciled against the wrong tree.
`lintUnknownAuthoringKeys(rawStack)` becomes `(rawStack, stackSchema)`. Required
rather than optional so a caller that forgets it fails to compile instead of
silently losing the check — the exact failure this rule reports. Injected rather
than imported because stack.zod.ts imports this module and importing back would
close a cycle.
Verified end to end: authoring `storage:` through defineStack warns at load
("defineStack: stack.storage: 'storage' is not a declared stack key…"), and
`os compile` reports it for configs that skip defineStack. spec 273 files /
7150 tests, cli 89 files / 916 tests, root `pnpm lint` clean.
Nothing that worked is being removed: `storage` was never in the schema, is
undocumented, and has no consumer in objectstack-ai/cloud (checked directly).
Closes #4167
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HrRNgrWaRtggzmrHpbomyh
* docs(validating-metadata): the undeclared-key rule covers top-level stack keys too (#4167)
The one page that documents the #3786 rule described it as object/field only,
which #4167 made inaccurate: `lintUnknownAuthoringKeys` now also reports keys
`ObjectStackDefinitionSchema` does not declare.
- Section title and intro name all three schemas.
- The gate table row becomes "Undeclared stack/object/field keys".
- Adds the top-level case with both guidance kinds, using `storage` as the
worked example — it is the one where the silence is easiest to miss, because
the key reads as configuration that took effect, and points at the two
channels that do configure storage (OS_STORAGE_* and the Settings UI, which is
also where credentials belong — a stack definition is committed to git and
compiled into any published artifact).
Docs-only; found by acting on the docs-drift advisory rather than waving it
through. `pnpm check:doc-authoring` clean.
Refs #4167
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HrRNgrWaRtggzmrHpbomyh
* chore(spec): record STACK_KEY_GUIDANCE in the API-surface snapshot (#4167)
`check:api-surface` failed on the #4167 commit: exporting `STACK_KEY_GUIDANCE`
adds to `@objectstack/spec`'s public surface, and the generated snapshot is the
gate's baseline.
./data
+ STACK_KEY_GUIDANCE (const)
@objectstack/spec public API changed: 0 breaking (removed/narrowed), 1 added.
Regenerated with the command the gate names. The diff is one line — the export
is additive, alongside the FIELD_KEY_GUIDANCE / OBJECT_KEY_GUIDANCE entries it
sits with, and nothing was removed or narrowed.
Also ran the rest of the gates CI runs, so this does not take another round:
doc-authoring, role-word, org-identifier, authz-resolver, route-envelope,
error-code-casing, wildcard-fallthrough, release-notes, node-version, plus the
spec package's authorable-surface, spec-changes, upgrade-guide, liveness and
docs checks — all green.
Refs #4167
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HrRNgrWaRtggzmrHpbomyh
* refactor(spec): rebuild the top-level stack-key lint on the #4178 walker
#4178 landed while this was in review and refactored the unknown-authoring-key
rule from a two-surface check (object/field) into a walker that derives its
coverage from `PLURAL_TO_SINGULAR` and reads each schema's own unknown-key
posture. This rebases the #4167 contribution onto that architecture instead of
carrying the version it replaced.
What changes vs. the pre-merge shape:
- `lintUnknownAuthoringKeys` keeps its single-argument signature. The top-level
pass is a separate export, `lintUnknownStackKeys(rawStack, stackSchema)`.
- It reuses the walker's own `keyPosture`, so it lints only while the stack
schema STRIPS unknown keys and goes quiet if that schema is ever made strict —
the lint can never become a second, disagreeing voice next to the parse.
- `STACK_KEY_GUIDANCE` stays in `data/authoring-key-lint.ts` beside the other
two curated tables, held to the same non-rotting discipline.
The two passes are complementary, not redundant: the walker iterates metadata
COLLECTIONS, so a stack whose only mistake is at the envelope level — no
objects, no pages, nothing to iterate — walks clean and reports nothing. A test
pins exactly that.
Verified end to end after the rebuild: `defineStack` warns at config-load time,
and `os validate` / `os build` report `stack.storage` with its prescription plus
`stack.datasource` with its edit-distance suggestion, alongside the field-level
findings the walker already produced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HrRNgrWaRtggzmrHpbomyh
* docs(cli): name the right lint export in the serve storage doc-comment
The top-level coverage moved to `lintUnknownStackKeys` when this was rebuilt on
#4178's walker; the comment still pointed at `lintUnknownAuthoringKeys`, which
no longer does that job.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HrRNgrWaRtggzmrHpbomyh
* fix(spec): the top-level lint must not call a working `onEnable` dropped
Caught by running the new lint against our own shipped examples before
merging: `examples/app-todo` and `examples/app-showcase` both got
⚠ stack.onEnable: 'onEnable' is not a declared stack key, so its value is
dropped at load.
which is false. `onEnable` is a function, so `ObjectStackDefinitionSchema`
cannot declare it and `dist/objectstack.json` cannot carry it — but `AppPlugin`
reads it straight off the authored bundle and calls it at `start()`
(`app-plugin.ts`), and the artifact-boot path grafts it back (#4095). It is the
documented place to register action handlers. "Not declared" and "dropped at
load" are different claims, and this is the one surface where they come apart.
New `STACK_RUNTIME_MEMBERS` names the authored top-level members the runtime
honours off the bundle; `lintUnknownStackKeys` treats them as declared. The
CLI's `GRAFTABLE_RUNTIME_MEMBERS` is now DERIVED from it instead of restating
it — two hand-written copies could disagree, and the disagreement would be
silent in exactly the direction this lint family exists to catch.
`onDisable` is deliberately excluded from the exemption: it is declared in the
protocol but no kernel, runtime or service calls it, so a value written there
really does go nowhere. A test pins both halves of that distinction.
Re-harvested after the fix: all three shipped examples validate with zero
undeclared-key warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HrRNgrWaRtggzmrHpbomyh
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent e59786e commit 6f23667
14 files changed
Lines changed: 395 additions & 66 deletions
File tree
- .changeset
- content/docs/deployment
- packages
- cli/src
- commands
- migrate
- utils
- spec
- src
- data
- kernel
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
248 | 248 | | |
249 | 249 | | |
250 | 250 | | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
251 | 271 | | |
252 | 272 | | |
253 | 273 | | |
| |||
273 | 293 | | |
274 | 294 | | |
275 | 295 | | |
276 | | - | |
| 296 | + | |
277 | 297 | | |
278 | 298 | | |
279 | 299 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
263 | 264 | | |
264 | 265 | | |
265 | 266 | | |
266 | | - | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
267 | 271 | | |
268 | 272 | | |
269 | 273 | | |
| |||
Lines changed: 12 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
| 20 | + | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
39 | | - | |
40 | | - | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
41 | 48 | | |
42 | 49 | | |
43 | 50 | | |
| |||
48 | 55 | | |
49 | 56 | | |
50 | 57 | | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
| 58 | + | |
| 59 | + | |
64 | 60 | | |
65 | 61 | | |
66 | 62 | | |
| |||
Lines changed: 23 additions & 25 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
19 | 26 | | |
20 | 27 | | |
21 | 28 | | |
| |||
24 | 31 | | |
25 | 32 | | |
26 | 33 | | |
27 | | - | |
| 34 | + | |
28 | 35 | | |
29 | 36 | | |
30 | 37 | | |
| |||
33 | 40 | | |
34 | 41 | | |
35 | 42 | | |
36 | | - | |
| 43 | + | |
37 | 44 | | |
38 | 45 | | |
39 | 46 | | |
40 | 47 | | |
41 | 48 | | |
42 | | - | |
| 49 | + | |
43 | 50 | | |
44 | 51 | | |
45 | 52 | | |
46 | 53 | | |
47 | 54 | | |
48 | 55 | | |
49 | | - | |
| 56 | + | |
50 | 57 | | |
51 | 58 | | |
52 | 59 | | |
53 | 60 | | |
54 | 61 | | |
55 | 62 | | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
61 | 68 | | |
62 | 69 | | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
71 | 77 | | |
72 | 78 | | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | 79 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2184 | 2184 | | |
2185 | 2185 | | |
2186 | 2186 | | |
2187 | | - | |
2188 | | - | |
2189 | | - | |
2190 | | - | |
| 2187 | + | |
2191 | 2188 | | |
2192 | 2189 | | |
| 2190 | + | |
| 2191 | + | |
2193 | 2192 | | |
2194 | | - | |
| 2193 | + | |
2195 | 2194 | | |
2196 | 2195 | | |
2197 | 2196 | | |
| |||
2665 | 2664 | | |
2666 | 2665 | | |
2667 | 2666 | | |
2668 | | - | |
2669 | | - | |
2670 | | - | |
2671 | | - | |
2672 | | - | |
2673 | | - | |
| 2667 | + | |
| 2668 | + | |
| 2669 | + | |
| 2670 | + | |
| 2671 | + | |
| 2672 | + | |
| 2673 | + | |
| 2674 | + | |
| 2675 | + | |
| 2676 | + | |
| 2677 | + | |
| 2678 | + | |
| 2679 | + | |
| 2680 | + | |
| 2681 | + | |
2674 | 2682 | | |
2675 | | - | |
2676 | | - | |
2677 | | - | |
2678 | | - | |
2679 | | - | |
2680 | | - | |
2681 | | - | |
| 2683 | + | |
2682 | 2684 | | |
2683 | 2685 | | |
2684 | 2686 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
97 | 98 | | |
98 | 99 | | |
99 | 100 | | |
100 | | - | |
101 | | - | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
102 | 105 | | |
103 | 106 | | |
104 | 107 | | |
| |||
0 commit comments