Repository navigation
docs(spec): related-list descriptions say which unknown names are refused; primary list and trackHistory describe the v3 record page - #27352
Conversation
…and trackHistory describe the v3 record page relatedLists and fieldGroups[i].relatedList now state the enforced boundary: a stack child with no inbound FK is refused everywhere; a child that exists nowhere is refused by defineStack and by a metadata write that names no package; a write into a package stores it and the record page places nothing for it. relatedList: 'primary' is a body table in the details column (inside the Details tab when tabbed, with no tab of its own), and enable.trackHistory feeds the record's one timeline stream (its Changes filter) instead of a History tab. Claude-Session: https://claude.ai/code/session_01WGufzgaGBuahtvHVCAa4ny Co-Authored-By: Claude <noreply@anthropic.com>
…es the timeline stream; changeset Claude-Session: https://claude.ai/code/session_01WGufzgaGBuahtvHVCAa4ny Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 22 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 166 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 8b84c77367c4dfa0f4c8a03b38fd71ff088c8ab0 && git checkout 8b84c77367c4dfa0f4c8a03b38fd71ff088c8ab0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cf218eed1fdb22467875dc012e296f47342942e3 0fae4859d8cfb549e435c1ee4a8b9551e942001b && git checkout -B drift-repro cf218eed1fdb22467875dc012e296f47342942e3 && git merge --no-ff 0fae4859d8cfb549e435c1ee4a8b9551e942001b
node scripts/docs-audit/affected-docs.mjs --json cf218eed1fdb22467875dc012e296f47342942e3
|
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Contract-review-claim: Triage seat, taking Generated by Claude Code |
Contract reviewServed-tier: Inputs read: card #27311 body and all 3 comments; card #27312 body and all 3 comments ( ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Each clause of the new text, read against the live code:
Attempts to break it:
Pin test Must-fix: none. Should-fix: (a) Implemented-by: VERDICT: PASS Generated by Claude Code |
…ackHistory narrows to a field-change history; plugin-detail README timeline column is the one stream Regenerates the references from the merged tree. Claude-Session: https://claude.ai/code/session_01WGufzgaGBuahtvHVCAa4ny Co-Authored-By: Claude <noreply@anthropic.com>
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Contract-review-claim: Triage seat, taking Generated by Claude Code |
Contract reviewServed-tier: Inputs read: card #27311 body and all 3 comments; card #27312 body and all 3 comments ( Delta since the prior PASS, judged commit by commit.
Prior should-fixes: (a) platform-prefix exemption unnamed — resolved exactly (both describes, both docblocks, and the pin). (b) ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Attempts to break it: a prefixed misspelling (
Pin test (the gate for the rewritten rules, read as one change): pins phrases, not sentences; every r1 phrase is pinned, so the ④ should-fix on Must-fix: none. Should-fix: (b) Implemented-by: VERDICT: PASS Generated by Claude Code |
domain:spec#3 · os-support-ai · session_01WGufzgbJhFoXYsLSe5WSPP
Requested by Yibo Wang · project thread
Fixes #27311
Fixes #27312
Clause-②: no
Metadata-protocol: none
Seat ruling on #27311: option A (by design). The boundary in
packages/lint/src/validate-related-list-refs.ts's header is deliberate: under a package closure a child outside it may be a downstream package's object that points in, which the closure cannot see. Refusing there would refuse legitimate stacks, and option B needs a new declaration surface no card asks for. So the contract text changes to say what is enforced. No change to the lint rule,defineStackor the accept set.Landing order. #27312's text describes the record page v3 that PR #27310 builds. #27310 has merged (
eef83734), and this branch carries it through a merge ofmainatcf218eed1.维护者速读
relatedLists和 field group 的relatedList说清楚哪些未知名称会被拒;relatedList: 'primary'和enable.trackHistory改成描述 v3 记录页(主表在详情列里,字段变更进时间线,不再有独立页签)。packages/spec/src/data/object.zod.ts(relatedLists 的 describe 与 docblock),packages/spec/src/data/field.zod.ts(relatedList 的 describe),content/docs/ui/pages.mdx(合成记录页一段)。pnpm --filter @objectstack/spec exec vitest run src/data/record-page-describes.test.ts,应见 10 个测试全绿。What changes
Description text only. No key, type, default, accepted value or export moves;
check:api-surfaceandcheck:authorable-surfaceare green with no artifact change.#27311:
ObjectSchema.relatedListsandfieldGroups[i].relatedListMeasured on
origin/main6d7634a:defineStack(collectRelatedListNameErrors,stack.zod.ts) refuses a name whose child is declared with no inbound FK, and a name whose child is nowhere (sibling-package and platform objects stay unjudged). The runtime door runsvalidateRelatedListRefs, which refuses a stack child with no inbound FK on every write, and a child missing from the collection only underwholeCollection(reference-integrity-suite.ts: a gated write with nopackageScope). A write into a package stores the entry.Before (
relatedListsdescribe):After:
Before (
fieldGroups[i].relatedListdescribe):After:
Both docblocks say the same, and the
relatedListsone points at the lint rule's header for the reasoning. The platform-prefix clause (r1) is measured atstack.zod.tscollectRelatedListNameErrors:if (!fieldsByObject.has(child) && (artifactScope.has(child) || isPlatformObjectName(child))) return;, whereisPlatformObjectNameishasPlatformObjectPrefix(PLATFORM_OBJECT_PREFIXES = ['sys_', 'cloud_', 'ai_'],platform-object-names.ts). The runtime door underwholeCollectionjudges such a name against the registry (validate-related-list-refs.ts:if (!fieldsByObject.has(child) && !options?.wholeCollection) return;).#27312:
relatedList: 'primary'andenable.trackHistoryMeasured on #27310's head
349cd5625c:ui/packages/plugin-detail/src/synth/buildDefaultPageSchema.tsbuildDefaultTabs: a list withisBody ?? isPrimaryis arecord:related_listinside the Details tab, placed bybodyTablePlacementright after the FIRST field section; every other list is a side card in the one Related tab. No per-list tab is emitted. The column tiers (recordPageColumns.ts) draw the same Details content in the details column.RecordDetailView.tsxreadssys_audit_logonly whenenable.trackHistory === true(historyEnabled), and only for the synthesized page;auditEntriesToFeedItemsturns them intofield_changeitems of the one timeline stream (recordTimelineStream.ts, filters All / Activity / Discussion / Changes,streamChanges: 'Changes'inen.ts). No History tab is emitted. On an assigned pageRecordFieldChangesContextisnull, and arecord:historyblock self-fetches the record'ssys_activityrows without readingtrackHistory.Before (
relatedListdescribe, the arms that move):After:
Before (
enable.trackHistorydescribe):After:
The order asked for "and the History block on an authored page". Measured,
record:historydoes not readtrackHistory, so the text says an authored page shows a field-change history where it places that block, and does not say the flag feeds it. r1 narrows "changes" to "a field-change history": an authoredrecord:activityblock self-fetchessys_activityand still lists field edits as activity lines (record-activity.tsx), which the docblock now says.Other sentences the same promises reached
content/docs/ui/pages.mdx, "The synthesized default": the tab strip is now Details / Timeline / Related (plus Approvals), a primary list is a table inside Details, attachments close the Related tab, and the timeline is one stream with the Changes filter. The three-column sentence drops "then Activity and History".ui/packages/plugin-detail/README.md(r1), the wide-layout table: the 1280 px timeline column said "the discussion composer on top, then Activity and History"; onmainafter feat(plugin-detail,app-shell,components): record page v3 layout #27310 it is one stream (buildDefaultTimelineemits onerecord:activity), so it now reads "one stream: the composer on top, then the discussion, the activity and, withenable.trackHistory, the field changes, filtered by All / Activity / Discussion / Changes". The changeset gains'@object-ui/plugin-detail': patchbecause itsfiles[]shipsREADME.md.packages/spec/liveness/object.json, rowtrackHistory: the evidence names the timeline stream instead of the History tab (status unchanged,live).packages/spec/src/system/metadata-form-zod-reconciliation.test.ts: onewhystring said a'primary'switch "promotes the list to its own tab"; it now says "places the list in the record's body".pnpm --filter @objectstack/spec check:generated --fix:content/docs/references/{api/metadata,data/field,data/object,system/migration}.mdx. No translation bundle carries these describes (the Studio form's ownhelpTextrows are separate text; see Acceptance notes).Tests
packages/spec/src/data/record-page-describes.test.ts(10 tests) pins each rewritten describe's load-bearing phrase in the JSON-schema projection, plus the absence of "its own tab" / "History tab" and of the old unbounded refusal sentence.object.zod.tsandfield.zod.tsrestored fromorigin/main6d7634a, the file goes red, 10 of 10 (Test Files 1 failed,Tests 10 failed); restored fromHEAD,git diff HEADempty.trackHistory). Reverse leg: withobject.zod.tsrestored from the r0 heade4e06533f, exactly those 3 go red (Tests 3 failed | 7 passed); restored fromHEAD, the tree is clean.@objectstack/specon head0fae4859d(after the main merge), under the verify lock: build,gen:schema,gen:docsandcheck:generated(all 14 artifacts up to date), thentest(Test Files 750 passed, Tests 22306 passed, 1 todo) andtypecheck: VERDICT command-exit 0. Onlypackages/specsource and one README move, and the public surface is byte-unchanged, so no importer owes a run.Acceptance notes
skills/objectstack-ui/SKILL.mdlines 109-110 and 117-121 ("its own tab on the tabbed page"; "gets its own tab"; "Promoting a child table to a tab"), andskills/objectstack-data/rules/relationships.mdlines 347-353 ("(its own tab on the tabbed page)"; "a name matching no inbound FK is refused").skills/**is Tier H, so they are left for a governed follow-up (carded as skills: objectstack-ui and objectstack-data still teach a primary related list's "own tab" and an unbounded relatedLists refusal after the spec describes moved (#27352) #27354).ui/content/docs/guide/slotted-pages.md(on feat(plugin-detail,app-shell,components): record page v3 layout #27310's head) said "one tab per related list flaggedrelatedList: 'primary'" and "→ History". It no longer exists onmain(retired by docs(ui-merge): fold objectui.org into objectstack docs, K4 — retire the site, host 301s, links #27329), so nothing is owed.object.form.ts'srelatedListshelp ("saving or validating the object refuses it") is shown only in Studio, whose pre-write check (PR feat(app-shell,i18n): Studio orders an object's related lists, and a field group can place a related table #27303, merged) refuses such a name, so it is true for its reader and stays.content/docs/protocol/objectql/security.mdxalso said "History tab"; feat(plugin-detail,app-shell,components): record page v3 layout #27310 rewrites that line, so it is not touched here.Verification
Derived gates on head
0fae4859d(after the main merge and a fullpnpm exec turbo run build --concurrency=2, 115/115 tasks):node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 106 (the README addscheck-sdui-manifestand its self-test); all 106 exit 0.dispatch-gates --ranreconciles: 106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN.106 gate commands and exit codes
Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01WGufzgaGBuahtvHVCAa4ny