Current PM
🟢 os-project-manager (GET /user in this act) · session session_01QcAS3qiYYZNezaxZxaUdMV · seated 2026-09-27T05:48Z on the maintainer's summons /pm-dispatch spec@objectstack. This is domain:spec execution seat 2 . ⛔ This post is not #6017 (seat 1).
Why seat 2: the same summons found seat 1 ([PM seat] domain:spec — 🟢 os-zhuang · session_01Rjy9MeetSfq34PKn81CRiN #6017 ) vacant, but session_01Rjy9MeetSfq34PKn81CRiN left its round-open marker there first (5851870367, 02:11Z). This session wrote nothing on [PM seat] domain:spec — 🟢 os-zhuang · session_01Rjy9MeetSfq34PKn81CRiN #6017 ; its body refresh was refused before any write. First come, first served ⇒ this session yields seat 1 and takes the lowest vacant spec seat.
Mutex for seat 2: readings 1–2: the brief 5791507651 and its postscript 5797043113 (the session ended) are the last seat events; the triage liveness patrol 5829533022 after them is cross-seat work. Reading 3: no seat-2 Claim: newer than the brief on any open lane pm:dispatched ∪ pm:queue card. The stray branch claude/issue-19365-sibling-list-door-pagination tips at a main commit dated 2026-09-20T23:38Z, before the brief. Reading 4: the newest closed lane card with a seat-2 claim is [finding] the tracked sdui.manifest.json has no object-tree entry although plugin-tree registers the renderer — the new object-tree spec row gets no parity comparison at all #18407 (claim 2026-09-20T08:04Z).
Dispatch posture: batch = 5 , the maintainer's ceiling. It counts every subagent in flight, reviewers included. Rulings, in this session's chat on 2026-09-27, verbatim: 「项目经理重开之后,并发应该回到3,不应该沿用之前的记录。这个也应该开 skills 卡。」, then 「任务很多,并发加到5」. The predecessor's serial posture is ⛔ not inherited; the protocol text is carried to the skills lane as pm-dispatch: a re-seated PM starts at the default concurrency (batch 3); a dispatch posture the maintainer gave a previous session (serial, batch N) ends with that session's shift and is not inherited through the handover brief #20154 .
Tier posture: this seat is not at CONTRACT_REVIEW_TIER; read it from scripts/pm/dispatch-gates.mjs on origin/main, ⛔ never carry it. Clause-② cards are built at the default tier, and the at-tier review runs in an isolated at-tier subagent. ⛔ No self-review.
Wake: self-bound hourly Routine trig_0182mYuLqBzFnHaogq9PTjxe; send_later only as an accelerator.
Harness: check-harness-current.mjs answers CURRENT at shared HEAD 8d1f7ab785 = origin/main. The PM skill's last touch is 7b068877ce, and it was read this fire.
Inherited ledger
Predecessor session_01UDXER3sdqfeVYpEWZs5mZx (os-warren): nothing in flight (postscript 5797043113). Its handed-over PRs merged under seat 5. Its full record is the previous revision of this body. ⛔ It is not restated here.
Landed this shift: 25 (each verified by content on origin/main, with a landing record on its card or PR):
[finding] A check on a select or delete row-level policy is accepted by RowLevelSecurityPolicySchema and never evaluated — and it does not suppress the using default either; no lint reports it #19965 → PR feat(spec)!: refuse a check on a select / delete row-level security policy #20167 → b276d4463f (at-tier PASS 82/82).
lint: validate-action-name-refs (action-name-undefined) never walks record:alert action.actionName or page:header actions ids, so a misspelled CTA or header action passes spec and lint and is dropped silently at runtime #20105 → PR fix(lint): action-name-undefined walks the record:alert CTA and page:header action ids #20171 → a243cfb4b7 (at-tier PASS 158/158).
[finding] manifestIdRefusal never says each segment must start with a letter, so com.example.2app satisfies every clause of its sentence and is refused #19870 → PR feat(spec): a package-id segment may open with a digit, and the refusal states the rule the pattern enforces #20172 → 3bd28e2b2e (at-tier PASS 66/66; DELIBERATE CORRECTION of three pending notes confirmed in the record).
A field-level requiredWhen / readonlyWhen that reads through a lookup (record.account.tier) is accepted at authoring, but the runtime never hydrates it, so since ADR-0137 D2 every write that reaches it is refused #20078 → PR fix(lint)!: refuse a field-level predicate that reads through a reference field at objectstack validate #20185 → e4471e643e (at-tier PASS 90/90).
[finding] AssembledInstalledPackage.manifest erodes to an index-signature type in the published .d.ts, so the assembled arm absorbs the authoring arm #19324 → PR docs(spec): record that the record-stage manifest type is deliberately an index signature, and the runtime schema is the contract #20191 → 6cc8dcd04a (at-tier PASS 55/55).
[Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152 → PR fix(spec): D3 gets one semantic entry per retirement family, even when D2 is lossless (#17152) #20181 → 9dacf6198c (at-tier PASS 115/115 at round 4; ruling B's one line on [Decision] nine cron- and template-typed keys in packages/spec are published, documented and read by nothing — retire them under ADR-0049 (the #14477 / #15513 shape), mark them experimental, or leave them? #15954 posted).
finding(spec): ChartDrillDownSchema's refusal text for mode sends authors to object-pivot and object-metric, which refuse drillDown.mode (objectui#9002, objectui#10685); only object-data-table reads it #20152 → PR fix(spec): chart drillDown mode guidance names object-data-table, not TABLE/PIVOT/METRIC #20211 → b09ce67870 (at-tier PASS 111/111).
import mapping: a fieldMapping.target that names no field passes objectstack validate and the dry run, then fails every row on commit #20150 → PR fix(rest,lint): refuse an import mapping target that names no field, on the dry run, the commit and validate alike #20208 → 7e7fab738f (at-tier PASS 87/87 after one REWORK round on the changeset's Clause-② arm).
spec(ui)+metadata save: judge a flattened view overlay's top-level options.KIND with the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 #20051 's door half → PR fix(spec)!: judge a flattened list view overlay's legacy options bag at the view write door (door half of #20051) #20183 → 6a4aec71d5 (two kick-outs: a merge conflict, then a client register() timeout diagnosed NOT PR-caused in 5855216065). The card went pm:dispatched → needs-user-decision with a Release: line; the analysis is 5853397704.
A list inside a nested-relation condition in a dataset or measure filter ({ account: { region: ['a'] } }) passes the save-time schema door and is refused only when the chart runs #20080 → PR fix(spec)!: a dataset or measure filter with a list inside a nested relation is refused on save (#20080) #20207 → e8fcf5554e (at-tier PASS 130/130 at 9425caec39, after a kick-out merge round and a wording patch; DELIBERATE CORRECTION re-confirmed).
finding(spec): InlineGridColumnSchema.scale still offers decimal places on a currency inline column, and its prefix describe promises a ¥ default the renderer no longer has — ruling B / 乙 left the inline grid mirror behind #20045 → PR fix(spec)!: refuse scale on a currency inline grid column; prefix promises no default symbol (#20045) #20223 → ca753c0149. After this seat's REWORK round 1 (a merge plus Clause-②: no (narrowing)), the director seat (summon feat: implement browser language detection and auto-redirect for homepage #30 续) rendered the at-tier PASS 5856253884 and landed it. The squash message's Clause-②: yes line is superseded (landing record on the card). Downstream: objectui#10783.
spec: every ConditionalValidationSchema TSDoc example predicate fails when copied — = for comparison is a CEL parse error and a bare order_total is an unknown variable — and the strings ship in the published spec d.ts #20026 → PR fix(spec): rewrite ConditionalValidationSchema TSDoc examples in evaluable CEL (#20026) #20239 → e0f17a376a (the seat widened the scope to the whole docblock; at-tier FAIL on the matches example's \\d escape as copied, then REWORK round 1, then PASS 140/140 at b7abe173cf). Filed from the review: spec: the two cross_field "Salesforce Examples" in validation.zod.ts's TSDoc fail when copied (lone =, AND, bare fields), and Example 1's condition is inverted — TRUE is the violation #20252 .
import mapping: no fieldMapping target can build a compound (address) value from separate CSV columns — a dotted target passes validate and dry-run, then fails every row on commit #20149 → PR feat(spec,rest,lint): an import mapping target may name a declared part of a compound field (mailing_address.street) #20246 → 443b2f4fdc (at-tier FAIL on the collision refusal's missing part list, then REWORK round 1, then PASS 64/64 at e652a77395).
packages: null on a release artifact: the schema and composeStacks refuse it, while every reader reads it as absent #19926 → PR fix(core,runtime,plugin-dev,plugin-security): refuse packages: null as malformed, never absent #20228 → a9fb83ef06 (at-tier FAIL on two changeset sentences, then REWORK round 1, then PASS 95/95; DELIBERATE CORRECTION of the 15293 note confirmed). It was enqueued FIRST because PR fix(cli): refuse a present non-array packages in the stack-collection and docs readers #20231 's null-packages-follows-resolver.test.ts needs core to refuse null before lint does (5857600574).
[finding] After PR #19962 rewrites the RLS check describe, three texts still quote the old "defaults to USING clause if not specified" contract, and the using text never says it is the insert check when no check is declared #19967 → PR fix(spec): state the RLS using / check texts as the write gate enforces them #20268 → 3f86dc52f2 (at-tier PASS 84/84, then a dead #16608 citation re-anchored to a commit, then PASS 78/78 at 7bfc993cb5).
lint: four recordsOf(stack.packages) readers treat a non-array packages as "no packages" instead of refusing it — the packages/lint half of #19925 (ruling #15293-A) #20206 → PR fix(lint): refuse a present non-array packages at all five stack.packages readers #20229 → 5f9d7d7864 (two REWORK rounds, then an ESCALATE ruled in-seat 5857515836, then five at-tier FAILs, then PASS 107/107). This closed the last reader leg of ruling 5805260775.
[finding] os validate / os build accept a view container whose object names no object in the stack — no error, no advisory — and the runtime's getViewsByObject then never finds the view #20216 → PR fix(lint)!: refuse a view container whose object names no object #20253 → ae8e3ca016 (ACCEPT, then a CI fixture round, then ACCEPT; no enqueue-gate limb).
spec: the two cross_field "Salesforce Examples" in validation.zod.ts's TSDoc fail when copied (lone =, AND, bare fields), and Example 1's condition is inverted — TRUE is the violation #20252 → PR fix(spec): rewrite the cross_field and script examples in validation.zod.ts in evaluable CEL, un-inverting two (#20252) #20276 → e6b7d8c861 (REWORK round 1 widened it to five predicates; at-tier PASS 73/73).
os migrate meta prints tracker numbers to the author: ADR-0087 migration entries' reason / replacement / acceptanceCriteria text carries ~2,060 of them, 178 dead, which AGENTS.md's runtime-string rule forbids #20233 stage 1 → PR fix(spec): os migrate meta guidance for the engine-* migration entries states each lesson in words, not tracker numbers (stage 1) #20285 → 2aa25efb4e (five engine-* entries made tracker-free, plus a queue-tier pin; at-tier PASS 91/91). The card stays open for the later stages.
[finding] build-react-blocks-contract.ts projects through a bare z.toJSONSchema into a PUBLISHED skills/** catalog file — 1 of its 3 block schemas is divergent under the override #19100 → PR fix(spec): project the react-blocks contract through projectPublishedJsonSchema #20304 → 4e0f72e8d2 (measured first: react-blocks.md came out byte-identical, so it was not governed; the allowance went from 3 rows to 2).
[finding] lint-startup-registry-verdict corpus test walks packages/ with a bare readdir-then-stat inside @objectstack/lint#test, so tsup's transient bundled config fails it with ENOENT (measured 4 of 8) #20269 → PR test(lint): skip a vanished entry in the startup-registry corpus walk #20303 → 10ea9eb2ed (REWORK round 1's git-listed corpus conflicted with check:cross-package-test-inputs, so round 2 re-ruled E: the fs walk, the vanished-entry rule on all three legs, and tmp skipped). This closed the random Test Core red.
os migrate meta prints tracker numbers to the author: ADR-0087 migration entries' reason / replacement / acceptanceCriteria text carries ~2,060 of them, 178 dead, which AGENTS.md's runtime-string rule forbids #20233 stage 2 → PR fix(spec): os migrate meta guidance for the ui-* and plugin-* migration entries states each lesson in words, not tracker numbers (stage 2) #20324 → dfd8e398aa (the ui- and plugin- families, 113 sites; the pin was widened; at-tier PASS 105/105).
spec(ui): layout: 'inline' | 'grid' on object-form / FormView parse green and render as vertical — enforce-or-remove (ADR-0049) #20221 → PR feat(spec)!: form layout accepts only vertical | horizontal — the inline and grid arms retired (#20221) #20262 → 569d4d2dbf. Tier H: hotlong APPROVED, the seat's landing round merged main and rewrote the ui- tracker id, and the maintainer merged it directly.
qa: os test prints suite and scenario names, filters by tags, and skips on unmet requires (5 keys) #20289 (four of five keys) → PR feat(qa): os test prints suite and scenario names and selects scenarios by --tags #20341 → 5a6267f486 (REWORK round 1, then a landing round; at-tier PASS 68/68 plus a delta PASS 94/94). The card moved to needs-user-decision for requires (analysis 5863075827, recommendation B, fallback C).
finding(formula,spec): expression refusal text renders English under zh-CN in objectui's flow designer — validateExpression / collectCelRootIdentifiers / predicateSlotRefusal messages carry no locale #20291 → PR feat(formula,spec): stable refusal codes and params beside every expression refusal message #20352 → 862b6ce869 (REWORK round 1 folded structuralConditionRefusal in; at-tier PASS 72/72; codes handed to objectui#10835).
In flight (5 of 5; all five devs were resumed after a container restart at about 04:00Z):
spec(integration): retire the connector health-probe, circuit-breaker, authored status and nested webhooks keys (16), which nothing enforces #20273 → PR feat(spec)!: retire the connector resilience family — health (probe + breaker), status and nested webhooks, sixteen keys nothing read (#20273) #20350 : at-tier PASS plus two delta PASSes (597e867f4d, 0c522e3fc1). The third merge round is under way (the liveness/state-counts.md's shared total row makes every liveness-touching PR dirty whenever another lands, so its CI never starts until a merge-and-regenerate round #20361 tax), then it enqueues.
analytics: an authored cube's public, refreshKey, format, granularities and descriptions take effect (8 keys) #20282 stage 1 → PR feat(analytics): enforce analytics_cube.public and default it to visible #20348 : at-tier FAIL 5863057917, then REWORK round 2 5863061968, the last under the cap. It covers the narrowing declaration, a D3 entry for pre-flip compiled artifacts, a byte-identical hidden-cube refusal, and three stale docs.
spec(ui): retire list.tabs and the view container's body name (2 keys); listViews + ViewTabBar and the row name already deliver both #20301 stage 1 → PR feat(spec)!: retire the list view's own tabs key; named presets are listViews entries #20357 (Tier H: a skills/** hand edit): a CI-fix round for four consumer pins of list.tabs. Then an at-tier review, then the Tier H four-piece.
os migrate meta prints tracker numbers to the author: ADR-0087 migration entries' reason / replacement / acceptanceCriteria text carries ~2,060 of them, 178 dead, which AGENTS.md's runtime-string rule forbids #20233 stage 3: the driver-, kernel- and system- families, 132 sites (claim 5862834658).
[finding] a decision branch with no label registers and validates clean, then at run time the decision takes EVERY out-edge; a non-object conditions element also registers #20316 (p2): refuse node config the build doors admit and the runtime misroutes (claim 5862958950; [finding] a loop node with no config.collection registers and validates clean, then fails at run time with 'loop config does not satisfy the loop contract' #20317 folds in).
In the decision box: qa: os test prints suite and scenario names, filters by tags, and skips on unmet requires (5 keys) #20289 requires (A: a new discovery field / B: services replaces plugins / C: retire).
Lane queue, not taken this round:
the p3 ENFORCE/RETIRE sweep cards spec(integration): build the connector sync executor that syncConfig and fieldMappings declare (14 keys), once and on the mainstream shape #20281 , automation: connector triggers start flows, and a connector action's description / outputSchema reach the flow designer (7 keys) #20287 , realtime: mount one subscription transport that parses SubscriptionSchema with the emitted data.record.* vocabulary (6 keys) #20288 , rest: the served OpenAPI document's info block comes from api.documentation (9 keys) #20294 , studio: show the authored label / description on flows, hooks, app areas, RLS policies and the view container (7 keys) #20299 , metadata: refuse to save or load a page whose requires names a plugin that is not loaded (1 key) #20312 , fields: number rendering honours the authored useGrouping (1 key) #20313 , i18n: the flow launcher and runner header read translation.flows.<flow>.label (1 key) #20318 and [finding] action.aria is graded live in the liveness ledger, but no action surface reads it — and the Studio action editor still renders an aria control #20323 : waiting for slots;
[finding] A type: 'matrix' report with columns and a chart parses, and the pinned renderer never draws the chart #20293 and spec(analytics): retire the inner name on cube measures and dimensions; the record key is the identity (2 keys) #20300 (area:reports) wait behind analytics: an authored cube's public, refreshKey, format, granularities and descriptions take effect (8 keys) #20282 ;
picklist metadata kind — spec: picklist collection, Field.select({ picklist }), server-resolved options, translation face (phase 1 of objectstack#18164) #19518 / spec: a shared picklist (global value set) metadata kind — option lists reused across objects and packages are TypeScript constants today #18164 : fenced by road order.
Closed not_planned: spec(ui): VIEW_FILTER_OPERATOR_ALIASES has no row for containsCaseInsensitive → icontains, the one objectui filter-builder spelling the protocol's alias table lacks #20092 . The premise was measured false (containsCaseInsensitive was never in stored view metadata), and the finding was carried to objectui#9559.
Filed: liveness/state-counts.md's shared total row makes every liveness-touching PR dirty whenever another lands, so its CI never starts until a merge-and-regenerate round #20361 (liveness/state-counts.md's shared total row makes every liveness PR dirty whenever another lands, so no CI runs until a merge round; measured on 4 PRs overnight), analytics: any member's inline dataset query replaces an authored cube for every user until restart, even when the query itself is refused 403 #20356 (analytics: any member's inline dataset query replaces an authored cube for every user until restart, even when refused 403; measured at the public door; filing seat suspects P1), cli nightly e2e: generate-agent-retired.e2e.test.ts expects the pre-#20195 object template import (import * as Data) and fails on main #20270 (cli nightly e2e drift), governed docs state the RLS write check wrong: ADR-0066 D-3 calls row policies "OR-combined" for every operation, and skills/objectstack-data/rules/security.md calls check the write filter with no stand-in rule #20275 (skills: governed ADR-0066 / security.md RLS text), dispatch-gates: the diff-scope check-issue-citations run is reported NOT MEASURED (workflow-valued), and pnpm check:issue-citations is only its self-test, so a dev's local gate sweep passes a PR that CI then reds #20278 (dispatch-gates: diff-scope citation blind spot), spec: the two cross_field "Salesforce Examples" in validation.zod.ts's TSDoc fail when copied (lone =, AND, bare fields), and Example 1's condition is inverted — TRUE is the violation #20252 (two cross_field Salesforce examples in validation.zod.ts fail when copied; Example 1 inverted), objectui#10783 (GridField currencyWidth still honours an authored scale on currency columns; triage item 4 of finding(spec): InlineGridColumnSchema.scale still offers decimal places on a currency inline column, and its prefix describe promises a ¥ default the renderer no longer has — ruling B / 乙 left the inline grid mirror behind #20045 ), pm-dispatch: a re-seated PM starts at the default concurrency (batch 3); a dispatch posture the maintainer gave a previous session (serial, batch N) ends with that session's shift and is not inherited through the handover brief #20154 (skills: a re-seated PM starts at batch 3), skills(objectstack-ui): pages.md and its eval teach full Action objects in page:header.properties.actions; the contract is action ids (ruling B on #11592) and os validate flags the taught shape #20173 (skills: page:header.actions taught as objects; plus the docs sibling on layout-dsl.mdx by pointer), ADR-0087 still defines each step's D3 semantic list as "the residue D2 cannot express losslessly", contradicting ruling B on #17152 (one D3 entry per retirement family, even when a lossless D2 conversion exists) #20188 (skills: the ADR-0087 residual split off [Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152 , plus the spec-property-retirement §3 site), Major 18 retirements justified "lossless, so no semantic residue" carry no D3 entry, which ruling B on #17152 now requires (tenancy.organizationField, and a census of the rest) #20201 (spec: major-18 retirements missing their D3 entry under ruling B; the collector for the entry-level prose sites). Folded: the dataset save door's other comparand arms → spec: FilterConditionSchema (the save-time door) admits a non-boolean $null / $exists flag, so a stored dataset or widget filter carrying one saves clean and is refused 400 on every query face #20116 as family collector.
Passed over this round (serial, re-derive before the next pick):
[finding] $ne with an array comparand splits across backends: driver-sql and driver-memory refuse (400), driver-mongodb answers, formula matches every row — and both shared faces pass it #19886 (p1): stage 2d waits on the director seat's ratification.
[finding] four more exported spec types resolve to unknown while their TSDoc promises a shape — ViewMetadataParsed, InlineAction, AssembledViewArtifact, JoinedReportBlock (the #19871 class, other sites) #19920 : report.zod.ts JoinedReportBlockSchema is inside [finding] a joined report's chart — container and blocks[].chart — parses and is never drawn, while the liveness ledger names the joined branch as its reader #20161 's in-flight claim (deferral note on the card).
[finding] A decision branch with no expression key registers and validates clean, although DecisionConditionSchema declares it required and the executor throws on the source-less envelope #19961 : serial behind PR feat(spec,automation): create_record / update_record fields.* accept the CEL value envelope — declared and evaluated together #20205 .
spec(ui): a flattened viewKind: 'list' view overlay with no columns is refused by the list overlay member, then ACCEPTED by the form overlay member, so its list keys are stripped unjudged and a wrong 200 stores it #20186 (p1): claimed by seat 1 (5855239789).
picklist metadata kind — spec: picklist collection, Field.select({ picklist }), server-resolved options, translation face (phase 1 of objectstack#18164) #19518 / spec: a shared picklist (global value set) metadata kind — option lists reused across objects and packages are TypeScript constants today #18164 (p2, picklist): the card's own road-order fence (after the open product P0/P1 items).
#19188 split: 145 top-level zod-only keys need a RECORDED REASON, never a form row — and none can be recorded until the ledger learns a root path #19333 : its Blocked-by: #19329 names a closed card; the unlock re-derivation is owed before any pick.
Seam: IObjectQLEngine gains an optional judge-only filter-admission method, run by the engine's own admission pipeline without executing (objectstack#19995 ruling C) #20157 : serial behind engine objectql + REST: the per-aggregation filter still lacks four of where's doors — a bad date, an addDays numeric pair, an undeclared { $field } and an unknown key answer 200 with every count 0 #20148 (seat 4's note 5852563458). [finding] a joined report's chart — container and blocks[].chart — parses and is never drawn, while the liveness ledger names the joined branch as its reader #20161 : serial behind seat 1's [finding] a type: 'joined' report accepts top-level dataset / rows / columns / values, and the renderer never reads them; the refinement refuses only order #19856 (report.zod.ts joined arm).
lint: validate-rls-predicate-enforceability is silent on == / != against a kernel membership key (current_user.org_user_ids …), a policy the runtime drops on every request once PR #19947 lands #19951 : the lint file is claimed by seat 4's [finding] A CEL row-level predicate comparing a field to the bare current_user root lowers against the whole caller object; a check written != current_user admits every write #19959 (p1 · security), whose order note puts lint: validate-rls-predicate-enforceability is silent on == / != against a kernel membership key (current_user.org_user_ids …), a policy the runtime drops on every request once PR #19947 lands #19951 after it.
Hot-file serial queue
⛔ Re-derive from live Claim: comments and each open PR's actual file list before every dispatch; never from this list.
Two spec seats are live (seat 1 session_01Rjy9MeetSfq34PKn81CRiN, and this one), with the same take order. Every claim is followed by a full-thread re-read. The earlier claim wins, and the loser yields.
packages/spec/liveness/state-counts.md and liveness/README.md are the new hot files. Every ENFORCE/RETIRE PR regenerates the counts, so each landing makes the others dirty, and a dirty PR runs no CI. Expect one merge-and-regenerate round per landing (gen:liveness-counts).
packages/spec/src/migrations/registry.ts: ordinary concurrency. On any base merge re-run the generator; ⛔ never hand-resolve a hunk in a generated file.
content/docs/references/** is merge=os-regen: use bash scripts/pm/os-regen-merge.sh plus a quoted-exact git grep against origin/main.
Notes
Write channel: post-stamped, label-write and the fleet-write relay pace themselves. Raw curl writes go through write-pace --run.
/search/issues is blocked by the proxy. Use REST list endpoints plus a local filter. MCP list_issues labels is OR; REST labels= is AND.
Before every selection, count the lane queue's open P0/P1 (NORTH-STAR 优先级 rule 3). While any is open, p2/p3 tooling and contract-hygiene cards are not dispatched.
Current PM
🟢
os-project-manager(GET /userin this act) · sessionsession_01QcAS3qiYYZNezaxZxaUdMV· seated 2026-09-27T05:48Z on the maintainer's summons/pm-dispatch spec@objectstack. This isdomain:specexecution seat 2. ⛔ This post is not #6017 (seat 1).session_01Rjy9MeetSfq34PKn81CRiNleft its round-open marker there first (5851870367, 02:11Z). This session wrote nothing on [PM seat] domain:spec — 🟢 os-zhuang · session_01Rjy9MeetSfq34PKn81CRiN #6017; its body refresh was refused before any write. First come, first served ⇒ this session yields seat 1 and takes the lowest vacant spec seat.5791507651and its postscript5797043113(the session ended) are the last seat events; the triage liveness patrol5829533022after them is cross-seat work. Reading 3: no seat-2Claim:newer than the brief on any open lanepm:dispatched∪pm:queuecard. The stray branchclaude/issue-19365-sibling-list-door-paginationtips at amaincommit dated 2026-09-20T23:38Z, before the brief. Reading 4: the newest closed lane card with a seat-2 claim is [finding] the trackedsdui.manifest.jsonhas noobject-treeentry althoughplugin-treeregisters the renderer — the newobject-treespec row gets no parity comparison at all #18407 (claim 2026-09-20T08:04Z).batch= 5, the maintainer's ceiling. It counts every subagent in flight, reviewers included. Rulings, in this session's chat on 2026-09-27, verbatim: 「项目经理重开之后,并发应该回到3,不应该沿用之前的记录。这个也应该开 skills 卡。」, then 「任务很多,并发加到5」. The predecessor's serial posture is ⛔ not inherited; the protocol text is carried to the skills lane as pm-dispatch: a re-seated PM starts at the default concurrency (batch 3); a dispatch posture the maintainer gave a previous session (serial, batch N) ends with that session's shift and is not inherited through the handover brief #20154.CONTRACT_REVIEW_TIER; read it fromscripts/pm/dispatch-gates.mjsonorigin/main, ⛔ never carry it. Clause-② cards are built at the default tier, and the at-tier review runs in an isolated at-tier subagent. ⛔ No self-review.trig_0182mYuLqBzFnHaogq9PTjxe;send_lateronly as an accelerator.check-harness-current.mjsanswers CURRENT at shared HEAD8d1f7ab785=origin/main. The PM skill's last touch is7b068877ce, and it was read this fire.Inherited ledger
session_01UDXER3sdqfeVYpEWZs5mZx(os-warren): nothing in flight (postscript5797043113). Its handed-over PRs merged under seat 5. Its full record is the previous revision of this body. ⛔ It is not restated here.origin/main, with a landing record on its card or PR):checkon aselectordeleterow-level policy is accepted byRowLevelSecurityPolicySchemaand never evaluated — and it does not suppress theusingdefault either; no lint reports it #19965 → PR feat(spec)!: refuse acheckon aselect/deleterow-level security policy #20167 →b276d4463f(at-tier PASS 82/82).validate-action-name-refs(action-name-undefined) never walksrecord:alertaction.actionNameorpage:headeractionsids, so a misspelled CTA or header action passes spec and lint and is dropped silently at runtime #20105 → PR fix(lint): action-name-undefined walks the record:alert CTA and page:header action ids #20171 →a243cfb4b7(at-tier PASS 158/158).manifestIdRefusalnever says each segment must start with a letter, socom.example.2appsatisfies every clause of its sentence and is refused #19870 → PR feat(spec): a package-id segment may open with a digit, and the refusal states the rule the pattern enforces #20172 →3bd28e2b2e(at-tier PASS 66/66; DELIBERATE CORRECTION of three pending notes confirmed in the record).requiredWhen/readonlyWhenthat reads through a lookup (record.account.tier) is accepted at authoring, but the runtime never hydrates it, so since ADR-0137 D2 every write that reaches it is refused #20078 → PR fix(lint)!: refuse a field-level predicate that reads through a reference field at objectstack validate #20185 →e4471e643e(at-tier PASS 90/90).6cc8dcd04a(at-tier PASS 55/55).9dacf6198c(at-tier PASS 115/115 at round 4; ruling B's one line on [Decision] nine cron- and template-typed keys in packages/spec are published, documented and read by nothing — retire them under ADR-0049 (the #14477 / #15513 shape), mark them experimental, or leave them? #15954 posted).modesends authors toobject-pivotandobject-metric, which refusedrillDown.mode(objectui#9002, objectui#10685); onlyobject-data-tablereads it #20152 → PR fix(spec): chart drillDown mode guidance names object-data-table, not TABLE/PIVOT/METRIC #20211 →b09ce67870(at-tier PASS 111/111).fieldMapping.targetthat names no field passesobjectstack validateand the dry run, then fails every row on commit #20150 → PR fix(rest,lint): refuse an import mapping target that names no field, on the dry run, the commit and validate alike #20208 →7e7fab738f(at-tier PASS 87/87 after one REWORK round on the changeset'sClause-②arm).options.KINDwith the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 #20051's door half → PR fix(spec)!: judge a flattened list view overlay's legacyoptionsbag at the view write door (door half of #20051) #20183 →6a4aec71d5(two kick-outs: a merge conflict, then a clientregister()timeout diagnosed NOT PR-caused in5855216065). The card wentpm:dispatched→needs-user-decisionwith aRelease:line; the analysis is5853397704.{ account: { region: ['a'] } }) passes the save-time schema door and is refused only when the chart runs #20080 → PR fix(spec)!: a dataset or measure filter with a list inside a nested relation is refused on save (#20080) #20207 →e8fcf5554e(at-tier PASS 130/130 at9425caec39, after a kick-out merge round and a wording patch; DELIBERATE CORRECTION re-confirmed).InlineGridColumnSchema.scalestill offers decimal places on acurrencyinline column, and itsprefixdescribe promises a¥default the renderer no longer has — ruling B / 乙 left the inline grid mirror behind #20045 → PR fix(spec)!: refusescaleon a currency inline grid column;prefixpromises no default symbol (#20045) #20223 →ca753c0149. After this seat's REWORK round 1 (a merge plusClause-②: no (narrowing)), the director seat (summon feat: implement browser language detection and auto-redirect for homepage #30 续) rendered the at-tier PASS5856253884and landed it. The squash message'sClause-②: yesline is superseded (landing record on the card). Downstream: objectui#10783.ConditionalValidationSchemaTSDoc example predicate fails when copied —=for comparison is a CEL parse error and a bareorder_totalis an unknown variable — and the strings ship in the published spec d.ts #20026 → PR fix(spec): rewrite ConditionalValidationSchema TSDoc examples in evaluable CEL (#20026) #20239 →e0f17a376a(the seat widened the scope to the whole docblock; at-tier FAIL on thematchesexample's\\descape as copied, then REWORK round 1, then PASS 140/140 atb7abe173cf). Filed from the review: spec: the twocross_field"Salesforce Examples" in validation.zod.ts's TSDoc fail when copied (lone=,AND, bare fields), and Example 1's condition is inverted — TRUE is the violation #20252.443b2f4fdc(at-tier FAIL on the collision refusal's missing part list, then REWORK round 1, then PASS 64/64 ate652a77395).packages: nullon a release artifact: the schema andcomposeStacksrefuse it, while every reader reads it as absent #19926 → PR fix(core,runtime,plugin-dev,plugin-security): refusepackages: nullas malformed, never absent #20228 →a9fb83ef06(at-tier FAIL on two changeset sentences, then REWORK round 1, then PASS 95/95; DELIBERATE CORRECTION of the 15293 note confirmed). It was enqueued FIRST because PR fix(cli): refuse a present non-arraypackagesin the stack-collection and docs readers #20231'snull-packages-follows-resolver.test.tsneeds core to refusenullbefore lint does (5857600574).checkdescribe, three texts still quote the old "defaults to USING clause if not specified" contract, and theusingtext never says it is the insert check when nocheckis declared #19967 → PR fix(spec): state the RLS using / check texts as the write gate enforces them #20268 →3f86dc52f2(at-tier PASS 84/84, then a dead#16608citation re-anchored to a commit, then PASS 78/78 at7bfc993cb5).recordsOf(stack.packages)readers treat a non-arraypackagesas "no packages" instead of refusing it — the packages/lint half of #19925 (ruling #15293-A) #20206 → PR fix(lint): refuse a present non-arraypackagesat all five stack.packages readers #20229 →5f9d7d7864(two REWORK rounds, then an ESCALATE ruled in-seat5857515836, then five at-tier FAILs, then PASS 107/107). This closed the last reader leg of ruling5805260775.os validate/os buildaccept a view container whoseobjectnames no object in the stack — no error, no advisory — and the runtime'sgetViewsByObjectthen never finds the view #20216 → PR fix(lint)!: refuse a view container whoseobjectnames no object #20253 →ae8e3ca016(ACCEPT, then a CI fixture round, then ACCEPT; no enqueue-gate limb).cross_field"Salesforce Examples" in validation.zod.ts's TSDoc fail when copied (lone=,AND, bare fields), and Example 1's condition is inverted — TRUE is the violation #20252 → PR fix(spec): rewrite the cross_field and script examples in validation.zod.ts in evaluable CEL, un-inverting two (#20252) #20276 →e6b7d8c861(REWORK round 1 widened it to five predicates; at-tier PASS 73/73).2aa25efb4e(fiveengine-*entries made tracker-free, plus a queue-tier pin; at-tier PASS 91/91). The card stays open for the later stages.4e0f72e8d2(measured first:react-blocks.mdcame out byte-identical, so it was not governed; the allowance went from 3 rows to 2).10ea9eb2ed(REWORK round 1's git-listed corpus conflicted withcheck:cross-package-test-inputs, so round 2 re-ruled E: the fs walk, the vanished-entry rule on all three legs, andtmpskipped). This closed the randomTest Corered.dfd8e398aa(theui-andplugin-families, 113 sites; the pin was widened; at-tier PASS 105/105).layout: 'inline' | 'grid'onobject-form/FormViewparse green and render asvertical— enforce-or-remove (ADR-0049) #20221 → PR feat(spec)!: formlayoutaccepts only vertical | horizontal — theinlineandgridarms retired (#20221) #20262 →569d4d2dbf. Tier H:hotlongAPPROVED, the seat's landing round mergedmainand rewrote theui-tracker id, and the maintainer merged it directly.os testprints suite and scenario names, filters bytags, and skips on unmetrequires(5 keys) #20289 (four of five keys) → PR feat(qa): os test prints suite and scenario names and selects scenarios by --tags #20341 →5a6267f486(REWORK round 1, then a landing round; at-tier PASS 68/68 plus a delta PASS 94/94). The card moved toneeds-user-decisionforrequires(analysis5863075827, recommendation B, fallback C).862b6ce869(REWORK round 1 foldedstructuralConditionRefusalin; at-tier PASS 72/72; codes handed to objectui#10835).statusand nestedwebhookskeys (16), which nothing enforces #20273 → PR feat(spec)!: retire the connector resilience family — health (probe + breaker), status and nested webhooks, sixteen keys nothing read (#20273) #20350: at-tier PASS plus two delta PASSes (597e867f4d,0c522e3fc1). The third merge round is under way (the liveness/state-counts.md's shared total row makes every liveness-touching PR dirty whenever another lands, so its CI never starts until a merge-and-regenerate round #20361 tax), then it enqueues.public,refreshKey,format,granularitiesand descriptions take effect (8 keys) #20282 stage 1 → PR feat(analytics): enforce analytics_cube.public and default it to visible #20348: at-tier FAIL5863057917, then REWORK round 25863061968, the last under the cap. It covers the narrowing declaration, a D3 entry for pre-flip compiled artifacts, a byte-identical hidden-cube refusal, and three stale docs.list.tabsand the view container's bodyname(2 keys);listViews+ ViewTabBar and the row name already deliver both #20301 stage 1 → PR feat(spec)!: retire the list view's owntabskey; named presets arelistViewsentries #20357 (Tier H: askills/**hand edit): a CI-fix round for four consumer pins oflist.tabs. Then an at-tier review, then the Tier H four-piece.driver-,kernel-andsystem-families, 132 sites (claim5862834658).5862958950; [finding] a loop node with no config.collection registers and validates clean, then fails at run time with 'loop config does not satisfy the loop contract' #20317 folds in).os testprints suite and scenario names, filters bytags, and skips on unmetrequires(5 keys) #20289requires(A: a new discovery field / B:servicesreplacesplugins/ C: retire).syncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281, automation: connector triggers start flows, and a connector action'sdescription/outputSchemareach the flow designer (7 keys) #20287, realtime: mount one subscription transport that parsesSubscriptionSchemawith the emitteddata.record.*vocabulary (6 keys) #20288, rest: the served OpenAPI document'sinfoblock comes fromapi.documentation(9 keys) #20294, studio: show the authoredlabel/descriptionon flows, hooks, app areas, RLS policies and the view container (7 keys) #20299, metadata: refuse to save or load a page whoserequiresnames a plugin that is not loaded (1 key) #20312, fields: number rendering honours the authoreduseGrouping(1 key) #20313, i18n: the flow launcher and runner header readtranslation.flows.<flow>.label(1 key) #20318 and [finding]action.ariais gradedlivein the liveness ledger, but no action surface reads it — and the Studio action editor still renders anariacontrol #20323: waiting for slots;type: 'matrix'report withcolumnsand achartparses, and the pinned renderer never draws the chart #20293 and spec(analytics): retire the innernameon cube measures and dimensions; the record key is the identity (2 keys) #20300 (area:reports) wait behind analytics: an authored cube'spublic,refreshKey,format,granularitiesand descriptions take effect (8 keys) #20282;picklistcollection,Field.select({ picklist }), server-resolved options, translation face (phase 1 of objectstack#18164) #19518 / spec: a shared picklist (global value set) metadata kind — option lists reused across objects and packages are TypeScript constants today #18164: fenced by road order.not_planned: spec(ui):VIEW_FILTER_OPERATOR_ALIASEShas no row forcontainsCaseInsensitive→icontains, the one objectui filter-builder spelling the protocol's alias table lacks #20092. The premise was measured false (containsCaseInsensitivewas never in stored view metadata), and the finding was carried to objectui#9559.liveness/state-counts.md's shared total row makes every liveness PR dirty whenever another lands, so no CI runs until a merge round; measured on 4 PRs overnight), analytics: any member's inline dataset query replaces an authored cube for every user until restart, even when the query itself is refused 403 #20356 (analytics: any member's inline dataset query replaces an authored cube for every user until restart, even when refused 403; measured at the public door; filing seat suspects P1), cli nightly e2e:generate-agent-retired.e2e.test.tsexpects the pre-#20195 object template import (import * as Data) and fails on main #20270 (cli nightly e2e drift), governed docs state the RLS write check wrong: ADR-0066 D-3 calls row policies "OR-combined" for every operation, and skills/objectstack-data/rules/security.md callscheckthe write filter with no stand-in rule #20275 (skills: governed ADR-0066 /security.mdRLS text), dispatch-gates: the diff-scopecheck-issue-citationsrun is reported NOT MEASURED (workflow-valued), andpnpm check:issue-citationsis only its self-test, so a dev's local gate sweep passes a PR that CI then reds #20278 (dispatch-gates: diff-scope citation blind spot), spec: the twocross_field"Salesforce Examples" in validation.zod.ts's TSDoc fail when copied (lone=,AND, bare fields), and Example 1's condition is inverted — TRUE is the violation #20252 (twocross_fieldSalesforce examples in validation.zod.ts fail when copied; Example 1 inverted), objectui#10783 (GridFieldcurrencyWidthstill honours an authoredscaleon currency columns; triage item 4 of finding(spec):InlineGridColumnSchema.scalestill offers decimal places on acurrencyinline column, and itsprefixdescribe promises a¥default the renderer no longer has — ruling B / 乙 left the inline grid mirror behind #20045), pm-dispatch: a re-seated PM starts at the default concurrency (batch 3); a dispatch posture the maintainer gave a previous session (serial, batch N) ends with that session's shift and is not inherited through the handover brief #20154 (skills: a re-seated PM starts at batch 3), skills(objectstack-ui): pages.md and its eval teach full Action objects inpage:header.properties.actions; the contract is action ids (ruling B on #11592) andos validateflags the taught shape #20173 (skills:page:header.actionstaught as objects; plus the docs sibling onlayout-dsl.mdxby pointer), ADR-0087 still defines each step's D3semanticlist as "the residue D2 cannot express losslessly", contradicting ruling B on #17152 (one D3 entry per retirement family, even when a lossless D2 conversion exists) #20188 (skills: the ADR-0087 residual split off [Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152, plus thespec-property-retirement§3 site), Major 18 retirements justified "lossless, so no semantic residue" carry no D3 entry, which ruling B on #17152 now requires (tenancy.organizationField, and a census of the rest) #20201 (spec: major-18 retirements missing their D3 entry under ruling B; the collector for the entry-level prose sites). Folded: the dataset save door's other comparand arms → spec: FilterConditionSchema (the save-time door) admits a non-boolean $null / $exists flag, so a stored dataset or widget filter carrying one saves clean and is refused 400 on every query face #20116 as family collector.$newith an array comparand splits across backends: driver-sql and driver-memory refuse (400), driver-mongodb answers, formula matches every row — and both shared faces pass it #19886 (p1): stage 2d waits on the director seat's ratification.unknownwhile their TSDoc promises a shape —ViewMetadataParsed,InlineAction,AssembledViewArtifact,JoinedReportBlock(the #19871 class, other sites) #19920:report.zod.tsJoinedReportBlockSchemais inside [finding] a joined report'schart— container andblocks[].chart— parses and is never drawn, while the liveness ledger names the joined branch as its reader #20161's in-flight claim (deferral note on the card).decisionbranch with noexpressionkey registers and validates clean, althoughDecisionConditionSchemadeclares it required and the executor throws on the source-less envelope #19961: serial behind PR feat(spec,automation): create_record / update_record fields.* accept the CEL value envelope — declared and evaluated together #20205.viewKind: 'list'view overlay with nocolumnsis refused by the list overlay member, then ACCEPTED by the form overlay member, so its list keys are stripped unjudged and a wrong 200 stores it #20186 (p1): claimed by seat 1 (5855239789).picklistcollection,Field.select({ picklist }), server-resolved options, translation face (phase 1 of objectstack#18164) #19518 / spec: a shared picklist (global value set) metadata kind — option lists reused across objects and packages are TypeScript constants today #18164 (p2, picklist): the card's own road-order fence (after the open product P0/P1 items).Blocked-by: #19329names a closed card; the unlock re-derivation is owed before any pick.IObjectQLEnginegains an optional judge-only filter-admission method, run by the engine's own admission pipeline without executing (objectstack#19995 ruling C) #20157: serial behind engine objectql + REST: the per-aggregationfilterstill lacks four ofwhere's doors — a bad date, anaddDaysnumeric pair, an undeclared{ $field }and an unknown key answer200with every count 0 #20148 (seat 4's note5852563458). [finding] a joined report'schart— container andblocks[].chart— parses and is never drawn, while the liveness ledger names the joined branch as its reader #20161: serial behind seat 1's [finding] atype: 'joined'report accepts top-leveldataset/rows/columns/values, and the renderer never reads them; the refinement refuses onlyorder#19856 (report.zod.tsjoined arm).validate-rls-predicate-enforceabilityis silent on==/!=against a kernel membership key (current_user.org_user_ids…), a policy the runtime drops on every request once PR #19947 lands #19951: the lint file is claimed by seat 4's [finding] A CEL row-level predicate comparing a field to the barecurrent_userroot lowers against the whole caller object; acheckwritten!= current_useradmits every write #19959 (p1 · security), whose order note puts lint:validate-rls-predicate-enforceabilityis silent on==/!=against a kernel membership key (current_user.org_user_ids…), a policy the runtime drops on every request once PR #19947 lands #19951 after it.Hot-file serial queue
⛔ Re-derive from live
Claim:comments and each open PR's actual file list before every dispatch; never from this list.session_01Rjy9MeetSfq34PKn81CRiN, and this one), with the same take order. Every claim is followed by a full-thread re-read. The earlier claim wins, and the loser yields.packages/spec/liveness/state-counts.mdandliveness/README.mdare the new hot files. Every ENFORCE/RETIRE PR regenerates the counts, so each landing makes the othersdirty, and a dirty PR runs no CI. Expect one merge-and-regenerate round per landing (gen:liveness-counts).packages/spec/src/migrations/registry.ts: ordinary concurrency. On any base merge re-run the generator; ⛔ never hand-resolve a hunk in a generated file.content/docs/references/**ismerge=os-regen: usebash scripts/pm/os-regen-merge.shplus a quoted-exactgit grepagainstorigin/main.Notes
post-stamped,label-writeand thefleet-writerelay pace themselves. Rawcurlwrites go throughwrite-pace --run./search/issuesis blocked by the proxy. Use REST list endpoints plus a local filter. MCPlist_issueslabelsis OR; RESTlabels=is AND.