Repository navigation
feat(spec,rest,lint)!: retire the form field's publicPicker and the anonymous lookup route (#21180) - #21222
Conversation
…e the anonymous lookup route (WIP) Ruling E: anonymous public forms no longer take lookup / master_detail / user fields. The key becomes a retiredKey() tombstone with an ADR-0087 D2 conversion and registry entries; GET /forms/:slug/lookup/:field and its helper module are deleted; the resolve route's strip is unconditional; the lint reader and the picker tests go. Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
…erate the spec artefacts The def leaves with its only carrier: a RETIRED_DEFS_BY_MAJOR[18] entry, its manifest and authorable-surface lines deleted deliberately, the dropped-refinements ledger corrected as the build printed it, and the api-surface, export-origins, declaration-map, reference docs and strictness counts regenerated by check:generated --fix. Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
…ed-route pin, as the served composition does Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
…ADR-0087 registered Also moves the dropped-refinements ledger's header totals with its body (212 → 210 schemas, 617 → 613 sites). Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
…of the file Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
…route took four legacy bodies with it (43 → 39, 58 → 54) Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
…tire-public-picker
Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
…tire-public-picker # Conflicts: # packages/spec/dropped-refinements.baseline.json
…s counts from the merged tree Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 4 package(s): 36 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 140 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 839406895e3f06efc5beda5d102196e12140aaec && git checkout 839406895e3f06efc5beda5d102196e12140aaec
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3a7b6eb0635827442fa248baffa14187a60f5a22 89bfe194274678c602899e5e5fa4efdd383720e1 && git checkout -B drift-repro 3a7b6eb0635827442fa248baffa14187a60f5a22 && git merge --no-ff 89bfe194274678c602899e5e5fa4efdd383720e1
node scripts/docs-audit/affected-docs.mjs --json 3a7b6eb0635827442fa248baffa14187a60f5a22
|
…nd-spot census and the query-slot floor after the picker route's deletion The matrix docblock's rest ledger figure 83 -> 82 rows (18 families). The blind-spot census: rest-route-ledger 83/83/0 -> 82/82/0, rest-server 72/19/53 -> 71/19/52 (the picker route was a blind spot, outside registerMetadataEndpoints), totals 67/72 -> 66/71. The canonical-query-AST floor for rest-server.ts 5 -> 4 query slots. Every figure re-measured. Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
…tire-public-picker # Conflicts: # content/docs/ui/forms.mdx # packages/qa/dogfood/test/public-picker-queryable-key.dogfood.test.ts # packages/qa/dogfood/test/zero-set-masking.dogfood.test.ts # packages/rest/src/public-form-lookup-picker-queryable-key.test.ts
… main's new key on top of the retirement's deliberate deletions Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #21180 (body and all six comments, the claim ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each named right or wrong:
② Semver level
③ Boundary flagsThe dev's
Check-runs on Implemented-by: VERDICT: PASS Generated by Claude Code |
…h model is retired by ruling E (objectstack-ai#21180) (objectstack-ai#21223) Refs objectstack-ai#21180 Docs-only. Adds one dated note to ADR-0061 under its D5 entry (`docs/adr/0061-record-search-architecture.md:54`). The original sentence says anonymous search "keeps the existing `publicPicker` model". The note records that this model is retired by the maintainer's ruling E on objectstack-ai#21079 (record `5933054144`, 「同意E」, 2026-10-01), which reverses the objectstack-ai#7467 model. The original text is not rewritten: the diff is exactly two added lines, a blank line and the note. **Tier H.** `docs/adr/**` is a governed surface, so this PR lands only on the maintainer's approval. No seat merges, queues or arms it. The code half (the retirement itself) is the separate PR objectstack-ai#21222; this PR carries no closing keyword, so merging it does not close the card. ## The note (its links shown as plain references) > **Note (2026-10-01) — anonymous search retired.** The `publicPicker` model the sentence above keeps is retired by the maintainer's ruling E on objectstack-ai#21079 (comment 5933054144, 2026-10-01), which reverses the objectstack-ai#7467 model (declare `publicPicker`). Anonymous public forms no longer take lookup, `master_detail` or `user` fields: the public-form resolve route leaves them off the anonymous rendering unconditionally, the anonymous record-search route `GET /forms/:slug/lookup/:field` is deleted, and `publicPicker` is a `retiredKey()` tombstone under ADR-0087 D2 (immediate retirement). So there is no public/anonymous record search; the rest of D5 stands. The retirement is objectstack-ai#21180. ## Gates (derived from this diff, head `6b16db083`) `dispatch-gates --commands` derives 19 families. All 19 ran to exit 0, among them `check-adr-links` (695 links resolve), `check-adr-symbol-anchors`, `check:adr-anchors`, `check:pm-governed-merges`, `check:doc-authoring`, `check:nul-bytes` and `check:closing-keyword-parity`. One, `check:doc-formula-expressions`, first refused with exit 3 because `formula` and `lint` were not built in this fresh worktree. It ran to exit 0 after the build its refusal prescribed. `skip-changeset` applies: the diff publishes nothing. ## 维护者速读(草稿) **改了什么**:只在 ADR-0061 的 D5 条目(`:54`)下追加一条注明日期(2026-10-01)的说明,原文一字不改;差异只有新增的两行。 **为什么改**:D5 原文写着匿名搜索"沿用现有的 `publicPicker` 模型"。裁决 E(objectstack-ai#21079,「同意E」)已退役该模型,推翻 objectstack-ai#7467 的"声明 `publicPicker`"。不加这条说明,ADR 会继续声明一个已被删除的匿名记录搜索口,下一个读者会据此重建它。 **风险与代价(含回滚)**:纯文档,不改任何代码或行为,不发布任何包。回滚就是删掉这两行。代码那一半(墓碑、删路由、迁移登记)在 objectstack-ai#21222,与本 PR 互不依赖,哪个先落都可以。 **席位意见**: **你要做的**:读一遍这条说明,确认措辞准确地记下了裁决 E,然后批准合并;本 PR 属 Tier H,只能由你点。 --- _Generated by [Claude Code](https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU)_ Co-authored-by: Claude <noreply@anthropic.com>
…bjectstack-ai#21290) ## What this is A pre-cut draft of the curated release page for **17.6.0**, `content/docs/releases/v17/17-6.mdx`, plus its entry in `content/docs/releases/v17/meta.json` and in `scripts/docs-audit/handwritten-docs.json`. It also appends one dated correction to `content/docs/releases/v17/17-5.mdx` (details below). Docs-only. Everything is under `content/docs/releases/` (release-owned) or the docs-audit list, so this is the dedicated docs-only PR `AGENTS.md` sanctions for that tree. It publishes nothing from any package, hence `skip-changeset`. Clause-②: no It follows the 17.5.0 page's structure: 1. Highlights 2. What's new, including the "read this before treating the version number as a safety guarantee" list of silent runtime changes 3. Breaking changes & migration (triaged, not exhaustive) 4. New capabilities 5. Notable fixes 6. New in Console (Studio) 7. Shipped in 17.5.0, listed again in 17.6.0's CHANGELOG 8. Upgrade checklist (every line marked *not exercised*) ## How it was compiled - **Source:** all 338 changesets pending on `main` at `748b2407`. Every changeset was read and triaged into breaking, feature, fix or internal. PR numbers and short SHAs are taken from the commit that added each changeset. - **Already shipped in 17.5.0:** 16 of the 338 changesets (from 15 commits) describe code that 17.5.0 already published. I checked each commit with `git merge-base --is-ancestor` against the version commit `8c87d26a` and the publish commit `0f6dcac5`. - Eight follow the version commit in first-parent order. The 17.5.0 page already covers them. - Seven are ancestors of the version commit but were not in the version PR when it merged. The 17.5.0 page does not mention them. Two of them are breaking (`e73ee2d` objectstack-ai#20567, `c876a74` objectstack-ai#20504), and `7a1faf1` (objectstack-ai#20579) makes `os validate --strict` fail on a live conversion. - The new section matches the list in `.changeset/20622-release-aftercare-upgrade-and-unannounced-notes.md`. - **Cross-check:** each of the nine ADR-0087 conversions added since 17.5.0 is covered by a migration entry on the page: - `action-block-endpoint-to-target` - `connector-sync-keys-removed` - `connector-triggers-removed` - `cube-refresh-key-removed` - `dataset-count-measure-empty-field-removed` - `form-field-public-picker-removed` - `form-view-subform-columns-canonicalized` - `page-header-breadcrumb-removed` - `time-default-utc-suffix-dropped` - **Console section:** built from the three pin-bump changesets (`dd3f7e1be356 → db11afd4967c → e420df310f5b → 31971ff1e28f`), which carry 232 releasing objectui changesets, 23 of them declared breaking. The range was also read with `scripts/objectui-range.mjs`. - **Fact-check pass:** a second pass checked every cited SHA's changeset against the page, in three slices. It found 31 claims that were wrong or overstated, and the three follow-up commits correct them. Some examples: - An RLS `contains` verdict that `d2bc644` (objectstack-ai#21253) later reversed in this same release. - The `manage_platform_settings` scope: reads of both types, but writes of `datasource` only. - The dataset door's status after `434c6c7` (objectstack-ai#21240). - Filter positions that differ per change. - `7a1faf1`, which is not a breaking change. - **Other corrections made while compiling:** - The connector migration table does not claim that a `job` schedules a `connectorSource` pull. `0efbdc3` says nothing schedules a pull until the `job` stage lands, and none landed in this release. - The public-form picker search-key change (`bafb8c9`, objectstack-ai#21136) is noted as moot, because the route it changed is retired later in the same release (`3dc33b2`, objectstack-ai#21222). ## Open items for the cut These are recorded in the page's RELEASE-TIME TODO comment. - **Anonymous endpoints.** Under the deny baseline (objectstack-ai#21217), an app-declared anonymous endpoint (`authRequired: false`) can no longer read or write objects. objectstack-ai#21158 is still open, and until it lands no supported channel grants anonymous callers a permission set. The page says so instead of prescribing a grant. - **Console pin.** Three 17.6.0 server changes refuse a body that the pinned Studio (`31971ff1e28f`) still sends. objectui fixed each one after the pin: | Studio action | Server change that refuses it | objectui fix (not in the pin) | |:--|:--|:--| | A dataset count measure saved with the Field box blank (`field: ''`) | objectstack-ai#21240 | `0858267e` | | An External / Validate-only datasource saved without a credential | objectstack-ai#21133 | `8001068b` | | Republishing an html page that gained a plugin component | objectstack-ai#20852 | `3ae91930` | The page lists these under "Known console issues". If the pin moves before the cut, the lines it fixes come out. Otherwise the accepted-for-GA waiver is recorded. ## The correction to 17.5.0 One dated correction line is appended in place to the "Also shipped in 17.5.0" paragraph of `17-5.mdx`, in the form objectstack-ai#20985 used. It names the seven commits that shipped in 17.5.0 with no CHANGELOG entry and links to the 17.6.0 section. The original text is unchanged. ## Deliberately not in this PR - `content/docs/releases/v17/index.mdx` is **untouched**. Its status blockquote and per-release list may only claim 17.6.0 after the release ships. - An MDX comment at the top of `17-6.mdx` lists the release-time edits: 1. the publish date; 2. changesets landed after `748b2407`; 3. the per-package CHANGELOG entry count; 4. the console-pin outcome; 5. objectstack-ai#21158 if it lands first; 6. the `v17/index.mdx` update. ## Verification Run locally on the head commit, with the workspace installed. All of these pass: - `check-issue-citations --base origin/main`: every added citation resolves. - `check:doc-anchors`, `check:role-word`, `check:docs-audit-scope`, `check:nul-bytes`, `check:doc-authoring`, `check:docs-single-h1`, `check:docs-redirects`, `check:docs-locale-catch-all` - `check:corpus-claim-drift`, `check:docs-spec-enumerations`, `check:published-readme-links` - `check:release-notes`, `check:release-page-status`, `check:release-index-currency-sync`, `check:release-body` - `check-release-section-coverage` (plain and `--strict`), `check-doc-frontmatter`, `check-docs-nav-label`, `check-docs-section-name`, `check-section-landing-index`, `check-doc-route-spelling --advisory` Both pages compile as MDX with `@mdx-js/mdx` 3 + `remark-gfm`, and the three tables parse with no ragged rows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_014VGCS11YUtYAiinRcdqQwL --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #21180
Clause-②: yes (narrowing)
Retires the form field's
publicPickerblock and deletes the anonymous lookup routeGET /forms/:slug/lookup/:field. This is the second half of ruling E on #21079 (record5933054144, maintainer 「同意E」, 2026-10-01). Anonymous public forms no longer take lookup,master_detailoruserfields. The ruling reverses the #7467 model ("declarepublicPicker"). It is an immediate retirement under ADR-0087 D2: no alias window and no dual spelling. No new gate: the resolve route's existing strip becomes unconditional.The ADR-0061 note rides a separate docs-only PR (Tier H), not this one.
What changed
FormFieldBaseSchema.publicPickeris aretiredKey()tombstone carrying the card's prescription (FROM → TO below).FormFieldPublicPickerSchema,FormFieldPublicPickerandFormFieldPublicPickerParsedare deleted (no other reader).form-field-public-picker-removed(protocol 18,retiredFromLoadPath,retiredAfter: 17.5.0, order 53). OneSTEP18_RATIONALEfragment. D3 semantic entryform-field-public-picker-retired.RETIRED_KEYS_BY_MAJOR[18]getsui/FormField:publicPickerandRETIRED_DEFS_BY_MAJOR[18]getsui/FormFieldPublicPicker. The registry is regenerated bygen:migration-registry, never hand-typed inside its markers.guest_portalpicker context and its picker-only helperview-filter-rule-lowering.ts(no other importer, not exported). The ledger row (rest-route-ledger.ts) goes too. The resolve route's strip loses itspublicPickercondition: lookup /master_detail/userfields are always left off the anonymous rendering.LOOKUP_NOT_PUBLICandLOOKUP_TARGET_MISSINGleaveerror-code-ledger.zod.ts. This follows the ledger's own retirement rule ("A row whose last EMITTER is deleted comes out with it"), so the row is deleted, with no graded tombstone. The picker handler was the only producer of both codes.publicPickerclaiming reader invalidate-preset-comparands.tsis removed, with its cases. The now-unusedgraphparameter ofboundObjectOf/bindAncestorsgoes too (noUnusedParameters).public-form-lookup-picker.test.ts,public-form-lookup-picker-queryable-key.test.ts,public-form-lookup-filter-lowering.test.ts,view-public-picker.test.ts,public-picker-queryable-key.dogfood.test.ts. Re-pinned:public-form-routes.test.ts,public-form-routes.stored-row.test.ts,rest-server-query-number-census.test.ts,rest-server-canonical-query-ast.test.ts,view-union-branch-focus.test.ts,protocol.save-union-issues.test.ts, and the picker door case ofzero-set-masking.dogfood.test.ts. New:form-field-public-picker-retirement.test.ts(tombstone at four doors, the conversion, registration, and a tree-scoped absence pin over the radius already declared for@objectstack/spec).content/docs/ui/forms.mdxis replaced by a short statement of current behaviour. The references regenerate. The platform checklist itemaccess-security.public-form-intakemoves to revision 2: its 403 clause now asserts the route's absence.[RETIRED]), strictness counts and references.dropped-refinements.baseline.jsonis corrected as the build printed it.engine-double-contract.pinned.jsonis regenerated with--write: 6 losses, all from the two deleted test files. Thecheck:route-envelopepins are banked: 43 → 39 and 58 → 54, the four{ code, error }answers the deleted handler carried..changeset/21180-retire-public-picker.md: BREAKING,Clause-②: yes (narrowing), the card's FROM → TO, and the ADR-0087 markerregistered form-field-public-picker-removed, form-field-public-picker-retired.FROM → TO: delete the
publicPickerblock; an anonymous public form no longer offers record search. Use aselectfield with staticoptions, or put the form behind sign-in.The PM's hypotheses, measured
b9087d77e9: 280 lines. By pattern:publicPicker129,FormFieldPublicPicker43,LOOKUP_NOT_PUBLIC19,/lookup/:field35,guest_portal65. No producer exists outside spec, tests, docs and the REST route; no example declares one. After, atdafa22868:publicPicker106,FormFieldPublicPicker10,LOOKUP_NOT_PUBLIC0,/lookup/:field15,guest_portal59. The kit accounts for the residue: the tombstone, the conversion and registry entries, the retirement and union pins, the H2/H3 pins, and generated artefacts (references, authorable-surface, and the base anchor, which only its own generator writes). Untouched history also remains: two dated audits,releases/v15.mdx, the ADR-0061 sentence, and the pending.changeset/21062-picker-queryable-key.md.guest_portalremains as permission-set names (examples, plugin-security tests, the published skill's resolve/submit text) and as the submit route's context. Inrest-server.tsthe picker's literal context was 1 of the 7 lines; the other 6 are the submit route and its docblock. All of those are outside this card.if (t !== 'lookup' && t !== 'master_detail' && t !== 'user') return true; return !!cfg?.publicPicker;. The condition is deleted; the function now returns the type test, with no new branch. Pinned by a stored row carrying the old block on a lookup, amaster_detailand auserfield: only the text fieldsubjectrenders.HonoHttpServer, with the unmatched-request seam installed asHonoServerPlugin.start()installs it,GET /api/v1/forms/test/lookup/owner_idanswers404witherror.codeENDPOINT_NOT_FOUND. That body is byte-identical (path aside) to a never-registered sibling path, andfindDatais never called. The registered resolve route on the same harness answers 200, the lit control.objectuiconsumer check: zero readers at the old pine420df310fand the new pin31971ff1e2(only an exemption reason string in a parity test). Thecloudconsumer check is NOT MEASURED: code search returned zero for both the codes and a lit control ("@objectstack/spec"), so it cannot see that repository.os validateon a fixture. Before, at BASE: the fixture authoringpublicPicker: { displayFields: ['name'], maxResults: 10 }gave exit 0,valid: true. After: exit 1,valid: false. The text face prints the prescription atviews.0.formViews.contact.sections.0.fields.1.publicPicker. The control fixture, identical without the key, gives exit 0 both times..objectui-shaimports neither name ate420df310fnor, after chore(objectui): bump the console pin to 31971ff1e28f (one zod instance in the vendored Console), add a single-zod canary to build-console.sh, and key the release console cache on the spec zod range #21149 moved it, at31971ff1e2: 7 string mentions and 0 import lines, with a lit control of 392 files importing@objectstack/spec/ui. Its spec-parity test enumeratesFormFieldSchema.in.shape, and the tombstone keeps the key there. No pin bump rides this PR.Reverse verification (the fix committed first, then BASE's route code restored)
Run against
rest-server.tsand the lowering module restored from BASE (blobc673773e46, verified on disk by hash). The pins import source, so no rebuild was needed. All three new pins went red in the expected direction:expected [ 'subject', 'contact_id', …(2) ] to deeply equal [ 'subject' ]expected true to be falseexpected 403 to be 404The other 20 cases in the file stayed green. Restore: back to the HEAD blob
b52e360ec3,git diff HEADempty,git statusclean, with a trap on the script.Tests and gates (head
dafa22868, after mergingmainat5e5ce48ce)@objectstack/rest, whole package: 252 files, 4776 passed. Typecheck green, includingcheck:test-typecheck.@objectstack/lint, whole package (pre-merge, untouched bymain): 118 files, 5486 passed. Typecheck green.@objectstack/spec: the local project ran 593 files with one failure, the ledger's header totals (fixed: 212 → 210 schemas, 617 → 613 sites), then 27/27. The repo project: 48 files, 849 passed. After the merge:src/ui,src/conversions,src/migrations, error-code ledger, migrate-sentence, alias-integrity and the merge-shape scripts give 120 files, 4299 passed.check:generated: 15/15 current. Typecheck green, includingcheck:scripts-typecheckandcheck:test-typecheck, which compiles the new test's@ts-expect-error.@objectstack/metadata-protocol:protocol.save-union-issues.test.ts25/25. Typecheck green.@objectstack/cli: unit tier 242 files, 3435 passed. The integration tier is declared to CI.@objectstack/dogfood:zero-set-maskingandexpression-conformance, 8/8. Typecheck green.dispatch-gates --commandson this head gives 138 families. All 138 ran to exit 0.--ranreconciliation: 138 run, 0 NOT-MEASURED, a derived zero with every exit code recorded. Three refusals were cleared by building their prerequisites, not by skipping:check:skill-examplesandcheck:dual-build-cjs-loadsexited 3 until seven packages outside this diff were built.check:pm-dispatch-gatesandcheck:type-check-debtwere re-run without the runner's 480 s cap.**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}and enables no type-aware linting. The 21 touched code files were all linted (--format json): 0 errors, 0 warnings. The repo-widepnpm lintis CI's.check:adr-0087-registration(registered, both ids new here),check:empty-changeset,check-changeset-no-majorandcheck:doc-authoringall pass. The level axis needs this PR's payload, so CI judges it.Serial with #21079 (PR #21217)
At open time PR #21217 is a draft and not merged, so this PR is first of the pair. This branch deletes both of #21062's picker pins and removes the picker door case of
zero-set-masking.dogfood.test.ts, along with the public form and inquiry object only that case booted, since the fixture can no longer carry the retired key. The record-door case is byte-identical. If #21217 lands first,maingets merged here and these deletions are kept.security-plugin.tsandsecurity-service.tsare untouched.Acceptance notes (observed, not filed)
.changeset/21062-picker-queryable-key.mdis an unreleased changeset describing a change to the route this PR deletes. If both ship in one release, the compiled notes will describe a route that no longer exists. Left for the release compiler; this PR does not edit another PR's changeset.objectSchemafromGET /forms/:slugstill publishes the definitions of declared lookup /master_detail/userfields. The ruled strip covers the rendered sections only, and widening it would be a new gate.ISecurityService.getQueryableFieldsloses its only REST reader with the picker (#20935). The method stays: it lives in security(plugin-security): a non-system caller who resolves no permission set is admitted to every object and read with no row scope; an empty set list grants by absence instead of answering the deny baseline #21079's surface.docs/audits/2026-06-*) and the ADR-0096 table name the route as history and are left as written.Deviations
minor, notmajor: the skill andcheck-changeset-no-majorrefusemajorin the launch window. BREAKING is carried by the banner, theClause-②line and the ADR-0087 marker.LOOKUP_TARGET_MISSINGalso leaves the ledger. The card names onlyLOOKUP_NOT_PUBLIC, but the ledger's rule applies to both, and the deleted handler was the only producer of each.view-filter-rule-lowering.ts(the picker-only helper),retired-defs/18.ui__FormFieldPublicPicker.ts(required by the build's manifest-deletion gate),dropped-refinements.baseline.json,engine-double-contract.pinned.json,scripts/check-route-envelope.mjs(ratchet banking),metadata-protocol(a stale comment and a test that rode the key), or the platform checklist item. Each follows mechanically from the deletion.migrations/registry.ts: the generated regions come fromgen:migration-registry. The one hand edit is theSTEP18_RATIONALEfragment outside the markers, which the retirement skill requires.Generated by Claude Code