Repository navigation
skills(objectstack-api): teach import applyOn, read FLOW_FAILED's innerRefusal, and the anonymous-form file refusal - #26392
Conversation
…erRefusal`, and the anonymous-form file refusal Three teachings the skills gap audit measured missing from the published objectstack-api package, each in the section the card names: - the import body's `mapping` entry form carries `applyOn` (`always` / `create`), so a re-run upsert leaves an admin's edit alone; - a flow door's `400 FLOW_FAILED` details are `FlowFailureDetailsSchema` with `innerRefusal`, and the order a client renders them in; - a form open to anonymous intake declares no file field: the gating lint rule, the publish gate, and the submit door's refusal for older forms. One eval fixture for the import. The eval file's token ceiling rises by the measured size of the fixture under the maintainer's ruling quoted in the ratchet row. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JQwxbaQ2Qeznx5DrAfybLP
Contract reviewReviewed at 2026-10-27T02:06Z · PR #26392 · card #26299 · independent contract review at the ceiling tier, read-only, adversarial to the dispatch order, the seat's claim and the dev's report alike. Served-tier: Landing state as read. A Check-runs on ① Derived judgments(a) Every new sentence is true of the spec and the implementation on
(b) Cuts. None. The diff removes nothing from the package (0 deletions in (c) Ratchet row. The script's measure is (d) Eval. One fixture, id 6, ids 1–6 unique and sequential, the file's existing shape ( (e) Wanted lines met, nothing beyond. All three Wanted items land in the named sections (verified above). Line 1 of (f) PR body shape. Line 1 is the harness attribution HTML comment, line 2 the harness's (g) Report. os-dev-report 6431775093 carries ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
维护者速读改了什么: 席位核对ACCEPT ·
Generated by Claude Code |
Requested by Warren · project thread
Fixes #26299
Clause-②: no
Metadata-protocol: none
维护者速读
skills/objectstack-api补三段教学,各落在卡点名的章节:SKILL.md公开表单段加「匿名表单不声明文件字段」(lint 规则public-form-file-field、发布门、旧表单提交时的400 VALIDATION_FAILED);派生操作表后加导入mapping条目的applyOn: 'always' | 'create';错误信封段加流程门的400 FLOW_FAILED的details(FlowFailureDetailsSchema:errorMessage/summary/innerRefusal)与渲染顺序。evals/endpoints-auth-routes.json加一题导入 eval;该 eval 文件在scripts/check-skills-token-ratchet.mjs的天花板行按实测抬 1042 → 1234。main上的 spec 与实现逐行核过(见 Acceptance notes)。回滚 = revert 本 PR。skills/objectstack-api/SKILL.md三个 hunk(行 104–111、269–275、372–380);②skills/objectstack-api/evals/endpoints-auth-routes.json新第 6 题;③scripts/check-skills-token-ratchet.mjs的endpoints-auth-routes.json行(1042 → 1234,引你 2026-10-27 的裁决)。node scripts/check-skills-token-ratchet.mjs | grep objectstack-api— 预期SKILL.md 5128 / 6314、endpoints-auth-routes.json 1234 / 1234 (+0)。skills/**):审阅通过后 approve,或直接 Merge when ready。What changes
Three teachings the skills gap audit (#26184, Part A § objectstack-api, rows API1–API3) measured missing from the published
objectstack-apipackage, each placed in the section the card names, in the fewest lines an AI author needs:FILE_REFERENCE_TYPES(image,file,avatar,video,audio) is refused by the gating lint rulepublic-form-file-fieldatos validate/os build/os lintand at the view's publish; a form published before that rule meets the same refusal at submit (flat400 VALIDATION_FAILED,fields[].code: 'invalid_value'); the prescription is a signed-in form.POST /data/{object}/importtakesmappingas{ Column: field }or asFieldMappingEntry[], and only the entry form carriesapplyOn('always'default;'create'writes the column only when the row is created, so a re-runupsertleaves an admin's edit alone; a create-only column is still read for matching; any other value is400 INVALID_REQUEST).400 FLOW_FAILEDcarrieserror.detailsshaped byFlowFailureDetailsSchema(errorMessage?,summary?,innerRefusal?as{ code, message }in the wire spelling), and a client renders a refused run as a record form renders a refused save: top-leveluserMessage, thendetails.innerRefusal, thenmessage; absent keys mean the run produced none.One eval fixture (id 6) for the import
applyOninevals/endpoints-auth-routes.json, in the file's existing shape.Token ceiling (ruling A).
scripts/check-skills-token-ratchet.mjsprices the eval file shrink-only at 1042 with 3 tokens of headroom onmain. The maintainer ruled on the seat's decision card 「skills 修复卡的教学和 eval 都撞 token 天花板,怎么付?」 with the tap, verbatim and untranslated, 「A:先削重复,余额按实测提高」. Paid down first: the file's five fixtures share no sentence, so there was no duplicate to cut (0 bytes). The fixture adds +780 bytes (4156 → 4936 bytes, 1039 → 1234 tokens); the 3 tokens of headroom are absorbed and the row moves 1042 → 1234 (+192), pinned AT the measurement with zero headroom.SKILL.mdfits inside its existing ceiling (4691 → 5128 of 6314) and its row does not move. The file surface widened to that one ratchet row under the seat's amended claim.Line readings (os-dev.md 290–291, whole file before → after):
skills/objectstack-api/SKILL.mdskills/objectstack-api/evals/endpoints-auth-routes.jsonskills/objectstack-api/references/_index.md(generator-owned, untouched)SKILL.md+references/**/*.md+rules/**/*.md)Token readings in the ratchet's convention (
ceil(utf8 bytes / 4)):SKILL.md4691 → 5128 (ceiling 6314); eval file 1039 → 1234 (ceiling 1042 → 1234). Nothing is removed: the card names no stale lines, and no line in the package duplicated what the three paragraphs teach (0 hits onmainforapplyOn,innerRefusal,FLOW_FAILED,file field).Why
The audit's acceptance is 「one fix card per skill package」; this is the objectstack-api card. Who pays today: an AI author of import jobs, who overwrites a column an admin owns on every re-import; a client author, who shows "flow failed" instead of the real reason; a public-form author, who declares a field no visitor can fill and meets the refusal only at publish.
Tests
pnpm --filter @objectstack/spec check:skill-docs— exit 0 (✅ Skill docs in sync; frontmatter untouched).pnpm --filter @objectstack/spec check:skill-refs— exit 0 (✅ 9 generated files in sync;references/_index.mdis generator-owned and unchanged — nogen:skill-refsneeded).pnpm --filter @objectstack/spec check:skill-examples— exit 0 afterpnpm --filter @objectstack/spec buildandpnpm --filter @objectstack/client-react... buildunder the verify lock (✅ self-test: flags a bareanyparameter / variable / property / return / alias / cast in a ✅ 264 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them); the first run exited 3 (PREREQUISITE NOT MET: nopackages/client-react/distdeclarations) and measured nothing (needs a builtpackages/spec/dist; the diff adds noos:checkblock).node scripts/check-skills-token-ratchet.mjs— exit 0 after the raise (81 authored bundle file(s) within their ceilings); before the raise it refused exactly this file (1234 tokens; the ratchet ceiling is 1042 (over by 192)), which is the reading the row quotes.--self-test: 79 cases pass.pnpm check:skill-identifier-liveness— exit 0 (Leg 1: 490 citations over 69 files; Leg 2: 8 registered sections, 0 gaps).pnpm check:skill-top-level-keys,check:skill-compatibility,check:skill-frame-sync— exit 0.npx eslint --no-inline-config scripts/check-skills-token-ratchet.mjs— exit 0;pnpm check:nul-bytes— exit 0.JSON.parse), and itsmust_containtokens appear in its ownexpected_output.pnpm testis owed: the diff touches nopackages/**source (local-verification ①/② empty); the one script edited is a gate whose own suite is its--self-test(run above).Acceptance notes
Every sentence is read off
mainat756220c4, not off the card's paraphrase:applyOn—packages/spec/src/api/export.zod.ts:174(FieldMappingEntrySchema.applyOn: ColumnApplyOnSchema.default('always')),packages/spec/src/data/column-apply-on.zod.ts(create | always); the import door reads it only from the entry-array form ofmapping(packages/rest/src/import-prepare.ts:610–627,createOnlyFields), the record shorthand cannot carry it, and a value outside the pair is400 INVALID_REQUEST(same file, 620–626); the runner leaves create-only targets out of the update patch (packages/core/src/utils/import-runner.ts:246).FLOW_FAILED—packages/spec/src/api/automation-api.zod.ts:585–602(FlowFailureDetailsSchema:errorMessage?,summary?,innerRefusal?),FlowInnerRefusalSchema(codein the wire spelling,message); the envelope's top-leveluserMessageisApiErrorSchema.userMessage(packages/spec/src/api/contract.zod.ts:93); the doors (trigger, resume,/actions, declaredtype: 'flow'endpoints) and the render orderuserMessage→details.innerRefusal→messageare the runtime CHANGELOG's own consumer note for that change (packages/runtime/CHANGELOG.md:52–63).packages/lint/src/validate-public-form-file-fields.ts(rule idpublic-form-file-field,FILE_REFERENCE_TYPES=image,file,avatar,video,audiofrompackages/spec/src/data/field-value.zod.ts:306), wiredtier: 'gating',commands: ALL,surfaces: CLI_AND_RUNTIME,runtimeTypes: ['view']inpackages/lint/src/authoring-rules.ts:1574–1596; the submit door's refusal for forms published before the rule ispackages/rest/src/form-routes.ts:650–663(code: 'invalid_value', folded into the flat400 VALIDATION_FAILEDwithfields[]). The seat's premise read did not locate this inpackages/spec/src/api; it lives in@objectstack/lint, so the paragraph names the lint rule rather than a spec symbol.SeedColumn.applyOntwin (objectstack-data's surface), the resume door'sResumeFailureDetailsSchemakeys, and theerrorMessage/summarykeys beyond naming them..github/PULL_REQUEST_TEMPLATE.mdlanded onmainafter this card was dispatched (756220c4); this body carries the os-dev.md section order (Fixes/ declaration lines /## 维护者速读/## What changes/## Tests/## Acceptance notes/## Verification) and adds the template's## Whyand## Human Minimumso every template heading is present.Verification
Derived gate list and local run (`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` on `3add67c7`; exit codes captured before any pipe)
node scripts/check-ci-filter-parity.mjsnode scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjsnode scripts/check-declaration-mirrors.mjsnode scripts/check-declaration-mirrors.mjs --self-testnode scripts/check-doc-route-spelling.mjs --advisorynode scripts/check-doc-route-spelling.mjs --self-testnode scripts/check-dts-emitted.mjs --self-testnode scripts/check-scripts-symbol-anchors.mjsnode scripts/check-scripts-symbol-anchors.mjs --self-testnode scripts/check-self-test-wired.mjsnode scripts/check-self-test-wired.mjs --self-testnode scripts/check-self-test-workflow-commands.mjsnode scripts/check-self-test-workflow-commands.mjs --self-testnode scripts/check-skills-token-ratchet.mjsnode scripts/check-skills-token-ratchet.mjs --self-testnode scripts/check-whole-set-label-write.mjsnode scripts/check-whole-set-label-write.mjs --self-testnode scripts/ci/assert-turbo-remote-cache.mjs --self-testnode scripts/ci/prune-turbo-cache.mjs --self-testnode scripts/pm/bare-root-worklist.mjs --self-testpnpm --filter @objectstack/lint run check:doc-formula-expressionspnpm --filter @objectstack/spec run check:skill-docspnpm check:agent-test-spellingpnpm check:bash32-floorpnpm check:cli-command-idspnpm check:corpus-claim-driftpnpm check:cross-package-test-inputspnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:dts-closurepnpm check:dual-build-cjs-loadspnpm build; the diff touches no package) — CI's run is the readingpnpm check:entry-guardpnpm check:gitlink-declaredpnpm check:lean-entry-closurepnpm check:merge-driverpnpm check:nul-bytespnpm check:parse-guardpnpm check:pm-dispatch-gatespnpm check:pm-governed-mergespnpm check:pm-skill-id-lintpnpm check:pnpm-filter-targetspnpm check:pnpm-flag-spellingspnpm check:ratchet-remedy-authoritypnpm check:refd-timer-probepnpm check:role-wordpnpm check:skill-compatibilitypnpm check:skill-frame-syncpnpm check:skill-identifier-livenesspnpm check:sourcemap-no-sources-contentpnpm check:watch-hint-literal`--ran` reconciliation
Token ratchet before and after the raise
Human Minimum
packages/specholds no runtime logic. (no schema touched)pnpm lintis CI's run).main, one pull request.skills/**carries nopackage.json; nothing is released →skip-changeset; the declaration lines are above)packages/spectouched: its generated artifacts are regenerated and committed. (not touched)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01JQwxbaQ2Qeznx5DrAfybLP