Repository navigation
fix(rest)!: /meta/_drafts refuses a bracketed packageId or type key instead of listing every draft - #29440
Conversation
…meta/_drafts The decision as data, RestServer's _drafts door, the runtime dispatcher's twin and the booted showcase over HTTP. Red on main: the bracketed key reads as no filter and every draft is listed. Claude-Session: https://claude.ai/code/session_01ADHkmSq83zsshBxiLpFJdE Co-authored-by: Claude <noreply@anthropic.com>
The arity doors' bracketed-package decision becomes one per-door table, metaBracketedFilterRefusal: the list and the book tree judge `package`, _drafts judges `packageId` and `type`. metaQueryArityRefusal asks it, so the runtime dispatcher's _drafts gets it with no runtime change; RestServer's _drafts door asks it right after its multiplicity gate. Claude-Session: https://claude.ai/code/session_01ADHkmSq83zsshBxiLpFJdE Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 29 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 19 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 2909be5b7511cdc2ee94fd20fd08c2f629cd8fc4 && git checkout 2909be5b7511cdc2ee94fd20fd08c2f629cd8fc4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 329121397b395e688cff1ca9e845e07ba53c652a ee0e30c96c3c8aba7fbb570fd5e9b00e58a80ccb && git checkout -B drift-repro 329121397b395e688cff1ca9e845e07ba53c652a && git merge --no-ff ee0e30c96c3c8aba7fbb570fd5e9b00e58a80ccb
node scripts/docs-audit/affected-docs.mjs --json 329121397b395e688cff1ca9e845e07ba53c652a
|
|
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt Contract reviewServed-tier: ① Derived judgmentsAccept-set and public-surface changes the diff implies, each read against the cited code on the merge-base and on the head:
Claims checked for wrong statements about the surface: the changeset's two "Now" sentences are the literals the builder and the tests pin; "no runtime source change" holds; "the ② Semver level
The declaration arm. #29298's record spelled ③ Boundary flags
④ Design judgmentThe narrowing, read against the published contract on Per The two-dialect note stands as it did on #28602 / #29046 / #29298 (the #29180 verdict thrown into Must-fix: none. Should-fix: none. Implemented-by: VERDICT: PASS |
|
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt 席位核对ACCEPT · The maintainer's words for this seat, verbatim: 「接手所有开发,包括跟进之前未完成的开发落地。」 There is no maintainer ruling on this card. It was filed by seat 2 as should-fix 2 of PR #29298's contract review, which is the same class as #29199.
Generated by Claude Code |
|
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt Landing record · PR #29440
Generated by Claude Code |
Requested by Steve · project thread
Fixes #29319
Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none — no skill or doc page teaches a bracketed
packageIdortypekey on_drafts(git grepoverskills/,content/docs/andapps/docs/finds none); the pages that name_draftsteach no query spelling at all.维护者速读
GET /meta/_drafts收到括号写法的packageId(?packageId[]=ID、?packageId[0]=ID)或type(?type[]=TYPE、?type[0]=TYPE)时答 400VALIDATION_ERROR并点名该键;以前这些键被忽略,返回全部包、全部类型的草稿。/meta上的最后一处。packages/rest(fix(rest)!: /meta list refuses a bracketed object key on views and a bracketed package key on the list and book tree #29298 的括号判定改成按门查表的一个函数,_drafts门入口加同一判定,两条传输共用),patch带 BREAKING 横幅;只影响发括号键的 API 调用方,@objectstack/client与控制台都不发。回滚即还原本 PR。packages/rest/src/meta-item-read-gate.ts(META_BRACKETED_FILTERS与metaBracketedFilterRefusal);packages/rest/src/meta-collection-routes.ts的_drafts门入口新增几行;.changeset/29319-bracketed-drafts-filter-refused.md。pnpm --filter @objectstack/rest exec vitest run src/meta-query-arity-doors.test.ts,预期全绿,其中a BRACKETED packageId or type key on _drafts用例断言 400。What changes
?packageId[]=ID,?packageId[0]=ID,?type[]=TYPEand?type[0]=TYPEreachGET /meta/_draftsunder the key as spelled: both Hono adapters hand query keys through unparsed (readQueryinplugin-hono-server,readDispatchQueryin@objectstack/hono). Both transports read onlyquery.packageIdandquery.type, solistDraftswas handed no package and no type, and an author who named one package's (or one type's) drafts got every draft with 200.packages/rest/src/meta-item-read-gate.ts: fix(rest)!: /meta list refuses a bracketed object key on views and a bracketed package key on the list and book tree #29298'smetaBracketedPackageRefusalbecomesmetaBracketedFilterRefusal(door, query), driven by one per-door table,META_BRACKETED_FILTERS: the list and the book tree judgepackage(as before),_draftsjudgespackageIdand thentype. It is the same function, generalised and renamed, not a second copy; it was module-level and never on the package index, so no export moves. The twopackagesentences are byte-identical (fix(rest)!: /meta list refuses a bracketed object key on views and a bracketed package key on the list and book tree #29298's pins pass unchanged).metaQueryArityRefusalalready asks it after the multiplicity rule, so the runtime dispatcher's_draftsgets the refusal with no runtime source change.packageId:Query parameter 'packageId[]' names no package; the 'packageId' filter takes exactly one package id, spelled ?packageId=ID. The list was not served: a bracketed 'packageId' key is not a filter. Name one package, or leave the parameter out to list the drafts of every package.type:Query parameter 'type[]' names no metadata type; the 'type' filter takes exactly one metadata type, spelled ?type=TYPE. The list was not served: a bracketed 'type' key is not a filter. Name one metadata type, or leave the parameter out to list the drafts of every type.packages/rest/src/meta-collection-routes.ts:RestServer's_draftsdoor asks the same decision right afterrefuseRepeatedQueryParams, beforelistDrafts, and writes it in the nested envelope its arity gate writes (as the list door does).packages/rest/src/meta-item-read-routes.ts: the book tree's call follows the rename (one line).MetaItemQueryRefusal.rulegains no member: the refusal reusesbracketed-parameter, soClause-②: no (narrowing).Measured on main before any edit (27c1c55). Real Hono request parsing on both transports (
RestServeron aHonoHttpServer, driven withapp.fetch; the dispatcher on a raw Hono app withreadDispatchQuery's exact query shape), an author caller, four seeded drafts (two packages × two types), brackets sent raw and percent-encoded:?packageId=crm/?type=view/ both?packageId[]=crm,?packageId[0]=crmlistDrafts({})?type[]=view,?type[0]=viewlistDrafts({})?packageId[]=crm&type[]=view?packageId=crm&packageId[]=hrProducer census.
@objectstack/client'slistDraftsbuilds both keys withURLSearchParams.set; the console builds?packageId=${encodeURIComponent(…)}(usePendingDrafts.ts,PackagesPage.tsx); neither has a qs library; no docs or skill example spells either key bracketed. No real producer sends them.Tests
packages/rest/src/meta-query-arity-doors.test.ts(appendeddescribe): the decision as data forpackageId[],packageId[0],packageId[id],type[],type[0],type[name](and beside the plain key); a repeated plainpackageIdstill answers the multiplicity sentence first; not judged on the list or the tree, nor fortypes[],packageIds[]orpackage[]on_drafts;RestServer's_draftsanswers the nested 400 andlistDraftsis never called; controls: the plain spellings still narrow, none reads every draft.packages/runtime/src/domains/dispatcher-query-arity.test.ts(appendeddescribe): the dispatcher's_draftsrefusespackageId[],packageId[0],type[],type[0]with the same code and sentence beforelistDrafts; beside the plain key too; control: the plain spellings reachlistDrafts.packages/qa/dogfood/test/meta-read-doors-live.dogfood.test.ts(booted showcase over Hono, appendeddescribe):/meta/_drafts?packageId[]=/?packageId[0]=com.example.showcaseand?type[]=/?type[0]=viewanswer 400VALIDATION_ERRORnaming the key, no list, as the admin; controls:?packageId=and?type=answer 200 with only matching rows, and a member is still refused 403 at the capability gate first.No existing test was edited; every hunk in a test file is an addition.
Red first on main (a2fcede, pins at b748eaa)
The pins were committed before the fix and run against main's source and dist:
Tests 10 failed | 39 passed (49): the six as-data cases and the fourRestServerdoor cases. Every control and every fix(rest)!: /meta list refuses a bracketed object key on views and a bracketed package key on the list and book tree #29298 pin passed.Tests 5 failed | 38 passed (43): the four dispatcher cases and the beside-the-plain-key case.Ablation: each refused key, and the RestServer hand-off, put back, reds only its own pins
Through
scripts/ablation-replace.mjs(wrap mode),pnpm --filter @objectstack/rest build, thenablation-dist-preflight.mjs packages/rest MARKER(marker present in 2 built files) before reading any run. Baseline on the fixed head first: rest49 passed, runtime43 passed, dogfood file24 passed._draftstable row'sparam: 'packageId'renamed to a marker, so the key is not judged. Anchor 1 → 0, blob977baa3c→69581359. rest5 failed | 44 passed (49)(the threepackageIdas-data cases, the twoRestServerpackageIdcases); runtime3 failed | 40 passed (43)(packageId[],packageId[0], beside-the-plain-key); dogfood2 failed | 22 passed (24)(packageId[],packageId[0]).param: 'type'renamed to a marker. Anchor 1 → 0, blob977baa3c→36e7b636. rest5 failed | 44 passed (49)(the threetypeas-data cases, the twoRestServertypecases); runtime2 failed | 41 passed (43)(type[],type[0]); dogfood2 failed | 22 passed (24)(type[],type[0])._draftsdoor's call asks an unknown door instead of'drafts'. Anchor 1 → 0, blob824a9f54→2d929242. rest4 failed | 45 passed (49)(the fourRestServerdoor cases); runtime43 passed (43)(the dispatcher asks the decision throughmetaQueryArityRefusal, untouched); dogfood4 failed | 20 passed (24)(the showcase is served byRestServer).Each restore: blob back to HEAD,
git diff HEADempty; then a rebuild, all three markers absent from all 6 built files, whole tree clean (git status --porcelainempty).Acceptance notes
?packageId=the bracketed key was silently dropped.meta-item-read-routes.ts, the other caller of the function this PR generalises.packages/runtime/src/domains/meta.tsneeded no edit: its_draftsalready asksmetaQueryArityRefusal('drafts', …). Noui/**, nopackages/spec/**.flow-runas.dogfood.test.ts(3) anddeclarative-endpoint-anonymous-guest.dogfood.test.ts(1) red with this PR's source set back to main's (see Verification); reported to the seat.?preview[]=drafton the list reads as no preview and?include[]=as no inclusion (fix(rest)!: /meta list refuses a bracketed object key on views and a bracketed package key on the list and book tree #29298's note). Not measured here; outside this card's keys.Verification
Every local reading below is at HEAD
ee0e30c9(the fix359c7496plus two merges oforigin/main, the last taken afterdfbe34d8), in a worktree onclaude/project-thread-ta4g7t. Families CI runs are cited from this PR's check-runs in the report, not re-run here.Package suites and typecheck (HEAD ee0e30c)
pnpm --filter @objectstack/rest test:VERDICT command-exit 0·Test Files 370 passed (370)·Tests 7043 passed | 332 skipped (7375)pnpm --filter @objectstack/rest typecheck && … runtime typecheck && … dogfood typecheck:VERDICT command-exit 0pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/domains/dispatcher-query-arity.test.ts src/domains/meta-list-projection-parity.test.ts:VERDICT command-exit 0·Tests 1185 passed (1185)--no-inline-configoverpackages/rest,packages/runtime,packages/qa/dogfood: exit 0, no findingsnode scripts/check-platform-checklist.mjs --changed-from origin/main(a module export was renamed): OK, 12 anchors judged, 0 lostFull dogfood battery (the narrowing owes it)
pnpm --filter @objectstack/dogfood testunder the verify lock, after the@objectstack/dogfood^...closure build:ee0e30c9:Test Files 2 failed | 391 passed | 3 skipped (396)·Tests 4 failed | 3244 passed | 28 skipped (3276). The four reds areflow-runas.dogfood.test.ts(three: the member's flow trigger answers404 RECORD_NOT_FOUND) anddeclarative-endpoint-anonymous-guest.dogfood.test.ts(one). They are not this change: with this PR's threepackages/rest/srcfiles set back toorigin/main's (blob-identical to the merged main),restrebuilt and the fix's sentence absent fromdist/, the same two files answer the sameTests 4 failed | 8 passed (12); restored,git status --porcelainempty. Neither file touches/meta; between the two heads main tookdfbe34d8, which changed the flow start doors (not bisected). Reported to the seat as a main-red, not fixed here.88dc5779(the same diff on the previous main):VERDICT command-exit 0·Test Files 393 passed | 3 skipped (396)·Tests 3248 passed | 28 skipped (3276).Derived gate set
node scripts/pm/dispatch-gates.mjs --commands(from the merge-base, atee0e30c9): 64 commands, all exit 0.--ranreconciliation with each exit code recorded:64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED (a DERIVED zero). On the previous head two first-pass exit 3s were prerequisites, each re-run green:check-plugin-teardown-shape.mjs --self-test(its pinned fixture commit fetched on this shallow clone) andcheck:dual-build-cjs-loads(7 packages outside the dogfood closure built).test:repo(the vitestrepoprojects, CITest Core). The onerepo-project file this diff could reach,meta-list-projection-parity.test.ts, was run directly (above).Generated by Claude Code