docs(skills): correct 8 false behavioral facts in objectstack-api (sweep flight ⑦) - #13827
Conversation
Flight ⑦ of the published-skills factual sweep (program #13658). Every claim verified against the implementing code plus executed probes; corrections are byte-neutral-or-shrinking under the token ratchet (net -8 tokens / -2 lines). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EnE7G31tqbxN1rqpQmzurT
Contract review — PASS at head
|
Fixes #13814
Part of #13658
Flight ⑦ of the published-skills factual sweep:
skills/objectstack-api/**(3 files, 707 lines). Every behavioral claim was located in the implementing code first — never verified against another document — and the behavior-bearing ones were settled with executed probes. 8 distinct false facts across 9 landing sites, all inSKILL.md.evals/README.mdand the generator-ownedreferences/_index.mdcame back clean.Landing sites — 落点 | before | after
SKILL.md:376dispatcher table headerHTTP Status | Error Type | WhenHTTP Status | Error Code | WhenSKILL.md:548-550best practice 3error: { code, message, type?, … },codeis the numeric HTTP statuserror: { code, message, httpStatus?, … },codeis the semantic stringSKILL.md:393-395handler-status notehandlerStatus… The dispatcher returns501 NOT_IMPLEMENTEDforstubandplannedhandlers"handlerStatusis DOCUMENTATION — nothing reads it at runtime. The dispatcher's501 NOT_IMPLEMENTEDcomes from the endpoint executor, never from this field."SKILL.md:557best practice 5handlerStatusto communicate implementation progress to consumers."SKILL.md:409-410websocket pointerfiltersnot enforced)"SKILL.md:505driver tablemongo | Document store (MongoDB)mongodb | Document store (mongo is a legacy alias)SKILL.md:507-508driver tabletursodescribed below the table as living outside the catalogsqlite-wasmandtursorows added; the two-line aside deletedSKILL.md:520IDataEnginevectorFind/batch/execute"vectorFind/execute"SKILL.md:559-560best practice 6upsertexists as anapiMethodsenum value"upsertis DERIVED (create∧update), not anapiMethodsvalue"Zero out-of-list changes. Site 4 is a deletion that both corrects a false claim and pays part of the token bill for sites 2, 5, 6 and 7 — the ceiling on this file had zero headroom, so every correction had to fund itself.
What each correction is settled against
1–2.
error.typeis gone anderror.codeis not a number.DispatcherErrorResponseSchema(packages/spec/src/api/dispatcher.zod.ts:189) declarescode: z.string(),message, andhttpStatus?; thetypesibling was removed in the #3842 consolidation, which moved the numeric status tohttpStatusand put the semantic spelling incode. Both directions executed: a body withcode: 404fails to parse, a body withcode: 'ROUTE_NOT_FOUND'parses, and a body supplyingtypecomes back with keys["code","message","httpStatus"]— the key is stripped. The table header at site 1 named the same removed field.3–4.
handlerStatushas no runtime consumer. Repo-wide, excludingskills/**and tests, the identifier appears three times and all three are inside its own declaring file (packages/spec/src/api/plugin-rest-api.zod.ts:177,:1415,:1431). The dispatcher'sNOT_IMPLEMENTEDis emitted from four sites, all in the declarative-endpoint executor (packages/runtime/src/endpoint-executor.ts×3,packages/runtime/src/api-mapping.ts×1), and none reads the field. Nothing serializes it to a consumer either:RouteCoverageReportSchemais the only shape that would, and nothing constructs one. Filed for central triage as #13823 (the declared-but-inert key itself is an ADR-0049 enforce-or-remove question, not something a skill edit settles).filtersare declared, not enforced.packages/spec/src/api/websocket.zod.ts:55-78records the measurement verbatim: no runtime ever evaluated an event filter,matchesSubscriptionmatches on object name and event type only, and "a subscriber settingfiltersreceived every event regardless"; the key survives with a NOT-YET-ENFORCED marker because retiring it needs a tombstone and a conversion. The bullet two lines above already carried exactly that caveat forRealtimeEventType— this one did not, so the file taught enforcement on one inert key and warned about the other.6–7. The driver catalog was one wrong spelling and two missing rows. Executed:
BUILTIN_DRIVER_IDSis["memory","sqlite","sqlite-wasm","postgres","mysql","mongodb","turso"], andresolveDriverId('mongo')answers'mongodb'. The canon was renamed in #6345 — both boot hosts, the npm package and every URL scheme saidmongodb— with the ADR-0087 conversiondatasource-driver-mongo-to-mongodbconverging stored rows;mongosurvives only as an alias.tursobecame a full builtin row in the same change (it now shipsTursoConfigSchema), so presenting it below the table as available "via the separate package" put a registered catalog id outside the catalog the heading promises.IDataEngine.batchwas retired. The tombstone is atpackages/spec/src/contracts/data-engine.ts:280-288: ADR-0119 D3 (chore(spec): retire IDataEngine.batch? per ADR-0119 D3 — declared-but-unimplemented, zero callers #4618) removed it — never implemented by any engine, never called, and it specified nothing about partial failure, ordering, cross-object references or rollback scope. The live members arefind/findOne/insert/update/delete/count/aggregateplus optionalvectorFind/execute(:224-296).upsertis not anapiMethodsvalue. Executed:ApiMethod.optionsis["get","list","create","update","delete","bulk"]and'upsert'is not in it; it sits inLEGACY_API_METHODS, stripped at parse bystripLegacyApiMethods, and derives fromcreate ∧ update. This also contradicted the file's own API Methods section 270 lines earlier, which states the six-primitive rule correctly — a cross-section contradiction settled against the implementation, not against either sentence.Executed evidence (non-vacuity control)
One probe file, run under the shared verify lock, 30+ assertions against
packages/specsources. Beyond the four falsehood proofs above:Every one of those confirms a sentence the skill already had right, which is what makes the eight corrections a measurement rather than a hunt.
Verified clean, worth naming
evals/README.md— all ten rubric descriptions check out against the implementation (the/dataprefix, aggregation throughPOST /data/{object}/querywith noGET .../aggregate, per-object vs cross-object batch, the flat{ error, code }data-route bodies withCONCURRENT_UPDATE→ 409 andVALIDATION_FAILED→ 400, theRestApiEndpointSchemashape, theobject_operation/flow-only execution set, the carve-out, theauthRequireddefault and the D6 armed-budget rule). Flight ③ found rubrics teaching graders to fail correct answers; this package's do not.references/_index.md—check:skill-refsreports all 9 generated files in sync, and every one of the 23 pointers resolves to a file that exists and is inside the publishedfilesglob (src/**/*.zod.ts). Not hand-edited.object_operation/flowpipeline targets — is accurate line for line againstendpoint-publish-gate.tsand the runtime modules it mirrors.One nuance deliberately NOT added
SKILL.md:363says/ready"returns 200 only when the kernel is fully running, and 503 while booting or shutting down". Both halves are true, but since #13408 (today)/readyalso answers 503 when the primary datasource driver is unhealthy. The sentence is incomplete, not false, and the ceiling has 8 tokens of headroom — so under the program's rule this is nuance overflow forcontent/docs/**, not new text for ratcheted skill prose. Recorded here rather than silently skipped. (The same staleness sits in a code comment atpackages/runtime/src/dispatcher-plugin.ts:977-979.)Budget — both readings
skills/objectstack-api/SKILL.md, linesskills/*/SKILL.md, linesCeilings unchanged.
check-skills-token-ratchet.mjswas red at exit 1 on the first draft (6364 tokens, over by 45) and the corrections were tightened and paid for until it went green with headroom 8 — no ceiling was touched, which is maintainer-only.Gates — all readings at
5a5804aeFamilies derived from the real diff with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(1 path, no STALE warning; re-derived after the commit and the list is byte-identical). 14 families, exit codes captured before any pipe:Plus, outside the derived list:
pnpm --filter @objectstack/spec check:skill-refsexit 0 ("9 generated files in sync"), and a control-character scan of the diff (grep -naPover the C0 set) with no hits.check-test-completeness.mjsexit 3 is its own PREREQUISITE-NOT-MET code: it grades a savedturbo run testlog that only CI produces, and its message says so — "the local reading for this gate is NOT MEASURED… it is not a red". Recorded as NOT MEASURED.check:doc-formula-expressionsfirst exited 1 on the same prerequisite branch (@objectstack/formulaand@objectstack/lintunbuilt). Both were built and it was re-run to a real exit 0; the first reading is reported as not-measured rather than as a failure.Declared narrowing —
check:skill-examples. The path derivation does not name this family forskills/**, and the diff's population for it is empty, measured three ways rather than assumed: ① the gate's own population isos:check-marked fences (packages/spec/scripts/check-skill-examples.ts); ② this file has 6 such fences, at lines 123-138, 171-185, 328-340, 438-446, 480-495 and 529-538, and the diff's hunks are at 376, 393-395, 409-411, 505-510, 520, 548-551, 557 and 559-561 — disjoint, zero overlap; ③ a fence's verdict is a function of its own text and the spec/client-react declaration types, neither of which this diff changes, so no untouched fence's verdict can move. Running it locally needs a@objectstack/client-reactbuild; CI runs it regardless.Posture
Published
skills/**is governed: this PR stays DRAFT for a human merge, andneeds:contract-reviewis attached to it and to card #13814 in the same stroke. Pureskills/**diff, nothing published from a package, soskip-changesetapplies.Generated by Claude Code
Generated by Claude Code