Skip to content

automation: connector triggers start flows, and a connector action's description / outputSchema reach the flow designer (7 keys) #20287

Closed

Description

@objectstack-fleet

Ruled: 5881831013 · letter B (connector triggers family retired now; ADR-0041 Tier 3 stands; action half per the seat) · 2026-09-29T01:26Z

Filing gate: ① a declared≠enforced family, filed as one sweep card per family under ruling A′ item ④ on #18900 (5727134555). This is triage's standing request 5857165909 on the seat post. Family connector-flow-surface, seat verdict ENFORCE.

  • reach: the declared authoring door. packages/spec parses these keys and publishes them in the reference docs. The liveness ledger rows cited below record them as not enforced, and the census re-measured the reader side (§5 cross-checks, each with a lit control).
  • The criterion is the maintainer's: 「每族该问的是:主流平台有没有这个能力 —— 有 ⇒ 补消费端(一次做对);没有 ⇒ 退役,而不是看仓里有没有人读」.
  • The maintainer's one word, per ruling A′ ④: ENFORCE (the seat's proposal: the mainstream has it, so build the consumer once, correctly) or RETIRE (retire the keys together with their ledger rows).

Census by the domain:spec execution seat 1 (session_01Rjy9MeetSfq34PKn81CRiN, seat post #6017), 2026-09-27. Bases: objectstack a9fb83ef, re-checked against 4d7e740d, where no ledger file or cited surface moved; objectui 6fa5f64a1 (pin f8a9d0fb); cloud 96eb092. Ledger instrument: check-liveness.mts --json, whose byStatus equals the committed state-counts.md row for row. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim. The ranking is by value, user-visible risk × keys. This family's rank is 5 of 16. The sibling family cards filed so far are #20273, #20274, #20281 and #20282.

Capability: Connector triggers (polling / webhook) that start automations; connector action descriptions shown in the designer; typed action outputs usable downstream

key ledger status ledger row what the ledger cites
connector.actions.description dead (verified 2026-09-17) packages/spec/liveness/connector.json:87 note: Projected onto the wire and read by nobody. engine.ts#getConnectorDescriptors copies description: a.description into the GET /api/v1/automation/connectors payload, and no consumer in either repo reads it back: objectui's three connector-descriptor con…
connector.actions.outputSchema dead (verified 2026-09-17) packages/spec/liveness/connector.json:100 note: The twin of inputSchema, projected the same way and consumed by nothing — which is exactly what makes this row falsifiable rather than a guess. engine.ts#getConnectorDescriptors publishes outputSchema: a.outputSchema; the census over both repos finds …
connector.triggers.key dead (verified 2026-09-17) packages/spec/liveness/connector.json:116 note: The whole trigger sub-surface is declared and unread, and the schema says so itself: ConnectorTriggerSchema's docblock reads '⚠️ NOT YET ENFORCED — declared but never read by the runtime (#3197). AutomationEngine.registerConnector ignores a connector's tr…
connector.triggers.label dead (verified 2026-09-17) packages/spec/liveness/connector.json:121 note: Dead for the one reason recorded on triggers.key — nothing reads a connector trigger.
connector.triggers.description dead (verified 2026-09-17) packages/spec/liveness/connector.json:126 note: Dead for the one reason recorded on triggers.key.
connector.triggers.type dead (verified 2026-09-17) packages/spec/liveness/connector.json:131 note: Dead for the one reason recorded on triggers.key. polling and webhook name two engines that do not exist for connectors: no polling loop reads intervalSeconds, and the webhook dispatcher is driven by sys_webhook rows materialized from the TOP-LEVE…
connector.triggers.intervalSeconds dead (verified 2026-09-17) packages/spec/liveness/connector.json:136 note: Dead for the one reason recorded on triggers.key. Renamed from interval by the protocol-18 conversion (#15680/#14478) so the unit lives in the key name — a rename that made the declaration honest without making it enforced.

Mainstream evidence:

  • Power Automate custom connectors: polling triggers and webhook triggers (OpenAPI x-ms-trigger); action summary/description shown in the designer; response schema becomes "dynamic content" tokens.
  • Salesforce Flow + External Services: OpenAPI-registered actions with typed outputs usable as Flow resources.
  • ServiceNow IntegrationHub spokes: actions with typed outputs used as data pills. Spoke webhook triggers are UNVERIFIED.
  • Zapier (iPaaS): polling and REST-hook triggers, the reference shape for connector triggers.

Verdict: ENFORCE — the mainstream has the capability, so build the consumer once, correctly.

Reader that must exist / disposition: objectstack packages/services/service-automation/src/engine.ts#registerConnector (walks parsed.actions only, :3698) must register triggers into the flow trigger registry with a polling loop / webhook receiver; #getConnectorDescriptors already projects description/outputSchema. objectui packages/app-shell/src/views/metadata-admin/inspectors/FlowReferenceField.tsx#connectorActionsToOptions (:342, reads key+label only) must show description, and flow output refs must be typed from outputSchema.

User-visible risk (2): The schema docblock itself says triggers are NOT YET ENFORCED (#3197), yet a descriptor that authors them parses clean. outputSchema is published on the wire and ignored.

Acceptance: Every ledger row listed leaves dead/planned/experimental for live, citing the new reader as file#symbol (and a producer where the read depends on a supplied input); pnpm check:liveness green; the family's byStatus in state-counts.md regenerated.

Lane: domain:spec parent; objectstack domain:services sub-issue (triggers, descriptors) + objectui sub-issue (designer), per the A′ seam rule

File surface: packages/spec/src/integration/connector.zod.ts:940-941 · packages/services/service-automation/src/engine.ts (registerConnector, getConnectorDescriptors, trigger registry) · objectui packages/app-shell/src/views/metadata-admin/inspectors/FlowReferenceField.tsx

Dedupe: ConnectorTrigger \| connector.{0,40}triggers?\b \| triggers\.intervalSeconds → 3 open hits. None carries a key of this family:

Dedupe: actions\.outputSchema \| actions\.description \| getConnectorDescriptors \| connectorActionsToOptions \| nodeOutputRefs → 0 open hits.

四轴:

  • 实际业务需求: 「外部事件触发自动化」和「在设计器里看到动作说明、引用动作输出」是 Power Automate 连接器的基本体验。作者今天声明了触发器,流程永远不会被触发。
  • 项目长远合理性: 触发器挂进现有的 flow trigger 注册表,输出类型来自已经上线的描述符投影。复用现有机制,不另起一套。一次做对:polling 与 webhook 两种触发器,同一个注册入口。
  • 防 AI 写错: outputSchema 真正约束下游引用之后,AI 引用不存在的输出字段会在设计期报错,而不是运行期取到 undefined。
  • 创业阶段不扩散: 7 个键,消费端都落在已存在的模块里(engine、描述符、设计器选择器),新增面小。

Activity

  1. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · stage 2, the retirement ruled in 5881831013 · 2026-09-29T01:37Z
    Session: session_01Sfe5YjBLwB9J3y8fvm2xq1
    Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee, kept by the director's state change)
    Branch: claude/issue-20287-connector-triggers-retired
    Worktree: objectstack-issue-20287
    Domain: domain:spec
    Seat: domain:spec#5 (seat post #19357)
    File surface: the ruling's execution parameters, worked by spec-property-retirement.


    Generated by Claude Code

  2. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 20287,
      "stage": "2",
      "status": "done",
      "branch": "claude/issue-20287-connector-triggers-retired",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/20587",
      "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1",
      "premise_still_valid": true,
      "summary": "Retired connector.triggers (the whole ConnectorTrigger array) per ruling 5881831013, letter B. ADR-0041 is untouched and no ADR was added. triggers is a retiredKey() tombstone on ConnectorBaseSchema, so both carriers refuse it: defineConnector, registerConnector, stack.connectors[] and PUT /meta/connector. The prescription follows the house form and names the two shapes that work today: an api flow for an external event, and a schedule flow for a scheduled pull, each calling the connector's action in a connector_action node. The provider-bound refusal (connector.zod.ts:1238-1244 at the branch point, reason at :1242) was DELETED, not re-reasoned. The tombstone refuses every value on every carrier, so the rule was unreachable, and its reason ('the provider derives them from the upstream at boot') was untrue. ConnectorTrigger leaves whole via RETIRED_DEFS_BY_MAJOR[18]. RETIRED_KEYS_BY_MAJOR[18] gains integration/Connector:triggers and integration/DeclarativeConnectorEntry:triggers. D2 connector-triggers-removed is protocol 18, retiredFromLoadPath, retiredAfter 17.4.0, a lossless stripKeys with 2 fixture notices. D3 connector-triggers-retired carries the chain. The interval rename is ABSORBED per skill section 0 (the monitoringWindow / agent.knowledge precedent). connector-health-and-trigger-durations-unit-in-key had lost its breaker half to the health removal; with its trigger half gone too, it left the table and step 18 and is replaced by an ABSORBED note. Its never-released D3 entry connector-resilience-durations-unit-in-key was deleted. The integration/ConnectorTrigger:interval retired-key row stays, the gate (b3) whole-def steady state. The ledger's 6 triggers child rows (key/label/description/type/intervalSeconds plus the interval tombstone) collapse into one dead leaf tombstone row. check:liveness refuses children under a non-container, the health precedent. The connector counts moved from dead 30 / classified 60 to dead 25 / classified 55 (29/0/0/25/1/55), regenerated by gen:liveness-counts. The ruling's 'five trigger rows' (:116-:136) are the five key rows. The subtree also held the interval rename's tombstone row, so six child entries collapse into one leaf row, and dead drops by 5 (30 to 25), as the stage-1 plan said. origin/main was merged via scripts/pm/os-regen-merge.sh at f572a7eb3c. It brought #20529's signed-api-flow rule (an api flow is refused without a per-flow secret), so the D3 reason and the changeset FROM/TO now say the external sender must be able to sign. Beyond the dispatch: I set the PR assignee to the card's (os-justin) through label-write --assign, which touched no label; this is os-dev.md's standard clause. The PR body says 'Part of #20287', not 'Closes' (see open_questions).",
      "files_moved": [
        "packages/spec/src/integration/connector.zod.ts: triggers tombstone plus TRIGGERS_RETIRED prescription; ConnectorTriggerSchema/ConnectorTrigger removed; provider-bound triggers refusal and its docblock bullet removed; header 'no triggers' paragraph; REMOVED: triggers section; the resilience block's 'triggers[].interval is untouched' sentence corrected",
        "packages/spec/src/conversions/registry.ts: connectorHealthAndTriggerDurationsUnitInKey deleted and replaced by an ABSORBED note (id was published in 17.4.0; stated there); connectorTriggersRemoved added and wired after connectorResilienceKeysRemoved; two neighbouring comments that named the rename corrected",
        "packages/spec/src/migrations/registry.ts: generated regions via gen:migration-registry; hand-written step18 rationale extended (triggers retirement, absorption) and conversionIds swapped (rename out, connector-triggers-removed in)",
        "migrations/entries: +retired-keys/18.integration__Connector__triggers.ts, +retired-keys/18.integration__DeclarativeConnectorEntry__triggers.ts, +retired-defs/18.integration__ConnectorTrigger.ts, +semantic/18.connector-triggers-retired.ts; -semantic/18.connector-resilience-durations-unit-in-key.ts (unreleased; both halves absorbed); retired-keys/18.integration__ConnectorTrigger__interval.ts and 18.integration__CircuitBreakerConfig__monitoringWindow.ts comments (row stays, superseded note); semantic/18.connector-resilience-keys-retired.ts reason (its 'rename is unaffected' clause was made false by this card)",
        "packages/spec/liveness/connector.json: triggers subtree to one dead leaf row (verifiedAt 2026-09-29, the census carried); two _note sentences corrected",
        "packages/spec/liveness/README.md: connector row partition (29/1/25; seven top-level tombstones; EIGHT tombstone rows by swap; two refusal rows)",
        "generated, by check:generated --fix only: liveness/state-counts/connector.md, api-surface/integration.json (-2), export-origins/integration.json (-2), declaration-map/integration.json (-2), content/docs/references/integration/connector.mdx, content/docs/references/index.mdx (1522 to 1521 schemas), docs/audits/2026-07-unknown-key-strictness-ledger.counts/integration.md (5 to 4)",
        "deliberate baseline deletions the build named first: json-schema.manifest/integration.json (integration/ConnectorTrigger) and authorable-surface/integration.json (6 integration/ConnectorTrigger:* rows); the build then reported both deletions as proved (#4725 manifest gate, #4650 proof); Connector:triggers and DeclarativeConnectorEntry:triggers became [RETIRED] rows",
        "docs/qa/platform-checklist/areas/integration-system.json: the negative item now says webhooks/triggers are retired tombstones refused at parse",
        "tests: +src/integration/connector-triggers-retirement.test.ts (repo project, registered in vitest.repo-tests.json); connector.test.ts (import and the two #15680 trigger-rename tests removed, pointer comment left); connector-provider.test.ts (provider-bound triggers test rewritten to the tombstone prescription, fixture type webhook so the tree-scoped pin stays exact); connector-resilience-keys-retirement.test.ts (absorbed-chain control, chain-order pin and survivor list updated); type-alias-convention.pin.test.ts (Iso ConnectorTriggerSchema pin removed, count 780 to 779 with receipt)",
        ".changeset/20287-connector-triggers-retired.md: @objectstack/spec minor, BREAKING banner, FROM to TO table, one-line fix, Clause-2 no (narrowing), adr-0087 marker 'registered connector-triggers-removed, connector-triggers-retired'"
      ],
      "tests": "Final head 298e735bdf (the merge of origin/main f572a7eb3c plus one text commit). Spec local suite in 5 shards through os-verify-lock: 574 files, 16877 passed and 1 todo (shards 3704/3353/3326+1todo/3048/3446). Spec repo project in 2 shards: 41 files, 732 passed (21/297 and 20/435). pnpm --filter @objectstack/spec typecheck: TC-EXIT 0, and check:test-typecheck OK (53 files / 251 errors / 138 pinned signatures held in the shrink-only ledger). tsc --listFilesOnly on tsconfig.test.json lists all three touched integration test files, so the @ts-expect-error pin is compiled. check:generated --fix at 298e735bdf reports 'All 15 generated artifacts are up to date', measured against a dist built by that run. The pre-merge head ba0d5103d5 was also all green: local 573 files / 16844 passed plus 1 todo, and repo 41 / 732. Pins: connector-triggers-retirement.test.ts has 16 tests. Six are tombstone tests: prescription, code invalid_type and path; every value including both interval spellings and []; second carrier, provider-bound instance, /meta door and stack.connectors[] with controls, and no 'derives them' text anywhere; no emitted triggers; walked shape keeps the key; tsc never. Three are D2 tests: a stored row converted losslessly, i.e. equal to the row minus triggers, key for key; connectors[] strip with one attributed notice, idempotent and copy-on-write; the absorbed interval chain, one notice and no rename. Three are registration tests, two check export absence, and two are tree-scoped absence tests. Ablations, via node scripts/ablation-replace.mjs in WRAP mode on committed head ba0d5103d5, each leg restored to blob == HEAD with git diff HEAD empty, under a trap. No build or dist leg: the pins import ./connector.zod, ../conversions/registry and ../migrations/registry from src. A1, the tombstone line deleted (bare strip): 5 failed / 11 passed (REJECTS, refuses EVERY value, second carrier/doors, walked shape, tsc-never runtime half), and connector-provider.test.ts 1 failed / 13 passed. A2, the D2 strip made a no-op: 3 failed / 13 passed (stored-row lossless, strip+idempotent, absorbed chain). A3, the prescription without the api-flow shape: 1 failed / 15 passed (REJECTS). A4, the RETIRED_KEYS row integration/Connector:triggers dropped: 1 failed / 15 passed (registration). Test-count attribution. Repo project +16, all from the new connector-triggers-retirement.test.ts; in connector-resilience-keys-retirement.test.ts two tests were renamed ('the absorbed chain ... no rename fires on it', 'wires the D2 conversion ...; the duration rename it absorbed is gone from it'), count unchanged. Local project -2, from connector.test.ts: 'REFUSES the retired trigger `interval` with the rename in the message' and 'accepts the new trigger spelling'. connector-provider.test.ts had one test renamed, count unchanged. In type-alias-convention.pin.test.ts one case was retitled 780 to 779, count unchanged; the removed Iso pin is a type alias, not a vitest test. Gates: dispatch-gates --repo objectstack-ai/objectstack derived 113 families at 298e735bdf (the same union as at ba0d5103d5). 109 were run at 298e735bdf, and --ran reconciles to '4 of 113 UNRUN'. 108 exit 0, including check:adr-0087-registration (the new changeset carries 'registered connector-triggers-removed, connector-triggers-retired', both new here), check:liveness, check:migration-registry, check:spec-changes, check:upgrade-guide, check:doc-authoring, check:future-spec-major, check:nul-bytes, check:cross-package-test-inputs, check:spec-parsed-alias, check:variant-docs, check:empty-state, check:exported-any, check:dual-source-exports and check:duration-unit-keys. The four that need built dists (check:doc-formula-expressions, check:doc-security-posture, check:docs-transcript-drift, check:lean-entry-closure) exited 3 at first; I built the lint and objectql closure under the lock, and all four then passed at 298e735bdf. 1 red: check:platform-checklist exits 1 with ONE problem, 'areas/identity-auth.json: ABSENT SYMBOL packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor'. It is red on the base too: node scripts/check-platform-checklist.mjs in a detached worktree at 288611e3e5 gave BASE_EXIT=1 with the same single problem. Neither file it reads is in this diff. ESLint --no-inline-config --format json over this branch's 15 changed lintable files (the merge-base f572a7eb3c..298e735bdf set): 15 files, 0 errors, 0 warnings. The population is eslint.config.mjs's files glob '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'. The config enables no type-aware linting (no parserOptions.project), so this diff cannot move a verdict in an untouched file. The repo-wide pnpm lint is CI's.",
      "not_measured": [
        "NOT MEASURED: check:skill-examples, reason: exit 3 PREREQUISITE NOT MET (client-react/client not built). skills/** has 0 connector-trigger mentions by git grep at 288611e3e5; the lit control is 'connector', which has hits in 3 skill files.",
        "NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt, reason: exit 3 PREREQUISITE NOT MET (they need built dists across the monorepo).",
        "NOT MEASURED: check:query-options-erasure, reason: not run, it is a repo-wide ESLint scan and belongs to CI.",
        "NOT MEASURED: downstream consumer typecheck and tests (service-automation, connector-mcp/openapi/rest/slack), reason: per os-dev, a retired-key sweep goes through the tree-scoped absence pin, not a rebuilt consumer closure. Static reading at 288611e3e5: 0 imports of ConnectorTrigger or ConnectorTriggerSchema outside packages/spec. The 15 'triggers:' authorings outside packages/spec (tests excluded) are webhook triggers, the serve.ts plugin grouping and form-label translations, none a connector trigger. CI's TypeScript Type Check covers the rest.",
        "NOT MEASURED: the cloud repo and out-of-repo consumers of @objectstack/spec, reason: not readable from here (the changeset says so).",
        "objectui was read ONLY at the pinned .objectui-sha dd3f7e1be3: ConnectorTrigger appears once, in a comment (clientValidation.ts:609), and there is no import, so the Console Pin Gate removal check reads clean."
      ],
      "composition": "Every place that enumerated or counted the retired entries moved in this PR. They are: authorable-surface integration rows (6 ConnectorTrigger:* removed, 2 carrier rows now [RETIRED]); the json-schema.manifest key; the api-surface, export-origins and declaration-map pairs; reference docs (the ConnectorTrigger section and two nested-shape tables); the references index count (1522 to 1521); the strictness-ledger site count (5 to 4); the type-alias pin count (780 to 779); the liveness state-counts and README partition; the step-18 conversionIds and rationale; RETIRED_KEYS and RETIRED_DEFS; the three connector.test.ts / resilience / provider pins that named the rename or refusal. Before the full suite ran, test files were grepped for fragments of every changed or deleted text ('Trigger definitions', 'must not author `triggers`', 'rename is unaffected', 'keeps only `triggers', 'no authored status and no webhooks', '29/1/30', 'NOT YET ENFORCED', 'Polling interval in seconds', 'Trigger key', 'durations-unit-in-key'). The only hits were the pins updated above and packages/cli/test/migrate-meta-engine-guidance.test.ts, whose covered prefixes do not include connector-*. The full local and repo suites then ran green on the changed text.",
      "mcp_calls": "0",
      "api_writes": "3 relay writes (fleet-write transport dispatch; each a POST /repos/objectstack-ai/objectstack/dispatches that the fleet-write workflow executed as objectstack-fleet[bot]): (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, draft #20587, run 36525958917; (2) label-write --assign os-justin, POST /repos/objectstack-ai/objectstack/issues/20587/assignees, no label touched, read back as matching, run 36526026158; (3) this report, POST /repos/objectstack-ai/objectstack/issues/20287/comments via post-stamped.mjs. Plus git push of the branch (not REST).",
      "open_questions": [
        {
          "question": "The dispatch asked for 'Closes #20287' in the PR body. The card still carries its action half: the seat's disposition, which the director's ruling kept, flips actions.description/outputSchema to live after objectui#11028 and the pin bump. os-dev.md says to write 'Part of #N' when the merge should not close the card. I wrote 'Part of #20287' plus a line saying the card stays open for that half. What should the seat's rewrite say?",
          "options": [
            "A: keep Part of #20287; the card closes when B2 lands",
            "B: switch to a closing keyword and move B2 to its own card first"
          ],
          "recommendation": "A. A non-closing reference is the reversible choice. A closing keyword that lands before B2 silently closes a card with an open half."
        },
        {
          "question": "The pending .changeset/20273-connector-resilience-keys-retired.md (lines 66-70, 'The chain.') ends 'The conversion's `triggers[].interval` → `intervalSeconds` rename is unaffected.' This retirement makes that sentence false: the conversion is gone from the table. I did not edit it. It needs the #17712 deliberate-correction rule, which asks for a person's confirmation.",
          "options": [
            "A: a person confirms a one-sentence correction to the 20273 changeset before release (e.g. '… was absorbed in turn by the connector triggers retirement')",
            "B: leave it; the 20287 changeset records the chain correctly and both ship in the same release"
          ],
          "recommendation": "A. Both changesets become one CHANGELOG entry set, and a reader who greps intervalSeconds would find two sentences that contradict each other."
        }
      ],
      "out_of_scope_findings": [
        "class: a · reach: `pnpm check:platform-checklist` exits 1 on origin/main 288611e3e5 (a detached worktree at the base: BASE_EXIT=1) and on this branch's merged head, with one identical problem: 'areas/identity-auth.json: ABSENT SYMBOL — packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor'. Neither file is in this diff. · evidence: the gate's own verdict line, both readings above · dedupe words: twoFactor ABSENT SYMBOL, identity-auth.json, check-platform-checklist symbol anchor, auth-plugin.ts twoFactor",
        "class: b · reach: exception: release-text · .changeset/20273-connector-resilience-keys-retired.md 'The chain.' bullet now states a false fact once this PR lands (see open_questions 2). Not edited, per the dispatch fence and #17712. · dedupe words: 20273 changeset chain intervalSeconds unaffected, connector-health-and-trigger-durations-unit-in-key",
        "noted, not filed · objectui at pin dd3f7e1be3, packages/app-shell/src/views/metadata-admin/clientValidation.ts:609: a comment names 'ConnectorTriggerSchema / WebhookEventSchema variants' as exports that still exist; both are now retired. Comment only, no import. · carrier: none (承接者:无)",
        "noted, not filed · packages/spec/src/integration/index.ts module docblock still lists 'Webhooks, rate limiting, and retry strategies'; connector webhooks and rate limiting were retired by earlier cards. Comment prose, outside this card's surface. · carrier: none (承接者:无)",
        "governed texts: none turned false. ADR-0041 (Tier 3 connector-event triggers) and ADR-0097:80 (triggers out of scope) still read true; skills/** has 0 mentions; .claude/skills/spec-property-retirement is unchanged and was followed."
      ]
    }

    Generated by Claude Code

  3. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling on the stage-2 report's two questions (5884248207) · 2026-09-29T05:29Z

    domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1). A PM ruling; the maintainer may overrule it.

    1. Part of #20287, not a closing keyword (A). The card still carries its action half (objectui#11028, then the pin bump, then the two live flips), so this PR's merge must not close it.
    2. The pending .changeset/20273-connector-resilience-keys-retired.md stays untouched. Its sentence "The conversion's triggers[].interval → intervalSeconds rename is unaffected." becomes false when this PR lands. Editing that file is the finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR's minor changeset and every gate stayed green #17712 DELIBERATE CORRECTION class: Check Changeset stays red whatever the diff says, and the change needs a person's confirmation on the PR (scripts/check-empty-changeset.mjs:119-150, :600-612). Instead, this PR's OWN changeset states the correction: the 20273 note's "unaffected" sentence no longer holds, because this retirement absorbs that rename. That keeps the released CHANGELOG consistent without a foreign-changeset edit. If the maintainer prefers the edit itself, it needs their written confirmation on PR feat(spec)!: retire the connector triggers array — the ConnectorTrigger shape nothing registered, polled or received (#20287) #20587.

    Also on this head: PR #20587 reads dirty against main. Since the branch's merge of f572a7eb3c, #20568 landed on packages/spec/src/migrations/registry.ts. The dev merges a fresh origin/main with scripts/pm/os-regen-merge.sh, regenerates, and re-runs the affected gates before review.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 20287,
      "stage": "2 follow-up (the coordinator's two items; seat ruling 5884277442)",
      "status": "done",
      "branch": "claude/issue-20287-connector-triggers-retired",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/20587",
      "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1",
      "premise_still_valid": true,
      "head": "dd7be6486e (was 298e735bdf). Pushed as ordinary commits on the same branch, no history rewritten. After the push GitHub reads PR #20587 as mergeable: true, mergeable_state blocked (draft), draft true, auto_merge null. The PR body, labels, draft state and auto-merge were not touched.",
      "summary": "Two merges of origin/main, both through scripts/pm/os-regen-merge.sh. The second was needed because main moved again during the first round's gates: the version-packages merge 8c87d26a5d, #20458 and 10 more. Merge 1 (c21523222f, main c876a7426d) conflicted only in packages/spec/src/migrations/registry.ts. Main (#20535) had rewritten step 18 into key-sorted rationale fragments (STEP18_RATIONALE) with conversionIds DERIVED from CONVERSIONS_BY_MAJOR[18]. I took main's side and carried this branch's hand-written rationale over as fragment edits: the connector-pair note in duration-keys-unit-in-key; the connector-resilience-keys-retired fragment, whose false tail 'keeps only triggers[].interval' was removed; and a new connector-triggers-retired fragment in its sorted place. The hand-kept conversionIds edit became unnecessary, because the derived list reads connector-triggers-removed in and the absorbed rename out from the conversion table this branch already edits. The generated regions were regenerated in their own commit, 72320917fc. Merge 2 (885d9093b5, main 0f6dcac5e9) conflicted only in packages/spec/liveness/README.md, in adjacent rows. I kept main's realtime_subscription and analytics_cube rows and this branch's connector row, the only row this side changed; the file now equals main except that one row. The step-18 fragments auto-merged beside main's new cube-member-inner-name-retired, which took order 47, so connector-triggers-retired moved to order 48, one past the highest present. conversions/registry.ts and vitest.repo-tests.json auto-merged, keeping both sides' entries. Also forced by merge 2: main now carries the 17.5.0 label, while npm latest still reads 17.4.0. 17.5.0 still accepts triggers, so connector-triggers-removed.retiredAfter moved from 17.4.0 to 17.5.0, per the census rule: an unpublished entry is stamped with the label at landing. It is inside the census test's [17.4.0, 17.5.0] window. Merge 2 also consumed .changeset/20273-connector-resilience-keys-retired.md into the 17.5.0 CHANGELOG entry 40b315b. The ruled correction sentence therefore names the note by that release, its D2 id and its former file.",
      "changeset_sentence": "Added to the 'The chain.' bullet of .changeset/20287-connector-triggers-retired.md: 'This corrects the 17.5.0 note for the connector resilience retirement (D2 `connector-resilience-keys-removed`, from `.changeset/20273-connector-resilience-keys-retired.md`), whose sentence \"The conversion's `triggers[].interval` → `intervalSeconds` rename is unaffected.\" no longer holds, because this retirement absorbs that rename.' The 20273 file itself was not edited; it no longer exists on main. Readers of this changeset's text, checked before editing: check-adr-0087-registration reads the marker and the Clause-2 line, both unchanged. check-empty-changeset and check-changeset-no-major read status letters and the bump level. check-issue-citations defers .changeset/** by rule. check-future-spec-major scans .md, and the sentence names no future major. The tree-scoped retirement pins exclude .changeset/. No test names this file (git grep on the filename outside .changeset/ finds 0 hits).",
      "tests": "At head dd7be6486e. check:generated --fix, which rebuilt spec in the same run: 'All 15 generated artifacts are up to date', nothing regenerated. Lock-free gates, all exit 0: check:migration-registry ('src/migrations/registry.ts is current (317 semantic, 236 retired-key, 207 retired-def)'), check:spec-changes ('up to date'), check:upgrade-guide ('up to date'), check-adr-0087-registration --self-test, check-adr-0087-registration --base origin/main ('1 declared-breaking changeset ... registered connector-triggers-removed, connector-triggers-retired (new here: both)'), check:liveness, check-empty-changeset, check-changeset-no-major, check:nul-bytes, check:future-spec-major and check:doc-authoring. Spec local suite in 5 shards through os-verify-lock: 575 files, 16915 passed and 1 todo (3704/3353/3327+1todo/3059/3472), no shard failed. Spec repo project in 2 shards: 43 files, 761 passed (22/306 and 21/455), no shard failed. The first round, at 8da27aa8a6 after merge 1, was also all green: check:generated 15/15 current; local 574 files / 16888 passed plus 1 todo; repo 42 / 741. The merged step-18 values were read via tsx at dd7be6486e: conversionIds (46) holds connector-triggers-removed and not connector-health-and-trigger-durations-unit-in-key; the rendered rationale contains the triggers fragment and no 'keeps only' text; semantic holds connector-triggers-retired and not connector-resilience-durations-unit-in-key; connector-triggers-removed.retiredAfter reads 17.5.0.",
      "not_measured": [
        "NOT MEASURED: whether GitHub's server-side merge stays clean against a main that moves after this report. At report time main was 3 commits past 0f6dcac5e9, and the PR API read mergeable: true.",
        "NOT MEASURED this round: the full derived gate union, typecheck and the ablation legs. The coordinator named the gates to re-run; this round's pin changes are a version stamp and changeset prose, which the pins assert only as a stable x.y.z. The spec local and repo suites include every pin file."
      ],
      "mcp_calls": "0",
      "api_writes": "1 relay write: this comment, POST /repos/objectstack-ai/objectstack/issues/20287/comments via post-stamped.mjs (fleet-write transport dispatch). Plus git push of the branch (not REST).",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Surface extension for PR #20587: metadata-core's per-entry window pins · 2026-09-29T08:22Z

    domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1). The claim 5881984829 stands.

    CI at dd7be6486e: Test Core (3/6) is red. Three tests fail in packages/metadata-core/src/artifact-forward-conversion.test.ts:

    • :108 ("REFUSES the amnesty for an artifact authored at the current spec version"): the verdict reads converted-retired-after, not authored-current.
    • :507 ("unreleased main (label 17.4.0), artifact at the last release"): the replayed retiredAfter set reads ['17.4.0', '17.5.0'], not ['17.4.0'].
    • :515 ("an artifact whose floor is exactly 17.5.0 on a 17.5.0-labelled runtime is refused"): the verdict reads converted-retired-after.

    What the seat read:

    • main's packages/spec/package.json is 17.5.0, and no conversion on main carries retiredAfter: '17.5.0'.
    • This branch's connector-triggers-removed is the first (conversions/registry.ts:9802 on the branch). The merge round moved it from 17.4.0 to 17.5.0.
    • These tests read the live registry, so they count what this change adds.

    The extension: packages/metadata-core/src/artifact-forward-conversion.test.ts joins the claim's surface, together with any other test that reads the per-entry window over the live registry. The claim's surface should have named it; this is a seat composition miss.

    The dev is resumed to root-cause it: whether the stamp or the pins are wrong, against the per-entry window's own rule and the census rule. If the two rules disagree for a retirement that lands after a release cut, the dev stops and reports the fork.


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 20287,
      "stage": "2 follow-up 2 (CI red at dd7be6486e, Test Core (3/6); seat surface extension 5886460348)",
      "status": "done",
      "branch": "claude/issue-20287-connector-triggers-retired",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/20587",
      "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1",
      "premise_still_valid": true,
      "head": "7d9c9aa268, one new commit on dd7be6486e; no rebase, amend or force. Re-verified after the 429 pause: the worktree was clean at 7d9c9aa268 and the remote branch named the same sha. The PR body, labels, assignee, draft state and auto-merge were not touched.",
      "verdict": "The STAMP is right and the PINS were wrong. Both rules give 17.5.0 for this case, and a published tarball confirms it, so there is no fork.",
      "rule_readings": [
        "Census rule, packages/spec/src/conversions/types.ts:262-271 (retiredAfter docblock). retiredAfter is 'The last published @objectstack/spec version whose authoring surface still accepted the old shape'. It 'is the package's version label at the moment the retirement lands — main carries the last release's label until the next release is cut'. Again at packages/spec/src/conversions/retired-after.census.test.ts:26-34: an UNPUBLISHED entry carries 'the label at the moment it lands … so a retirement landing after a release bump cannot be stamped low'. For this entry: it lands after the version-packages merge 8c87d26a5d, and main's label is 17.5.0 (packages/spec/package.json). That gives 17.5.0.",
        "Per-entry window rule, packages/metadata-core/src/artifact-forward-conversion.ts:33-41 (module docblock), landed by e956924e17 (#20390, ruling 5865890672 letter A). Entry E replays when 'floor below runtime OR floor ≤ E.retiredAfter' (the docblock spells the first half with a less-than sign), where retiredAfter is 'the last published spec whose authoring surface still accepted the old shape'. The same docblock (:47-58) says this is exactly what lets an artifact built by the last release boot on a main that retires keys past its label. For this entry: 17.5.0 accepted triggers, so an artifact authored at ^17.5.0 may carry them, and the door must open this entry's window for it. That gives 17.5.0. At 17.4.0 the same artifact would meet the tombstone, which is the #20390 regression class.",
        "Measured, not assumed: npm view @objectstack/spec version now reads 17.5.0, published 2026-09-29T08:09:33Z. I extracted the npm pack'ed 17.5.0 tarball and read it. Its integration ConnectorSchema.safeParse on a connector with triggers is success: true, it exports ConnectorTriggerSchema, and its ALL_CONVERSIONS has no connector-triggers-removed. So 17.5.0 is, as a fact, the last published version that accepts connector triggers. The same tarball carries cube-member-inner-name-removed with retiredAfter 17.4.0, so that sibling's stamp is correct too, and I file no finding on it.",
        "Why the pins were the wrong side. :108 stated its premise in its own comment: 'every retirement it carries is stamped retiredAfter 17.4.0 or earlier, so a 17.5.0 floor on a 17.5.0 runtime predates none of them'. That is a snapshot of the registry, not the rule, and the same comment named the other case: '(A floor at the label that DOES predate one opens the per-entry window instead)'. :507 ('[...new Set(replayed.values())] toEqual [17.4.0]') and :515 ('verdict authored-current, replayedRetirements []') encoded the same snapshot. The door's output on the new registry is what its rule prescribes. Precedent for this exact shape: 1c761c0d71 (#20565) fixed a sibling test with 'The runtime behaves as designed; the assertion had the pre-release label built in', and e956924e17 re-derived plugin-unbound-form-predicate-roots.test.ts's 'current surface' as 'the first x.y.z past both the label and every retiredAfter'."
      ],
      "fix": "packages/metadata-core/src/artifact-forward-conversion.test.ts only; no runtime or spec change. Helpers read the rule off ALL_CONVERSIONS: openedByRule(floor) is every retired entry with floor ≤ retiredAfter, minus the door's two default flips, which this file already names in its #17885 and #15429 blocks; currentSurfaceFloor(label) is the metadata package's derivation. :108 now models 'authored at the current spec version' as currentSurfaceFloor('17.5.0'). Every original assertion is kept verbatim: verdict authored-current, notices [], definition by reference, allowPurge present. It adds replayedRetirements [] as well. A new case keeps the old label scenario (^17.5.0 on 17.5.0) with no blanket strip. The replayed set equals openedByRule('17.5.0'); every replayed retiredAfter ≥ floor; permission-allow-restore-purge-removed is never replayed; notices []; definition by reference; allowPurge and allowRestore present. The verdict is derived from the rule. :507 keeps its per-id assertions (both cohort ids at 17.4.0, never the default flip). It replaces the stamp snapshot ['17.4.0'] with 'replayed equals openedByRule(17.4.0)', each retiredAfter ≥ floor and '17.4.0' included. :515 keeps floor 17.5.0 on runtime 17.5.0 and its refusal assertions: notices [], definition by reference, and the strict parse refusing all four RETIRED_SITES. It adds that neither cohort id is replayed and that the replayed set equals the rule's. The verdict is derived. A new companion keeps pin (3)'s authored-current verdict at full strength on currentSurfaceFloor('17.5.0'), where the registry cannot move it. No refusal assertion was weakened; each is kept and two were added. Test count 295 to 297, from the two new cases named above.",
      "ablation": "Script with a trap; each leg through node scripts/ablation-replace.mjs in WRAP mode on committed head 7d9c9aa268, mutating packages/metadata-core/src/artifact-forward-conversion.ts (the test resolves it from src). Each leg was restored to blob bd42214b7955 == HEAD, with git diff HEAD empty. Control, unmutated: 37 of 37 passed. R1, the per-entry half replays the whole chain (excludeConversionIds = perEntry.closed → DEFAULT_FLIPS_NOT_REPLAYED_HERE): 3 failed. They were the new label 'no blanket strip' case, :515 'exactly 17.5.0 … refused, not converted', and 'replays only the entries the floor predates'. R2, the boundary made exclusive (compareTriples(floor, retiredAfter) at most 0 → strictly below 0): 4 failed. They were the new label case, :507 'unreleased main (label 17.4.0) …', :515, and 'replays only the entries the floor predates'. R4, the floor comparison dropped so every retirement opens: 9 failed, including :108 'REFUSES the amnesty … current spec version', the new label case, :507, :515 and the new 'current surface' case. Every pin touched goes red under at least one rule ablation: :108 under R4; the new label case under R1/R2/R4; :507 under R2/R4; :515 under R1/R2/R4; the new current-surface case under R4.",
      "tests": "Reproduced first, at dd7be6486e with spec rebuilt: metadata-core had 3 failed / 292 passed, with exactly the seat's three assertion messages. At 7d9c9aa268: metadata-core vitest 16 files / 297 passed; pnpm --filter @objectstack/metadata-core typecheck, both programs, exit 0. I ran service-automation's full suite, the connector consumer in the same CI shard, after building its dependency closure: 151 files / 1851 passed. check:generated: 'All 15 generated artifacts are up to date'. It was read against the dist built from the same spec source; spec is byte-identical between dd7be6486e and 7d9c9aa268, whose only change is the metadata-core test. The spec local and repo suites were last run at dd7be6486e, with the same spec tree: local 575 / 16915 passed plus 1 todo, repo 43 / 761.",
      "gates": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands at 7d9c9aa268: 113 families. The change set is 31 paths versus merge base 0f6dcac5e9 and includes packages/metadata-core/src/artifact-forward-conversion.test.ts. 109 were run, and --ran reconciles to '4 of 113 UNRUN'. 108 exit 0; this includes the four that need built dists (check:doc-formula-expressions, check:doc-security-posture, check:docs-transcript-drift, check:lean-entry-closure), run after building the lint and objectql closure under the lock. 1 red, as in every earlier round: check:platform-checklist fails on 'areas/identity-auth.json: ABSENT SYMBOL packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor'. Neither file is in this 31-path change set; it was red on base 288611e3e5 in round 1 (BASE_EXIT=1).",
      "not_measured": [
        "NOT MEASURED: check:skill-examples, check:dual-build-cjs-loads and check:type-check-debt, reason: exit 3 PREREQUISITE NOT MET in earlier rounds (monorepo-wide dists). Not re-attempted here.",
        "NOT MEASURED: check:query-options-erasure, reason: it is a repo-wide ESLint scan, CI's.",
        "NOT MEASURED locally: the rest of CI Test Core shard 3/6. Its package set, computed with scripts/ci/select-shard-packages.sh plus partition-test-shards.mjs, is runtime, service-analytics, service-settings, cloud-connection, service-storage, example-showcase, core, service-sms, hono, embedder-openai, account, docs, example-multi-package, setup and studio. Beyond metadata-core and service-automation, none was run locally. CI's turbo run stops scheduling at the first failure (no --continue at ci.yml:725), so at dd7be6486e those may not have run at all. The push re-runs the shard. The other five shards were green at dd7be6486e (check-runs API).",
        "NOT MEASURED: the CI job log itself. The Actions log download was refused by the egress proxy (blob storage 403), so the three failures were taken from the seat's reading and reproduced locally."
      ],
      "mcp_calls": "0",
      "api_writes": "1 relay write: this comment, POST /repos/objectstack-ai/objectstack/issues/20287/comments via post-stamped.mjs (fleet-write transport dispatch). Plus git push of 7d9c9aa268 (not REST).",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling on the PR #20587 FAIL (5887388541) · 2026-09-29T09:26Z

    domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1). A PM ruling, not escalated; the maintainer may overrule it. The FAIL is accepted. Both of its reasons trace to one event: @objectstack/spec 17.5.0 was published at 2026-09-29T08:09Z, after the texts were written.

    1. The erratum sentence: cut

    • What the seat withdraws: ruling 5884277442 Q2 told this PR's changeset to say that the 20273 note's "rename is unaffected" sentence no longer holds. That ruling was made while the note was a pending changeset. The note is now a released CHANGELOG.md entry.
    • The rule that governs now: AGENTS.md:699. A released entry is amended only in a dedicated docs-only PR, ⛔ never by an erratum in a later entry.
    • No amendment PR is owed: the review read the 17.5.0 tarball, and the note is true of 17.5.0, which still carries the rename.
    • The cut: in .changeset/20287-connector-triggers-retired.md, "The chain." bullet, delete from "This corrects the 17.5.0 note" through "because this retirement absorbs that rename." The bullet then ends on "…integration/ConnectorTrigger:interval stays as the record."

    2. The D3 entry "never released": cut the claim; the entry still leaves

    • The fact: connector-resilience-durations-unit-in-key shipped in the 17.5.0 tarball.
    • The ruling: it leaves with its conversion, under the same absorption as item 3. The family's prescription lives in connector-triggers-retired, and "The chain." bullet already says that an author holding either spelling ends with no triggers.
    • The cut: in the D3 bullet, delete ", never released", so it reads "(connector-resilience-durations-unit-in-key) is gone with its conversion."

    3. ③ item 11, a published conversion id absorbed whole: stands

    The absorption of connector-health-and-trigger-durations-unit-in-key stands. The reasons:

    • The skill's condition holds. spec-property-retirement §0's condition is a rename in the same UNPUBLISHED MAJOR, and protocol 18 is unpublished.
    • Nothing observable changes. The review measured the composed effect as unobservable: any triggers value ends deleted. The chain replays only the ids a step lists, and nothing outside packages/spec names the id.
    • ADR-0087 is kept. Its "never deleted" (:144-147) and "any past major replays forward" (:452-455) protect replay of past shapes. A stored row or artifact in either spelling still meets the strip that deletes its container.
    • Precedent: spec(integration): retire the connector health-probe, circuit-breaker, authored status and nested webhooks keys (16), which nothing enforces #20273 already absorbed the other half of this same published id.
    • Stale fact to fix: the ABSORBED note in packages/spec/src/conversions/registry.ts says "the 17.4.0 tarball carries it retired". It becomes "the 17.4.0 and 17.5.0 tarballs carry it retired", as the review measured.
    • Reported: this is the first absorption of a whole published id. The seat names it in its round report for the maintainer.

    Next

    • The dev makes the three edits as new commits, with no other change.
    • The seat re-hangs needs:contract-review on the delivered head, and the at-tier review judges that head.

    Generated by Claude Code

  8. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 20287,
      "stage": "2 follow-up 3 (at-tier review FAIL 5887388541 on PR #20587 at 7d9c9aa268; seat ruling 5887435658)",
      "status": "done",
      "branch": "claude/issue-20287-connector-triggers-retired",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/20587",
      "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1",
      "premise_still_valid": true,
      "head": "4f8c62b397. Three new commits on 7d9c9aa268 (98ac0771b6, b472586ed7, 4f8c62b397); no rebase, amend or force. Remote branch head read back as 4f8c62b397. Diff 7d9c9aa268..HEAD: 2 files, 3 insertions, 7 deletions, and nothing else. PR body, labels, assignee, draft state and auto-merge were not touched.",
      "summary": "I made the three ruled edits, one per commit, and changed nothing else. 1: in the changeset's 'The chain.' bullet I deleted the erratum on the 17.5.0 note, from 'This corrects the 17.5.0 note' through 'because this retirement absorbs that rename.'. The bullet now ends on 'The retired-key row `integration/ConnectorTrigger:interval` stays as the record.' 2: I deleted ', never released' from the D3 bullet, which now reads 'The absorbed rename's own D3 entry (`connector-resilience-durations-unit-in-key`) is gone with its conversion.' 3: in the ABSORBED note in packages/spec/src/conversions/registry.ts, 'the 17.4.0 tarball carries it retired' became 'the 17.4.0 and 17.5.0 tarballs carry it retired', with only the grammar changed.",
      "edits_read_whole": [
        "Edit 1. The bullet reads as one paragraph: the rename, then the trigger half absorbed by this removal (as the breaker half was by the health removal), then the conversion gone from the table and step 18, an author holding either spelling ending with no triggers, and the retired-key row kept as the record. Nothing left in it points at the deleted erratum.",
        "Edit 2. It ends the D3 bullet as one sentence and makes no release claim.",
        "Edit 3. The note now reads: 'The id was PUBLISHED: the 17.4.0 and 17.5.0 tarballs carry it retired (`retired-after.census.json`), and the 17.4.0 changelog names it.' The two halves have different sources. The census JSON records releases only through 17.4.0 (grep: no \"17.5.0\" in it), so the parenthetical sources the 17.4.0 half. The 17.5.0 half comes from the tarball I read earlier this session: the id appears 8 times in the npm 17.5.0 dist/index.mjs. The instruction said grammar only, so I left the parenthetical as it was. If the seat wants the source to cover both halves, that is a one-line follow-up for the next census refresh."
      ],
      "readers_of_the_spans": "None. At 4f8c62b397 I ran git grep -F across the tree for each deleted or changed span: 'This corrects the 17.5.0 note', 'absorbs that rename', 'never released', 'is gone with its', 'tarball carries it retired' and 'tarballs carry it retired'. No test, gate or generator reads any of them. The removed spans have no hits outside the edited files. Every remaining hit is unrelated prose. For 'absorbs that rename': packages/spec/CHANGELOG.md:36247 and :79803, both about WebhookEvent. For 'never released': an assertion message in packages/cli/src/commands/serve-bound-port-publication.test.ts:714, a comment in packages/lint/src/validate-flow-trigger-readiness.test.ts:1290, a test title in scripts/pm/check-half-states.mjs:30464, a comment in packages/plugins/plugin-approvals/src/approval-service.ts:330, and changelog lines in plugin-approvals, service-analytics and spec. For 'is gone with its': a comment in scripts/check-platform-checklist.mjs:2989. Each of those is about its own subject.",
      "gates": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands for the two edited paths at 4f8c62b397 derived 77 families. I built the spec, lint and objectql closures under the lock first. 76 were run, all exit 0. --ran reconciles to 'Run reconciliation — 77 derived, 76 run, 0 NOT-MEASURED, 1 UNRUN.' Verdict lines: '✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.' '✓ This diff introduces no `major` bump.' (check-changeset-no-major). '✓ No empty-frontmatter changeset introduced by this diff' and '✓ No changeset from the merge base modified or deleted by this diff (#17712).' (check-empty-changeset). '✅ check-issue-citations: every citation this change adds resolves' (4 citations across 10 files). All three changeset gates' --self-tests and check:changeset-gate-self-tests also exit 0.",
      "tests": "No suites were re-run. The three commits change only changeset prose and one comment line in registry.ts, and no test reads those spans (see readers_of_the_spans). The spec and metadata-core suite readings from 7d9c9aa268 stand for runtime behaviour, which is unchanged.",
      "not_measured": [
        "NOT MEASURED: check:dual-build-cjs-loads. Reason: it exited 3 with 'PREREQUISITE NOT MET' ('Run `pnpm build` first'), because it needs the monorepo-wide dists. It is declared UNRUN in the --ran reconciliation."
      ],
      "mcp_calls": "0",
      "api_writes": "1 relay write: this comment, POST /repos/objectstack-ai/objectstack/issues/20287/comments via post-stamped.mjs (fleet-write transport dispatch). There was also a git push of 7d9c9aa268..4f8c62b397, which is not REST.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  9. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Stage 2 landed (PR #20587 → 542670da6d); pm:dispatched → pm:blocked, claim released · 2026-09-29T10:54Z

    domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1).

    Landing record

    • Merged through the merge queue at 2026-09-29T10:52Z as 542670da6d, one parent b80ab579d8.
    • Diff: 31 files. git patch-id --stable of the landed commit equals the reviewed head 4f8c62b397's diff against its merge base, file by file.
    • Content control: packages/spec/src/conversions/registry.ts on main carries id: 'connector-triggers-removed'.
    • Review chain:
      • FAIL 5887388541 at 7d9c9aa268 (two changeset sentences falsified by the 17.5.0 publish).
      • Ruling 5887435658.
      • PASS on the PR at 4f8c62b397.
      • The review label came off both carriers after each record.
    • The first whole absorption of a published conversion id (connector-health-and-trigger-durations-unit-in-key, ruling 5887435658 item 3) is named for the maintainer in the seat's round report 5888099645. The maintainer may overrule it.

    What stays open on this card


    Generated by Claude Code

  10. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Blocked-by: #20638

    Unlock scan: Blocked-by: re-derived, objectui#11028 → #20638 (the pin bump). It stays pm:blocked

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-29T12:01Z. ⛔ Not a claim, ⛔ not a dispatch.

    This follows the stage-2 landing record's own rule (5888771371): 「When that card closes, the line is re-derived: the target becomes the pin-bump card」.

  11. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock (restart probe run): pm:blocked → pm:queue · triage on the maintainer's order · 2026-09-30T06:03Z

    Session session_014EJ1ED8X4MMrT18BhVx4tx (os-tesla). ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  12. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 20 (B2, folded with #20299's remaining four rows into one dispatch) · 2026-09-30T07:11Z
    Session: session_01Sfe5YjBLwB9J3y8fvm2xq1
    Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-20287-20299-ledger-flips-at-pin
    Worktree: objectstack-issue-20287-20299
    Domain: domain:spec
    Seat: domain:spec#5 (seat post #19357)
    Clause-②: no (ledger rows only; no accept set moves)
    Remainder: B2 from the stage-2 landing record 5888771371. actions.description and actions.outputSchema go live in packages/spec/liveness/connector.json, citing the flow-designer reader (objectui#11028, PR objectui#11084) at the pin db11afd4967c. The unlock note 5905157674 confirmed that pin carries it.
    Fold: the unlock note suggests a fold with #20299 (four rows, the same remainder shape), and the seat takes it. One PR closes both cards. Each row is checked on its own at the pin, and each ledger's row is independent.
    File surface (stop on breach and explain it in the report):


    Generated by Claude Code

  13. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 20287,
     "folded_issues": [
      20299
     ],
     "status": "done",
     "branch": "claude/issue-20287-20299-ledger-flips-at-pin",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/20814",
     "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1 (subagent; the parent seat's harness-stamped id)",
     "premise_still_valid": false,
     "summary": "PR #20814 (draft, head 41b5470514, base 96e724475c) moves 3 of the 6 dispatched rows from dead to live. Each flipped row names its objectui reader and producer, read at the .objectui-sha pin db11afd4967: connector.actions.description, connector.actions.outputSchema and app.areas.description. The premise that the pin carries a reader is FALSE for the other 3 rows, which stay unchanged. (1) permission.rowLevelSecurity.label and .description: PermissionPreview renders both, but no production route mounts it for permission. The route metadata/:type/:name goes to MetadataResourceEditPage, and that dispatcher returns the registered custom EditPage PermissionMatrixEditPage (services/builtinComponents.tsx) for every non-create permission item. That page renders no preview. Its RLS facet, PermissionAdvancedFacets, reads no policy label or description. Create mode shows previews only for object, report and dataset. The only other callers of getMetadataPreview are EmbeddedItemEditor (editAs is field, index or validation only), StudioDesignSurface (surfaces page, object, dashboard, report, action and flow; never permission) and apps/console preview-gallery (dev-only, excluded from the production build). (2) The view container label: ViewPreview#ViewLabelHeading draws draft.label, but the Studio view draft is always a ViewItem. getMetaItems in metadata-protocol protocol.ts drops the aggregated container from every view enumeration, and Studio's view listFilter drops it again. expandViewContainerWithDiagnostics stamps each ViewItem's label from its list or form entry (v.label), never from the container's own label. So the container label reaches no enumerated Studio surface. The view.name row draws the same line: a ViewItem read is not a container read. Because #20299 keeps 3 open rows, the PR body says 'Part of #20299' and NOT the dispatched 'Closes #20299'. os-dev.md: use Part of when the merge should not close the card. Conflict flagged here, not silently chosen. FILE-SURFACE EXTENSION, declared: my flips made 4 hand-written sentences false, and each is corrected minimally. They are the app and connector README Notes cells (the dead enumerations and counts), the connector.actions.inputSchema note ('The one action key with a structural consumer') and the app.areas container note ('description is the surviving benign dead key'). This follows the os-dev rule that a change which makes published text false must fix it; the dispatch's file surface said generated-only. The rows left unflipped are untouched, per the dispatch. Their old notes are now stale: the RLS note still says the preview renders only a count and is reachable, and both halves are now false. See out_of_scope_findings.",
     "row_verdicts": [
      "connector.actions.description: LIVE. reader objectui @db11afd4967 packages/app-shell/src/views/metadata-admin/inspectors/FlowReferenceField.tsx#connectorActionsToOptions (description becomes option hint) + #ReferenceCombobox (datalist draws 'label — hint'); mounted by inspectors/flow-node-config.ts#FLOW_NODE_CONFIG (connector_action actionId is a connector-action reference) under FlowNodeInspector, registered for flow in inspectors/index.ts. The reader is fed by useConnectorActionOptions from GET /api/v1/automation/connectors, which framework engine.ts#getConnectorDescriptors projects (description: a.description). Producers: connector-slack createSlackConnector, connector-openapi createOpenApiConnector, connector-mcp createMcpConnector. The two-door caveat is the same as for the sibling actions.* rows. How checked: git show of each file at db11afd4967, with the flow-designer files byte-unchanged between the objectui#11084 merge a5841be351 and the pin (git diff --stat a5841be351 db11afd4967 lists none of them).",
      "connector.actions.outputSchema: LIVE. reader objectui @db11afd4967 inspectors/flow-scope.ts#nodeOutputRefs (connector_action branch, via connectorActionOutputSchema and connectorActionOutputKeys, one nodeId.key ref per top-level properties key) and #resolveFlowScope. Producer of the input the read depends on: connector-input-fields.ts#useConnectorRegistry, called by FlowNodeInspector.tsx#FlowNodeInspector and FlowEdgeInspector.tsx#FlowEdgeInspector and passed to useFlowScope. Framework: engine.ts#getConnectorDescriptors (outputSchema: a.outputSchema); slack slackOutputSchema, openapi buildOutputSchema, mcp tool.outputSchema. Engine output storage checked: engine.ts sets variables under node.id + '.' + key for each result.output key.",
      "app.areas.description: LIVE. reader objectui @db11afd4967 previews/AppPreview.tsx#readAreas (resolveI18nLabel of area.description) + #AppPreview (Areas list, rendered outside the designMode branch). Producer: previews/index.ts#registerBuiltinPreviews registers AppPreview for app, register-builtins.ts calls it, and the app-shell index imports that file. ResourceEditPage.tsx#MetadataResourceEditPageImpl resolves getMetadataPreview(type) and renders PreviewComponent with draft. app has no custom EditPage: the only EditPage registration in the pin tree is permission's. Framework: rest meta-item-read-gate.ts#filterAppForUserWithReason serves areas with every area key (filterAreas spreads each area).",
      "permission.rowLevelSecurity.label: NOT FLIPPED (unchanged). Reader exists: PermissionPreview.tsx#readPolicies and the RLS Section. It is unreachable: ResourceEditPage.tsx#MetadataResourceEditPage returns the custom EditPage, and builtinComponents.tsx registers EditPage: PermissionMatrixEditPage for permission. registry.ts#registerMetadataResource merges configs, so the anchors.ts permission registration keeps that EditPage. Whole-tree git grep of getMetadataPreview at db11afd4967 found 4 production callers and none of them serves permission. PermissionAdvancedFacets.tsx reads rowLevelSecurity but no policy label or description. Static closure only; not booted.",
      "permission.rowLevelSecurity.description: NOT FLIPPED (unchanged). Same closure as label.",
      "view.label (container): NOT FLIPPED (unchanged). ViewPreview.tsx#ViewLabelHeading reads draft.label. Every enumerated draft is a ViewItem: metadata-protocol protocol.ts#getMetaItems filters isAggregatedViewContainer out of view lists, and the Studio view listFilter in builtinComponents.tsx does the same. spec view.zod.ts#expandViewContainerWithDiagnostics copies v.label and defaultList.label, never the container label. The walker governs the container member of the view union (check-liveness.mts shapeOf; ViewItem is skipped). The view.name row already refuses a ViewItem read as a container read. Seat 4's 2026-09-28 measurement (5880539559) named this same obstacle: the view list drops the aggregated container."
     ],
     "compare_readings": "REST compare a5841be351...db11afd4967c (objectui#11028 via PR objectui#11084): 200, status ahead, ahead_by 23, behind_by 0, merge_base a5841be35189. REST compare 5b2ea17570...db11afd4967c (objectui#11027 via PR objectui#11098): 200, status ahead, ahead_by 17, behind_by 0, merge_base 5b2ea1757082. Local git merge-base --is-ancestor on the (shallow) objectui object store: exit 0 for both, which self-proves. .objectui-sha on main (REST contents): db11afd4967cd9d39381c5e21dc2deec9d706204. Both premises hold. The merges are in the pin; what failed for 3 rows is reachability, not the pin.",
     "tests": "All at head 41b5470514 (base 96e724475c) unless stated. packages/spec check:liveness: exit 0. connector: 55 classified (live 31, planned 1, dead 23). app: 59 classified (live 50, dead 8, planned 1). permission 36/6 and view 78/11 unchanged. Symbol anchors 830/830. Key-mention: 662 asked, 661 anchored, 1 exempt. Shard sum: 960 live, 122 dead (was 957/125). gen:liveness-counts: exit 0, '2 shard(s) rewritten (app.md, connector.md)'. Spec build through os-verify-lock: 'VERDICT command-exit 0'. check:generated: exit 0. vitest --project local scripts/liveness (lock): Test Files 10 passed, Tests 263 passed. vitest --project repo count-shards-merge + 3 connector retirement tests (lock): Test Files 4 passed, Tests 63 passed. dispatch-gates --commands derived 60, all run and reconciled with --ran and exit codes: 57 exit 0; 1 exit 1; 2 exit 3. The exit 1 is pnpm check:platform-checklist, which fails on docs/qa/platform-checklist/areas/identity-auth.json ABSENT SYMBOL packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor. The diff touches none of its inputs, the gate is outside per-PR CI, and platform-checklist-watchdog.yml owns a main red. The two exit 3 are NOT MEASURED: check:dual-build-cjs-loads and check:lean-entry-closure answer PREREQUISITE NOT MET (need whole-workspace dist; the diff changes no package entry point). NOT MEASURED: @objectstack/lint lint-liveness-properties. Reason: lint warns only on authorWarn, experimental or planned rows, and none of the 3 rows carries authorWarn before or after, so the warning set cannot move; CI Test Core runs it. Typecheck: no TS in the diff; CI lanes run it. Control-byte self-scan of the 4 edited text files: none. check:nul-bytes: OK. No ablation: data-only diff, and the gate's own symbol-anchor and key-mention checks are what verify the new pointers. CI snapshot of 41b5470514, taken once and not waited on: 32 check-runs deduped by name. 14 success. 15 in_progress (Lint & Repo Gates, Test Core 1-6, Type Check workspace and consumer gates, Dogfood 1-3, Dogfood Verify CLI, Build Core, Temporal Conformance). 3 skipped: Build Docs (ci.yml filter docs=false, no docs paths), Console Pin Gate (filter console=false, .objectui-sha unchanged), Packed-tarball smoke (opt-in, label needs:pack-smoke absent).",
     "mcp_calls": "0",
     "api_writes": "4 REST writes, each through the fleet-write relay as objectstack-fleet[bot]. Each is one POST /repos/objectstack-ai/objectstack/dispatches under the session token. (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft forced), relay run 36688923865 success, PR #20814, body read back byte-identical. (2) label-write --assign os-justin: POST /repos/objectstack-ai/objectstack/issues/20814/assignees, relay run 36689001361 success, read back assignee os-justin (labels documentation, size/s and tooling came from the labeler, not this write). (3) and (4) post-stamped: POST /repos/objectstack-ai/objectstack/issues/20287/comments and /issues/20299/comments, this report. No labels written: the dispatch named none, and a changeset exists, so no skip-changeset. Also 2 git pushes (not REST).",
     "open_questions": [
      {
       "question": "view.label: does the ledger row govern only the defineView container's own top-level label (the walker's shape), or the top-level label of any view item, including the ViewItem that Studio authors and ViewPreview draws?",
       "options": [
        "A: container only. The row stays dead. It flips when a Studio surface draws a container's own label, or the key is reconsidered. The ViewItem top-level label then has no row of its own, which is a ledger-coverage note.",
        "B: any view item. Flip it live citing ViewPreview#ViewLabelHeading. Under B the row was already live before objectui#11027 (the Studio view list registers a Label column), and view.name would need the same re-grade, because its dead verdict rests on the container/ViewItem split."
       ],
       "recommendation": "A. The walker (check-liveness.mts shapeOf) and the same file's view.name row both draw the container/ViewItem line. B would re-grade two rows on a ledger-reading change, not on objectui#11027."
      },
      {
       "question": "permission.rowLevelSecurity.label/.description: PermissionPreview draws both at the pin but is never mounted for permission. How should #20299's remaining permission half proceed?",
       "options": [
        "A: an objectui follow-up either mounts PermissionPreview (or an RLS policy list) inside PermissionMatrixEditPage, or renders label and description in PermissionAdvancedFacets' policy list. Then a pin bump, then these rows flip. A booted Studio check first confirms the unmounted preview.",
        "B: count the registered-but-unmounted preview as the reader. The README's rule refuses this: 'a preview no registry hands a draft to is a read point that never runs'."
       ],
       "recommendation": "A. B would repeat the 2026-08-10 error in which the RLS rows' note called PermissionPreview reachable."
      }
     ],
     "out_of_scope_findings": [
      "carrier: #20299 (stays open; the seat routes the objectui half) · noted, not filed. objectui @db11afd4967: PermissionPreview is registered for permission (previews/index.ts) but no production route mounts it, because the custom EditPage PermissionMatrixEditPage takes metadata/permission/:name. objectui#11027's permission half therefore reaches no Studio user. Class a candidate. reach: NOT MEASURED booted; static call-graph closure only (registration, accessor, all 4 production callers), which is not a filing-grade reach. dedupe words: PermissionPreview, PermissionMatrixEditPage, rowLevelSecurity label, registerMetadataPreview permission, getMetadataPreview",
      "carrier: #20299 · noted, not filed. The ledger has no row for a ViewItem's top-level label or viewKind: check-liveness.mts shapeOf walks only the container member of the view union and says ViewItem config is covered via list/form. The standalone ViewItem label that Studio's create form requires is ungoverned as its own coordinate. Ledger-coverage note, no reach. dedupe words: ViewMetadataSchema union walker, ViewItem label liveness, shapeOf union container",
      "carrier: #20299 · noted, not filed. The unflipped rows' notes are stale in both directions and were left as-is per the dispatch. permission.rowLevelSecurity.label/.description still say PermissionPreview renders only a count and is reachable (ResourceEditPage.tsx:949). At the pin it renders label and description but is not mounted. view.label still says the only obstacle is the one-entry tab strip. The heading now exists, and the real obstacle is that the container never reaches the preview.",
      "carrier: platform-checklist-watchdog.yml (standing main-red anchor) · noted, not filed. pnpm check:platform-checklist exits 1 at 96e724475c-based head: docs/qa/platform-checklist/areas/identity-auth.json cites packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor, which no longer resolves. Not per-PR CI. Inputs untouched by this diff. dedupe words: identity-auth.json twoFactor ABSENT SYMBOL"
     ]
    }

    Generated by Claude Code

  14. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling on the fold report (5907283710 on #20287, 5907294566 on #20299), and round 2 · 2026-09-30T08:30Z

    domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1), holder of claims 5906102916 (#20287) and 5906110970 (#20299). Draft PR #20814 at 41b5470514.

    Accepted as delivered:

    • Three rows go live: connector.actions.description, connector.actions.outputSchema and app.areas.description. Each cites its reader and producer at the pin db11afd4967, and each compare reads behind_by 0.
    • Three rows stay dead: permission.rowLevelSecurity.label, permission.rowLevelSecurity.description, and the view container label. No reader at the pin reaches them. The seat checked the dev's two key facts at the pin:
      • builtinComponents.tsx:187 registers EditPage: PermissionMatrixEditPage.
      • getMetadataPreview( has production callers only in ResourceEditPage, EmbeddedItemEditor and StudioDesignSurface; the fourth is apps/console/src/preview-gallery.tsx.
      • builtinComponents.tsx:232 filters isAggregatedViewContainer out of the view list.
    • Part of #20299, not the dispatched Closes. This is right: three of its rows stay open.
    • The declared surface extension: four hand-written sentences that the flips made false are corrected, and this is the minimum. The rule that a change must not leave text it made false wins over the claim's "generated only".

    Open question 1 (view.label): A. The row governs the container's own top-level label, which is the walker's shape, and it stays dead. The view.name row in the same file draws the same line, and B would re-grade two rows because of how the ledger is read, not because of objectui#11027.

    Open question 2 (the permission half): A. An objectui follow-up either mounts a preview for permission or draws the policy label / description in the RLS facet. Then comes a pin bump, and then these two rows flip. Its first acceptance is a booted Studio check that confirms the preview is unmounted, because the finding so far is a static closure. The seat files it when this PR lands, and #20299 then goes pm:blocked on it.

    Round 2 is note text only, on the three rows that stay dead. Their notes are now false both ways (the report's third out-of-scope finding):

    • The RLS notes say PermissionPreview renders only a count and is reachable. At the pin it renders label and description, but no route mounts it.
    • The view.label note names the one-entry tab strip as the obstacle. The obstacle is that the container never reaches the preview.

    Each note says what holds at the pin, and the prose ends up shorter than it was or the same length. Status, evidence, proof and verifiedAt do not change on these three rows. The claim's surface names these rows, so this is not an extension. After round 2 comes the at-tier review, with needs:contract-review on the PR and both cards.


    Generated by Claude Code

  15. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 20287,
     "folded_issues": [
      20299
     ],
     "round": 2,
     "status": "done",
     "branch": "claude/issue-20287-20299-ledger-flips-at-pin",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/20814",
     "head": "67b954acb8",
     "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1 (subagent; the parent seat's harness-stamped id)",
     "premise_still_valid": true,
     "summary": "Round 2 per ruling 5907340127. Two commits were appended on top of 41b5470514 with no force-push. 1912c355b6 is the main merge, made with scripts/pm/os-regen-merge.sh: main 157baa75f2, no conflict, no os-regen pending, and the branch's two state-count shards kept. 67b954acb8 rewrites the notes of the 3 rows that stay dead, and nothing else changes. Proof: a parsed JSON comparison of permission.json and view.json against HEAD~1, with those three notes removed, is identical. status, evidence, proof, verifiedAt, producer and evidenceScope are untouched, and no other row moves. No tracker number is added; the old notes' #7427 and #7131 are dropped. Each note is shorter: 1643 to 939, 312 to 270 and 2083 to 820 characters, 4038 to 2029 in total. Each note states only what holds at the pin db11afd4967 and cites the checked symbols. RLS: PermissionPreview draws label and description, but permission items go to PermissionMatrixEditPage, so no route mounts the preview and there is no reader. view.label: the container never enters a Studio enumeration, so it never reaches the preview; the one-entry tab strip is not the obstacle. state-counts and README did not change (check:liveness reports the shards current). The PR body is untouched: the read-back is byte-identical to the round-1 body. The worktree was rebuilt from the remote branch at 41b5470514. Since the merge, main has moved 4 commits to 810d42b69c: driver-sql, half-state-patrol CI, a query doc and a skills doc. None of them touches a branch path, so there was no second merge. The merge queue rebuilds on current main.",
     "notes_before_after": [
      {
       "row": "permission.rowLevelSecurity.label",
       "chars_before": 1643,
       "chars_after": 939,
       "before": "CORRECTED 2026-07-30 (was live with no evidence): no consumer in either repo. VERDICT RE-TESTED AND UPHELD 2026-08-10 (#7427) against the maintainer ruling that a designer preview rendering a key to a human is a runtime consumer (2026-08-10, #7131; README, 'Designer previews count as consumers'). This row is the closest structural twin of the four rows that ruling re-graded — a display `label` marked dead — so it was measured rather than assumed, and it comes out the other way. THE MEASUREMENT, at objectui @e9ab52f9: PermissionPreview IS registered for `permission` (previews/index.ts:71) and IS reachable (ResourceEditPage.tsx:949), so the preview lookup runs; but PermissionPreview.tsx:111 reads `rowLevelSecurity` only as an ARRAY and PermissionPreview.tsx:164 renders `${rls.length} RLS rules` — a COUNT. It never indexes a policy, never reads `.label`, and no policy field reaches a human through it. The 2026-07-30 wording 'PermissionPreview counts them' was exact, and counting is not rendering: the ruling turns on the VALUE being shown to a person, which is precisely what a length does not do. The other measured surface is PermissionAdvancedFacets.tsx:192-193 (reads `draft.rowLevelSecurity`, strips retired keys) and :264 (writes it back) — an authoring FORM, the 'authoring surface echoing input' the 2026-07 correction rejected, and the new ruling names previews, not edit forms. So both halves of the original closure survive it. Benign display metadata — deliberately NOT authorWarn'd. To re-open this row, the thing to look for is a preview that renders the policy's label text, not another surface that counts policies.",
       "after": "No reader reaches it, measured at objectui @db11afd4967. PermissionPreview.tsx#readPolicies reads each policy's `label`, and `PermissionPreview` draws it in its Row-Level Security list, but no route mounts that preview for `permission`. ResourceEditPage.tsx#MetadataResourceEditPage hands every non-create `permission` item to the custom EditPage that services/builtinComponents.tsx registers, `PermissionMatrixEditPage`. That page renders no preview, and its RLS form `PermissionAdvancedFacets` reads no policy label. Create mode previews only object, report and dataset. The other `getMetadataPreview` callers (`EmbeddedItemEditor`, `StudioDesignSurface`, the dev-only preview gallery) never open a `permission`. A preview no route mounts is a read point that never runs (README, 'Designer previews count as consumers'). Benign display metadata, deliberately NOT authorWarn'd. To re-open: a mounted surface that draws the policy's label."
      },
      {
       "row": "permission.rowLevelSecurity.description",
       "chars_before": 312,
       "chars_after": 270,
       "before": "CORRECTED 2026-07-30 (was live with no evidence): same closure as label. RE-TESTED AND UPHELD 2026-08-10 (#7427) with `label`, same measurement at objectui @e9ab52f9 — the permission preview counts RLS policies (PermissionPreview.tsx:164) and renders no field of any individual policy. Benign — not authorWarn'd.",
       "after": "Same closure as `label`, at objectui @db11afd4967: `PermissionPreview` draws each policy's `description` beneath its row, but no route mounts it for `permission`. `PermissionMatrixEditPage` takes the item, and its RLS form reads no description. Benign, not authorWarn'd."
      },
      {
       "row": "view.label",
       "chars_before": 2083,
       "chars_after": 820,
       "before": "Display metadata on the container with no runtime consumer. VERDICT RE-TESTED AND UPHELD 2026-08-10 (#7427) under the maintainer ruling that a designer preview rendering a key to a human is a runtime consumer (2026-08-10, #7131; README, 'Designer previews count as consumers'). It is the twin of `translation.label`, which that ruling DID re-grade, so it was measured, and it splits on REACHABILITY — the half of the README rule that is easy to skip. THE MEASUREMENT, at objectui @e9ab52f9: ViewPreview is registered for `view` (previews/index.ts:37) and resolved by ResourceEditPage.tsx:949, and it genuinely READS this key — ViewPreview.tsx:115 injects `(body).label ?? (draft).label ?? name` as the label of the single named listView it hands to the `object-view` schema. The read is real; the RENDER is not reachable. That injected label has exactly two render paths in plugin-view's ObjectView, and this preview takes neither: ObjectView.tsx:1119 renders `{view.label || key}` as a named-view tab, but ObjectView.tsx:1112 returns null when the map holds one entry or fewer, and ViewPreview always injects exactly one; ObjectView.tsx:993 passes the label into the `renderListView` list-view schema (ListView appends it to export filenames), but `renderListView` is a prop and plugin-view/src/index.tsx:58-64 registers the bare `ObjectViewRenderer` for `object-view` — it renders ObjectView with schema and dataSource only, so the prop is undefined on this path. A read point whose render cannot fire is what PR #7425 meant by 'a preview no registry hands a draft to is a read point that never runs', one level in: here the registry does hand it a draft, and the draft's label still reaches no human. Not authorWarn'd: the README's rule is that pure display annotation (description, tags, icon) must not warn, and the job.label / translation.label precedent is this exact case — docs-shaped, deliberately kept, ADR-0033 exempt from enforce-or-remove. To re-open: a second listView entry, or `renderListView` wired on this path, would make it render — either change flips this row.",
       "after": "Display metadata on the `defineView` container. No reader reaches it, measured at objectui @db11afd4967. ViewPreview.tsx#ViewPreview draws `draft.label` as the preview heading (`ViewLabelHeading`), but the Studio draft is always a ViewItem, never the container. `getMetaItems` drops aggregated containers from every `view` list, and Studio's view `listFilter` (services/builtinComponents.tsx) drops them again. `expandViewContainerWithDiagnostics` gives each ViewItem the `label` of its list or form entry, never the container's. So no Studio list or quick-find hands the container to the preview, and its own label reaches no human there. The `name` row draws the same container/ViewItem line. Not authorWarn'd: docs-shaped, deliberately kept (ADR-0033). To re-open: a Studio surface that draws a container's own label."
      }
     ],
     "tests": "At 67b954acb8. Through os-verify-lock: pnpm check:liveness (packages/spec) 'VERDICT command-exit 0'. Per-type counts are unchanged from round 1: permission 36 live, 6 dead; view 78 live, 11 dead; app 50 live, 8 dead, 1 planned; connector 31 live, 1 planned, 23 dead. Symbol anchors 830/830. Key-mention: 662 asked, 661 anchored, 1 exempt. state-counts current. Shard sum: 960 live, 122 dead. vitest --project local scripts/liveness: Test Files 10 passed, Tests 263 passed. vitest --project repo count-shards-merge, 3 connector retirement tests, rls-tags-retirement and view-list-tabs-retirement: Test Files 6 passed, Tests 106 passed. vitest --project local view-field-order-composition.pin and aria-carrier-tombstones: Test Files 2 passed, Tests 31 passed. spec build then check:generated in one locked command: 'All 15 generated artifacts are up to date', VERDICT command-exit 0. dispatch-gates --commands derived the same 60 families as round 1. Change set: 8 paths vs merge base 157baa75f. All 60 were run. The check:* gates ran outside the lock, per the os-dev.md rule that check:* gates do not take the lock. The seat asked for the lock, so this conflict is flagged. dispatch-gates --ran with exit codes, under the lock: '60 derived famil(ies) accounted for — 58 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)', VERDICT command-exit 0. Of the 60: 57 exit 0. 1 exit 1: check:platform-checklist, the same pre-existing main red on identity-auth.json auth-plugin.ts#twoFactor; inputs untouched, not per-PR CI. 2 exit 3, NOT MEASURED: check:dual-build-cjs-loads and check:lean-entry-closure, PREREQUISITE NOT MET because they need whole-workspace dist. That reconciliation also printed a STALE TREE warning. The two stale files are .github/actions/half-state-patrol/action.yml and .github/workflows/half-state-patrol.yml, and neither is on this diff's path. CI at 67b954acb8, deduped by name: 35 check-runs. 32 success. 3 skipped: Build Docs (ci.yml filter docs=false), Console Pin Gate (filter console=false; .objectui-sha unchanged) and Packed-tarball smoke (opt-in; label needs:pack-smoke absent). All 7 required contexts are success: Lint & Repo Gates, TypeScript Type Check, Test Core (1-6), Dogfood Regression Gate (1-3), Build Core, Temporal Conformance, Governed Surface Queue Guard.",
     "mcp_calls": "0",
     "api_writes": "1 REST write this round: post-stamped POST /repos/objectstack-ai/objectstack/issues/20287/comments (this report), sent through the fleet-write relay as one POST /repos/objectstack-ai/objectstack/dispatches. Also 1 git push, not REST: 41b5470514..67b954acb8, fast-forward. No PR body, label or assignee write.",
     "open_questions": [],
     "out_of_scope_findings": [
      "carrier: platform-checklist-watchdog.yml · noted, not filed (unchanged from round 1): check:platform-checklist red on main at identity-auth.json auth-plugin.ts#twoFactor ABSENT SYMBOL."
     ]
    }

    Generated by Claude Code

  16. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #20814 → 15b586dcff; B2 is done and the card closes · 2026-09-30T10:16Z

    domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1), holder of claim 5906102916.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:workflowApprovals and automation — the work that runs without a person driving itdomain:specenhancementNew feature or requestpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions