Skip to content

fix(spec): grade connector.actions.description/outputSchema and app.areas.description live at the objectui pin - #20814

Merged
os-justin merged 3 commits into
mainfrom
claude/issue-20287-20299-ledger-flips-at-pin
Sep 30, 2026
Merged

os-justin merged 3 commits into
mainfrom
claude/issue-20287-20299-ledger-flips-at-pin

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Closes #20287
Part of #20299
Clause-②: no

Three liveness rows go dead → live. Each cites its objectui reader and producer, read at the .objectui-sha pin db11afd4967:

  • connector.actions.description: the flow designer's Action picker (FlowReferenceField.tsx#connectorActionsToOptions), from objectui#11028.
  • connector.actions.outputSchema: the flow designer's downstream references (flow-scope.ts#nodeOutputRefs), from objectui#11028.
  • app.areas.description: the Studio app preview's Areas list (AppPreview.tsx#readAreas), from objectui#11027.

#20299 stays open. Three of its rows are unchanged, because no reader at the pin reaches them:

  • permission.rowLevelSecurity.label and .description: PermissionPreview renders both, but no Studio route mounts it for permission. MetadataResourceEditPage hands a permission item to the registered custom PermissionMatrixEditPage. That page renders no preview, and it reads no policy label or description.
  • The view container label: ViewPreview draws the draft's label, but the draft is always a ViewItem. Both the metadata list and the Studio list drop the aggregated container. Expansion takes each ViewItem's label from its list or form entry, not from the container.

Also in the diff: the regenerated state-counts/app.md and state-counts/connector.md. Four hand-written sentences that these flips made false are also corrected: the app and connector README Notes cells, the connector.actions.inputSchema note, and the app.areas container note.

Acceptance notes

  • check:liveness passes at 41b5470514: connector is 31 live, 1 planned, 23 dead, and app is 50 live, 8 dead, 1 planned. The shard sum is 960 live and 122 dead.
  • The pin verdicts come from a static call-graph closure at db11afd4967. No Studio was booted.

Generated by Claude Code

…reas.description live at the objectui pin

The flow designer reads a connector action's description and outputSchema
(objectui#11028) and the Studio app preview draws each area's description
(objectui#11027), both at the .objectui-sha pin db11afd4967. Each row cites
the reader and its producer. Count shards regenerated; the two README
Notes cells that enumerated these keys as dead are corrected.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 30, 2026
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 7 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/app.json, packages/spec/liveness/connector.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 7 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/app.json, packages/spec/liveness/connector.json, …) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 137 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 157baa75f276e5f9e157cf83c10b4774449cf481 → packageMentionDocs.

permission.rowLevelSecurity.label/.description: PermissionPreview draws both,
but no route mounts it for permission (the custom PermissionMatrixEditPage
takes the item). view container label: the container never enters a Studio
enumeration, so it never reaches ViewPreview. Notes only; status, evidence,
verifiedAt and every other field unchanged, and each note is shorter.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 67b954acb85938ba14e274926fa173426a26e388
Local-runs: none

Scope read: cards #20287 and #20299 (bodies and every comment, including claims 5906102916 / 5906110970, unlock notes 5905157674 / 5905183954, stage records 5888771371 / 5882682058, ruling 5907340127, pointer 5907355002, dev reports 5907283710 / 5907294566 (byte-identical) / 5908364745); PR #20814 body, file list, and the net diff of the head against origin/main (8 files, +40 / -18; the 3-dot diff against main's tip and the 2-dot diff against the merge base 157baa75f2 are the same 8 files, so main's later commits do not interfere). objectui read at the .objectui-sha pin db11afd4967c (unchanged on the head, on main and on the merge base). Every objectui symbol below was read with git show / git grep at that pin; every objectstack symbol at the head.

① Derived judgments

Structural read of the diff — right.

  • A parsed-JSON leaf diff of app.json, connector.json, permission.json, view.json (main vs head) finds exactly these changes and nothing else: app.areas.description (status, verifiedAt, evidence, evidenceScope, producer, note) and the app.areas container note; connector.actions.description and connector.actions.outputSchema (the same six fields each) and the connector.actions.inputSchema note; permission.rowLevelSecurity.label note and .description note; view.label note. No other row moves. The three dead rows change only note: status, evidenceScope and verifiedAt are byte-equal to main.
  • None of the three flipped rows carries authorWarn on main or on the head, so the lint's warning set cannot move (the changeset's claim, confirmed).
  • Regenerated shards match the ledgers by name, not by arithmetic alone: the app dead set at the head is exactly homePageId plus the seven feat(spec)!: AppSchema 七个死键墓碑化(#4001 app 步 PR A) #4142 tombstones (version, aria, objects, apis, sharing, embed, mobileNavigation) = 8, planned = branding.logo = 1; the connector dead set is syncConfig (8) + fieldMappings (7) + metadata + the seven tombstones (rateLimitConfig, errorMapping, connectionTimeoutMs, health, status, webhooks, triggers) = 23, planned = authentication = 1. Shard lines move 49/9 to 50/8 and 29/25 to 31/23, the only two shard lines in the diff; the shard sum at the head is 960 live / 122 dead, as the PR body says. The Spec property liveness check-run on the head is the gate's own confirmation.
  • The only other prose moved is the four sentences the report names: the README app Notes cell (dead 9 to dead 8, areas.description named as the one that left), the README connector Notes cell (29/1/25 to 31/1/23, the 25-to-23 step named), the connector.actions.inputSchema note ("the one action key with a structural consumer" is false once outputSchema has one), and the app.areas container note ("description is the surviving benign dead key" is false once it is live). A grep of every ledger and the README at the head for the six keys, PermissionPreview, ViewPreview, "no surface renders" and "consumed by nothing" finds no other sentence left stating the old verdicts; the only tree hits outside liveness/ are generated translation tables.

Flip 1, connector.actions.description dead to live — right. At the pin, FlowReferenceField.tsx#connectorActionsToOptions (:354) reads a.description, trims it, and sets it as the option hint (:360-364); #ReferenceCombobox (:640-843) draws label — hint (:808). Reachability: FLOW_NODE_CONFIG.connector_action declares actionId as a reference of kind connector-action (flow-node-config.ts:1095); FlowNodeConfigField.tsx:185-187 renders every reference field through FlowReferenceField (:844), which resolves the connector (:655), calls useConnectorActionOptions (:656, fetching /api/v1/automation/connectors and mapping conn.actions through connectorActionsToOptions) and renders ReferenceCombobox (:850). FlowInspector is registered for flow (inspectors/index.ts:30), mounted by ResourceEditPage.tsx:1955 (getMetadataInspector(type)) on the metadata/:type/:name route (AppContent.tsx:982), and flow registers no custom EditPage (the only EditPage: in the tree is permission's). The Automations pillar mounts the same inspector (StudioDesignSurface.tsx:3911). Framework side: engine.ts#getConnectorDescriptors projects description: a.description (:4132); producers createSlackConnector (three actions with description), createOpenApiConnector (description: op.description, :214) and createMcpConnector (description: tool.description, :257) exist as cited. The "two-door caveat" is the actions.key row's, unchanged. The objectui#11028 merge a5841be351 (2026-09-29T10:44Z) is an ancestor of the pin. The note's "was true until objectui#11028" is consistent with the superseded row's 2026-09-17 verification.

Flip 2, connector.actions.outputSchema dead to live — right. flow-scope.ts#nodeOutputRefs (:249) takes the committed connector action (:307-311), reads its outputSchema through connectorActionOutputSchema (:172-183, action?.outputSchema) and offers one nodeId.key per top-level properties key through connectorActionOutputKeys (:196-201; no schema or no properties yields none). #resolveFlowScope (:334) hands them on. The producer of the input the read depends on: connector-input-fields.ts#useConnectorRegistry (:244, fetching /automation/connectors), read by FlowNodeInspector.tsx#FlowNodeInspector (:206-215, passed to useFlowScope) and FlowEdgeInspector.tsx#FlowEdgeInspector (:72-75); useFlowScope.ts:103 forwards connectors to resolveFlowScope. Framework: getConnectorDescriptors projects outputSchema: a.outputSchema (:4134); slackOutputSchema() on all three Slack actions, buildOutputSchema(op) (openapi-connector.ts:216), tool.outputSchema (mcp-connector.ts:262). The note's sentence "the engine stores each top-level key of a node's output as nodeId.key" is true: engine.ts:10950-10953 writes variables.set(node.id + '.' + key, value) for every result output, and the connector_action executor (builtin/connector-nodes.ts:108-110) returns the handler's result as output. "Nothing validates a reference against the schema" is true and the source says so itself (flow-scope.ts:191-195).

Flip 3, app.areas.description dead to live — right. AppPreview.tsx#readAreas (:184-193) resolves area.description through resolveI18nLabel in the designer locale and leaves it undefined when unauthored; #AppPreview (:243) calls it (:276) and draws the description beneath the area label in the Areas list (:326-341). The Areas block sits inside the unconditional PreviewErrorBoundary body (:309-311) and above the designMode ? ternary (:345), so "in read and design mode" holds. Producer: previews/index.ts#registerBuiltinPreviews registers AppPreview for app (:40), register-builtins.ts:73 calls it; ResourceEditPage.tsx#MetadataResourceEditPageImpl resolves getMetadataPreview(type) (:1938-1942) and renders it (:2662) whenever the type has no custom EditPage, and app has none. Framework: meta-item-read-gate.ts#filterAppForUserWithReason (:732) keeps each served area whole (filterAreas, :898-906, spreads ...a). The objectui#11027 merge 5b2ea17570 (2026-09-29T12:58Z) is an ancestor of the pin.

Dead row 1 and 2, permission.rowLevelSecurity.label / .description — the new notes are true at the pin, sentence by sentence. PermissionPreview.tsx#readPolicies (:110-118) reads policy.label and policy.description; #PermissionPreview draws them (:329, :332). ResourceEditPage.tsx#MetadataResourceEditPage (:296-318) returns customConfig.EditPage for every non-create item, and packages/app-shell/src/services/builtinComponents.tsx:187 registers EditPage: PermissionMatrixEditPage for permission (the ruling's :187 is exact); registry.ts#registerMetadataResource (:398-409) merges only defined keys, and anchors.ts:373-374 sets no EditPage, so the registration survives. PermissionMatrixEditor.tsx contains no Preview and no rowLevelSecurity read; its RLS form PermissionAdvancedFacets.tsx types a policy as name / object / operation / using / check / enabled (:45-52) and reads no label or description. "Create mode previews only object, report and dataset" is CREATE_MODE_CANVAS_TYPES (:203) gating PreviewComponent (:1937-1941). The other production getMetadataPreview( callers at the pin are exactly EmbeddedItemEditor.tsx:81 (its editAs comes only from anchors.ts as field, index, validation), StudioDesignSurface.tsx:1867 (Interfaces pillar: resolveSurface yields only page, object, dashboard, report, action) and :3910 (Automations pillar: flow), plus apps/console/src/preview-gallery.tsx:106, a separate preview-gallery.html entry that is not a rollupOptions build input. The Access pillar does build a { type: 'permission' } identity (:4364), but only for the ?surface= deep link; its main panel mounts PermissionMatrixEditPage directly (:4699), never a preview. The ruling's second key fact (four getMetadataPreview callers, none serving permission) is confirmed. One wording point: the note's "A preview no route mounts is a read point that never runs (README, ...)" is a paraphrase; the README sentence (line 431) reads "a preview no registry hands a draft to is a read point that never runs". Same standard, not a quotation.

Dead row 3, the view container label — the new note is true at the pin, with one sentence wider than what was measured. ViewPreview.tsx#ViewPreview (:156) reads draft.label (:169-172) and draws it through ViewLabelHeading (:151-153, rendered at :232 / :254 / :270). getMetaItems (packages/metadata-protocol/src/protocol.ts:8225-8227, this repo) filters isAggregatedViewContainer out of every view / views enumeration; Studio's view listFilter (services/builtinComponents.tsx:232, the ruling's :232) drops it again; QuickFind.tsx:104 indexes through client.list(type), the same door. expandViewContainerWithDiagnostics (packages/spec/src/ui/view.zod.ts:6823, this repo) stamps each ViewItem with v.label / defaultList.label / defaultForm.label (:6839, :6854, :6876, :6887), never the container's. The walker governs the container member of the view union (check-liveness.mts:636-641), so the ruling's open-question 1 answer A is the row's actual shape. Two remarks, neither verdict-moving: (a) the note opens "No reader reaches it, measured at objectui @db11afd4967" and then cites getMetaItems and expandViewContainerWithDiagnostics without saying they are this repo's symbols; (b) the measured doors are the list and quick-find, and the note's own closing sentence is scoped to them, but the opening "No reader reaches it" is wider: getMetaItem (the single-item door behind the mounted metadata/:type/:name route) has no view special-case, and the protocol comment at :8218-8220 says the container is kept under the bare object key "for defensive single-item reads". No Studio surface links to that name, so on the README's standard (a surface handing the preview a draft) the row stays dead; the follow-up that re-opens this row should enumerate that door.

The verifiedAt freeze on the three dead rows — ordered by the ruling, conservative, and named here as a mismatch. The notes say "measured at objectui @db11afd4967" (2026-09-30) while verifiedAt stays 2026-08-10; the README's rule is "date them as you re-verify" and age is a worklist, never a gate. The clock reads older than the truth, so the only effect is an earlier appearance on the stale-verification worklist. No published sentence is made false by it. Recorded, not failed.

Author-shown and AI-facing text, sentence by sentence.

  • Changeset: "Studio reads each of them at the .objectui-sha pin" — true. "Ledger data, two README Notes cells and the regenerated count shards only" — true (the two extra note edits are ledger data). "No schema, parse, .describe() or accept-set change" — true; no packages/spec/src/** in the diff. "The ledgers ship inside this package (files[] includes liveness)" — true (packages/spec/package.json). "@objectstack/lint reads them" — true (lint-liveness-properties.ts). "None of the three rows sets authorWarn" — true. The three bullet descriptions match the readers above. "The actions an author writes on a metadata connector entry never reach the registry the designer reads" — true per the actions.key row (the entry schema refuses actions on a provider-bound instance, and a descriptor never reaches the registry). The count line matches the shards. All true on main when this PR lands.
  • PR body: Closes #20287 — right; the unlock note says B2 was the card's whole remainder. Part of #20299 — right, and the Part-of PR must not also close its card check-run is success. The three permission/view sentences are true as tested above. "Four hand-written sentences ... corrected" — exactly four, confirmed. Acceptance note "check:liveness passes at 41b5470514" names round 1's head; the counts are unchanged at 67b954acb8 (structural count above) and the head's Spec property liveness run is success, so it is stale as a pointer, not false. "No Studio was booted" — honest, and the RLS follow-up's first acceptance is that boot.
  • Flipped-row notes: every claim tested above; "the superseded note measured AppPreview at @e9ab52f9 with zero areas reads" quotes the old note correctly.
  • README cells: "the Studio app preview draws it (objectui#11027)" and "the flow designer reads both, objectui#11028" — true at the pin; "only areas.description has left that set since 清空剩余 6 条 authorWarn 死键 —— book ×2 / job.id / translation.validationMessages / app.homePageId / app.areas[].order(ADR-0049,v17 限时) #4667, and nothing has joined it" — consistent with the prior cell (dead 9, nothing left or joined) minus one.

② Semver level

.changeset/20287-connector-actions-and-app-areas-live.md: @objectstack/spec patch. Right: the diff changes only files under packages/spec/liveness/ (ledgers, README, shards), which ship in the package, and no type, schema, parse behaviour, .describe() text or accept set moves; the lint's warning set is unchanged because no authorWarn moves. Clause-②: no in the claim and in the PR body — right, no accept set moves. Check Changeset on the head is success.

③ Boundary flags

Dev-report deviations and flags (5907283710 round 1, 5908364745 round 2), each answered:

  • premise_still_valid: false for three of six rows, left unflipped per the claim's "no flip without a reader" — right; the seat accepted (5907340127).
  • Part of #20299 instead of the dispatched Closes — right, ruled right, gate-confirmed.
  • Declared file-surface extension (four hand-written sentences, dispatch said generated-only) — the minimum needed; confirmed exactly four; seat accepted.
  • Open question 1 (view.label container vs any view item) — ruled A; the walker's union rule (check-liveness.mts:636-641) makes A the row's shape. Answered.
  • Open question 2 (permission half) — ruled A: an objectui follow-up that mounts a preview for permission or draws policy label / description in the RLS facet, then a pin bump, then the flips, with a booted-Studio check first. Escalated as pending: the seat said it files that card when this PR lands and studio: show the authored label / description on flows, hooks, app areas, RLS policies and the view container (7 keys) #20299 then goes pm:blocked on it; the card does not exist yet. The single-item view door noted in ① belongs on the sibling follow-up for view.label.
  • Out-of-scope 1 (PermissionPreview registered but unmounted at the pin; objectui#11027's permission half reaches no user) — confirmed at the pin; carrier studio: show the authored label / description on flows, hooks, app areas, RLS policies and the view container (7 keys) #20299 / the follow-up above. Noted.
  • Out-of-scope 2 (no ledger row governs a ViewItem's own top-level label) — consistent with the walker's union rule; a ledger-coverage note, no reach. Noted.
  • Out-of-scope 3 (stale notes on the unflipped rows) — closed by round 2, verified above.
  • Out-of-scope 4 (check:platform-checklist red on main at identity-auth.json / auth-plugin.ts#twoFactor) — inputs untouched by this diff, not per-PR CI; carrier platform-checklist-watchdog.yml. Noted, not this PR's.
  • Round-2 process flags: check:* gates run outside the verify lock (os-dev rule vs the seat's ask) and a STALE TREE warning on two half-state-patrol files off the diff path — neither touches the diff or its gates. Noted.
  • Round-2 verifiedAt freeze on the dead rows — by ruling; recorded in ① as a data-vs-note mismatch in the conservative direction.

Check-runs on 67b954acb85938ba14e274926fa173426a26e388 (39 returned, 35 after dedupe by name keeping the newest started_at): 30 success, 5 skipped (Auto Label, Check PR Size, Build Docs — docs filter false, Console Pin Gate — .objectui-sha unchanged, Packed-tarball smoke — opt-in label absent), 0 failure, none still running. Success includes Lint & Repo Gates, Spec property liveness, Check Changeset, TypeScript Type Check and its four sub-gates, Test Core (1-6), Dogfood Regression Gate (1-3), Dogfood Verify CLI, Build Core, Temporal Conformance, Governed Surface Queue Guard, Part-of PR must not also close its card, The card this PR closes must claim this branch, and both single-writer guards.

Implemented-by: claude/issue-20287-20299-ledger-flips-at-pin
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1

VERDICT: PASS

Adopted and posted by domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-09-30T09:47Z · rendered by the seat's at-tier review subagent on this head. The seat read its served tier family from the subagent transcript before posting.

  • The three remarks are kept as they are, with no new head. The record finds none of them false: the README paraphrase, the view note's opening sentence being wider than the doors measured, and verifiedAt staying on the dead rows.
  • When this PR lands, the seat files the objectui follow-up for the permission half (ruling 5907340127, Q2 A), and studio: show the authored label / description on flows, hooks, app areas, RLS policies and the view container (7 keys) #20299 goes pm:blocked on it.
  • This PR is not governed. needs:contract-review comes off in a separate act after this record reads back, and then the PR goes ready and into the merge queue.

Generated by Claude Code

@os-justin
os-justin marked this pull request as ready for review September 30, 2026 09:49
@os-justin
os-justin enabled auto-merge September 30, 2026 09:49
@os-justin
os-justin added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 15b586d Sep 30, 2026
44 checks passed
@os-justin
os-justin deleted the claude/issue-20287-20299-ledger-flips-at-pin branch September 30, 2026 10:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants