Repository navigation
Description
Filed out-of-scope from the published-skills factual sweep, flight ⑦ (skills/objectstack-api, card #13814, program #13658). Recording only — unassigned.
The measurement
handlerStatus (implemented / stub / planned) is an authorable key on RestApiEndpointSchema (packages/spec/src/api/plugin-rest-api.zod.ts). A repo-wide search for the identifier, excluding skills/** and test files, returns three hits and all three are in the declaring file itself:
plugin-rest-api.zod.ts:177— the declaration onRestApiEndpointSchemaplugin-rest-api.zod.ts:1415— the same enum re-declared onRouteCoverageEntrySchemaplugin-rest-api.zod.ts:1431— a docblock saying adapters SHOULD warn whenhandlerStatus !== 'implemented'
Plus one CHANGELOG row. No consumer in packages/runtime, packages/rest, or anywhere else reads the key.
Why it is worth a card rather than a shrug
The key's documented effect is a specific runtime behaviour, and that behaviour has a different cause. DispatcherErrorCode.enum.NOT_IMPLEMENTED is emitted from exactly four sites — runtime/src/endpoint-executor.ts (three) and runtime/src/api-mapping.ts (one) — and every one of them is the declarative-endpoint executor refusing a target or a mapping it cannot serve. None consults handlerStatus. So an author who sets handlerStatus: 'stub' expecting the dispatcher to answer 501 gets an ordinary served route, and the declaration reports progress to nobody: RouteCoverageReportSchema is the only shape that would carry it outward and nothing constructs one.
This is the ADR-0049 enforce-or-remove class, and the same shape as #13613: a declared surface whose semantics no code implements. It is also a declared = enforced gap on the endpoint vocabulary — the same surface ApiEndpointSchema closed strictly in #5384 precisely so a typo could not parse green and configure nothing.
Two routes, for triage
- Remove. Retire the key from
RestApiEndpointSchemawith a tombstone, and either retireRouteCoverageReportSchemaalongside it or keep it as the report-only vocabulary it already is. Cheapest, and it matches the startup-scope posture for a key with no pull. - Enforce. Have the route registrar consult
handlerStatusand mount a 501 stub forstub/planned. That is a real capability — a declared-but-unbuilt route answering an honest 501 instead of 404 — but it is new behaviour needing its own ruling, not a repair.
Recommendation: remove, unless someone can name a consumer that wants the coverage report. The evidence for "no pull" is that the key has shipped since the CHANGELOG row and nothing has grown to read it.
Provenance
Found while verifying the skill sentence "The dispatcher returns 501 NOT_IMPLEMENTED for stub and planned handlers", which flight ⑦ corrects in the skill text. Fixing the skill stops the false teaching; it does not close the declared-but-inert key, which is why this is a separate card.
Activity
os-warren commentedon Aug 31, 2026
分诊定级 →
domain:spec·needs-user-decision· p3 · tooling。摘finding。锚定。
packages/spec/src/api/plugin-rest-api.zod.ts⇒domain:spec(唯一所有者)。⭐ 与 #13612 / #13613 是同一个待裁问题的第三个实例 —— 一次裁决覆盖三张
三张都是 ADR-0049 的 enforce-or-remove,都问同一句「绑定/强制,还是退役」:
SnakeCaseIdentifierSchema不允许)⇒ 存量元数据可能被拒EventNameSchema的三个绑定 schema 零运行期消费者WebSocketEventSchema.channel(今天是裸z.string())RestApiEndpointSchema.handlerStatus零消费者⇒ 建议一句方向覆盖三张(「本仓对 ADR-0049 的 declared-but-unenforced,默认走 retire 还是 bind」),各卡再按自己的半径评估落地代价。⛔ 三张不合并 —— 半径差一个数量级,合并会让最小的那张背上最大的那张的风险。
本卡自身的读数,已复核口径
标识符全仓搜索(排除
skills/**与测试)三处命中,全在声明文件内::177(RestApiEndpointSchema上的声明)·:1415(RouteCoverageEntrySchema上重复声明的同一 enum)·:1431(一段说 adapter SHOULD 在handlerStatus !== 'implemented'时告警的 docblock),外加一行 CHANGELOG。⇒packages/runtime、packages/rest及其它任何地方零消费者。⭐ 而它被文档化的效果,来自别处:
DispatcherErrorCode.enum.NOT_IMPLEMENTED恰好从四个站点发出(runtime/src/endpoint-executor.ts三处 +runtime/src/api-mapping.ts一处),每一处都是声明式端点执行器拒绝一个它服务不了的 target 或 mapping,没有一处查handlerStatus。⇒ 一个设了
handlerStatus: 'stub'并期待 dispatcher 回 501 的作者,得到的是一条正常服务的路由;而那个声明向谁都没有报告进度 ——RouteCoverageReportSchema是唯一会把它带出去的形状,而没有任何东西构造它。我认同「remove」的推荐,并补一条判据
卡片说:remove,除非有人能指名一个想要 coverage report 的消费者。⇒ 这条判据是可判定的,应当在裁决前跑一次:除了
RouteCoverageReportSchema之外,有没有任何 published skill / 文档 / 外部集成在教作者用handlerStatus?501 NOT_IMPLEMENTEDforstubandplannedhandlers」时发现的 —— 也就是说,已发布的教材曾经在教这件不存在的事(flight ⑦ 正在改)。⇒ 若还有别处也在教,remove 就要带一次教材清扫,而不只是一次墓碑。定级 p3:零行为、零错误答案上线。不判更低,是因为它同时是一处
declared = enforced缺口,而ApiEndpointSchema在 #5384 上恰恰严格关掉过同一类(让一个拼写错误无法解析成功却什么都不配置)—— 同一个端点词表上两种口径。Refs:#13814 / #13658(发现现场)· #13612 / #13613(同问题的另两个实例)· #5384(同词表上的严格化先例)· ADR-0049。
Generated by Claude Code
huangyiirene commentedon Sep 1, 2026
裁决:remove ——
handlerStatus带墓碑退役;类方向同笔入册(维护者 2026-09-01,总监批 #27)项目总监席 · session
session_01KGtaLpkW1mycWgkbSb3H6t· 维护者对本批逐字:「同意」。RestApiEndpointSchema.handlerStatus按spec-property-retirement手册退役(liveness 台账 verdict 确认 → ADR-0087 转换 → 墓碑 + 生成基线/表单/文档/pin 全套);RouteCoverageEntrySchema/RouteCoverageReportSchema随同一 PR 按 liveness 读数处置(零构造者即同退);handlerStatus;非零则同笔清扫(教材曾教过不存在的 501 行为,是本卡的出处);needs:contract-review同笔,本席复审;changeset 按退役惯例。状态转移(同笔)
needs-user-decision→pm:queue;toolingpriority:p3domain:spec不动。Generated by Claude Code
claude commentedon Sep 2, 2026
Claim: PM loop,
domain:specseat (seat post #6017)Session:
session_01GDA48PuRFrHyRfdkBz8m21Branch:
claude/issue-13823-retire-handler-statusWorktree:
objectstack-issue-13823Domain:
domain:specFile surface:
packages/spec/src/api/plugin-rest-api.zod.ts(handlerStatusonRestApiEndpointSchemaretired with a tombstone;RouteCoverageEntrySchema/RouteCoverageReportSchemaretired alongside on the liveness reading — zero constructors) + the retirement playbook's full set: liveness ledger verdict, ADR-0087 conversion entry (tail-append underpackages/spec/src/migrations/entries/+ regeneratedregistry.ts), generated baselines (api-surface / authorable-surface / export-origins / json-schema / declaration-map), forms if any offer the key, reference docs, pin tests, changeset; plus the teaching sweep's hits incontent/docs/**andexamples/**(publishedskills/**hits are reported, not edited — governed surface, skills lane). ⛔ No enforce path (ruled out).Container & model: M,
mode:subagent, servedclaude-fable-5-1(Clause-② yes: published accept set narrows; fuse literal readsclaude-fable-5, posture on #14303).Clause-②: yes (declared on both carriers at PR time).
Ruling implemented: director batch #27, maintainer 2026-09-01 verbatim 「同意」 (comment 5494755488) — remove; teaching sweep first; enforce excluded; class direction registered for #13612 / #13613 (not ruled here).
Serial constraints: no in-flight writer of
plugin-rest-api.zod.ts; ADR-0087registry.tstail-append andmerge=os-regenproducts relay against the other in-flight spec PRs (#13801 phase 2, #13809 — describe-only, no registry entries) — the later pusher resyncs viascripts/pm/os-regen-merge.sh.Generated by Claude Code
feat(spec): retire RestApiEndpoint.handlerStatus and the Route Covera…
chore(spec): regenerate artifacts for the handlerStatus / Route Cover…
fix(spec): tombstone text carries the ADR, not the issue id; isomorph…
chore(spec): regenerate the reference page for the tombstone text (#1…
fix(spec): the isomorphic-pin count assertion literal follows the rec…
docs(changeset): the skills sentence states what main carries, not th…
claude commentedon Sep 2, 2026
os-dev-report
{ "issue": 13823, "status": "done", "branch": "claude/issue-13823-retire-handler-status", "pr": "https://github.com/objectstack-ai/objectstack/pull/14531", "head": "875e3fa5e", "premise_still_valid": true, "summary": "Retired RestApiEndpointSchema.handlerStatus per the spec-property-retirement playbook, exactly as ruled (remove; enforce excluded): retiredKey() tombstone on the non-strict RestApiEndpointSchema (prescription cites ADR-0049; the issue id lives in the adjacent comment after check:doc-authoring refused it in customer-facing text), whole-def removal of RouteCoverageEntrySchema / RouteCoverageReportSchema (re-measured: zero constructors in objectstack, objectui @d8ec8d6d, cloud) and of the now-orphan HandlerStatusSchema enum; ADR-0087 entries tail-appended (retired-keys 18.api__RestApiEndpoint__handlerStatus, retired-defs 18.api__HandlerStatus / RouteCoverageEntry / RouteCoverageReport, semantic 18.rest-api-endpoint-handler-status-retired) with registry.ts regenerated; deliberately no D2 conversion (nothing parses RestApiEndpointSchema outside its own unit tests; no stack seam) and no os migrate meta sentence. Premise re-measured on origin/main a9b2be0b0: the three declaring-file hits are unchanged; NOT_IMPLEMENTED emitters are endpoint-executor.ts x3, api-mapping.ts x1 plus one the card did not count (api-endpoint-step.ts:343), none reading the key. Liveness ledger: no row moves because RestApiEndpointSchema is outside the walked population (not a registered metadata type, not in SPEC_ONLY_SCHEMAS) - adding one would be an ORPHAN; the dead verdict is recorded in the schema, the D3 entry and the changeset. Generated baselines: json-schema.manifest -3 keys and authorable-surface -10 rows deleted deliberately (the #4725 manifest deletion gate and check (c) adjudicated them), handlerStatus row now [RETIRED], api-surface / export-origins / declaration-map -6 names, reference docs + strictness-ledger counts regenerated; spec-changes.json and the upgrade guide are unchanged because they project majors up to 17 only (same as every 18-registered precedent). Pin tests added (rejection with code+path+prescription for all three former values incl. the prose-only default 'implemented', the route-registration embed path, positive control, shipped default registrations, tsc channel, zero holders for six names, registry membership); type-alias pin count rebalanced 835->832. Teaching sweep: no hand-written docs/examples/READMEs/create-objectstack hit; generated reference regenerated; skills/objectstack-api/SKILL.md taught the key at the base (four sites, not edited - governed surface) but main already cut every one in #14448 (bae4088f3): zero hits under skills/ at 99b8faf8f with RestApiEndpoint still hitting as the live control - none on main, no skills-lane follow-up; the changeset says so. Relay: 12 commits landed on main since BASE; the only moved os-regen paths are 14 content/docs/references pages my diff does not touch, so no merge was needed. Clause-two: needs:contract-review added to PR #14531 and the card with matching comparative read-backs; PR left draft. Two CI/coordinator corrections applied as plain commits after the draft opened: the pin-count assertion literal (7cbf0ed60) and the changeset's skills sentence (875e3fa5e).", "tests": "Every heavy step under scripts/pm/os-verify-lock.sh; exits captured before any pipe; verdict lines quoted. FINAL HEAD 875e3fa5e: spec build exit 0 (gen:schema: '3 schema(s) left the published set since a9b2be0b02a3, each declared (#4725)'; '2 baseline deletion(s) ... carry their own proof (#4650)'; check-dts-emitted 34/34); check:generated exit 0 ('All 15 generated artifacts are up to date'); targeted vitest (exec vitest run --maxWorkers=2 type-alias-convention.pin + plugin-rest-api.handler-status-retirement + plugin-rest-api + retired-key-migrate-sentence + migrations): 'Test Files 5 passed (5)', 'Tests 172 passed (172)'; check:skill-examples exit 0 ('265 prose examples type-check across 3 surface(s)', client-react closure built); check:adr-0087-registration exit 0 ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition'); check:empty-changeset, check-changeset-no-major, check:nul-bytes, check:doc-authoring, check:spec-parsed-alias exit 0; git status clean after the build. 7cbf0ed60: targeted vitest 5/172 passed; check:test-typecheck 0; spec-parsed-alias/doc-authoring/nul-bytes 0; check:api-surface refused (dist older than src after the test edit - not a measurement; settled by the rebuild). 90d567cd3 (full union): spec typecheck exit 0 (tsc + scripts + 'check:test-typecheck: OK - 54 file(s) / 262 error(s) / 146 pinned signature(s)', ledger unchanged; new test file proven in the tsconfig.test.json program via --listFiles); FULL spec vitest --maxWorkers=2: 'Test Files 1 failed | 451 passed (452)', 'Tests 1 failed | 12190 passed (12191)' - the one failure was the pin-count toHaveLength literal (835), fixed in 7cbf0ed60 and green in chains 3 and 4; check:skill-examples 0; check:exported-any / dual-source-exports / entry-nameability / browser-reachable-entries 0; check:generated 0 (15/15); doc-authoring / nul-bytes / adr-0087 / spec-parsed-alias 0; check-dev-prereqs exit 1 = 'The workspace is not built - 32 of 67 workspace packages declare an entry point under dist/ that is not on disk' (pnpm dev precondition, unrelated - NOT MEASURED). Also exit 0 on the same tree earlier: check:variant-docs, empty-state, llms-txt, yaml-examples, template-manifests, error-code-provenance, objectui-pin-citations, quick-reference-counts, corpus-claim-drift, skill-identifier-liveness, doc-anchors, docs-single-h1, docs-audit-scope, test-source-alias, cross-package-test-inputs, published-files, type-check-coverage, migration-registry + liveness (inside check:generated), and root check-doc-frontmatter / docs-section-name / doc-route-spelling / section-landing-index / keyed-text-bounds / ci-filter-parity / comment-mask-adoption / shard-attestation / system-context-census / undeclared-dep-imports / plugin-teardown-shape / docs-audit affected-docs + drift-comment / pm half-states. Earlier reds fixed before landing: check:doc-authoring (issue id in the tombstone string) and the pin-count literal. NOT MEASURED (CI-owned): check:type-check-debt / --re-measure (exit 3 PREREQUISITE NOT MET, whole closure not built), check-test-completeness (grades CI's turbo test log), check:react-declaration-parity (objectui manifest), repo-wide pnpm lint. DECLARED NARROWING: the full 452-file suite ran on 90d567cd3; the two later commits touch one test literal and changeset prose and were re-verified with the targeted files, not the whole suite - CI's Test Core covers it on the final head. Gate list from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 74 commands at b86231707). CI on 875e3fa5e at report time: 31 check-runs — completed/skipped: 2, completed/success: 14, in_progress/None: 15.", "mcp_calls": "0 - every GitHub read/write went through authenticated REST (curl) and local git; no MCP GitHub tool was called", "open_questions": [], "out_of_scope_findings": [ "skills/** teaching hits: none on main (measured at 99b8faf8f - git grep -n -i -E 'handlerStatus|handler status|RouteCoverage|HandlerStatus' origin/main -- skills/ returns zero; reverse control RestApiEndpoint still hits skills/objectstack-api/SKILL.md). At this PR's base a9b2be0b0 the skill carried four sites (SKILL.md:136 handlerStatus: 'implemented' in the os:check-marked RestApiEndpointSchema.parse example; :380 the '501 NOT_IMPLEMENTED - Route declared but handler is a stub' row; :383-391 the Handler Status section; :393-395 flight 7's #13808 correction) - all cut by #14448 (bae4088f3) before this report; nothing to card.", "OBSERVATION, no card needed: the card's NOT_IMPLEMENTED emitter count (four) misses packages/runtime/src/api-endpoint-step.ts:343 - a fifth emitter of the same class (executor refusing what it cannot serve), none consulting handlerStatus; recorded in the PR body and the D3 entry." ] }Generated by Claude Code
Generated by Claude Code
claude commentedon Sep 2, 2026
ACCEPT — in-seat Clause-② contract review PASS on PR #14531 (head
875e3fa5).domain:specseat, sessionsession_01GDA48PuRFrHyRfdkBz8m21, seat post #6017; served model stringclaude-fable-5-1(floor reading per the #14303 record, open to maintainer veto).Report: 5508368645 (
status: done; full spec suite 452 files / 12190 passed on90d567cd, the two later commits re-verified with targeted files and declared as such; gates derived, exits captured; NOT MEASURED items are CI-owned and CI is running them on the final head).Review against the director ruling (batch #27 「同意」: remove, enforce excluded) and the
spec-property-retirementplaybook:RestApiEndpointSchemais a non-strictz.object, sohandlerStatus: retiredKey(HANDLER_STATUS_RETIRED)is the correct shape (bare deletion = silent strip, ADR-0104). Prescription names the key, the removal, the real 501 mechanism (declarative-endpoint executor), the action (delete the key) and ADR-0049; no issue id in customer-facing text (check:doc-authoring).RouteCoverageEntrySchema/RouteCoverageReportSchemahad zero constructors (route 3) andHandlerStatusSchemais an orphan value enum once both carriers leave (refactor(spec)!: remove the plugin sandboxing / integrity / approval config that never existed (#3896 follow-up) #3950). Seat census at objectui63b4e0eand cloud061e83c: zero holders of all six names, grep validated by a live control.retired-keys/18.api__RestApiEndpoint__handlerStatus,retired-defs/18.api__{HandlerStatus,RouteCoverageEntry,RouteCoverageReport},semantic/18.rest-api-endpoint-handler-status-retired;registry.tsdiff lands only inside the three generated regions. No D2 conversion by design (no seam ever parses the schema) and the pin asserts that absence. Registered under 18 while shipping on the 17.x line, with the tombstone worded "removed in @objectstack/spec 17" — matches the dominant 18-registered precedent (context.previewMode,manifest.capabilities,measures.<metric>.filters).invalid_typeat['handlerStatus']for all three former values including the prose-only default, the nested path throughRestApiRouteRegistrationSchema, the no-strip positive control, the shipped default registrations, the tscneverchannel, zero holders on every public entry, registry membership under 18; type-alias pin count rebalanced 835 → 832 with its receipt.[RETIRED]row + −10 rows of the two removed defs, json-schema manifest −3 (adjudicated by the json-schema.manifest.json 的「deliberate removal」删行仍是纪律而非门禁 —— #4650 的同类洞,上移一层(整 schema 级) #4725 gate), strictness ledgerapi/455 → 452, reference pages 1593 → 1590. Liveness ledger correctly untouched (schema outside the walked population).@objectstack/specminor, BREAKING,<!-- adr-0087: registered rest-api-endpoint-handler-status-retired -->, FROM → TO rows, one-line fix; the teaching-sweep sentence now states whatmaincarries (the skill's four sites were cut by skills(api): optimization flight — RestApiEndpointSchema example and other packages' anchors cut, the apiMethods rule stated once, http.server mount / error ledger / OCC taught (net −1,676 tokens) #14448 /bae4088f3before this PR) — no skills-lane card is needed; the seat re-measured: zeroskills/**hits atc1f7e9f6and99b8faf8.7cbf0ed6) and the changeset sentence (875e3fa5); neither widened the diff.875e3fa5: closing keyword on the PR body =Fixes #13823only;check-governed-merges --teston the 18-file list: NOT governed; nocontent/docs/releases/edit; trial merge CLEAN vsorigin/mainfa7292c4; zero base drift on the PR's files. (Re-run once more at the flip.)Observation from the report, recorded and not carded: a fifth
NOT_IMPLEMENTEDemitter (packages/runtime/src/api-endpoint-step.ts:343) of the same executor class, also never consulting the key — the retirement record already names it.Gate:
needs:contract-reviewcleared on both carriers by this comment's transaction (read-modify-write, compared read-back). Landing: ready + auto-merge only once every check on head875e3fa5is green (CI restarted 10:41Z); the queue squashes.Generated by Claude Code
test(spec): the second api/ file group's test titles state each cited…