Repository navigation
Commit be7763a
fix(client): the published README's
Fixes #18607
The `client.packages.install` example in the **published**
`@objectstack/client`
README shipped a manifest that the contract its own call site declares
refuses on
three counts. The example is what was wrong; `ManifestSchema`'s strict
close is
what is right, so nothing in `packages/spec` moves here.
## BEFORE — the three refusals, reproduced
Driven against `PackageInstallRequestSchema` (whose `manifest` key
**is**
`ManifestSchema`), with the literal read out of the README **by
content**, not by
line number:
```
LITERAL AS PUBLISHED:
{
name: 'vendor_plugin',
label: 'Vendor Plugin',
version: '1.0.0',
}
success: false
code=invalid_type path=[manifest, id]
code=invalid_value path=[manifest, type]
code=unrecognized_keys path=[manifest] keys=["label"]
ISSUE COUNT: 3
```
All three reproduce exactly as the card states them.
## AFTER — parsed and accepted
```
LITERAL AS PUBLISHED:
{
id: 'com.vendor.plugin',
type: 'plugin',
name: 'Vendor Plugin',
version: '1.0.0',
}
success: true
```
Parsed value, defaults applied:
`{"id":"com.vendor.plugin","defaultDatasource":"default","version":"1.0.0","type":"plugin","scope":"project","name":"Vendor
Plugin"}`
### Controls — each one had to fire, and each did
| candidate | verdict |
|:--|:--|
| chosen manifest | accepted |
| chosen minus `id` | `invalid_type` at `[manifest, id]` |
| chosen plus `label` | `unrecognized_keys` at `[manifest]` |
| chosen with `type: 'vendor'` | `invalid_value` at `[manifest, type]` |
## `label` at the ROOT — what the schema actually declares
Read off the schema itself, not off a neighbour: `ManifestSchema`
declares **25**
root keys and `label` is **not** one of them. The required-and-absent
set, taken
from `ManifestSchema.safeParse({})`, is `id name type version`.
`name` — already present in the old example — is declared as the
*human-readable*
package name, so the old `label: 'Vendor Plugin'` value moves to `name`,
and the
machine identifier becomes the reverse-domain `id` that key documents.
The retired `{ id, label, path }` shape carrying a `label` near this
surface is
`manifest.contributes.themes`, a **nested** block removed in v17.x — a
sibling of
the root, never the root itself. A root `label` was never declared and
was
therefore never retired: it gets the strict close's plain
unrecognised-key
refusal, with the surface's own history text attached.
## Why `type: 'plugin'`
The closed set, quoted from the refusal the schema itself emits:
`"plugin" | "ui" | "driver" | "server" | "app" | "theme" | "agent" |
"objectql" | "module" | "gateway" | "adapter"`.
`plugin` is the schema's own "general-purpose functionality extension",
and it is
what this example's subject already says it is — the literal was named
`vendor_plugin`/`Vendor Plugin` and the line beneath it enables a plugin
id.
`app` is the consumer-installable business bundle (ADR-0019, the one
user-visible
noun a tenant browses and installs) and would change what the example
teaches;
`gateway` is marked deprecated in the enum's own docblock. Not the first
member
that parses — the member the example means.
## Duplicate copies — reported, NOT silently folded in
Swept the tree for the same example and for the same defect class.
* **The same literal: nowhere else.** `vendor_plugin` and `Vendor
Plugin` each
occur exactly once in the repository, both in this README.
* **Same defect class, different literal, ⛔ NOT in this PR:**
`content/docs/api/client-sdk.mdx` line 337 —
`await client.packages.install({ id: 'com.objectstack.plugin-auth',
version: '1.0.0' });`
— refused on **two** counts, `invalid_value` at `[manifest, type]` and
`invalid_type` at `[manifest, name]`. It is a hand-written page (it is
listed in
`scripts/docs-audit/handwritten-docs.json`). Left for a ruling rather
than
silently widened or silently left broken.
* **Not a defect:** `packages/client/src/return-type-precision.test.ts`
carries the
same two-count shape, but as a `expectTypeOf` pin against a parameter
typed
`any`; nothing parses it and it is not published material.
* **Not a duplicate:** the many `label:` keys under
`content/docs/references/api/`
belong to *sibling* collections (`apps`, `pages`, `flows`, `agents`, …)
which
each declare `label` legitimately. That is the root-versus-sibling
distinction,
confirmed rather than assumed.
* `packages/client/CHANGELOG.md` also quotes an install call.
Release-owned —
untouched, by rule.
## Is there already a pin? Measured, with a control
**No — and the two nearest instruments are provably blind to this
class.**
`scripts/measure-markdown-ts-blocks.mjs` compiles the fenced TypeScript
blocks of
package-root Markdown, which is exactly this file. Run on this README
before and
after the fix, its per-block diagnostic sets are **byte-identical**:
```
fixed : [{"i":0,"tf":true,"codes":"2307"},{"i":1,"tf":true,"codes":"2304,18046,18046,18046,18046,18046"},{"i":2,"tf":false,"codes":"2304"},{"i":3,"tf":true,"codes":"2304,2304,2304,2304,2552,2552,18004,2304"}]
broken : [{"i":0,"tf":true,"codes":"2307"},{"i":1,"tf":true,"codes":"2304,18046,18046,18046,18046,18046"},{"i":2,"tf":false,"codes":"2304"},{"i":3,"tf":true,"codes":"2304,2304,2304,2304,2552,2552,18004,2304"}]
IDENTICAL: true
```
That is a **lit** instrument, not a dark one: it reports live
diagnostics on other
blocks of the same file in both runs. It cannot see this defect because
`install` declares its first parameter `any`
(`packages/client/src/index.ts`, `install: async (manifest: any, …)`),
so `tsc`
has nothing to check. It is also a census by its own header, wired to no
CI job.
`check:published-readme-exports` has the right population but reads
fenced blocks
for **imported symbols** and has no notion of a schema.
`check:skill-examples` type-checks marked prose blocks on a `client SDK`
surface,
but `packages/client/README.md` is not in its population at all, and the
`client.packages.install` block on the one SDK docs page that *is*
carries no
check marker.
So this PR adds the pin:
`packages/client/src/readme-package-install-example.test.ts`
extracts every `packages.install` manifest literal from this README with
the
TypeScript AST and parses it against `PackageInstallRequestSchema`. It
refuses to
pass on an empty corpus, and it throws rather than skipping on an object
member it
cannot model.
### Ablation — the pin can fail, proven from the committed state
Restored the original broken literal, proved the mutation on disk
(injected text
count 1, removed text count 0, blob `93c5186941…` distinct from `HEAD`
blob
`59ea1c0638…`), ran the pin:
```
× README line 246 is accepted by PackageInstallRequestSchema
→ invalid_type at [manifest, id]; invalid_value at [manifest, type]; unrecognized_keys at [manifest]
Test Files 1 failed (1)
Tests 1 failed | 1 passed (2)
```
The `1 passed` is the anti-vacuity floor still finding the corpus, so
the red is
about the manifest and not about a lost anchor. Restored with
`git checkout HEAD -- packages/client/README.md`; the restored blob
hashes back to
`59ea1c0638…` and `git diff HEAD` plus `git status --porcelain` are both
empty.
## Verification
| run | verdict |
|:--|:--|
| `pnpm --filter @objectstack/client test` | exit 0 — 45 files, 537
tests passed |
| `pnpm --filter @objectstack/client typecheck` | exit 0 — test layer
compiles, 0 errors, 0 pinned signatures |
| `pnpm --filter '@objectstack/client^...' build` | exit 0 — dependency
closure, 103 build successes |
| dispatch-gates families | 58 derived, 58 run, **0 NOT-MEASURED, 0
UNRUN** (reconciled with exit codes via `--ran`) |
| eslint, narrowed | exit 0 — 1 file linted, 0 errors, 0 warnings |
Two gate families first answered PREREQUISITE NOT MET on an unbuilt
checkout
(`check:skill-examples`, `check:dual-build-cjs-loads`); both exit 0
after
`pnpm build`. Readings taken at `a37583f9c1`.
**The eslint narrowing is a measurement, not a skip.** Population, read
from
`eslint.config.mjs` itself: files matching
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}`
minus the config's five global ignores — so the two Markdown files in
this diff are
outside eslint's population by the config's own selectors. Count, read
from
`--format json`: 1 file. Invariance, quoted from that config: *"this
repo runs one
`eslint.config.mjs`, which never enables type-aware linting (no
`parserOptions.project`, no typed `@typescript-eslint` rules) for ANY
file, test or
not"* — so this diff cannot move the verdict on any untouched file.
## Changeset
`patch` on `@objectstack/client`, measured rather than assumed: the
package's
`files[]` is `["dist", "README.md", "CHANGELOG.md"]` and the package is
not
private, so this README **is** published output and `skip-changeset` —
the label
for a diff that publishes nothing from any released package — does not
apply.
Clause-②: no
No schema key, no closed-set member, no published export and no registry
entry is
added or narrowed; the only TypeScript this PR adds is a test.
## Acceptance notes
* Triage placed an **ordering constraint** on this card: it must land
before, or
together with, #18604 / #18606 — closing that door without this repair
turns a
silently-wrong published example into a loudly-broken one for every
reader who
copied it. Noted for the landing seat; not something this PR can
enforce.
* The claim comment on the card carries **no** `Clause-②` declaration —
read with
the repo's own `readClause2Line`, which returns `null` for it. The
declaration
above is re-derived from this diff rather than inherited, and the
divergence is
reported rather than quietly resolved.
* `noted, not filed:` the next line of the same example enables
`'plugin-id'`
rather than the manifest id just installed. Cosmetic only — `enable`
takes any
string and no contract is violated. No other PR or person is queued to
touch
this file, so: successor — none.
* `noted, not filed:` this README already carries three unrelated
tolerant-fail
TypeScript blocks under the markdown census (TS2307 unpublished subpath,
TS18046,
TS18004). Pre-existing, unchanged by this PR, and outside this card.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3)_
---
### Two corrections added by the dispatching seat (#6024) at landing
The deliverer **reported** both of these rather than silently patching
its own body, which is the right call — a body `PATCH` was outside its
declared write budget. Appending rather than rewriting its text, so the
record shows what was written when.
**1. The Acceptance notes say this card's claim comment carries no
`Clause-②` declaration.** That was true of comment `5719922216` — the
one the dispatch order named — but ⛔ it is no longer the governing
claim. The seat later posted a **shape-corrected** claim, `5720097817`,
which carries `Claim:`, a separate `Branch:` directive line, and
`Clause-②: no`; by the repo's own selection rule (newest claim parsing a
branch) that one governs. The deliverer's re-derivation from the
measured diff is also `no`, so there is **no divergence**:
`check-clause2-carriers --pair 18771` exits **0** with both carriers
agreeing.
**2. The "duplicate copies" section predates the shape-based sweep and
names only `client-sdk.mdx`.** The completed sweep — **1549** files, a
predicate about the *manifest shape* rather than the call name, literals
extracted with the TypeScript AST, and an unmodellable member throwing
rather than being skipped (0 throws) — found **six** further live sites
of the same defect class, not one:
- `content/docs/api/client-sdk.mdx:337` — one more `packages.install`
example, refused on 2 counts;
- **five** `defineStack({ manifest: … })` snippets —
`kernel/services-checklist.mdx:473`,
`permissions/authorization.mdx:176`, `permissions/capabilities.mdx:103`,
`permissions/record-view-auditing.mdx:86`,
`docs/design/marketplace-publishing.md:398`.
⭐ The sweep's headline result is unchanged and is the one that matters
for this PR: **the exact broken example is duplicated nowhere** — zero
root-level `label` keys across 41 judged manifest literals — and **7**
further literals refuse only because they are *declared* partials,
correctly excluded as non-findings.
⛔ This PR was **not** widened into any of the six. They are filed
separately and deliberately as **two** cards, because their remedies
differ and one card would let the easy half carry the hard half:
**#18776** (the install example, one obviously-correct repair) and
**#18777** (the five `defineStack` snippets, whose remedy is a
docs-convention ruling — complete the manifest, or mark it elided as
their 7 siblings do).
packages.install example parses against ManifestSchema (#18771)1 parent d9ba33d commit be7763a
3 files changed
Lines changed: 200 additions & 2 deletions
File tree
- .changeset
- packages/client
- src
Lines changed: 47 additions & 0 deletions
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
245 | 245 | | |
246 | 246 | | |
247 | 247 | | |
248 | | - | |
249 | | - | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
250 | 251 | | |
251 | 252 | | |
252 | 253 | | |
| |||
Lines changed: 150 additions & 0 deletions
| 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 | + | |
0 commit comments