Skip to content

docs(skills): teach the recycle bin restore, purge and list doors in objectstack-api - #31769

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-3lyy7q
Nov 9, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-3lyy7q

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #30847

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:objectstack-api 技能改写两处:派生动词段落改为 restore / purge 由 delete 派生;常见陷阱第 2 条教会回收站的四个 REST 入口、两个权限和四个恢复拒绝码;"恢复已删发票" eval 改为按新教学打分。
  • 为什么改:engine: snapshot deletes into the recycle bin, restore under original ids, purge, and the TTL reap (#3146 slice 3) #30237 已上线回收站的恢复、清除、列表接口,技能却仍说"没有恢复或清除入口",AI 会因此自造软删除,或告诉用户删掉的记录找不回来。
  • 风险与回滚:只改对外发布的技能文字与一个 eval,无运行时代码;SKILL.md 字节净增 0(5157 tokens,上限 6314)。回滚即 revert 本 PR。
  • 先看:skills/objectstack-api/SKILL.md 常见陷阱第 2 条;同文件"Derived operations"表下的 restore / purge 句;skills/objectstack-api/evals/endpoints-auth-routes.json 第 5 例。
  • 两分钟验证:node scripts/check-skills-token-ratchet.mjs | grep objectstack-api/SKILL,预期 5157 tokens (ceiling 6314; headroom 1157)。
  • 需要你做的:作为受管审批人审阅并批准本 Tier H 草稿 PR。

What changes

  • skills/objectstack-api/SKILL.md, passage under Derived operations: "restore / purge never derive: the recycle bin has no restore or purge door yet" becomes "restore / purge derive from delete and open the recycle-bin doors (pitfall 2)" (API_METHOD_DERIVATION in packages/spec/src/data/api-derivation.ts).
  • Same file, Common Pitfalls 2 ("Treating DELETE as final"): what a delete keeps (business-object rows, 30-day default retention, not multi: true / platform rows / rows deleted before the bin existed), the recycleBinEntryId the delete answers, the four doors under /api/v1/data/{object}/recycle-bin (POST /:entryId/restore, GET, DELETE /:entryId, DELETE), who may use them (allowRestoreOwnDeleted for one's own deletes, manageRecycleBin for others' and for list / purge / empty), and the four 409 restore refusal codes.
  • skills/objectstack-api/evals/endpoints-auth-routes.json case 5: expected_output teaches that restore derives from delete (so the asked-for read-only API also closes the bin's doors) and names the restore door and grants; must_contain swaps recycle bin for /recycle-bin/; must_not_contain adds the stale phrase nothing lists or restores. 'restore' stays: it still guards declaring a derived verb in apiMethods.
  • New bytes paid inside the same file (net 0 bytes, net 0 lines); each cut rule survives:
    • "aggregation through the query one" → the Derived operations table row aggregate → POST /data/{object}/query.
    • "The authorable values are the six primitives — see API Methods" → the API Methods (Operations) opening sentence.
    • "Always prefix with /api/v1/ for versioning" → every row of the Endpoint Naming Conventions table.
    • the D6 cross-reference title in Auth Configuration, shortened to "D6 above" → authRequired and the D6 pairing.
    • Best Practice "Apply least-privilege auth" → Auth Configuration (the flat public + permissions pair).
    • "pointer-style — read the spec source for exact shapes" → References ("Always Read the source for exact field shapes").
  • .changeset/30847-api-skill-recycle-bin-doors.md: '@objectstack/skills': patch.

Doors and grants, each read in the live tree (main 9e1f1f9): packages/rest/src/recycle-bin-routes.ts (four routes and their restore / purge exposure), packages/spec/src/api/protocol.zod.ts (DeleteDataResponseSchema.recycleBinEntryId, the restore path), packages/objectql/src/recycle-bin/restore.ts, list.ts, purge.ts, retention.ts (gates, 30-day default), packages/core/src/security/effective-object-permissions.ts (GRANT_BITS), packages/spec/src/data/recycle-bin.zod.ts (RECYCLE_BIN_RESTORE_REFUSAL_CODES, status 409).

Tests

Teaching text only; no runtime code moves. pnpm --filter @objectstack/spec run check:skill-examples: exit 0, 332 prose examples type-check.

Acceptance notes

  • Skill readings (lines, whole file): skills/objectstack-api/SKILL.md 441 → 441 lines, 20646 → 20646 bytes, 5157 → 5157 tokens (ceiling 6314). Whole pack (every SKILL.md): 4599 → 4599 lines.
  • Model: dispatch-gates --tier mandates the contract-review model for skills/**; this build ran on the default tier under the no-Fable-build exit, and the contract review runs on the lane's isolated reviewer.
  • No REST door for a permanent delete exists (permanent: true is an engine option only), so the skill teaches none.

Verification

Derived gates (46) on 7d3edcd

45 exit 0. pnpm check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, needs every package's dist): NOT MEASURED, left to CI. dispatch-gates --ran: 46 accounted, 45 run, 1 NOT-MEASURED. The merge of origin/main (head 4efbaef) brought 6 commits touching no file of this diff; check-skills-token-ratchet re-run on 4efbaef: exit 0.

🤖 Generated with Claude Code


Generated by Claude Code

…n objectstack-api

The api skill said the recycle bin had no restore or purge door and that
restore / purge never derive. Both shipped with ADR-0148: restore and purge
derive from delete, and four REST doors sit under
/api/v1/data/{object}/recycle-bin, gated by allowRestoreOwnDeleted and
manageRecycleBin. Rewrite both passages from the live tree, regrade the
undelete eval, and pay the bytes with cuts whose rules survive in the file.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jTbKfNKjrN2RAefJDH
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jTbKfNKjrN2RAefJDH
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 9, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · zhuangjianguo · session_01QsR2jULfPnqQFGcZUtKDFH

Contract-review-claim: session_01QsR2jULfPnqQFGcZUtKDFH · head 4efbaef · 2026-11-09T03:17Z

The lane seat's isolated Fable reviewer runs the CONTRACT_REVIEW_TIER review (maintainer 2026-11-08 16:12Z 「有 fable 车道就可以契约复审啊」); the director seat is off duty.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · zhuangjianguo · session_01QsR2jULfPnqQFGcZUtKDFH

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4efbaef8ff553b0c8d789de8bc624c148517a6f3
Local-runs: none
Scope: skills/objectstack-api/SKILL.md, skills/objectstack-api/evals/endpoints-auth-routes.json + none (the diff holds no implementing code; .changeset/30847-api-skill-recycle-bin-doors.md is the lane seat's ACCEPT at TIER_DEFAULT). Read, never reviewed, in the live tree on main c2bb879: packages/rest/src/recycle-bin-routes.ts, packages/rest/src/crud-routes.ts, packages/rest/src/rest-route-ledger.ts, packages/metadata-protocol/src/protocol-data-writes.ts, packages/objectql/src/recycle-bin/restore.ts, list.ts, purge.ts, retention.ts, packages/objectql/src/engine.ts (restore / empty docblocks), packages/spec/src/data/api-derivation.ts, packages/spec/src/data/recycle-bin.zod.ts, packages/spec/src/api/protocol.zod.ts, packages/spec/src/api/error-code-ledger.zod.ts, packages/spec/src/security/permission.zod.ts, packages/core/src/security/effective-object-permissions.ts, docs/adr/0148-recycle-bin-separate-store-restore-under-original-ids-retention-in-days.md, ui/scripts/check-skill-eval-tokens.mjs + skill-bundle-root.mjs, .changeset/31278-eval-must-not-contain-narrowed.md.

Inputs: card #30847 body and all eight comments (triage 6679098621, the first-half claim / widen / ACCEPT / release 6687853924–6691834213, unblock 6730635071, this round's claim 6731158435 and dev report 6731390373); PR #31769 body, file list (3 files) and git diff origin/main...pr/31769 (= the diff against main c2bb879: 36 insertions, 21 deletions); the check-runs on the head.

Gate verdicts on the head (read, not re-run): every concluded gate is success or skipped — Check Changeset, Governed Surface Queue Guard, PR-body guards, Lint · PM dispatch-gates, Lint · repo & tooling gates (the skills token ratchet), Lint · docs & code guards, Lint · CI, release & engine gates, Lint · ESLint, Type Check · workspace / source gates / debt ledger, Test Core (2/8), Dogfood Regression Gate, Test UI. ⚠️ NOT concluded at the time of this record: Type Check · consumer gates (the job that runs check:skill-examples over the two os:check blocks this file keeps) and Test Core (1/8), both in_progress. The seat reads both before adopting. Lint UI is skipped (no ui/ change), so check:skill-eval-tokens did not run; it would not have covered this eval anyway — skill-bundle-root.mjs walks skills/objectui alone (see ④ F1).

① Derived judgments

  1. restore / purge derive from delete (SKILL.md L269-271, was "never derive … no door yet") — RIGHT. API_METHOD_DERIVATION on main (packages/spec/src/data/api-derivation.ts L151-152): restore: { all: ['delete'] }, purge: { all: ['delete'] }, with the docblock L136-142 naming ADR-0148 / engine: snapshot deletes into the recycle bin, restore under original ids, purge, and the TTL reap (#3146 slice 3) #30237 and leaving who-may to the security layer. The clause "open the recycle-bin doors" agrees with recycle-bin-routes.ts "API exposure": listing and restore pass enforceApiAccess as restore, purge and empty as purge. The sentence "Declaring any derived verb in apiMethods is stripped at parse with a FROM → TO warning" is kept unchanged and still holds for the two verbs.
  2. The four doors under /api/v1/data/{object}/recycle-bin (pitfall 2) — RIGHT, route by route against recycle-bin-routes.ts and rest-route-ledger.ts L393-396: GET (list, newest first), DELETE (empty), POST /:entryId/restore, DELETE /:entryId (purge). No fifth door is taught; the PR body's note that permanent: true has no REST door agrees with purge.ts (an engine option on a by-id ObjectQL.delete only) and the skill says nothing about it.
  3. Grants — RIGHT. Restore: allowRestoreOwnDeleted at the object gate, then own entry (deleted_by is the caller's userId) or manageRecycleBin for anyone else's (restore.ts "Who may restore", assertCallerMayRestoreEntry; permission.zod.ts L690: implied by manageRecycleBin and modifyAllRecords, not by allowDelete). List: manageRecycleBin (list.ts "Who may list"). Purge and empty: manageRecycleBin (purge.ts "One verb: purge", Q3 ruled A). Both spellings are GRANT_BITS members (effective-object-permissions.ts L336-339). The skill's bracket "(manageRecycleBin)" after "GET lists, DELETE /:entryId purges, DELETE empties" covers all three, which is what the tree does.
  4. recycleBinEntryId on the delete answer — RIGHT. DeleteDataResponseSchema.recycleBinEntryId (protocol.zod.ts L3219-3227, optional); protocol-data-writes.ts#deleteData passes onRecycleBinEntry and spreads the id into the answer; crud-routes.ts L558 serves p.deleteData(...)'s result as the body. Optional in the schema, and the skill's "not multi: true deletes, platform rows or rows deleted before the bin existed" is exactly the absent cases list.ts L53-55 and capture.ts list (plus permanent: true, engine-only, rightly untaught).
  5. The four 409 RESTORE_* codes, entry kept — RIGHT. RECYCLE_BIN_RESTORE_REFUSAL_CODES and RECYCLE_BIN_RESTORE_REFUSAL_STATUS = 409 (recycle-bin.zod.ts L258-270); all four registered under @objectstack/spec in error-code-ledger.zod.ts L1094-1102 at status: 409; restore.ts#stampRefusal sets status = httpStatus = 409 and "the entry stays". The parenthetical "(restore that parent's entry first)" on RESTORE_PARENT_IN_RECYCLE_BIN is the ADR-0148 D2 fourth bullet and restore.ts L45-48 verbatim in sense.
  6. 30 days by default; forward-only — RIGHT and kept from the first half: RECYCLE_BIN_DEFAULT_RETENTION_DAYS = 30 (recycle-bin.zod.ts L42, retention.ts L1232/1310); "rows deleted before the bin existed" stays in pitfall 2 (ADR-0148 D4 forward-only).
  7. Six byte-payment cuts, each with a surviving home on the head — judged on pr/31769:skills/objectstack-api/SKILL.md: (a) "aggregation through the query one" → Derived table row aggregate → POST /data/{object}/query L264, survives; (b) "authorable values are the six primitives — see API Methods" → L244 "The authorable ApiMethod enum is the SIX PRIMITIVES", survives; (c) "Always prefix with /api/v1/" → every Endpoint Naming row L228-232 and the carve-out L167 carry the prefix, survives; (d) the D6 cross-reference shortened to "D6 above" → the section authRequired and the D6 pairing L182 exists above, survives; (e) Best Practice "Apply least-privilege auth. Every endpoint should declare its required permissions explicitly" → Auth Configuration L325-329 names the public + permissions pair but does not prescribe declaring permissions — the RULE is gone, only its vocabulary survives (④ F2, nit); (f) "pointer-style — read the spec source for exact shapes" → References L439 "Always Read the source for exact field shapes", survives.
  8. Eval case 5 grading set — must_contain recycle bin → /recycle-bin/: WRONG as a token (④ F1); apiMethods, /batch, sys_recycle_bin unchanged and taught. must_not_contain adds nothing lists or restores: RIGHT — the stale first-half phrase removed from SKILL.md L414 in this diff, so an answer still parroting it is failed. 'restore' kept: RIGHT — see ③.
  9. Public surface — none moves. Teaching text and one eval fixture; @objectstack/skills ships skills/** through packages/skills/scripts/sync-catalog.mjs at the same version; no schema, route, code or jsdoc in the diff. The remaining bundle file references/_index.md carries no restore / recycle text and is untouched.

② Semver level

.changeset/30847-api-skill-recycle-bin-doors.md: '@objectstack/skills': patch — matches what the diff publishes (teaching text of a shipped skill plus its eval fixture, no API or metadata change). The card's filing line ("It owes '@objectstack/skills': patch") and the claim 6731158435 say the same. Check Changeset on the head: success. Clause-②: no on the changeset and on the PR body; no narrowing is declared and none happens (the skill GAINS a teaching; nothing a consumer could do is taken away). Metadata-protocol: none, right — no packages/spec/src/** change.

③ Boundary flags

  • Dev deviation 1 — "must_not_contain keeps 'restore' (quoted literal): it still guards declaring a derived verb in apiMethods" — ANSWERED, agreed. The token is the quoted-literal shape skills evals: 15 cases forbid (must_not_contain) a string their own expected_output uses, so a careful answer that names the trap fails #31278 fixed the whole eval corpus to ("the shape a wrong answer writes", .changeset/31278-eval-must-not-contain-narrowed.md): it fails apiMethods: ['get', 'list', 'restore'] and passes the reference answer, which writes restore in backticks (as the prompt itself does) and apiMethods: ['get', 'list']. A correct answer that writes the single-quoted literal in a negated sentence would be failed — the same honest limit 'search' has carried in this case since skills evals: 15 cases forbid (must_not_contain) a string their own expected_output uses, so a careful answer that names the trap fails #31278, and the one the repository has ruled acceptable. No finding.
  • Dev deviation 2 — built on the default tier under the no-Fable-build exit; dispatch-gates --tier mandates the contract-review model for skills/** — ANSWERED: this record is the CONTRACT_REVIEW_TIER review, claimed on the PR (comment 2026-11-09T03:18Z) by the lane's isolated reviewer; maintainer ruling 2026-11-08 16:12Z quoted there.
  • Dev deviation 3 — test:repo and dogfood not run locally — ANSWERED by the head's check-runs: Dogfood Regression Gate success, Test Core (2/8) success; Test Core (1/8) and Type Check · consumer gates still in_progress (escalated to the seat: read before adopting; check:skill-examples is the one derived gate this diff can move, through the two kept os:check blocks, which the diff does not edit).
  • open_questions: the report carries none; out_of_scope_findings: [].
  • Card scope: the claim's file surface (SKILL.md, rules/*.md / references/*.md only where restore teaching lives, evals/*.json cases the rewrite breaks, one changeset) is respected; rules/ does not exist in this bundle, references/_index.md carries no restore text, only case 5 changed. Fixes #30847 is right: the second half closes the card (the first half merged as docs(skills): objectstack-api teaches the recycle bin a by-id delete keeps, not a hard delete #30994 with Refs).
  • Length budget (card: "Keep each passage within its current length budget"): 441 → 441 lines, net 0 bytes by the dev's measure; the skills token ratchet runs in Lint · repo & tooling gates, success on the head.

④ Design judgment

skills/objectstack-api/SKILL.md — against the card's eval prompt (case 5, "undelete invoices") and the spec it teaches on main.

Every door, route, grant and refusal code the two rewritten passages name exists in the live tree exactly as taught (① 1-6). Tried to break it: (a) "allowRestoreOwnDeleted for your own deletes" — a caller holding only allowDelete cannot restore its own delete (permission.zod.ts L273/L690: allowDelete implies neither grant); the skill's wording does not claim otherwise, so it holds. (b) "GET lists … (manageRecycleBin)" — the REST GET door's exposure gate is the derived restore verb while its grant is manageRecycleBin; the skill teaches the grant, which is what an author must provision, and the exposure follows from delete per the Derived passage — consistent. (c) With apiMethods lacking delete, enforceApiAccess refuses every door (restore / purge derive from delete alone) — the skill's "(pitfall 2)" cross-reference plus the Key rule make that derivable, and the eval's expected_output spells it out. (d) A refused restore's envelope: recycle-bin-routes.ts#answerThrown answers through nestedRefusalEnvelope / sendError, and the pitfall names only status and code, never the envelope shape — no contradiction with Best Practice 2's envelope caveat. (e) 404 RECORD_NOT_FOUND for an entry not visible and 501 NOT_IMPLEMENTED with no bin are untaught — acceptable omissions at this budget, not contradictions. The Derived passage's one-line "derive from delete and open the recycle-bin doors (pitfall 2)" agrees with API_METHOD_DERIVATION sentence by sentence. The ADR-0148 "Nothing here is shipped" preamble is the record's own dating; the skill rightly teaches the tree, not the ADR.

  • F2 · nit — cut (e): Best Practice 3 "Apply least-privilege auth. Every endpoint should declare its required permissions explicitly" is removed and its named survivor, Auth Configuration L325-329, describes the public + permissions pair without prescribing it. A rule became vocabulary. No effect on correctness or the contract (nothing in the tree enforces it; authRequired default-true and D6 still carry the safe default). Accepted deviation at this byte budget, or a follow-up card if the lane wants the rule back; not to be fixed on this head.

skills/objectstack-api/evals/endpoints-auth-routes.json case 5 — against the prompt and the SKILL.md it grades.

The rewritten expected_output is right against the tree: apiMethods: ['get', 'list']; search / export derive from list; no /bulk; restore derives from delete; the conflict (a read-only API closes the bin's doors, so undeleting over the API needs delete); the restore path POST /api/v1/data/invoice/recycle-bin/:entryId/restore; recycleBinEntryId; the two grants; no custom soft-delete. It grades the new teaching, and must_not_contain nothing lists or restores fails the old one.

  • F1 · should-fix — must_contain /recycle-bin/ is a token the bundle never spells. The token is not identifier-shaped at either end, so it grades as an exact substring (the whole-token rule of ui/scripts/check-skill-eval-tokens.mjs, the repository's stated oracle for eval tokens). SKILL.md pitfall 2 writes the door as "Under /api/v1/data/{object}/recycle-bin: POST /:entryId/restore …" — base path, then relative paths — and git grep -F '/recycle-bin/' pr/31769 -- skills/ finds no occurrence outside the eval file. An answer that echoes the skill's own phrasing ("the doors live under /api/v1/data/invoice/recycle-bin; POST /:entryId/restore restores") teaches everything the case asks and is failed by this token; only an answer that happens to write the restore path in one piece passes. The case therefore does not reliably grade the teaching it was rewritten for. The eval is outside the check:skill-eval-tokens gate (that gate walks skills/objectui alone), so CI cannot catch it. Fix, either face: change the token to recycle-bin (whole-token: matches /recycle-bin and the full path, not sys_recycle_bin), or spell the full restore path once in SKILL.md pitfall 2 (POST /api/v1/data/{object}/recycle-bin/:entryId/restore) if the byte budget allows. Either fix moves the head and earns a delta review.

Judged and not a finding: 'restore' in must_not_contain (③, deviation 1).

Implemented-by: claude/project-thread-3lyy7q
Reviewed-by: session_01QsR2jULfPnqQFGcZUtKDFH

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · zhuangjianguo · session_01QsR2jULfPnqQFGcZUtKDFH

Contract-review-claim: session_01QsR2jULfPnqQFGcZUtKDFH · head f150834 · 2026-11-09T03:26Z

The lane seat's isolated Fable reviewer runs the CONTRACT_REVIEW_TIER review (maintainer 2026-11-08 16:12Z 「有 fable 车道就可以契约复审啊」); the director seat is off duty.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · zhuangjianguo · session_01QsR2jULfPnqQFGcZUtKDFH

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f15083410f7b7a9b614a62b8e1a416cf02e1e444
Local-runs: none
Scope: skills/objectstack-api/SKILL.md, skills/objectstack-api/evals/endpoints-auth-routes.json + none (the diff holds no implementing code; .changeset/30847-api-skill-recycle-bin-doors.md is the lane seat's ACCEPT at TIER_DEFAULT). Read, never reviewed, this round: ui/scripts/check-skill-eval-tokens.mjs on the head (the whole-token rule and the stated grading rule, "plain substring, case-sensitive"); everything else named in the Scope line of 6731493470 is unchanged on this head and carried by reference.

DELTA ROUND on the previous head 4efbaef8ff553b0c8d789de8bc624c148517a6f3, whose full record is PR comment 6731493470 (PASS, should-fix F1, nit F2). f15083410f7b7a9b614a62b8e1a416cf02e1e444 is one commit on top of 4efbaef8 (its sole parent), and git diff 4efbaef8 f1508341 touches ONE file, ONE line: skills/objectstack-api/evals/endpoints-auth-routes.json case 5, must_contain /recycle-bin/ becomes recycle-bin. The rest of the tree — SKILL.md, the changeset, every other eval case — is byte-identical to 4efbaef8. The net diff against main is still 3 files, 36 insertions, 21 deletions. ① to ④ below are carried forward by reference to 6731493470 wherever the delta does not touch them; only the delta is re-judged in full.

Inputs: card #30847 body and all comments (no comment newer than the dev report 6731390373; the fix commit carries no report of its own); PR #31769 body (unchanged since the previous record), file list (3 files), git diff origin/main...pr/31769 and git diff 4efbaef8 f1508341; PR comments after 6731493470 (one: the contract-review claim 6731509538 on this head); the check-runs on the head.

Gate verdicts on the head (read at 2026-11-09T03:28Z, not re-run): every concluded gate is success or skipped — Check Changeset, Governed Surface Queue Guard, Check PR Size, PR-body guards, Lint · PM dispatch-gates, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, Lint · scoped · query-options erasure, Type Check · workspace, Type Check · debt ledger, Test UI, Dogfood Regression Gate. ⚠️ NOT concluded at the time of this record: Type Check · consumer gates (the job that runs check:skill-examples), Lint · repo & tooling gates (the skills token ratchet), Lint · ESLint, Lint · docs & code guards, Lint · CI, release & engine gates, Type Check · source gates, Build Core, Plan Test Core shards, Live E2E UI — all in_progress. The seat reads them before adopting. Neither of the two gates this diff can move is moved by the delta: the one changed line is inside an eval fixture, not an os:check block (consumer gates) and not SKILL.md (token ratchet), and both were success on 4efbaef8 (6731493470). Lint UI is skipped, so check:skill-eval-tokens did not run and would not cover this eval anyway (it walks skills/objectui alone).

① Derived judgments

Items 1 to 7 and 9 of 6731493470 ① carry forward unchanged: SKILL.md is byte-identical to 4efbaef8, so every door, route, grant, refusal code, the derivation claim, forward-only, the six cuts and the public-surface judgment (none moves) stand as recorded there.

  1. Eval case 5 grading set — re-judged on the delta. must_contain is now apiMethods, /batch, sys_recycle_bin, recycle-bin. The new token recycle-bin is RIGHT: it is spelled in the skill bundle as taught — pr/31769:skills/objectstack-api/SKILL.md L270 "open the recycle-bin doors (pitfall 2)" and L408 "Under /api/v1/data/{object}/recycle-bin:" — and in the case's own expected_output (POST /api/v1/data/invoice/recycle-bin/:entryId/restore). git grep -F 'recycle-bin' on the head over skills/objectstack-api/ finds exactly those two SKILL.md lines plus the eval, and git grep -F 'recycle_bin' finds only sys_recycle_bin (L406 and the eval), so the two bin tokens do not vouch for each other. The other three must_contain tokens and the whole must_not_contain set are unchanged and stand as judged.

② Semver level

Unchanged from 6731493470: .changeset/30847-api-skill-recycle-bin-doors.md '@objectstack/skills': patch matches what the diff publishes (teaching text plus one eval fixture; the delta changes one grading token inside that fixture and publishes nothing further). Check Changeset on this head: success. Clause-②: no on the changeset and the PR body; no narrowing is declared and none happens. Metadata-protocol: none, right.

③ Boundary flags

  • Dev deviations 1 to 3 of the report 6731390373 — ANSWERED in 6731493470 and unchanged: the fix commit f1508341 (message "grade the api undelete eval on a token the skill spells") adds no deviation and carries no open_questions; 'restore' stays in must_not_contain as judged there (deviation 1, no finding). Deviation 3 (gates not run locally) is answered by the head's check-runs to the extent concluded; the still-running jobs are escalated to the seat above.
  • The fix is the first of the two faces F1 offered ("change the token to recycle-bin"); the SKILL.md face was not taken, so the byte budget (441 lines, net 0 bytes) is untouched and the token ratchet cannot have moved.
  • No new card comment and no dev report for the delta; the contract-review claim 6731509538 names this head. Fixes #30847 unchanged and right.

④ Design judgment

skills/objectstack-api/SKILL.md — byte-identical to 4efbaef8; the judgment and the attempts to break it in 6731493470 ④ stand by reference. F2 (nit, cut (e): Best Practice 3 became vocabulary in Auth Configuration) is carried forward unchanged as an accepted deviation at this byte budget or a follow-up card; not to be fixed on this head.

skills/objectstack-api/evals/endpoints-auth-routes.json case 5 — the delta, judged against the prompt ("undelete invoices") and the SKILL.md it grades.

  • F1 of 6731493470 (should-fix) — RESOLVED. The assertion is graded as a plain case-sensitive substring (check-skill-eval-tokens.mjs header, "Plain substring, case-sensitive: that is how the assertion is graded"), and under the stricter whole-token oracle of the same file recycle-bin is identifier-shaped at both ends (r, n; IDENT_CHAR is [A-Za-z0-9_$], so the hyphen inside is simply part of the literal), which means it needs a non-identifier character on each side: SKILL.md L270 (space / space) and L408 (/ / backtick) both count, 2 whole-token occurrences, 2 substring occurrences — the bundle spells the token as taught, under either oracle. A correct answer to the prompt must name the restore door to tell the user how to undelete over the API; the skill teaches that door only as the base path /api/v1/data/{object}/recycle-bin plus relative POST /:entryId/restore, and an answer written either that way (the skill's own phrasing, which the old token /recycle-bin/ failed) or as the full path /api/v1/data/invoice/recycle-bin/:entryId/restore (the expected_output's phrasing) contains recycle-bin. The case now grades the teaching it was rewritten for. sys_recycle_bin (underscores) does not contain recycle-bin, so an answer that names only the table and no door still fails the token, which is the right polarity.
  • Too weak a token? Judged and not a finding. The one answer recycle-bin passes that /recycle-bin/ would have failed and that the case would rather fail is one that writes the word only as prose ("opens the recycle-bin doors", SKILL.md L270's own phrase) and never names a route. No must_contain token in this grammar can require a route to be spelled unless the skill spells the whole path in one piece — the SKILL.md face of F1, which the dev did not take and the byte budget argues against. That residue is the limit the corpus already states for every token ("the bundle says this word somewhere, never that the bundle teaches the thing the eval means", the header's create case); the wrong-answer guards for this case live in must_not_contain ('restore', 'search', nothing lists or restores), which are unchanged. No effect on correctness or the contract.
  • F3 · nit (prose face) — the PR body's "What changes" still reads "must_contain swaps recycle bin for /recycle-bin/"; the tree on this head carries recycle-bin. The body's account is now stale against the diff it describes. Fix on the PR body alone (it does not move the head), or leave as an accepted deviation; the record judges the tree, and the tree is right.

Judged and not a finding: 'restore' in must_not_contain (③, deviation 1, as before); the eval's other five cases, unchanged on this head.

Implemented-by: claude/project-thread-3lyy7q
Reviewed-by: session_01QsR2jULfPnqQFGcZUtKDFH

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · zhuangjianguo · session_01QsR2jULfPnqQFGcZUtKDFH

维护者速读

  • 改了什么:已发布的 api 技能不再说“回收站没有门、restore/purge 不派生”,改为教当前 main 上真实存在的:restore/purge 由 delete 派生,/api/v1/data/{object}/recycle-bin 下的四个门,两个授权位(allowRestoreOwnDeleted、manageRecycleBin),删除应答里的 recycleBinEntryId,以及四个 409 RESTORE_* 拒绝码。评测第 5 例随之改写。
  • 为什么改:ADR-0148 的回收站已在 main 落地(前半张卡 docs(skills): objectstack-api teaches the recycle bin a by-id delete keeps, not a hard delete #30994 已合入),技能还在教“没有恢复门”,AI 会据此写出自制软删除或错误的 apiMethods。
  • 风险与回滚:只改技能文档、一个评测夹具和一个 changeset,不碰运行时。SKILL.md 净 0 字节(5157 词元不变,上限 6314),新增句子靠同文件删掉六句重复说明抵掉,复审逐条核过每句在文件里另有出处。回滚即 revert 本 PR。
  • 先看:skills/objectstack-api/SKILL.md 的 Derived 段(L269–271)和 pitfall 2(L405–414),然后 skills/objectstack-api/evals/endpoints-auth-routes.json 第 5 例。
  • 两分钟验证:git grep -n 'recycle-bin' skills/objectstack-api/ 应看到 SKILL.md 两处加评测一处;pnpm --filter @objectstack/spec check:skill-examples 退出 0。
  • 需要你做的:批准本 PR(Approve review)。契约复审 PASS 记录是评论 6731493470(全量)与 6731554357(增量,修复 F1),Head-sha f15083410f7b7a9b614a62b8e1a416cf02e1e444;批准后由席位落地。
  • 席位意见:ACCEPT。席位读了 diff 和两份复审:每个门、授权位和拒绝码都对得上 recycle-bin-routes.ts、api-derivation.ts、recycle-bin.zod.ts 和 restore.ts;本 head 上 Type Check · consumer gates(示例编译)和 Lint · repo & tooling gates(词元上限)均为 success。

席位核对

Head-sha: f15083410f7b7a9b614a62b8e1a416cf02e1e444

  1. Shape: draft, base main, body Fixes #30847, Clause-②: no, Metadata-protocol: none, changeset '@objectstack/skills': patch. 3 files, +36/−21. Tier H (skills/**). The second half of the card; the first half merged as docs(skills): objectstack-api teaches the recycle bin a by-id delete keeps, not a hard delete #30994 with Refs.
  2. Premise measured: on main c2bb879 the tree ships the recycle-bin doors (packages/rest/src/recycle-bin-routes.ts, ledger rows L393–396) and API_METHOD_DERIVATION derives restore/purge from delete, while the skill still said they never derive and nothing lists or restores. Dev report 6731390373: premise_still_valid: true.
  3. Gates: dev gates 46 derived, 45 exit 0; dual-build-cjs-loads exit 3 (needs a full build; no build input touched). check:skill-examples run by hand, exit 0 (332 blocks). Check-runs on this head at ACCEPT: 28 success, 15 skipped, 0 failure, 0 running.
  4. Accepted deviations: CR F1 (should-fix, the eval token /recycle-bin/ the bundle never spells) was fixed in f1508341 (token recycle-bin), delta review 6731554357 PASS. Accepted as is: F2 (nit), the cut Best Practice 3 "Apply least-privilege auth" leaves only the public + permissions vocabulary in Auth Configuration, the price of the 0-byte budget; F3 (nit), the PR body's "What changes" still says the token became /recycle-bin/ while the tree carries recycle-bin; the tree is right and the body is left as it stands. Dev deviation 1, agreed by the review: must_not_contain keeps the quoted literal 'restore', the skills evals: 15 cases forbid (must_not_contain) a string their own expected_output uses, so a careful answer that names the trap fails #31278 shape that fails apiMethods: ['get', 'list', 'restore']. Model: opus build under the standing no-Fable-build rule, Fable review (claim 6731158435).
  5. Out of scope / Skills-docs: untaught on purpose at this budget: the 404 for an entry the caller cannot see, the 501 with no bin, and the engine-only permanent: true. The eval sits outside check:skill-eval-tokens (that gate walks skills/objectui alone). Metadata-protocol: none, so no Skills-docs: line is owed.
  6. Landing: Tier H. pm:awaiting-merge + reviewers os-zhuang, hotlong; stays draft until an authorized approval or a maintainer merge. K = 4 first-parent merges since merge-base c2bb879f (fix(objectql): a server boot's schema sync takes the schema-migration lock, so replicas starting together create each table once #31747, feat(service-automation)!: the flow index and the activation ledger consult on (package, name) (ADR-0150 E1) #31738, feat(showcase): make the invoice the live multi-currency example #31757, feat(objectql, runtime): action handlers keyed by (package, objectName, name) #31737); none touches skills/objectstack-api/, and the merge with main is clean.

@objectstack-fleet objectstack-fleet Bot added the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 9, 2026
@objectstack-fleet objectstack-fleet Bot removed the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 9, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 9, 2026 03:51
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 9, 2026 03:51
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 9, 2026
Merged via the queue into main with commit 630a0ba Nov 9, 2026
52 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-3lyy7q branch November 9, 2026 04:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills: objectstack-api still teaches that DELETE is unrecoverable and that there is no recycle bin (#30237, ADR-0148)

2 participants