Skip to content

feat(spec)!: retire the view-level api data source — a view binds its object or static rows; an API call is the page element's binding (#23082) - #23160

Merged
os-zhuang merged 7 commits into
mainfrom
claude/issue-23082-retire-view-api-source
Oct 13, 2026
Merged

os-zhuang merged 7 commits into
mainfrom
claude/issue-23082-retire-view-api-source

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #23082
Clause-②: no (narrowing)

Ruling 6134088274 (batch #331 item 5, letter C): the view-level api data source retires under ADR-0049 enforce-or-remove. A view binds its object or static rows; a metadata-declared API call is the page element's dataSource binding (the #23016 rule, PR #23079), same-origin and credential-free. Executed per the spec-property-retirement playbook as a VALUE retirement (the retired bare-string sort clause's route): data survives one arm lighter, so there is no retiredKey() tombstone and no RETIRED_KEYS_BY_MAJOR row — the prescription rides ViewDataSchema's own discriminated-union error map, keyed on the input's provider === 'api', so every other unknown provider keeps zod's default report (which lists the three live arms).

What changed

  • packages/spec/src/ui/view.zod.ts — the provider: 'api' arm of ViewDataSchema (its read / write HttpRequestSchema blocks and the arm's own near-miss guidance) is removed; the union carries VIEW_DATA_API_PROVIDER_RETIRED as its error map. Two stale prose sites in the same file no longer teach the arm (the gantt objectField describe, the form data docblock). HttpRequestSchema / HttpMethodSubsetSchema and their ui re-exports are untouched; the page-element api binding (ElementApiDataSourceSchema, ui/page.zod.ts) is untouched.
  • ADR-0087: D3 semantic entry view-data-api-provider-retired (migrations/entries/semantic/18.view-data-api-provider-retired.ts) and its STEP18_RATIONALE fragment (order 95). No D2 conversion, deliberately: the replacement lives on a different metadata type (a page element), and the only mechanical edit a conversion could make — deleting the view's data so the view falls back to querying its bound object — would silently change what the view shows. An authored source or a stored row still carrying the arm is refused at parse with the prescription (the rest-api-endpoint-handler-status-retired / kernel/Manifest:loading no-D2 precedents).
  • Liveness ledger packages/spec/liveness/view.json: list.data and form.data stay live (their surviving arms are read) and carry the arm's retirement record; list.data gains verifiedAt / evidenceScope: cross-repo with objectui read points cited at 7578323a544e. A renderer reader existed in objectui, measured at 7578323a544e (.objectui-sha on main is 1fb1ced36243; the next cross-repo re-measure re-reads ListView.tsx / ObjectGantt.tsx there; ListView.tsx routed an api block to ApiDataSource, ObjectGantt.tsx built one from the block's read / write) and is now unreachable from authored metadata. The ledger has no per-arm row (the walk never descends a union arm), so the record sits on the key's row; the README's per-type counts do not move.
  • packages/spec/variant-docs.json: the union key is now provider:object|schema|value.
  • Pins: src/ui/view-data-api-provider-retirement.test.ts — the refusal at provider / data.provider / list.data.provider with its prescription, the three surviving arms value-for-value and still closed, zod's default report for any other unknown provider, the D3 registration and the absence of any key row, and a tree-scoped absence scan over the authored spelling (TS / JSON / YAML dialects; the page element's dataSource: { provider: 'api' … } deliberately does not match). It joins the spec repo vitest project and is the second witness on the spec: the rest of the incident-response, training and change-management families — every remaining key and all fifteen defs — has zero readers; whole-def enforce-or-remove is the open question left after #14477 #15513 absence-walk rows in scripts/cross-package-test-inputs.mjs. The four fixtures that pinned the arm were re-judged: two accept pins replaced by one refusal control (view.test.ts), the kanban fixture moved to the value arm (its point is a non-object source), the batch-18 strictness row for the arm removed, the component-props-unknown-members ledger loses its HTTP_REQUEST lines (no data member reaches a request block any more) and now refuses their return, and the two @objectstack/lint fixtures that used the arm to mean "a non-object provider" use the value arm.
  • Teaching: content/docs/ui/views.mdx loses the view-level example and points at Element data sources; skills/objectstack-ui/rules/list-views.md the same (readings below); the generated reference pages regenerate (gen:docs), the strictness counts shard regenerates (ui/view.zod.ts strict sites 60 → 59).
  • Changeset .changeset/23082-view-data-api-provider-retired.md: @objectstack/spec minor, BREAKING, FROM → TO, one-line fix, the prescription verbatim, adr-0087: registered view-data-api-provider-retired.

Measurements the ruling asked for

  • Producers of a view-level provider: 'api', outside documentation — each repo with a lit control in the same run:
    • objectstack at bcebd8409 (this branch's base): 0 outside packages/spec/src/** (its own tests), CHANGELOG.md, generated reference pages, the two hand-written docs pages and the skill rule file; control provider: 'object' 150 files. The only case 'api': reader in a runtime package (service-automation engine.ts) switches on a flow trigger kind, not on ViewData.
    • hotcrm at 0f84c9c: 0; control provider: 'object' in 5 view files (src/*/views/*.view.ts).
    • objectui at origin/main f228d710: 0 authored producers. apps/console/src/services/approvalRequestsDataSource.ts constructs new ApiDataSource({ read: { url, method, params? }, fetch }) in code — an ApiDataSourceConfig, never a ViewData; the phrase provider: 'api' there is its docblock. Not a fork. Note for the objectui follow-up: its "delete ApiDataSource and its related-count-store use" step must keep or re-home the approvals route (three construction sites in that file). The 29 non-test provider: 'api' / === 'api' sites are readers (ListView, ObjectGantt, resolveDataSource, ViewDataProvider, useViewData, createAuthenticatedFetch), the objectui follow-up's population. packages/types re-exports type ViewData / type HttpRequest from the spec and declares no provider: 'api' view type of its own; ViewDataProvider.ts carries an in-code ViewDataConfig union with an api member that does not derive from the spec. Control: provider: 'object' 169 files.
    • cloud: NOT MEASURED from this session (the ruling's 「同意」 covers it); the changeset says so.
  • write leg: no producer anywhere above (the fork clause's second trigger).
  • HttpRequestSchema readers (packages/**, excluding dist): ui/view.zod.ts (the removed arm, and the ui re-export at line 108 — kept), shared/http.test.ts, ui/view.test.ts (single-source and backward-compat pins — kept), scripts/schema-index.test.ts (a fixture), scripts/build-docs.ts (a comment), ui/component-props-unknown-members.pin.test.ts (the HTTP_REQUEST lines — removed here), plus the generated shards (api-surface, authorable-surface, export-origins, declaration-map, json-schema.manifest for shared/HttpRequest and ui/HttpRequest; contracts.json names the unrelated IHttpRequest). After this change no schema in the package composes HttpRequestSchema: its remaining readers are the two public re-exports and tests. Left in place per the ruling ("touches only the ViewDataSchema branch"); the orphan — and the now-stale describes on HttpMethod / HttpMethodSubsetSchema that still say "what view data sources may request" — is recorded in the acceptance notes for a follow-up, not acted on here.
  • Console Pin Gate: scripts/build-console.sh injects this tree's spec only through the console SPA's Vite hook (OBJECTSTACK_SPEC_DIST, apps/console/vite.config.ts); objectui's tsc-building packages (types, core, react, app-shell) type-check against the published spec in objectui's own lockfile (^17.x), so the arm's removal cannot red that gate on a type, and no objectui file at the pin parses authored provider: 'api' metadata at build time.
  • Ratchets, by route: a value retirement is byte-invisible to the four ratchets, and it was — api-surface/, authorable-surface/ (+ .base.json), export-origins/, declaration-map/, json-schema.manifest/ all green and unchanged (check:generated on the built tree). What did move: content/docs/references/** (the data type column; the gantt's nested data[provider='api'] section), the strictness counts shard, and (prose only) references/system/migration.mdx.

skills/** readings (Tier H; net budget ≤ 0)

file lines before → after tokens (ceil(bytes/4)) before → after ceiling
skills/objectstack-ui/rules/list-views.md 305 → 303 3136 → 3133 3141
all skills/*/SKILL.md (sum) 4409 → 4409 — —
all hand-authored skills/**/*.md (sum) 12570 → 12568 — —

The replacement pointer ("A view never calls an API: an API read is a page element's dataSource binding") costs less than the example it replaces. check:skills-token-ratchet green.

Verification

Head be64fe664 (= 41045ab6f + a merge of origin/main 287fd6e5d through scripts/pm/os-regen-merge.sh + the hand-off commit regenerating component.mdx). Every exit code below was captured before any pipe; builds and tests ran through scripts/pm/os-verify-lock.sh and are quoted from its VERDICT command-exit lines.

  • ① dependency closure: pnpm --filter '@objectstack/lint^...' build — the diff touches packages/spec (built, command-exit 0, three times on this head's tree) and packages/lint (tests only; its closure spec + formula + sdui-parser built, then turbo run build --filter=@objectstack/lint --filter=@objectstack/objectql, command-exit 0).
  • ② the touched packages' own suites, on this head: pnpm --filter @objectstack/spec typecheck (gen:migration-registry, tsc --noEmit, check:scripts-typecheck, check:test-typecheck: OK, 52 files / 246 errors / 135 pinned signatures held — the retirement pin compiles clean) · pnpm --filter @objectstack/spec test (the local project): 660 test files passed · pnpm --filter @objectstack/spec test:repo: 58 files / 1007 tests passed (the retirement pin's refusal, controls, D3 registration and the tree-scoped absence walk are in this project) · pnpm --filter @objectstack/lint typecheck: OK · pnpm --filter @objectstack/lint test: 138 files / 6649 passed, 5 skipped. Downstream importers of ViewData / ViewDataSchema outside spec: none in any src/ (git grep; the two hits are release-owned CHANGELOGs), so no consumer package owes a typecheck for the narrowed type; the full closure is CI's TypeScript Type Check.
  • ③/④ the gate union, derived on this head: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack → 135 commands (the PM's lead list of 100 plus 35 the tree adds; none dropped), every one run on be64fe664 with its exit recorded, then --ran reconciled with every exit code recorded (COMMAND :: exit N per line): Run reconciliation — 135 derived, 132 run, 3 NOT-MEASURED, 0 UNRUN (the 3 derived from recorded exit 3s), reconcile exit 0. 131 exit 0. The rest, each read at its own log, none a finding against this diff:
    • pnpm check:platform-checklist — exit 1, inherited: 8 ABSENT SYMBOL / anchor-floor problems in docs/qa/platform-checklist/areas/{access-security,attachments-storage,search}.json citing packages/metadata-protocol/src/protocol.ts#anonymousFormIntakeOrgScopeRefusal, #anonymousFormIntakeReopenRefusal, #envWideRawViewRows, #countable and service-storage/.../attachment-access-hooks.ts#canEdit. All four protocol symbols are absent on origin/main (287fd6e5d) and on this branch's base (bcebd8409) alike; this diff touches none of those files. A main-red for its lane (e5f9f0160 / c8a9783ec are the main commits that moved protocol.ts since the base); reported, not repaired here.
    • pnpm --filter @objectstack/spec run check:skill-examples — exit 3, PREREQUISITE NOT MET: its client-SDK surface reads @objectstack/client-react's built output, which needs the core closure built; the shared verify lock was held by other seats' builds for two full queue budgets (holders named in the logs), so it is NOT MEASURED here. Narrowing stated, not assumed: the gate type-checks only os:check-marked blocks, and neither edited block is marked (skills/objectstack-ui/rules/list-views.md markers at 17/166/221, the Data Source block at 38–50 is unmarked; content/docs/ui/views.mdx markers at 13/168/426/472, the block at 295 is unmarked; view.zod.ts carries no marker), so its verdict is invariant under this diff; CI runs it.
    • pnpm check:type-check-debt — exit 3, NOT MEASURED: it builds twelve packages itself and @objectstack/core#build collided with a concurrent build of mine under the lock (the batch's own log says so); spec's and lint's tsc --noEmit ran green directly (②), so their rows cannot have moved; CI re-measures.
    • pnpm check:dual-build-cjs-loads — exit 3, NOT MEASURED: it loads every package's built output and 53 of 68 workspace packages (adapters, apps) are unbuilt in this worktree; a whole-workspace build is CI's run.
    • Four more refused at first only because @objectstack/lint / @objectstack/objectql were not yet built, and were re-run green (exit 0) on this head once they were: check:doc-formula-expressions, check:doc-security-posture, check:docs-transcript-drift, check:lean-entry-closure.
  • Generated artifacts: pnpm --filter @objectstack/spec check:generated on this head: all 14 up to date (check:docs, check:strictness-ledger, check:liveness, check:test-typecheck, check:authorable-surface, check:api-surface, check:export-origins, check:declaration-map … all ✓). The 14 pure source audits it does not run were run as one group on the built tree: all ✓ except check:skill-examples (above).
  • Repo-level scans (pnpm lint and the farm) are CI's; this PR adds no lint-relevant surface.

Acceptance notes

  • HttpRequestSchema / HttpMethodSubsetSchema orphaned (class b candidate, carrier: the spec seat's follow-up or the objectui follow-up card): zero schema readers in @objectstack/spec after this PR; both still exported from ./shared and ./ui, and their describes still speak of "view data sources". objectui's packages/types imports HttpRequestSchema from the spec in a test and re-exports type HttpRequest, so a def retirement is cross-repo and belongs with the objectui follow-up (the cross-repo unlock rule). Not touched here per the ruling.
  • objectui follow-up population (another seat's card, filed when the spec release is installable): ApiDataSource, resolveDataSource's 'api' arm, ViewDataProvider.resolveApi, useViewData's provider === 'api' branches, createAuthenticatedFetch's provider: 'api' lane, the gantt's adapter arm, content/docs/plugins/plugin-gantt.mdx (teaches a gantt data: { provider: 'api' }), plugin-calendar.mdx / plugin-map.mdx (mention it as not honoured), and packages/core/src/adapters/README.md. The approvals route constructs ApiDataSource directly and must be kept or re-homed.
  • sdui.manifest.json (repo root; generated from objectui's built tree at the pin by gen-sdui-manifest-node.mjs) still describes the gantt's data as accepting { provider: 'api', read }; it regenerates on the next pin bump, not here.
  • Stored sys_metadata view rows carrying the arm (none measured; cloud unmeasured) are refused at rehydration with the prescription — loud by design, the D3 entry says what to do.

维护者速读(草稿)

改了什么: 视图(列表/表单)的 data 数据源不再接受 provider: 'api'(自带 read / write 请求块、任意 url 与 headers 的那一种)。视图只绑定对象(object)或静态行(value);元数据里声明的 API 调用只剩一条路:页面元素的 dataSource 绑定(同源、不带凭据、用控制台登录态请求)。写了旧写法的视图在校验、发布、保存时被拒绝,错误信息里直接写明改法。对外技能文本与文档同步改成指向页面元素绑定;spec 发一个 minor 版本并带 BREAKING 说明与迁移对照表。

为什么改: 您在 #23082 批了 C。旧的视图级 API 源可以把密钥写进 headers、把请求指向任意域名,这些会随视图一起存储、下发给每个读取该视图的客户端、并由控制台照原样发出;而页面元素那条路已经按 #23016 的规则锁死。同一件事两套规则,按裁决保留受治理的那一套、撤掉另一套。实测三个仓(objectstack、objectui、hotcrm)都没有视图在用它;cloud 未测,您的「同意」已覆盖。

风险与代价(含回滚): 风险在未测的存量元数据:若 cloud 或某个客户项目有视图写了 provider: 'api',升级后该视图加载时会被拒绝并给出改法(不会悄悄换成别的数据),需要作者把 API 读取改到页面元素上。没有自动迁移(D2),因为机械删掉 data 会让视图静默改读对象。回滚:撤销本 PR 即恢复旧分支,无数据迁移需要回退。objectui 侧的删除(ApiDataSource 等)是另一张卡,等 spec 发版后再做,本 PR 不动 objectui。

席位意见:

你要做的: 本 PR 动了 skills/**(受治理,Tier H),需要您的批准才能合入:请审阅上面的中文说明与 skills/objectstack-ui/rules/list-views.md 的改动(净减 2 行),同意则在 PR 上点 Approve,由席位排队合入。


Generated by Claude Code

… pins

Regenerated: content/docs/references/** (gen:docs), the strictness counts
shard (gen:strictness-ledger). The retirement pin joins the spec repo
project (it walks five repo roots) and is the second witness on the
absence-walk radius rows.

Claude-Session: https://claude.ai/code/session_01BRQ9Mp1yH7R1az6MaUM18H
Co-authored-by: Claude <noreply@anthropic.com>
…rom the merged tree

origin/main (287fd6e) brought PR #23101's component rows; the branch's
retirement removes the view-level api arm from the same page. Regenerated
with gen:docs on the merged tree per scripts/pm/os-regen-merge.sh step 4.

Claude-Session: https://claude.ai/code/session_01BRQ9Mp1yH7R1az6MaUM18H
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 10 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/spec/liveness/view.json, packages/spec/src/migrations/registry.ts.template, packages/spec/variant-docs.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/protocol/objectui/concept.mdx (via FormViewSchema (symbol, a top-level const))
  • content/docs/protocol/objectui/index.mdx (via FormViewSchema (symbol, a top-level const))
  • content/docs/protocol/objectui/layout-dsl.mdx (via FormViewSchema (symbol, a top-level const))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-1.mdx (via GanttConfigSchema (symbol, a top-level const))
  • content/docs/releases/v17/17-3.mdx (via FormViewSchema (symbol, a top-level const), ViewDataSchema (symbol, a top-level const))
  • content/docs/releases/v17/17-4.mdx (via GanttConfigSchema (symbol, a top-level const))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/spec/liveness/view.json, packages/spec/src/migrations/registry.ts.template, packages/spec/variant-docs.json, …) — pages documenting those are invisible to this run
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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 — 141 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 dc93781c1947b53cff50a550ad551e462a464594 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from c9876c263d6f588d9f934bfcc6a7dd3f08e56430 — the merge of head 12f86031637d7aaffe0ce28819166fb55bad8931 into base dc93781c1947b53cff50a550ad551e462a464594, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c9876c263d6f588d9f934bfcc6a7dd3f08e56430 && git checkout c9876c263d6f588d9f934bfcc6a7dd3f08e56430
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dc93781c1947b53cff50a550ad551e462a464594 12f86031637d7aaffe0ce28819166fb55bad8931 && git checkout -B drift-repro dc93781c1947b53cff50a550ad551e462a464594 && git merge --no-ff 12f86031637d7aaffe0ce28819166fb55bad8931

node scripts/docs-audit/affected-docs.mjs --json dc93781c1947b53cff50a550ad551e462a464594

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs dc93781c1947b53cff50a550ad551e462a464594 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: be64fe6648c2175f6ef7dc24523ae79ba41d0fba
Local-runs: none

Inputs read: card #23082 (body and all 4 comments, ruling 6134088274 letter C, claim 6136106180, dev report 6138457534), PR #23160 (body, 23-file list, net diff against main), the 39 check-runs on the head, and origin/main objects by git show / git grep for context. Nothing built, run or re-run.

Check-runs on the head as read for this record: 27 completed success; skipped: Console Pin Gate (its if: excludes pull_request and merge_group events — it never runs on a PR head, so its verdict is not available here), Dogfood Verify CLI, Dogfood Regression Gate (matrix), Packed-tarball smoke, Temporal Conformance; still in_progress and therefore NOT assumed: Build Core, Test Core (1/6) through (6/6), Type Check · workspace, Type Check · debt ledger. Those carry the retirement pin, the absence walk and the narrowed ViewData type across the workspace; the dev's local reads of them are claims until these runs complete.

① Derived judgments

Accept set and public surface, each judged:

  1. ViewDataSchema loses its api arm — RIGHT. data.provider: 'api' on a list or form view (and through ViewSchema.list.data) is refused at parse with the prescription on the union's error map keyed on the input's provider === 'api', at path provider / data.provider (invalid_union); any other unknown provider keeps zod's own report listing object, value, schema; the three surviving arms are value-identical and still closed. This is ruling C, executed on the ViewDataSchema branch only: HttpRequestSchema / HttpMethodSubsetSchema (shared/http.zod.ts) are untouched and still imported and re-exported from ui/view.zod.ts (lines 87 and 108 on origin/main), and the page element's ElementApiDataSourceSchema (ui/page.zod.ts) is not in the file list.
  2. ViewData type narrows (the arm leaves z.input / z.infer) — RIGHT, no in-repo casualty. On origin/main no src/ outside packages/spec imports ViewData / ViewDataSchema; the only case 'api': in a runtime package (service-automation/src/engine.ts) switches on a trigger kind, and packages/lint's validate-preset-comparands.ts reads r.data.provider without comparing it to 'api' (its two test fixtures move to the value arm in this diff). The workspace-wide answer is Type Check · workspace, in progress.
  3. Four ratchets byte-identical, HttpRequest defs orphaned but still exported — RIGHT. authorable-surface.base.json, api-surface/, export-origins/, declaration-map/ and json-schema.manifest/ record shared/HttpRequest:* and ui/HttpRequest:* by def name and carry no leaf path under the api arm, so a value retirement cannot move them; the orphan is recorded, not acted on, per the ruling's "touches only the ViewDataSchema branch".
  4. Prescription names a release that will not exist — WRONG. VIEW_DATA_API_PROVIDER_RETIRED says "was removed in @objectstack/spec 17.8.0", the retirement pin asserts that exact string (PRESCRIPTION regex, 17\.8\.0), and the changeset quotes it verbatim for CHANGELOG.md. origin/main is in Changesets pre mode (.changeset/pre.json: mode: pre, tag next) with pending major changesets on the fixed group that includes @objectstack/spec (22080-v18-line-opens, 22130-protocol-18, the 19939-flow-* set and others), so the first release carrying this removal is 18.0.0-next.N and the first stable one is 18.0.0; a 17.8.0 on this line is not a version that will ship this change. The six prescriptions added to packages/spec/src since the v18 line opened all stamp the bare major (17), none a minted minor. The FROM column's "17.7 and earlier" is right (17.7.0 is the newest published stable). Fix: the schema string, the test regex and the changeset quotation, in the house form the kit prescribes for the open line (the spec-property-retirement skill writes was removed in @objectstack/spec [version] (ADR-XXXX) without saying what the version is under pre mode — see ③ item E). A wrong version in a released CHANGELOG entry can only be amended by a dedicated docs-only PR later (AGENTS.md, packages/*/CHANGELOG.md row), which is why it must be right before landing.
  5. Four published describes still teach the arm — WRONG, in this PR's own surface. packages/spec/src/ui/component.zod.ts lines 5743 (ObjectGrid data), 8695 (ObjectMap), 9245 (ObjectTree) and 9821 (ObjectGantt) on origin/main describe data as "ViewDataSchema — discriminated on provider: object | api | value | schema". The diff does not touch them, and its own regenerated content/docs/references/ui/component.mdx hunks show that sentence beside the narrowed three-arm type column. These are the shipped JSON-schema descriptions and reference pages an author reads; they now teach a discriminator value the contract refuses. The dev's absence matcher cannot see them (it anchors on a data: { block), and the PR body's "two stale prose sites in the same file" covers view.zod.ts only. Fix: the four describes plus gen:docs. (shared/polarity-axes.ts line 100 carries a source-comment table row naming "the api data source" — comment only, cosmetic, fold in if touched.)
  6. ADR-0087 disposition: D3 view-data-api-provider-retired, STEP18 rationale fragment, no D2 — RIGHT. The registry generator discovers one file per semantic entry under entries/semantic/18.*.ts, the gate resolves a registered id against that directory, and Check Changeset is green on the head. The no-D2 reasoning holds: the replacement is a binding on a different metadata type, and the only mechanical edit (deleting the view's data) would silently re-bind the view to its object; the rest-api-endpoint-handler-status-retired precedent is the same shape. Shared order values in STEP18_RATIONALE are ordinary (several fragments already share 86–92).
  7. Liveness ledger: list.data and form.data stay live with the arm's retirement record on the key's row — RIGHT; the pin label is stale — WRONG in detail. The skill's route table keeps a key whose surviving arms are read, and the walk never descends a union arm, so the record sits on the row. But the new list.data.evidence cites objectui @7578323a544e and the PR body and changeset call that "the objectui pin": .objectui-sha is 1fb1ced36243 at origin/main and already at 287fd6e5d, the main this head merged (moved by PR chore(objectui): move the console pin to 1fb1ced36243 (objectui main tip, Half 1 of #23104) #23110, which the claim itself lists as a concurrent PR). A citation at a named SHA is the ledger's tolerated form (other rows cite @4997995022f8, @ab1879721595, @db11afd4967c, @9dfaca654311), so Spec property liveness is green, but the label "the pin" is one hop stale and the next pin-citation re-measure will have to re-read ListView.tsx / ObjectGantt.tsx at 1fb1ced36243. Fix with the two items above: cite the pin the head actually carries, or say "measured at 7578323a544e" without calling it the pin.
  8. Hand-written error map on the discriminated union versus the kit's rule — judged acceptable, gap escalated (③ item F). The spec-property-retirement skill (rule added 2026-09-30, docs(skills): retirement kit routes a RETIRED_KEYS_BY_MAJOR entry through entries/retired-keys/ and gen:migration-registry, and an enum-value narrowing through enumWithRetiredValues() #20828) says an enum-value narrowing hangs its prescription on enumWithRetiredValues() and no longer hand-writes an error map. That helper wraps a z.enum; provider here is a per-arm z.literal discriminator of a z.discriminatedUnion, which the helper cannot wrap, and the only in-tree precedent for a union arm (the bare-string sort clause, feat(spec)!: retire the bare string sort clause on the list-view doors — the PRODUCER half of the sort seam #17914, LIST_VIEW_SORT_STRING_RETIRED) is exactly this hand-written shape. The dev's map keeps the helper's discipline (a literal comparison, returns undefined for every other input so zod's report stands) and the pin controls it (provider: 'rest', a non-object input). Right on substance; the kit owes a helper or a written exception.
  9. Generated references, strictness counts (view.zod.ts strict sites 60 to 59, ui/ 214 to 213), variant-docs.json key provider:object|schema|value — RIGHT, subject to item 5 (the component.mdx rows regenerate again once the describes are fixed).
  10. Tests and fixtures — RIGHT. The retirement pin covers refusal at three doors with the prescription, the surviving arms value-for-value and still closed, zod's default report for other providers, the D3 registration, the absence of any ui/ViewData:* key row, and a tree-scoped absence walk whose matcher is proven on positives, on the page element's dataSource as a negative, and on prose; it joins vitest.repo-tests.json and is the second witness on the fifteen per-extension rows in scripts/cross-package-test-inputs.mjs (skills/**, content/**, scripts/** are declared whole by other rows). The two accept pins in view.test.ts become one refusal control, the kanban fixture moves to value, the batch-18 strictness row for the arm leaves, and component-props-unknown-members.pin.test.ts drops its HTTP_REQUEST lines and refuses their return.
  11. Teaching: content/docs/ui/views.mdx — RIGHT. The view-level example leaves; the pointer /docs/ui/pages#element-data-sources resolves (### Element data sources at pages.mdx line 191 on origin/main), and the prescription's regions[].components[] / dataSource: { provider: 'api', read: { url }, path? } spelling matches page.zod.ts (components at line 50, dataSource at line 696). Outside this diff, the only other view-level provider: 'api' sites on origin/main are release-owned prose (content/docs/releases/v16.mdx) and the page element's own example in pages.mdx, both correctly left.
  12. Governed surface skills/objectstack-ui/rules/list-views.md (Tier H), judged at this tier — text RIGHT, one omission flagged. The hunk removes the provider: 'api' example and the "Custom API endpoint" line, counts "two providers" for a list view (accurate: schema is form-only and was not counted before either), and adds "A view never calls an API: an API read is a page element's dataSource binding." Nothing it teaches is refused by the contract. What it omits: the replacement's spelling. The published catalog teaches dataSource nowhere — git grep dataSource origin/main -- skills is empty, and rules/pages.md teaches regions and components without the binding — so an author following the pointer finds no example inside the skill; the prescription and views.mdx carry it instead. The file's token ceiling (3141; 3136 to 3133 here) leaves no room in this file. Not a defect of this hunk; a Tier H follow-up on rules/pages.md (③ item G).

② Semver level

.changeset/23082-view-data-api-provider-retired.md: @objectstack/spec: minor, title feat(spec)!:, a BREAKING header, a FROM → TO table (both legs and the headers key), the one-line fix, the prescription verbatim, the ADR-0087 marker adr-0087: registered view-data-api-provider-retired, and the declaration line. Level matches what the diff publishes — RIGHT: an accept-set narrowing on a published authoring surface, shipped as minor under the repo's launch-window convention (the 17.x CHANGELOG carries the same sentence on at least nine entries); under pre mode the fixed group versions to 18.0.0-next.N regardless. No skip-changeset, correctly: @objectstack/spec publishes. Check Changeset is green on the head.

Clause-②: line — Clause-②: no (narrowing) on line 2 of the PR body and in the changeset body: the closed-pair arm the gate reads, consistent with BREAKING, and the dev's deviation from the claim's prose parenthetical is the right call (the claim's no is the same value). The version text inside the changeset is the one wrong fact (① item 4).

③ Boundary flags

Dev report 6138457534: open_questions: []; premise_still_valid: true; six deviations and four out-of-scope findings, each answered:

  • D1 Clause-② spelling (no (narrowing) versus the claim's no) — answered: right, the gate's arm spelling; matches the changeset.
  • D2 interrupted run; background jobs read, not re-run — answered: accepted; the head's check-runs are the gate verdicts, and the families those jobs carry (Build Core, Test Core, both Type Check legs) are still in progress as read above, so nothing in this record assumes them.
  • D3 verify-lock timeouts, check:skill-examples NOT MEASURED — answered: the narrowing argument (neither edited block is os:check-marked) is plausible; CI's run is the verdict; same in-progress caveat.
  • D4 nested client-react build collision — answered: own concurrency, no finding.
  • D5 harness refusal of a bash -c loop — answered: re-issued directly, no finding.
  • D6 no MCP writes, no PR-body PATCH, no label writes — answered: consistent with the PR as read (draft, author objectstack-fleet[bot], assignee os-justin, auto-merge off).
  • O1 check:platform-checklist main-red — answered: inherited on origin/main and on this branch's base; none of the cited files is in this diff; the triage lane carries it, not this card.
  • O2 HttpRequestSchema / HttpMethodSubsetSchema orphaned, describes still say "view data sources" — answered: left per the ruling; cross-repo (objectui packages/types imports the schema in a test), carrier the objectui follow-up. ⛔ The four component.zod.ts describes (① item 5) are NOT in this class: they are this PR's own teaching surface and are owed here.
  • O3 objectui reader population; approvals route constructs ApiDataSource in code — answered: the follow-up card's population, filed when the spec release is installable (cross-repo unlock rule). Added reading: Console Pin Gate is skipped on every PR head by its filter contract, so the dev's "cannot red that gate on a type" is unmeasured until main runs it after merge; the objectui-side tsc consequence of the narrowed ViewData (any provider === 'api' comparison on a spec-typed value) is the follow-up's first step.
  • O4 sdui.manifest.json gantt data still names the api arm — answered: regenerates with the next pin bump, not here.
  • Fork clause of the ruling — answered: measured zero producers of a view-level provider: 'api' outside documentation in objectstack, objectui and hotcrm with lit controls, zero producers of the write leg, cloud NOT MEASURED and covered by the maintainer's word; no fork, the card stays on this PR.

Escalations raised by this review:

  • E. Version stamping under pre mode (kit owner, spec-property-retirement). The skill's sentence rule leaves [version] unspecified, and 23042-retired-after-prerelease-stamp wrote the pre-mode rule for retiredAfter only. Six prescriptions since the line opened stamp 17; this one stamps 17.8.0. One written rule is owed; this PR follows it once written, or stamps the bare major like its siblings.
  • F. No helper for a discriminated-union arm retirement (kit owner): enumWithRetiredValues() covers z.enum only; two union-arm retirements (feat(spec)!: retire the bare string sort clause on the list-view doors — the PRODUCER half of the sort seam #17914, this one) now hand-write the map the skill forbids for enums.
  • G. The published skill catalog teaches dataSource nowhere (Tier H follow-up on skills/objectstack-ui/rules/pages.md): the replacement this PR points authors at has no spelling inside the catalog.
  • H. Stale pin label (this PR): ① item 7.

Required before a PASS on the next head: ① items 4, 5 and 7 (three string edits plus gen:docs; the pin re-cite), and the in-progress check-runs reported green.

Implemented-by: claude/issue-23082-retire-view-api-source
Reviewed-by: session_01BRQ9Mp1yH7R1az6MaUM18H

VERDICT: FAIL

…omponent describes, pin wording

Review 6138665801 ① items 4, 5, 7: the prescription, its pin regex and the
changeset quotation stamp the bare major (the house form of every
prescription added since the v18 line opened; 18.0.0-next.N is the first
release carrying this); ObjectGrid/ObjectMap/ObjectTree/ObjectGantt
`data` describes no longer list the `api` discriminator; the ledger note
and the changeset say 'measured at 7578323a544e' instead of 'the pin'.

Claude-Session: https://claude.ai/code/session_01BRQ9Mp1yH7R1az6MaUM18H
Co-authored-by: Claude <noreply@anthropic.com>
… the four data describes drop the api discriminator

gen:docs on the merged tree; check:generated reads all 14 artifacts up to date.

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 12f86031637d7aaffe0ce28819166fb55bad8931
Local-runs: none

Inputs read: card #23082 (body and all 5 comments: triage 6129766256, ruling 6134088274 letter C, claim 6136106180, dev reports 6138457534 and 6139676792), PR #23160 (body, both comments — the docs-drift advisory and the earlier record 6138665801 — the 24-file list, the net diff against the merge base dc93781c1, +634 / −250), the 48 check-runs on the head, and origin/main objects by git show / git grep for context. Nothing built, run or re-run. This is the review after the FAIL on be64fe664; its three required items are answered first.

Check-runs on the head as read for this record: 48 runs, every one completed; none in_progress, none failure. success: the five required contexts (Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard), Test Core (1/6) through (6/6), Type Check · workspace, Type Check · debt ledger, Type Check · source gates, Type Check · consumer gates, every Lint · leg, Build Docs, Dogfood Regression Gate, Spec property liveness, Check Changeset, Check Documentation Links, Flag docs affected by code changes, the claim and single-writer guards, Auto Label, Check PR Size. skipped by their own filters: Console Pin Gate (its if: excludes PR heads, so its verdict is not available here), the Dogfood Regression Gate matrix row, Dogfood Verify CLI, Temporal Conformance (live PG + MySQL), Packed-tarball smoke (opt-in), and one superseded Check PR Size / Auto Label pair. Test Core runs turbo run test test:repo (ci.yml line 941), so the spec repo project that carries the new retirement pin is inside that green. The earlier record's condition "the in-progress check-runs reported green" is met.

① Derived judgments

The three items 6138665801 required for a PASS, each answered:

  • Item 4, prescription version — FIXED. VIEW_DATA_API_PROVIDER_RETIRED (view.zod.ts) now reads was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove); the pin's PRESCRIPTION regex asserts 17 \(ADR-0049; the changeset's blockquote quotes the same string. Measured here: git diff a87d8be29 origin/main -- packages/spec/src (the commit that entered pre mode) adds six prescriptions to the package and all six stamp the bare major 17; packages/spec/package.json on main is 17.7.0, so the FROM column's "17.7 and earlier" stands. This is the form the earlier record offered ("stamps the bare major like its siblings"). The written rule for [version] under pre mode is still owed by the kit owner (escalation E, ③).
  • Item 5, four component.zod.ts describes — FIXED. The data describes on ObjectGridPropsSchema, ObjectMapPropsSchema, ObjectGanttPropsSchema and ObjectTreePropsSchema now read "discriminated on provider: object | value | schema"; content/docs/references/ui/component.mdx is regenerated in the head commit 12f860316 (four data rows). Across every regenerated reference hunk in the diff, no added line names the api provider; the removed lines are the former nested data[provider='api'] shapes and the ViewData[provider='api'].read / .write tables. shared/polarity-axes.ts keeps its source-comment row (cosmetic, as before).
  • Item 7, stale pin label — FIXED. packages/spec/liveness/view.json list.data.note now says "measured at 7578323a544e — .objectui-sha has since moved to 1fb1ced36243 and the next cross-repo re-measure re-reads it there"; its evidence cites objectui @7578323a544e (the sha form other rows use; Spec property liveness is green); the changeset says "measured at 7578323a544e"; the PR body's Liveness bullet carries the replacement sentence and names 1fb1ced36243 as the pin on main, which .objectui-sha confirms at origin/main and at the merge base dc93781c1. One cosmetic residue: the new test file's header comment still says "a renderer reader EXISTED at the objectui pin" without naming a sha — a source comment, not a contract or published surface; fold in on the next touch.

The whole net diff on this head, accept set and public surface:

  1. ViewDataSchema loses its api arm — RIGHT. provider: 'api' is refused at parse with the prescription on the union's error map, keyed on the input's provider === 'api', as an invalid_union issue at provider / data.provider / list.data.provider; any other unknown provider keeps zod's own report listing object, value, schema; the three surviving arms are value-identical and still closed (an undeclared key is refused). Ruling C executed on the ViewDataSchema branch only: HttpRequestSchema / HttpMethodSubsetSchema are untouched, still imported at view.zod.ts line 87 and re-exported at line 108 (so the import is not orphaned and Lint · ESLint is green); ui/page.zod.ts and the page element's ElementApiDataSourceSchema are not in the file list.
  2. ViewData narrows across the workspace — RIGHT, now measured. Type Check · workspace and Type Check · debt ledger are success on this head (the previous record could only read them in progress). No src/ outside packages/spec imports ViewData / ViewDataSchema on origin/main; the two @objectstack/lint fixtures move to the value arm.
  3. Four ratchets byte-identical, HttpRequest defs orphaned but exported — RIGHT. No ratchet file is in the 24-file list, the value-retirement shape; check:generated is inside the green Lint & Repo Gates / Build Core. The orphan (zero schema composes HttpRequestSchema after this change) is recorded, not acted on, per the ruling's "touches only the ViewDataSchema branch".
  4. ADR-0087 disposition: D3 view-data-api-provider-retired, STEP18_RATIONALE fragment at order 95, no D2 — RIGHT. No other fragment on origin/main uses order 95. The no-D2 reasoning holds (the replacement is a binding on a different metadata type; deleting a view's data would silently re-bind it to its object). The pin asserts the entry is in MIGRATIONS_BY_MAJOR[18].semantic and that no conversion id matches view-data-api; Check Changeset reads adr-0087: registered view-data-api-provider-retired and is green.
  5. Hand-written error map on a discriminated union versus the kit's enumWithRetiredValues() rule — acceptable, gap still escalated (F). The helper wraps a z.enum; a union arm's provider is a per-arm z.literal. The in-tree precedents on origin/main are the same shape: LIST_VIEW_SORT_STRING_RETIRED (view.zod.ts line 2948) and LIST_VIEW_TYPE_PAGE_RETIRED (line 2861). The map returns undefined for every other input and the pin controls it (provider: 'rest', a non-object input).
  6. Generated references, strictness counts (view.zod.ts 60 → 59, ui/ 214 → 213), variant-docs.json key provider:object|schema|value — RIGHT. migration.mdx's listViews.data type column now renders object \| object \| object where it read object \| … +3 more — the generator's rendering of a three-object union, not a hand edit.
  7. Tests and fixtures — RIGHT. view-data-api-provider-retirement.test.ts pins refusal at three doors with the prescription, the surviving arms value-for-value and closed, zod's default report for other providers, the D3 registration, the absence of any ui/ViewData:* key row, and a tree-scoped absence walk whose matcher is proven on positives, on the page element's dataSource as a negative, and on prose; it is listed in vitest.repo-tests.json (the repo project Test Core runs) and is the second witness on the fifteen per-extension rows in scripts/cross-package-test-inputs.mjs (check:cross-package-test-inputs is inside the green Lint & Repo Gates). view.test.ts keeps one refusal control, the kanban fixture moves to value, the batch-18 strictness row for the arm leaves, component-props-unknown-members.pin.test.ts drops its HTTP_REQUEST lines and refuses their return.
  8. Teaching: content/docs/ui/views.mdx — RIGHT. The view-level example leaves; the new paragraph states the same-origin, no-credential, signed-in-fetch rule the spec(ui): a page element dataSource can name an API endpoint (provider: 'api'), so a product page composes from a control-plane summary in metadata instead of a product-specific React widget (maintainer ruling 2026-10-12) #23016 ruling set, and the pointer /docs/ui/pages#element-data-sources resolves (### Element data sources, pages.mdx line 191 on origin/main).
  9. Governed surface skills/objectstack-ui/rules/list-views.md (Tier H), judged at this tier — text RIGHT, unchanged since the previous head (+3 / −5). The hunk removes the provider: 'api' example and the "Custom API endpoint" line, counts "one of two providers" for a list view (object, value; schema is form-only and was never counted in this list-view rule), and adds "A view never calls an API: an API read is a page element's dataSource binding." Nothing it teaches is refused by the contract, and what it points at exists in the contract (dataSource on page.zod.ts) and in views.mdx / pages.mdx. Tokens 3136 → 3133 under the file's 3141 ceiling; check:skills-token-ratchet is inside the green lint job. The omission the previous record flagged — the catalog teaches dataSource nowhere — is not this hunk's defect and stays a Tier H follow-up on rules/pages.md (G).
  10. The ruling's fork clause, re-measured here on origin/main d4aaeaf0e: provider: 'api' outside packages/spec/src, CHANGELOGs, the generated references and release prose hits only the page element's own changeset and example (.changeset/23016-*, pages.mdx line 215), prose (.changeset/23036-*, the liveness/page.json note), the generated sdui.manifest.json gantt description at the pin, and the four sites this diff removes or rewrites (views.mdx line 305, list-views.md line 47, the two lint fixtures); control provider: 'object' in 155 files. Zero producers of a view-level api source, none of the write leg; cloud NOT MEASURED and covered by the maintainer's word. No fork; the card stays on this PR.

Landing, stated so the record is not misread: the diff touches skills/**, so the PR is Tier H whole. This PASS is the contract-tier record the claim says the card owes before the approval request; the landing itself waits for an authorized APPROVED review on skills/**, then the owning seat. The PR is a draft, auto-merge is off, head repo equals base repo (not a fork), 884 changed lines.

② Semver level

.changeset/23082-view-data-api-provider-retired.md: '@objectstack/spec': minor, title feat(spec)!:, a BREAKING header, the FROM → TO table (both legs and the headers key), the one-line fix, the prescription verbatim (now stamping 17, ① item 4), adr-0087: registered view-data-api-provider-retired, and the declaration line. Level matches what the diff publishes — RIGHT: an accept-set narrowing on a published authoring surface, minor being the only admissible spelling — scripts/check-changeset-no-major.mjs refuses major in the launch window — and under pre mode (.changeset/pre.json mode pre, tag next, with pending major changesets on the fixed group such as 11509-element-flat-binding-retired and the 19939-flow-* set) the group versions to 18.0.0-next.N regardless. No skip-changeset label (the PR carries documentation, size/l, tests, tooling, protocol:ui), correctly: @objectstack/spec publishes. Check Changeset is green on the head.

Clause-②: line — Clause-②: no (narrowing) on line 2 of the PR body and in the changeset body: the closed-pair arm the gate reads, consistent with BREAKING; the claim's no is the same value. The one wrong fact the earlier record found in this section (the version text) is corrected.

③ Boundary flags

Dev report 6139676792 (patch round 1, head 12f860316): open_questions: []; premise_still_valid: true; five deviations, four out-of-scope findings and three PR-body notes, each answered:

  • D1 worktree re-created after the previous round's cleanup — no finding; the first-run gate refusals it caused were rebuilt and re-run by the dev, and the head's check-runs are the verdicts.
  • D2 the coordinator's interim reading (check:docs stale on 2c1dd7b97) — answered by the head: 12f860316 is the gen:docs commit, and Lint & Repo Gates / Build Core are green on it.
  • D3 lock contention, harness-backgrounded waits — no finding.
  • D4 gate batch run outside the lock with the two workspace-wide gates last — no finding.
  • D5 no MCP writes, no PR-body PATCH, no label or assignee write — consistent with the PR as read (draft, author objectstack-fleet[bot], assignee os-justin, auto-merge off, labels unchanged). The PR body's Liveness bullet now carries the report's replacement sentence, so another actor applied it (the seat the note was addressed to), not the dev; the optional Console Pin Gate sentence was not applied, and the body's Verification section still describes head be64fe664 — prose about a prior head, superseded by the card's newest report and by this head's check-runs; not a contract surface, no finding.
  • PR-body note 3 (sdui.manifest.json line accurate as written) — agreed; the manifest is generated from objectui's built tree at whatever pin is current.
  • O1 check:platform-checklist exit 1, the same 8 inherited problems — answered: .github/workflows/lint.yml (lines 2459–2465) keeps that gate out of per-PR CI by maintainer decision (run by hand and by the watchdog), which is why Lint & Repo Gates is green over it; none of the cited files is in this diff; the lane's main-red convention carries it, not this card.
  • O2 escalations E and F followed by measurement — answered: E (the kit's [version] sentence rule under pre mode; the bare-major prescription beside a "17.7 and earlier" FROM column is the tension a written rule resolves) and F (no helper for a discriminated-union arm) stay with the kit owner; this PR follows the measured house form and the in-tree precedent.
  • O3 escalation G — stands: a Tier H follow-up on skills/objectstack-ui/rules/pages.md so the catalog teaches the dataSource spelling it now points at.
  • O4 HttpRequestSchema / HttpMethodSubsetSchema orphan, objectui reader population, sdui.manifest.json — answered: cross-repo, carrier the objectui follow-up card (filed when the spec release is installable) and the next pin bump. Console Pin Gate is skipped on every PR head, so the objectui-side tsc consequence of the narrowed ViewData stays unmeasured until main runs it after merge — the follow-up's first step.
  • Fork clause of the ruling — re-measured here (① item 10): no fork.

Escalations standing after this record: E and F (kit owner, spec-property-retirement); G (Tier H follow-up on rules/pages.md). H (the stale pin label) is resolved on this head.

What landing still needs beyond this record: the maintainer's authorized APPROVED review on skills/** (Tier H); the body's maintainer quick-read section (维护者速读(草稿)) is in place for it.

Implemented-by: claude/issue-23082-retire-view-api-source
Reviewed-by: session_01BRQ9Mp1yH7R1az6MaUM18H

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

domain:spec seat 2 · os-justin · session session_01BRQ9Mp1yH7R1az6MaUM18H · 2026-10-13T07:33Z · head 12f860316。契约复审档复核 PASS:6140601222。

改了什么: 视图(列表、表单)的 data 数据源不再接受 provider: 'api',也就是自带 read / write 请求块、可以写任意地址和 headers 的那种写法。视图只能绑定对象(object)或静态行(value)。在元数据里声明的 API 调用只剩一条路:页面元素的 dataSource 绑定,只能请求本站地址,不带凭据,由控制台用登录态发出。旧写法在校验、发布、保存时被拒绝,错误信息里写明改法。对外技能文本 skills/objectstack-ui/rules/list-views.md(净减 2 行)和文档同步改成指向页面元素绑定。spec 发 minor 版,标 BREAKING,并附迁移对照表。

为什么改: 您在 #23082 批了 C。旧的视图级 API 源能把密钥写进 headers,也能把请求指向任意域名。这些内容会随视图一起存储,下发给每个读取该视图的客户端,再由控制台原样发出。页面元素那条路已按 #23016 的规则锁死。同一件事有两套规则时,按裁决保留受治理的那一套,撤掉另一套。实测 objectstack、HotCRM、objectui 三个仓都没有视图在用它。cloud 未测,您的「同意」已覆盖。

风险与代价(含回滚): 风险在没测到的存量元数据。如果 cloud 或客户项目有视图写了 provider: 'api',升级后该视图加载时会被拒绝并给出改法(不会悄悄改读别的数据),作者需要把 API 读取改到页面元素上。本 PR 不做自动迁移:机械地删掉 data,会让视图悄悄改读它绑定的对象。回滚方式是撤销本 PR,恢复旧写法,没有数据迁移需要回退。objectui 那边的删除(ApiDataSource 等)是另一张卡,等 spec 发版、能装上之后再做,本 PR 不动 objectui。

席位意见: 建议批准。第一轮契约复核判 FAIL:提示语里的版本号、四处组件描述仍在教已退役写法、液性账本里的 pin 引用过期。补丁轮已全部修正。第二轮复核 PASS,48 个 CI 检查全部完成,没有失败。技能文本只删不增,没有教任何已被拒绝的写法。还有一个不阻塞的缺口留作后续:技能包里还没有任何一处教页面元素 dataSource 的写法。席位已记账,作为 skills/objectstack-ui/rules/pages.md 的 Tier H 后续。

你要做的: 在本 PR 上点 Approve(请审已发给 os-zhuang 与 hotlong)。批准后由席位落地前检、摘标、翻 ready 并入合并队列。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 13, 2026 08:46
@os-zhuang
os-zhuang enabled auto-merge October 13, 2026 08:46
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 1912af4 Oct 13, 2026
56 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-23082-retire-view-api-source branch October 13, 2026 09:14
This was referenced Oct 13, 2026
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 protocol:ui size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Decision] Does a view's api data source adopt the page-element rule — a same-origin url and no headers written in metadata?

3 participants