Repository navigation
spec: catalog references in authoring surfaces become names (approver position binding, decision-output positions, the position form's permissionSets widget), ADR-0131 D4, from #15204 #22682
Description
This card carries the packages/spec half of three items found by objectui#7611's C9 build (report objectui 6097735087; seat answers objectui 6097763840). The ADR-0131 cutover on #15204 keeps every other half: the server clone door, the activation door, stage 8. The objectui consumers follow this card in a C9 follow-up PR.
Filing gate: ④, a per-layer sub-issue of the cross-layer cutover (epic #15194, card #15204). Acting reader: the domain:spec seat claims and builds it; objectui#7611's follow-up PR is the consumer. Dedupe, read just before this card was created: every domain:spec issue, open and closed (1,609 non-PR rows over 17 pages, the last short), and every open issue in this repository (122 non-PR rows). Their titles were grepped for approver.*position, decision ?output, DecisionOutput, APPROVER_VALUE, position\.form and permissionSets: 0 hits on the subject. Control: approv hits 3 open titles in the same corpus, so the instrument works. The six closed approver hits are about ApproverType's describe text, onEmptyApprovers and minApprovals, not this subject.
What changes, and why it cannot wait
ADR-0131 D4, verbatim: "References to declared items are by machine name, resolved registry-first." Stage 8 of #15204 retires sys_position, so every spec construct that binds a position as a sys_position record has to bind it by name first.
APPROVER_VALUE_BINDINGS.positionis today a record binding onsys_position(valueFieldname), from which objectui derives its approver readers (useApproverDirectory,approverIdentity). It becomes a registry binding forposition, with the value being the machine name.DecisionOutputDef's position output says a position output collectssys_positionrecord ids. It collects position names.- The position form (
position.form.ts) gainspermissionSets, with a registry multi-picker widget key, once refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write undersingleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 stage 1 landsPositionSchema.permissionSets. The form spec stays the single authoring surface for the position → permission-set binding. objectui implements the widget in metadata-admin's widget registry.
Rulings and premises
- Seat answers (objectui 6097763840), open to the maintainer's veto: Q2 → A (the spec binds positions by name; the alternative, a local objectui override, is the mirror the spec's own comment records as a cost, 审批节点「处理人 Value」应改为记录 lookup(现查 metadata 端点→只能手填);附 approver value 语义核实 + queue 未实现 #3508), and Q3 → A.
- Premises to re-verify on
origin/mainbefore building, each from the C9 report and not re-read by the filing seat:- the location and shape of
APPROVER_VALUE_BINDINGSandDecisionOutputDef; - that no registry binding kind for
positionexists yet; - that
position.form.tshas nopermissionSetsline.
- the location and shape of
- Ordering: items 1 and 2 are buildable now. Item 3 waits for refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write under
singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 stage 1 (branchclaude/issue-15204-s1-position-permission-sets) to merge. refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write undersingleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204's stage 8 waits for items 1 and 2. Clause-②: the claimant judges it at claim. Replacing a record binding with a name binding is a published-contract change, so expectyesand a contract-tier review.
Part of #15194 (ADR-0131 execution tree). Parent card: #15204.
Blocked-by: objectstack-ai/objectui#12126 (item 3: the ref-multi:permission widget lands in objectui first, then objectstack's .objectui-sha pin bump, then PR #22794 lands; seat ACCEPT 6106524871)
Activity
objectstack-fleet commentedon Oct 11, 2026
Restart: the hold's
Restart-when:holds.pm:on-hold→pm:queuefor item 3 alonedomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-11T05:23Z. The seat set the hold6101455734, and this act lifts it. ⛔ Not a claim.origin/main:PositionSchemaacceptspermissionSets:packages/spec/src/identity/position.zod.ts:106isz.array(SnakeCaseIdentifierSchema).optional().singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 stage 1, PR feat(spec,core)!: positions declare their permissionSets; the authorization resolver reads the security catalog and the activation ledger #22723 →bfc15d275b(2026-10-11T01:13Z).position.form.ts:28is{ field: 'permissionSets', type: 'tags', … }.domain:specseat does first.This act replaces
pm:on-holdwithpm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
Claim: PM loop round 1 (#22682 item 3 alone, per the restart
6105803819: the position form'spermissionSetsrow binds the permission-set registry by name, ADR-0131 D4) · 2026-10-11T05:37ZSession:
session_01S3aAf11JjbW1mSGL1EhfFjAccount:
os-project-manager(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-22682-position-permission-sets-pickerWorktree:
objectstack-issue-22682Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface (at
origin/maine84aeb36ceor later; stop on breach and explain in the report):packages/spec/src/identity/position.form.ts: thepermissionSetsrow (:28, todaytype: 'tags', free text) takes the widget key that binds the permission-set registry by machine name, so the designer offers declared names.ref:<type>convention (ref:object,ref:dataset,ref:component), in its multi-value form for an array field.ref:key behaves on an array..changeset/22682-*.md(@objectstack/spec, at the level the changeset check requires).PositionSchema(permissionSetslanded with refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write undersingleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 stage 1, PR feat(spec,core)!: positions declare their permissionSets; the authorization resolver reads the security catalog and the activation ledger #22723 →bfc15d275b), to the approver bindings (items 1–2, PR feat(spec)!: approver and decision-output positions are names from the metadata registry (ADR-0131 D4) #22710 →b32ee3eaf3), or to objectui.Container & model:
S,mode:subagent,model: default. A form widget key is a hint string (FormField.widgetisz.string(), with no spec-side widget-key registry; measured at this claim). No contract face is widened, so this seat reviews it itself unless the dev's measurement adds a published vocabulary.Clause-②: no
Responsibility:
packages/spec's position form offers a free-text tags box forpermissionSets, where ADR-0131 D4 binds declared items by machine name, registry-first | the server resolves the names it stores | admins and AI authors editing a position in the designer, who can type a permission-set name that does not existThread-read: 6105803819
Serial constraints cleared:
position.form.tsoridentity/position*(all open PRs' file lists, read at this claim).singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 stage 1 landed (bfc15d275b), so the hold6101455734'sRestart-when:holds (the restart6105803819).domain:specseat does first"): seat 2 holds three in-flight cards, and this seat has a free slot.packages/app-shell/src/views/metadata-admin/widgets.tsx,WIDGETS:ref:object/ref:component/ref:dataset, single pickers) implements the widget. Per cross-repo rules 2 and 3, the spec side lands first, and this seat files the objectui follow-up withBlocked-by:at the ACCEPT, once the key is measured.This act moves the card
pm:queue→pm:dispatchedand assignsos-project-manager.objectstack-fleet commentedon Oct 11, 2026
os-dev-report
{
"issue": 22682,
"status": "done",
"item": "3 alone (the position form's permissionSets widget key), per claim 6105901038",
"branch": "claude/issue-22682-position-permission-sets-picker",
"pr": "#22794",
"head": "8996b566b5",
"session": "session_01S3aAf11JjbW1mSGL1EhfFj (the harness-stamped Claude-Session id; a subagent of the PM, so the parent's)",
"premise_still_valid": true,
"summary": "Draft PR #22794 ('Fixes #22682', 'Clause-②: no', head 8996b56, assignee os-project-manager) changes one row. packages/spec/src/identity/position.form.ts: the permissionSets row goes from type 'tags' to widget 'ref-multi:permission', with the same colSpan and an unchanged helpText. It adds a 4-test pin to position.test.ts and a @objectstack/spec patch changeset. Every PM premise held at e84aeb3: the row was at :28, FormField.widget is z.string().optional() at view.zod.ts:3509, ref:object has 8 uses (7 form rows plus 1 docblock example), ref:dataset 2 and ref:component 1, and objectui registers exactly those three single pickers. The key was chosen by measurement. No multi-value ref: spelling exists in either repo. The vocabulary spells every multi-value picker as its own key (field-ref/field-multi, select/multiselect, action-multi). objectui's single ref widgets cannot take an array: they read String(value) and write one string, and WIDGET_LABELLING declares one labelling per key. The registry type is 'permission' on both sides. So the key is the ref: family's list form, with the arity in the prefix and the exact registry type after the colon. objectui renders an unregistered key as the raw-JSON face, so the landing order is put to the seat as an open question.",
"tests": "Commands and verdicts from the head named in each item. All builds and tests went through os-verify-lock; each result quotes its VERDICT line. (1) pnpm --filter @objectstack/spec build at 0cb430a (spec src identical at 8996b56): VERDICT command-exit 0, tree clean after. (2) pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: 'Test Files 645 passed (645)', 'Tests 19226 passed | 1 todo (19227)', VERDICT command-exit 0. (3) pnpm --filter @objectstack/spec typecheck (tsc --noEmit, scripts program, test layer under tsconfig.test.json, whose include is src/**/* with no test exclusion): VERDICT command-exit 0; 'check:test-typecheck: OK … 52 file(s) / 246 error(s) / 135 pinned signature(s) held'. (4) pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/identity/position.test.ts: 'Tests 23 passed (23)' (19 before plus 4 new). (5) Ablation through scripts/ablation-replace.mjs in wrap mode, on committed head 8996b56, with the test leg under the lock. Anchor "{ field: 'permissionSets', widget: 'ref-multi:permission'," x1 -> x0; replacement "{ field: 'permissionSets', type: 'tags'," x0 -> x1; blob 62270c8d2388 -> 9776a96594c8. The pin went red, 'Tests 2 failed | 21 passed (23)': the widget pin and the registry-type pin failed, and the controls and the list pin stayed green, which is the expected direction. Restore: 'blob == HEAD (62270c8d2388) and git diff HEAD is empty', and git status was empty. The restore leg re-ran 'Tests 23 passed (23)', VERDICT command-exit 0. No dist leg: the test reaches the form through relative source imports (../system/metadata-form-registry, then ../identity, then ./position.form). No @objectstack/spec specifier resolves through exports, so no dist is involved.",
"gates": {
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths: 86 commands at 0cb430a, and an identical 86 at 8996b56 (diffed: same). The PM's lead at e84aeb3 had 74; the 12 it lacked (check-empty-changeset x2, release-rehearsal-clone and release-pending-publish self-tests, check:engine-double-contract, check:objectql-double-limit, check:objectui-changeset, check:pm-changeset-deadline-census, check:query-options-erasure, check:type-check-coverage, check:type-check-debt without --re-measure, check:where-matcher) were all run, all exit 0.",
"ran": "dispatch-gates --ran over the recorded 'cmd :: exit N' list: 'Run reconciliation — 86 derived, 86 run, 0 NOT-MEASURED, 0 UNRUN.' Every exit code is 0. Extra: pnpm --filter @objectstack/spec check:generated, 'All 14 generated artifacts are up to date', run at 8996b56.",
"rerun_to_green": [
"check:doc-formula-expressions, check:i18n, check:lean-entry-closure and check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET, nothing measured). All four exit 0 after one turbo build of their closures (cli, platform-objects, plugin-approvals/audit/security/sharing/webhooks, service-messaging/realtime/storage, formula, lint, objectql). check:i18n: 'all bundles in sync, no undeclared authoring keys'. check:dual-build-cjs-loads: '107 published require entry point(s) across 66 package(s) load'.",
"The first closure build failed on plugin-webhooks DTS: TS7016, no declaration file for @objectstack/service-messaging. That package is a declared dependency. A rerun with no change went '59 successful, 59 total' (all cached), and service-messaging/dist/index.d.ts then existed. Read as a build-order or cache-replay race, not this diff, which touches no build input of either package."
],
"changeset_families_on_final_head": "The second commit changed only the changeset's wording. The derivation for that path (20 commands) was re-run at 8996b56, all exit 0. Among them: check-adr-0087-registration --base origin/main ('this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'), check-changeset-no-major ('This diff introduces no major bump'), check-empty-changeset ('1 declaring changeset(s) added') and check:nul-bytes ('no raw ASCII control bytes').",
"not_measured": [
"Declared for CI, with no local invocation by the derivation's own account: the 5 path-scheduled CI jobs (Test Core, Temporal Conformance, Dogfood Regression Gate, Dogfood Verify CLI, Build Core) and the 4 type-check lanes (spec tsc, the workspace turbo typecheck, the examples, downstream-contract).",
"pnpm lint (eslint, repo-wide) is CI's run and was not run locally.",
"The derivation does not name @objectstack/spec test:repo, and it was not run."
],
"ci": "Not read. The PR opened at the moment of this report."
},
"line_budget": {
"pr": "objectstack#22794",
"additions": 60,
"deletions": 1,
"changed_lines": 61,
"files": 3,
"budget": 3000,
"under_budget": true
},
"files_changed": [
".changeset/22682-position-form-permission-sets-picker.md",
"packages/spec/src/identity/position.form.ts",
"packages/spec/src/identity/position.test.ts"
],
"objectui_companion": {
"widget_key": "ref-multi:permission",
"registry_type": "permission (spec MetadataTypeSchema, kernel/metadata-plugin.zod.ts:166; getMetadataTypeSchema('permission') === PermissionSetSchema; objectui registerMetadataResource({ type: 'permission' }) at PermissionMatrixEditor.tsx:28, and anchors.ts:374)",
"objectui_files": [
"packages/app-shell/src/views/metadata-admin/widgets.tsx: a WIDGETS entry (:3033), a WIDGET_LABELLING entry (:3142), a WidgetContext catalog member for the permission-set names, and the widget. It offers the declared names, commits each pick's name into the list, and keeps a stored name the catalog lacks visible and flagged, as RefDatasetWidget (:684) does.",
"packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx: feeds the 'permission' list into widgetContext; the position form arrives as entry?.form from /meta/types (:456, :457, :2943).",
"packages/app-shell/src/views/metadata-admin/SchemaForm.widgetLabelling.test.tsx: its CASES roster must name every WIDGETS key (:199), and WIDGET_LABELLING must equal the WIDGETS key set (:181).",
"packages/app-shell/src/views/metadata-admin/tests/SchemaForm.controlWidgetFailureArmNaming-9931.test.tsx, if declared 'control': its population is the WIDGET_LABELLING table (:262 to :266).",
"packages/app-shell/src/views/metadata-admin/i18n.ts: the widget's placeholder and failure strings (en and zh)."
],
"renders_today": "Measured by code path at objectui origin/main 206505c and at the pin .objectui-sha 20c6d351ad74; both read identically at every cited line. inferWidget returns an explicit widget first (SchemaForm.tsx:447). resolveFieldFace sends an unregistered, non-passthrough hint to { kind: 'raw-json', hint } (:870), rendered at :2223 as a RawJsonEditor textarea under 'widget ref-multi:permission — falling back to JSON until a custom renderer is registered.' (i18n.ts:2166; zh: '组件 ref-multi:permission 尚未注册自定义渲染器,暂时使用 JSON 编辑。'). objectui's own SchemaForm.nonRegistryLabelling.test.tsx pins that face (case '(fallback) raw-json / unregistered widget hint', :225). The textarea still writes a JSON list of names, so the stored shape holds, but the row moves from today's string-tags box to a JSON box. No objectui test pins that every spec-form widget key is registered. The only key-set parities are internal (widgetLabelling test :181, :199), and the core validator's namespace refusal is for the runtime form renderer, a different shape (schema-validator.ts:390). So a spec bump turns no objectui gate red.",
"needs_from_this_pr": "Nothing. The key is a string objectui owns, and the 'permission' catalog exists today, so objectui can land first; see open_questions."
},
"deviations": [
"Route: the helpText is unchanged, where the suggested route said to reword it. It stays true under a picker, under objectui's freeform fallback, and under the raw-JSON face. Rewording it would move the en metadata-forms bundle (generated) and three hand-translated locales (zh-CN, ja-JP, es-ES) in packages/platform-objects, which are outside the claim's file surface. check:i18n confirms the bundles are in sync with the unchanged text.",
"Route: the key is a distinct list key (ref-multi:permission), not ref:permission on the array. The suggested route's condition for ref:permission ('if objectui's single ref widgets can be told to take an array') measured false. The PR body records the reasons and the alternatives.",
"No published vocabulary and no schema change: FormField.widget stays z.string().optional(). The changeset is patch with Clause-② no, as the claim judged.",
"origin/main moved to a2e94c2 (2 commits: ADR-0139's document and the skills package's changeset config). Neither touches packages/spec/src, so the branch was not re-merged; CI's merge ref covers the join.",
"The spec local suite exceeded the 598 s tool cap while queued, and the harness moved it to the background. I waited in the foreground with tail --pid on the lock holder's pid and read its VERDICT line; nothing was polled.",
"My first gate re-run reused log names 001 to 004 and overwrote the first battery's logs for check-adr-0087-registration (--base and --self-test) and check-changeset-no-major (--base and --self-test). Their exit-0 codes stand in the results file, and all four were re-run with kept logs on the final head.",
"Commit trailers use the model-free pair AGENTS.md prescribes (Claude-Session plus 'Co-authored-by: Claude'), where the harness reminder asked for a model-named trailer. The PR footer uses AGENTS.md's session-URL form, where the reminder asked for a different footer.",
"Cleanup is done: worktree ../objectstack-issue-22682 was clean, its head 8996b56 was pushed, and it was removed (node_modules first, then git worktree remove, no --force). No background process of this run is alive."
],
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "2 relay strokes, 3 REST requests, all as objectstack-fleet[bot] through scripts/pm. (1) fleet-write dispatch, run 38119995242, success: POST /repos/objectstack-ai/objectstack/pulls (draft pr_create, '11329 byte(s) sent, 11329 stored — identical'), plus its assignee leg POST /repos//issues/22794/assignees (os-project-manager, read back as listed). (2) POST /repos//issues/22682/comments, this report, through post-stamped. No label write: the dispatch names no label, and skip-changeset does not apply. Plus 3 git pushes of the branch (the empty probe, 0cb430a and 8996b56), which are not REST writes. Reads were REST GETs and git only.",
"open_questions": [
{
"question": "Landing order for PR #22794. Once the server runs this spec, every console without a ref-multi:permission widget, including the pinned one at 20c6d351ad74, renders the position form's permissionSets row as the announced raw-JSON box instead of today's tag box. The claim orders spec first (cross-repo rule 2, with the objectui follow-up carrying Blocked-by). The ref:dataset precedent ran the other way: objectui#11601 classed exactly this face as the defect, and #21714 declared the hint only after the pin carried the renderer. objectui needs nothing from this PR.",
"options": [
"A. Land #22794 on ACCEPT (spec first), and file the objectui companion with Blocked-by. Cost: a window, lasting until the objectui widget lands and the pin moves past it, where admins and AI authors edit a position's permission sets as a JSON array. The stored value and parse are unchanged, but the row is worse than today's tags input.",
"B. File the objectui companion now, with no Blocked-by, since it depends on nothing here. Hold #22794 in draft until the pin bump carries the widget, then land it. Cost: item 3 waits one objectui PR plus one pin bump; no regression window.",
"C. Change the key or the fallback in spec to avoid the window (for example, keep type 'tags' beside the widget). Measured to be no fix: an explicit widget outranks type in objectui (SchemaForm.tsx:447, :870), so type would not be consulted."
],
"recommendation": "B. Business need: the person editing a position today gets a tags input; A swaps it for a JSON box for an unbounded window, which is a measured UX regression on a security-binding form. Long-term soundness: B reaches the same end state with no transitional face, and it is the order the repo already used for ref:dataset. Preventing AI authoring errors: under A, an AI author in the window sees a raw JSON editor and an English 'falling back' note, which invites hand-typed names, exactly what the picker exists to stop. Startup scope: neither option adds surface; B only reorders two PRs and needs no new gate or rule."
}
],
"out_of_scope_findings": [
"carrier: none · the plugin-webhooks DTS TS7016 against @objectstack/service-messaging on a closure turbo build (a declared dependency; a rerun was clean and all cached) · same class as the objectql/metadata-protocol DTS race the round-1 report of this card recorded · noted, not filed (not reproduced; no reach)"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
✅ ACCEPT: PR #22794 at
8996b566b5(item 3). It is held in draft until objectui renders the key, then lands. The card goespm:dispatched→pm:blockeddomain:specseat 1 (#6017) ·os-project-manager· sessionsession_01S3aAf11JjbW1mSGL1EhfFj· 2026-10-11T07:07Z · holder of claim6105901038, released by this act. Report:os-dev-report6106502193. Thread-read: 6106502193.Blocked-by: objectstack-ai/objectui#12126
Restart-when: objectstack's
.objectui-shapin carries objectui#12126's merge (aref-multi:permissionentry in metadata-admin'sWIDGETS). Then PR #22794 leaves draft and lands through the queue, with no code change needed.Checklist, read against GitHub:
main,Fixes #22682(item 3 is the card's lastpackages/specitem),Clause-②: no, assigneeos-project-manager.position.form.ts: thepermissionSetsrow iswidget: 'ref-multi:permission'where it wastype: 'tags'. The helpText is unchanged and stays true under the picker and under the fallback.position.test.ts..changeset/22682-position-form-permission-sets-picker.md(@objectstack/specpatch).Clause-②: nois right:FormField.widgetstays a free string, and no schema, parse, export or accept set moves. This seat reviews it; no contract face is touched.ref:spelling exists in either repo. The vocabulary spells every multi-value picker as its own key (field-ref/field-multi,select/multiselect,action-multi). objectui's singleref:widgets readString(value). The registry type ispermissionon both sides.Evidence:
position.test.ts23 tests (19 + 4 new). spec typecheck exits 0.Landing order: B (the report's open question 1), decided by this seat.
SchemaForm.tsx:447,:870) on a security-binding form, for as long as the pin lacks the widget.ref:datasettook the same order (objectui#11601, then spec(ui):reportForm's "Joined blocks" row offers a block'sdatasetas free text with norequired, so Studio saves the block #21702 now refuses #21714).domain:ui) lands first, and this PR waits in draft. Cross-repo rule 2 ("spec first") orders contract availability, and here the contract is a string objectui already owns.Release: this seat stands down by the maintainer's word (「当前任务处理完,合并后就下班」), so the claim is released here with the card
pm:blocked. Whicheverdomain:specseat sees the restart condition takes the card back through a claim and lands PR #22794 as it stands. Its review is this ACCEPT; it needs only a merge ofmainif the base moved, and green CI.Release:
session_01S3aAf11JjbW1mSGL1EhfFj· why: blocked on the objectui companion (landing order B), and this seat stands down · to:pm:blocked, unassigned. This act moves the cardpm:dispatched→pm:blockedand removes the assigneeos-project-manager.objectstack-fleet commentedon Oct 11, 2026
Blocked-by: none
Triage:
pm:blocked→pm:queue. objectui's half has landed; what remains is the console pin plus PR #22794's landing, one round for thedomain:speclaneTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-11T11:59Z. ⛔ Not a claim, ⛔ not a dispatch.6106524871: objectui widget → console pin bump → PR feat(spec): the position form picks permissionSets from the permission-set registry (ref-multi:permission), with the console pin moved to 4997995022f8 #22794 lands.ref-multi:permissionwidget (multi-pick declared permission sets by name) so the position form'spermissionSetsrow can bind the registry (objectstack#22682 item 3, ADR-0131 D4) objectui#12126 closed when objectui PR Resolve the 2 DIALECT_LEDGER rows, then promote "every dialect-scored cell has a matrix-routed suite" to an invariant (#12014 follow-up) #12136 merged (4997995022)..objectui-shais20c6d351ad. The pin bump in flight, PR chore(objectui): bump the console pin to 0df67f237c5a (carries objectui#12089) #22812 (Part of #15204, pin0df67f237c5a), does NOT carry4997995022; I measured the ancestry in objectui..objectui-shato an objectuimaincommit that carries4997995022. Do it inside PR feat(spec): the position form picks permissionSets from the permission-set registry (ref-multi:permission), with the console pin moved to 4997995022f8 #22794, or as a pin-bump PR right before it.chore(objectui): bump the console pin …).domain:specseat that holds PR feat(spec): the position form picks permissionSets from the permission-set registry (ref-multi:permission), with the console pin moved to 4997995022f8 #22794. p1 andtarget:v18stand.objectstack-fleet commentedon Oct 11, 2026
Claim: PM loop round 8 (#22682 item 3, the landing round triage's unlock
6108789665describes: move.objectui-shato an objectuimaincommit that carries objectstack-ai/objectui#12136's merge, then land PR #22794) · 2026-10-11T12:34ZSession:
session_01KNKBCRDJCu5tGy3TEbvtrFAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-22682-position-permission-sets-picker(PR #22794's own branch, continued; accepted at8996b566b5, ACCEPT6106524871)Worktree:
objectstack-issue-22682Domain:
domain:specSeat:
domain:spec#3(seat post #18883)File surface (at
origin/mainefcbac73ccor later; stop on breach and explain in the report):packages/spec/src/identity/position.form.ts,position.test.tsand.changeset/22682-position-form-permission-sets-picker.md..objectui-sha, from0df67f237c5a(PR chore(objectui): bump the console pin to 0df67f237c5a (carries objectui#12089) #22812) to4997995022, objectui#12136's merge (ref-multi:permissionin metadata-admin'sWIDGETS). That is the nearest objectuimaincommit that carries it: 6 commits past the pin, measured withgit merge-base --is-ancestorin objectui. A latermaincommit is taken only if the dev measures a reason, stated in the report.sdui.manifest.json,scripts/sdui-manifest.record.jsonand parts ofpackages/spec/src/ui/*.zod.ts. ⛔ If the regeneration moves a published contract (a.zod.tsaccept set or export), stop and report before pushing: the bump then owes its ownClause-②line and an at-tier review.Container & model:
M,mode:subagent,model: default tier(dispatch-gates --tier: no path-derived mandate). PR feat(spec): the position form picks permissionSets from the permission-set registry (ref-multi:permission), with the console pin moved to 4997995022f8 #22794's own diff isClause-②: no(seat 1's ACCEPT). The pin hop is judged on what its regeneration moves, measured.Clause-②: no (PR feat(spec): the position form picks permissionSets from the permission-set registry (ref-multi:permission), with the console pin moved to 4997995022f8 #22794's diff, as accepted). The pin hop's own line is set from the measurement.
Responsibility: n/a — not a defect card (ADR-0131 D4's spec half, filed from objectui#7611's C9 build)
Thread-read: 6108789665
Serial constraints cleared:
.objectui-shaisSINGLE_CLAIM_PATHS: no open PR touches it. PR chore(objectui): bump the console pin to 0df67f237c5a (carries objectui#12089) #22812 (refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write undersingleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 S5, the last pin bump) merged at 2026-10-11T12:04Z, so the two pin edits cannot collide. The file lists of all 12 open PRs were read at 2026-10-11T12:34Z.packages/spec/src/identity/position*.6107479932lists spec: catalog references in authoring surfaces become names (approver position binding, decision-output positions, the position form's permissionSets widget), ADR-0131 D4, from #15204 #22682 under "Parked, with no holder": "Whichever seat sees the pin carry it lands the PR." Triage's unlock names the lane, not a seat.This act moves the card
pm:queue→pm:dispatchedand assignszhuangjianguo.Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
os-dev-report
{
"issue": 22682,
"status": "done",
"branch": "claude/issue-22682-position-permission-sets-picker",
"pr": "#22794",
"session": "session_01KNKBCRDJCu5tGy3TEbvtrF",
"premise_still_valid": true,
"summary": "PR #22794 now carries the console pin bump inside it, as triage unlock 6108789665 asked. .objectui-sha moves from 0df67f237c5a to 4997995022f8, the merge of objectui#12136 (objectui#12126, the ref-multi:permission widget). That is the nearest objectui main commit carrying the widget: 7 commits, 0 merges, 80 paths. The bump and its regeneration are one commit, aaa50c6. It was made with bump-objectui.sh, objectui:build, gen-sdui-manifest-node.mjs and check-sdui-lockstep --update, plus the 55 asserting objectui pin citations re-measured by hand (check:objectui-pin-citations has no generator by design) and gen:docs for the one describe whose sha moved. sdui.manifest.json is byte-identical, and no published contract moves, so the hop stays Clause-② no. Round 1's three files are unchanged. main was merged twice through os-regen-merge.sh (aa734f0, then 483fddb). The PR stays draft; its title and body were updated once and its assignee swapped to zhuangjianguo.",
"tests": "All at head 483fddb unless noted, every build and test through os-verify-lock (slot issue-22682); verdicts are quoted. spec build VERDICT command-exit 0, declaration stamp 9278c1ed04e9 still matching at 483fddb. spec local suite (vitest --project local --maxWorkers=2): Test Files 648 passed (648), Tests 19346 passed | 1 todo (19347), VERDICT 0; identical at aaa50c6. spec typecheck: VERDICT 0, test layer 52 file(s) / 246 error(s) / 135 pinned signature(s) held. sdui-parser test: 14 files, 225 passed; typecheck exit 0. check:generated: All 14 generated artifacts are up to date (check:api-surface and check:authorable-surface green inside it; check:docs was the one stale artifact before gen:docs). check:objectui-pin-citations: 55 asserting citation(s) match .objectui-sha (499799502), 145 historical, 1983 sources; --verify-anchors: 13 anchor content assertion(s) verified at 499799502. check:sdui-lockstep OK at 4997995022f8 (214 grammar lines, blob 0131f27cf86d, 25 codes, predicate 76c18fb95d1f). check-sdui-manifest OK (sha256 9d87162a3ef6, 107 components, recorded at the live pin). Console Pin Gate local equivalent: turbo build of the @objectstack/client... closure plus sdui-parser, 34/34, VERDICT 0; bash scripts/build-console.sh VERDICT 0 (8m18s), with the bundle canary, single-zod canary and spec-injection lines green and the dist ready at 64384 KB; check:console-sha and check:console-injection --require-stamp exit 0. TypeScript-scanner token diff of the 14 changed spec .ts files against aa734f0: 12 identical, 2 migration entries gain one + joining one string fragment, no identifier moves. ESLint --no-inline-config over the 16 changed TS files: 0 errors, 0 warnings, 16 files by --format json (population: eslint.config.mjs:971; no type-aware linting, :328). CI at 483fddb, read 2026-10-11T13:57Z: all seven required contexts and Console Pin Gate completed success. One Check Changeset re-run, started by the body edit, was in_progress, and this report does not wait for it. No ablation this round: round 1 recorded one at 8996b56 (widget pin and registry-type pin red, restore blob-equal), and those files are unchanged.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 483fddb (no paths): 126 commands from 23 paths, merge base 1eff322. All 126 ran; --ran: 126 derived, 126 run, 0 NOT-MEASURED, 0 UNRUN. Three first exited 3 (PREREQUISITE NOT MET, not a reading): check:skill-examples, check:i18n and check:dual-build-cjs-loads. All three exit 0 after pnpm --filter @objectstack/client-react build and the CLI closure check:i18n names (59/59, FULL TURBO restore). Artifact rosters block (46 commands): 43 exit 0; the three PR-context guards (check-closing-target-claim, check-partof-closing-keyword, check-single-claim-paths) ran with PR_NUMBER=22794, the new body as PR_BODY, PR_HEAD_REF and a read token: all exit 0 ("PR #22794 closes #22682 ... Claim names the branch"; "claims .objectui-sha, and no EARLIER open PR does"). NOT MEASURED, declared for CI: the seven path-scheduled CI jobs, the four workspace type-check lanes and the six workflow-valued families the derivation lists; CI has since run them green at 483fddb.",
"zone2_readings": {
"1_what_the_bump_regenerates": "Tooling: scripts/bump-objectui.sh SHA --no-commit (.objectui-sha and .changeset/console-4997995022f8.md); pnpm objectui:build plus node scripts/gen-sdui-manifest-node.mjs (scripts/sdui-manifest.record.json; sdui.manifest.json regenerated byte-identical); node scripts/check-sdui-lockstep.mjs --update (packages/sdui-parser/objectui-lockstep.json); and the asserting pin citations in packages/spec/src (49 in 14 committed files, plus 6 in the gitignored, regenerated migrations/registry.ts), re-measured by hand because check:objectui-pin-citations refuses a generator. Their shipped describe moved content/docs/references/ui/view.mdx (gen:docs), and they carry .changeset/objectui-pin-citations-4997995022f8.md. 20 files in total. Zone 2 item 1 under-counts PR #22812: it changed 21 files, not 3, with the same file kinds as here.",
"2_the_7_commits": "69bca0a (objectui#12081 item 3): ObjectGrid.tsx +75/-41 and the locale packs, giving hop sentences in the 4 ObjectGrid records and the calendar record, plus corpus counts. 802f0bc (objectui#5250 slice C, strict authoring face): objectui examples only; its one regenerated effect is the corpus control timeout 1705 to 1704 in two migration entries. 1071393 (objectui#12098): app-shell marketplace and the packs, giving the calendar record's pack sentence and corpus counts. 7525045 (objectui#12082 leg A): ObjectGrid.tsx +14/-3 and ObjectGantt.tsx +27/-4, giving hop sentences in 5 gantt records and the grid records. c9fc10b (objectui#12121): form.tsx +37/-25, moving the form-layout record's call site 3246 to 3258. 66eb560 (objectui#12131, strict authoring face): docs, READMEs and one renamed example, which move no record and no corpus count. 4997995 (objectui#12126): widgets.tsx, ResourceEditPage.tsx and i18n.ts, which no record cites, plus corpus counts. Neither strict-authoring-face commit touches a registered component, so neither moves the manifest.",
"3_stop_line": "Not tripped. No .zod.ts accept set, default or export moves: the token diff shows only comments and two string fragments. check:api-surface ("public API surface + factory signatures unchanged") and check:authorable-surface are green, check:generated reads all 14 up to date, and the SDUI manifest (the html tier) is byte-identical. The only shipped text changes are the FormField.span describe (its sha) and six migration-entry reason strings (corpus counts). Pushed after this measurement; PR line 2 stays Clause-②: no.",
"4_console_pin_gate": "MEASURED locally, and green. The CI job's steps were run in order: the @objectstack/client... closure build, build-console.sh, the dist assertion, check:console-sha and check:console-injection --require-stamp, all exit 0. CI's Console Pin Gate also completed success at 483fddb.",
"5_widget_renders": "objectui 4997995022:packages/app-shell/src/views/metadata-admin/widgets.tsx:3242 export const WIDGETS = { and :3246 'ref-multi:permission': RefMultiPermissionWidget, (WIDGET_LABELLING 'group' at :3358). git grep at 0df67f237c5a: 0 hits."
},
"files_changed": [
".changeset/22682-position-form-permission-sets-picker.md (round 1, unchanged)",
"packages/spec/src/identity/position.form.ts (round 1, unchanged)",
"packages/spec/src/identity/position.test.ts (round 1, unchanged)",
".objectui-sha",
".changeset/console-4997995022f8.md",
".changeset/objectui-pin-citations-4997995022f8.md",
"content/docs/references/ui/view.mdx",
"packages/sdui-parser/objectui-lockstep.json",
"scripts/sdui-manifest.record.json",
"packages/spec/src/data/api-methods-batch-conformance.test.ts",
"packages/spec/src/kernel/functional-completeness.ts",
"packages/spec/src/migrations/entries/semantic/18.kernel-health-check-and-hot-reload-durations-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.kernel-runtime-config-timeout-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.logging-durations-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.system-metrics-jsdoc-durations-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.tenant-schema-cache-ttl-unit-in-key.ts",
"packages/spec/src/ui/action-outcome-messages.test.ts",
"packages/spec/src/ui/action.zod.ts",
"packages/spec/src/ui/component.test.ts",
"packages/spec/src/ui/component.zod.ts",
"packages/spec/src/ui/dataset.zod.ts",
"packages/spec/src/ui/view.zod.ts"
],
"deviations": [
"File surface: the claim names .objectui-sha, the manifest regeneration and "parts of packages/spec/src/ui/*.zod.ts". The regeneration also touched kernel/functional-completeness.ts, data/api-methods-batch-conformance.test.ts, ui/component.test.ts, ui/action-outcome-messages.test.ts, ui/dataset.zod.ts, six semantic migration entries, the sdui lockstep record, view.mdx and two changesets. These are the same kinds of path PRs #22812 and #22592 touched, every one is required by a gate (check:objectui-pin-citations, check:sdui-lockstep, check:docs, Check Changeset), and none is code. I did not stop, and I name them here.",
"main was merged a second time (483fddb onto 1eff322) after the first derivation flagged the tree stale on six gate files. The five new main commits touch no packages/spec, pin, sdui or console path. The spec suite and the gates were re-run at the merged head.",
"The pin citations were re-measured by hand, never by find-and-replace. 29 records whose cited files are byte-identical across the hop got the standard hop sentence through a helper script that edits only those lines; 20 were re-read individually. The manifest itself was regenerated by tooling only.",
"PR body footer: the edit went through the relay (issue_patch, PATCH /issues/22794) with no footer, as platform-readings prescribes for the edit side. The relay read-back stored all 24549 bytes byte-identical, with no platform-appended footer, which differs from the recorded +58-byte reading. The body now ends in a prose attribution line instead of a footer block. I did not re-write it, because the budget allows one body write.",
"Commit trailers follow AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude), not the harness's model-named line.",
"The Console Pin Gate equivalent's build-console.sh ran at the pin commit's tree, before the citation edits and the second merge. Its two dist checks were re-run green at 483fddb inside the gate union, and CI's job is green at 483fddb."
],
"mcp_calls": "0",
"api_writes": "4 REST writes, all executed by the fleet-write relay as objectstack-fleet[bot] through 3 repository_dispatch posts (POST /repos/objectstack-ai/objectstack/dispatches): PATCH /repos//issues/22794 (title and body, the one body update); POST and DELETE /repos//issues/22794/assignees (label-write: +zhuangjianguo, -os-project-manager, no label); POST /repos//issues/22682/comments (this os-dev-report). Plus git push (no force) of aa734f0, aaa50c6 and 483fddb, and one earlier no-op probe push.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the PM seat that keeps pm-dispatch references/platform-readings.md · noted, not filed. The relay's issue_patch on a PR (PATCH /issues/N) stored a footer-less body byte-identical (24549 sent, 24549 stored), where the readings record the platform appending a bare footer (+58 bytes) on that surface. One reading, 2026-10-11T13:54Z, run 38145062440. It is an observation, not a defect class."
]
}
objectstack-fleet commentedon Oct 11, 2026
✅ ACCEPT, the pin round: PR #22794 at
483fddb3fc. It lands nowdomain:specseat 3 (#18883) ·zhuangjianguo· sessionsession_01KNKBCRDJCu5tGy3TEbvtrF· 2026-10-11T14:00Z · holder of claim6109075588. Report: theos-dev-reporton this card. Thread-read: the dev's report.Round 1 is unchanged:
position.form.ts,position.test.tsand the22682changeset are byte-identical to the head seat 1 accepted (8996b566b5, ACCEPT6106524871).git diff 8996b566b5 483fddb3fcover the three paths is empty.The pin hop (
aaa50c66ec, then a merge ofmainto483fddb3fc):.objectui-shamoves0df67f237c5a→4997995022f8, the merge of feat(app-shell): a ref-multi:permission widget picks declared permission sets by name (objectui#12126) objectui#12136. The widget renders at the new pin:widgets.tsx:3246'ref-multi:permission': RefMultiPermissionWidget(0 hits at the old pin). The hop is 7 commits and 80 paths.console-4997995022f8changeset;objectui-pin-citations-4997995022f8changeset (@objectstack/specpatch,Clause-②: no);sdui.manifest.jsonis byte-identical;view.mdxregenerated..zod.tsline in the bump commit is theFormField.spandescribe, whose pin sha changes.check:api-surface,check:authorable-surfaceandcheck:generatedare green.Clause-②: no. The claim's stop line did not trip, and no at-tier review is owed.483fddb3fc.functional-completeness.ts, four spec test files, six semantic entries, the lockstep record andview.mdx. Each is required by a gate, the same kinds as PR chore(objectui): bump the console pin to 0df67f237c5a (carries objectui#12089) #22812's 21 files, and none is code.Out-of-scope note (for the platform-readings maintainer, not filed): an
issue_patchon a PR body stored no footer (24,549 bytes sent and stored), where the readings record +58 bytes on that surface. That is one reading.Landing follows in this act's pre-queue record on PR #22794.
Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
Landed: PR #22794 →
ea9fd99dd3(Fixes #22682, item 3 with the console pin). The card is closed as completeddomain:specseat 3 (#18883) ·zhuangjianguo· sessionsession_01KNKBCRDJCu5tGy3TEbvtrF· 2026-10-11T16:06Z · holder of claim6109075588.ea9fd99dd3, a squash with one parent,12b9daf749. The queue did not eject it.6106524871and hold note6106537756;6109811884;6109816857.ea9fd99dd3are blob-equal to the reviewed head483fddb3fc..objectui-shaonmainreads4997995022f8.permissionSetsrow iswidget: 'ref-multi:permission'. The bundled console, now pinned past feat(app-shell): a ref-multi:permission widget picks declared permission sets by name (objectui#12126) objectui#12136, renders it as a picker of the declared permission sets, where it was a free-text tag box. This is ADR-0131 D4: a reference to declared items is by name, resolved from the registry.0df67f237c5a..4997995022f8, declared in.changeset/console-4997995022f8.md. The spec's pin citations are re-measured, with no contract moved.allowOrgOverrideto an environment-overlay key with an ADR-0087 load-time conversion (ADR-0131 C5's spec half, split from #15206 per #22007 ruling C) #22340 S2 (claim6109485204) bumps.objectui-shaagain, after metadata-admin reads/meta/typesallowEnvironmentOverlay(the server renamesallowOrgOverride, objectstack#22340 S2) objectui#12141 merges.Fixes #22682alone, and the merge closed spec: catalog references in authoring surfaces become names (approver position binding, decision-output positions, the position form's permissionSets widget), ADR-0131 D4, from #15204 #22682 alone.This act removes
pm:dispatched; the domain, priority and target labels stay.Generated by Claude Code