Commit 445a25f
fix(spec): reconcile SKILL_MAP with the SKILL.md bodies that own each surface, and guard it (#15268)
* fix(spec): narrow the objectstack-ai reference index to what its SKILL.md teaches
The published `objectstack-ai` index advertised five schemas the body never
teaches -- `conversation`, `mcp`, `embedding`, `knowledge-document`, `usage`,
three of them with zero consumers outside `packages/spec` -- and omitted
`ai/solution-blueprint.zod.ts`, the schema behind the `solution_design`
built-in skill the body's own table names. An index entry is a POINTER, so
pointing at a schema the body cannot help with sends an agent reading into
`node_modules` for guidance that does not exist. The schemas keep existing and
stay importable; only the false promise leaves.
`embedding` is still published, as a transitive dependency:
`knowledge-source.zod.ts` composes `EmbeddingModelSchema`, so that pointer IS
reachable from the authorable face -- the test the other four fail.
Adds the first map-level guard, in `scripts/lib/` beside `export-list.ts` and
`file-description.ts` because the generator self-executes on import: a core
entry that is not a `*.zod.ts` path emits no pointer row at all today, with no
`missing` row and a green `--check`, because the closure filter drops it
silently before the index template ever sees it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2oQebDDxYKfWZusyd8GXk
* fix(spec): stop routing automation authors at the ADR-0020-retired state machine
The published `objectstack-automation` index pointed at
`automation/state-machine.zod.ts`. ADR-0020 retired that shape AS A
RECORD-LIFECYCLE DECLARATION -- the top-level `workflow` metadata type and
`object.stateMachines` are both gone -- and a record's legal transitions are
now a `state_machine` validation rule in `data/validation.zod.ts`, which was
already the last entry of the same list. So the index did not merely
over-promise: it pointed at something the platform deliberately removed from
this package's surface. The file's one surviving door is `ai/agent.zod.ts`'s
`lifecycle`, an objectstack-ai door, and that index reaches it transitively.
The other direction, from the same audit: the two per-node-type `config`
schemas the body teaches were in no package's list at all. Screen `fields` and
the ADR-0031 loop/parallel/try_catch containers reach
`automation/builtin-node-config.zod.ts`; `NotifyConfigSchema` and the `http`
`timeoutMs` reach `automation/io-node-config.zod.ts`. An agent told to always
read the source for exact field shapes had no route to either.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2oQebDDxYKfWZusyd8GXk
* fix(spec): objectstack-query is the sole owner of date macros, and the map now says so
`data/date-macros.zod.ts` sat in two core lists, so the published
`objectstack-formula` index carried a Date Macro Tokens row while its own
SKILL.md routes that surface away: view list filters are not a CEL surface, and
the token list lives in objectstack-query's `rules/filters.md`. The catalog's
whole contract is "this package owns this surface", and a reader following the
formula index landed on a schema that skill will not teach.
The duplicate is the whole defect, so the entry leaves the formula list and the
generator gains the guard that refuses the next one at generation time.
The guard is NOT the flat "at most one owner" rule it was asked for, and the
reason is measured rather than argued: `date-macros` was one of FOUR duplicates
in the map, and the other three are deliberate -- `data/validation.zod.ts`
(data + automation) and `data/datasource.zod.ts` / `data/seed.zod.ts` (data +
platform, the surface absorbed from the retired quickstart skill, already
carrying that reason as a comment). The flat rule would refuse the map on its
first run. So duplicates must now be DECLARED with a reason in
`SHARED_CORE_SCHEMAS`, and two further guards keep that ledger from becoming a
silent allowlist: a row with no reason is refused, and so is a row whose
sharing has gone. The deviation from the letter of the instruction is recorded
in the ledger's own header and in the PR body.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2oQebDDxYKfWZusyd8GXk
* fix(spec): stop the i18n index pointing at five schemas a translation bundle cannot reach
The `objectstack-i18n` core list is correct; the damage was downstream, in the
transitive closure. Eight pointers shipped in that index and seven arrived
through a single edge: `shared/strict-object.ts` imports
`shared/suggestions.zod.ts` for its "did you mean?" text, which imports
`data/field.zod.ts`, which drags in filter, expression, field-value,
identifiers and value-domain. That is a schema-building helper's
implementation, not the authorable shape of a translation bundle -- which
addresses everything by name string. The largest of them, the Unified Query
DSL, is a different skill's whole subject, shipped into every i18n session with
an instruction to read it.
The feasibility question the finding asked -- a general reachability rule, or a
per-package list -- is answered first, and against the general rule. Cutting
traversal through non-shipping helpers is the precise version of that rule, and
it removes five of the five pointers named; it also removes
`shared/identifiers.zod.ts`, which must STAY (bundle keys are exactly those
`snake_case` identifiers, and the SKILL.md spends a table and a "Critical:" note
on it, while nothing imports the file), and it keeps
`kernel/metadata-protection.zod.ts`, which must go (a first-class direct
import). A depth-4 pointer reached through a helper belongs on the keep side
and a depth-1 pointer reached through a schema edge on the drop side: no
predicate over the import graph orders those that way, because the fact that
separates them is not in the graph.
So: a per-package allowlist beside the map, opt-in, with a guard that refuses a
package name the map does not have, a file the closure never reaches, a file
that is already core, and a repeat. An allowlist rather than a denylist because
`shared/value-domain.zod.ts` joined this index recently and unnoticed, when a
new import edge appeared several files away -- a denylist misses every new
arrival by construction. `data/field.zod.ts` is kept deliberately:
`FieldTranslationSchema.options` is keyed by select-option value, and
`SelectOptionSchema` is the declaration those keys must match.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2oQebDDxYKfWZusyd8GXk
* fix(spec): keep internal tracker ids out of the published skill catalog
`check:doc-authoring` went red the moment `automation/io-node-config.zod.ts`
joined a package list: the generator publishes the first sentence of a module's
doc block, and that file's opens with a bare tracker citation. The gate has no
per-passage exemption, by design, and its argument is the audience -- `skills`
ships to customer projects and is loaded WHOLE into customer context windows,
where a tracker id resolves to nothing for the people paying for the tokens.
The gate prescribes stripping the id AT THE SOURCE and regenerating. That
remedy is not taken here, and the reasons are specific rather than convenient:
the source is a `.zod.ts` this card is forbidden to edit; it is a file the
package PUBLISHES, so editing it would change what `@objectstack/spec` ships
and reopen the changeset decision this PR settled from precedent; and the same
sentence is also projected to `content/docs/references/automation`, which would
drag a second generated tree into a diff whose surface is the skill catalog.
Three surfaces to remove one token.
The decisive measurement is that the gate does NOT flag that identical sentence
on the docs page: the rule is about the skill catalog specifically. So the
strip is applied at the boundary INTO that catalog, where the rule lives. Every
future pointer row is covered, rather than this one being corrected once.
The criterion is the gate's own, restated with a pin over the shapes that must
and must not match -- an ordinal, a hex colour, an over-long number and a
doubled hash all survive untouched.
This is a deviation from the gate's stated remedy and is flagged as such in the
PR body for a reviewer to overrule.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2oQebDDxYKfWZusyd8GXk
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 0e24b0c commit 445a25f
7 files changed
Lines changed: 639 additions & 24 deletions
File tree
- packages/spec/scripts
- lib
- skills
- objectstack-ai/references
- objectstack-automation/references
- objectstack-formula/references
- objectstack-i18n/references
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
31 | 39 | | |
32 | 40 | | |
33 | 41 | | |
| |||
100 | 108 | | |
101 | 109 | | |
102 | 110 | | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | 111 | | |
107 | | - | |
108 | | - | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
109 | 123 | | |
110 | 124 | | |
111 | 125 | | |
| |||
121 | 135 | | |
122 | 136 | | |
123 | 137 | | |
124 | | - | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
125 | 146 | | |
126 | 147 | | |
127 | 148 | | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
128 | 155 | | |
129 | 156 | | |
130 | 157 | | |
| |||
162 | 189 | | |
163 | 190 | | |
164 | 191 | | |
165 | | - | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
166 | 197 | | |
167 | 198 | | |
168 | 199 | | |
| |||
271 | 302 | | |
272 | 303 | | |
273 | 304 | | |
274 | | - | |
| 305 | + | |
275 | 306 | | |
276 | 307 | | |
277 | 308 | | |
278 | | - | |
| 309 | + | |
279 | 310 | | |
280 | 311 | | |
281 | 312 | | |
| |||
359 | 390 | | |
360 | 391 | | |
361 | 392 | | |
362 | | - | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
363 | 400 | | |
364 | 401 | | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
365 | 410 | | |
366 | 411 | | |
367 | 412 | | |
| |||
370 | 415 | | |
371 | 416 | | |
372 | 417 | | |
373 | | - | |
| 418 | + | |
374 | 419 | | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
375 | 431 | | |
376 | 432 | | |
377 | 433 | | |
| |||
0 commit comments