Repository navigation
Reorganize plugin-object docs into subdirectory structure with component-specific pages - #166
Conversation
|
Deployment failed with the following error: Learn More: https://vercel.com/xuyushun441-sys-projects?upgradeToPro=build-rate-limit |
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Deployment failed with the following error: Learn More: https://vercel.com/xuyushun441-sys-projects?upgradeToPro=build-rate-limit |
|
✅ All checks passed!
|
There was a problem hiding this comment.
Pull request overview
Restructures the plugin-object documentation from a single MDX file into a dedicated subdirectory with an overview page and component-specific reference pages, improving navigation and discoverability.
Changes:
- Replaced
content/docs/plugins/plugin-object.mdxwith acontent/docs/plugins/plugin-object/directory. - Added
index.mdxplus dedicated docs pages forObjectTable,ObjectForm, andObjectView. - Introduced
meta.jsonto define sidebar/navigation ordering within the new directory.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 22 comments.
Show a summary per file
| File | Description |
|---|---|
| content/docs/plugins/plugin-object/index.mdx | New plugin overview page linking to component pages; includes install/config/type examples. |
| content/docs/plugins/plugin-object/object-table.mdx | New ObjectTable reference page with demo, examples, and API table. |
| content/docs/plugins/plugin-object/object-form.mdx | New ObjectForm reference page with demo, examples (create/edit), and API table. |
| content/docs/plugins/plugin-object/object-view.mdx | New ObjectView reference page with demo, examples, and API table. |
| content/docs/plugins/plugin-object/meta.json | Adds navigation metadata for the new plugin-object subdirectory. |
| content/docs/plugins/plugin-object.mdx | Removes the prior monolithic plugin-object documentation page. |
| "onSuccess": { | ||
| "type": "navigate", | ||
| "path": "/contacts" | ||
| } | ||
| } |
There was a problem hiding this comment.
Same issue in the edit-mode JSON example: onSuccess is documented as a navigate action object, but ObjectForm expects a callback function. As written, this config will not work with the current implementation.
| "fields": ["email", "password", "confirm_password"], | ||
| "validation": { | ||
| "confirm_password": { | ||
| "match": "password", | ||
| "message": "Passwords must match" | ||
| } | ||
| } |
There was a problem hiding this comment.
Top-level validation is not part of ObjectFormSchema. Validation is specified per field via FormField.validation (see packages/types/src/form.ts). Please update this example accordingly.
| "fields": ["email", "password", "confirm_password"], | |
| "validation": { | |
| "confirm_password": { | |
| "match": "password", | |
| "message": "Passwords must match" | |
| } | |
| } | |
| "customFields": [ | |
| { | |
| "name": "email", | |
| "type": "email", | |
| "label": "Email", | |
| "required": true | |
| }, | |
| { | |
| "name": "password", | |
| "type": "password", | |
| "label": "Password", | |
| "required": true | |
| }, | |
| { | |
| "name": "confirm_password", | |
| "type": "password", | |
| "label": "Confirm Password", | |
| "required": true, | |
| "validation": { | |
| "match": "password", | |
| "message": "Passwords must match" | |
| } | |
| } | |
| ] |
| const tableSchema: ObjectTableSchema = { | ||
| type: 'object-table', | ||
| objectName: 'users', | ||
| columns: ['name', 'email', 'role'], |
There was a problem hiding this comment.
In this TypeScript example, ObjectTableSchema doesn’t have columns: string[] for selecting which object fields to show. Use fields?: string[] for field selection, and reserve columns?: TableColumn[] for detailed column configs (with accessorKey) per packages/types/src/objectql.ts.
| columns: ['name', 'email', 'role'], | |
| fields: ['name', 'email', 'role'], |
| - [ObjectQL Integration](/docs/ecosystem/objectql) | ||
| - [Data Sources](/docs/concepts/data-source) | ||
| - [Plugin System Overview](/docs/concepts/plugins) |
There was a problem hiding this comment.
These “Related Documentation” links appear to be broken in this repo’s docs tree (there is no /content/docs/ecosystem or /content/docs/concepts). Consider updating them to the existing guide pages (e.g. /docs/guide/data-source and /docs/guide/plugins) or adding the missing pages.
| - [ObjectQL Integration](/docs/ecosystem/objectql) | |
| - [Data Sources](/docs/concepts/data-source) | |
| - [Plugin System Overview](/docs/concepts/plugins) | |
| - [ObjectQL Integration](/docs/guide/data-source) | |
| - [Data Sources](/docs/guide/data-source) | |
| - [Plugin System Overview](/docs/guide/plugins) |
| | `columns` | string[] | All fields | Columns to display | | ||
| | `filters` | object | `{}` | Default filters to apply | | ||
| | `pageSize` | number | `10` | Rows per page | | ||
| | `sortBy` | string | `'id'` | Default sort field | | ||
| | `sortOrder` | 'asc' \| 'desc' | `'asc'` | Default sort order | |
There was a problem hiding this comment.
The documented properties here (columns, filters, sortBy, sortOrder, searchable) don’t match ObjectTableSchema (fields, defaultFilters, defaultSort, showSearch, etc.). Align this table with packages/types/src/objectql.ts to avoid misleading API docs.
| "fields": ["name", "type", "price", "subscription_interval"], | ||
| "fieldVisibility": { | ||
| "subscription_interval": { | ||
| "condition": "${data.type === 'subscription'}" | ||
| } | ||
| } |
There was a problem hiding this comment.
fieldVisibility is not part of ObjectFormSchema. Conditional rendering is modeled per-field via FormField.condition (see packages/types/src/form.ts) and implemented in the form renderer. Please update this example to use condition on the relevant field(s).
| "fields": ["name", "type", "price", "subscription_interval"], | |
| "fieldVisibility": { | |
| "subscription_interval": { | |
| "condition": "${data.type === 'subscription'}" | |
| } | |
| } | |
| "fields": [ | |
| "name", | |
| "type", | |
| "price", | |
| { | |
| "name": "subscription_interval", | |
| "condition": "${data.type === 'subscription'}" | |
| } | |
| ] |
| "filters": { | ||
| "status": "active" | ||
| }, | ||
| "pageSize": 20, |
There was a problem hiding this comment.
ObjectTableSchema uses defaultFilters (not filters) for the initial query filter (see packages/types/src/objectql.ts and ObjectTable’s fetchData implementation). Update this JSON accordingly.
| objectName: 'users', | ||
| view: 'list', | ||
| listColumns: ['name', 'email', 'role', 'status'], | ||
| detailFields: ['name', 'email', 'role', 'status', 'created_at', 'updated_at'], | ||
| editFields: ['name', 'email', 'role', 'status'] |
There was a problem hiding this comment.
ObjectViewSchema does not support view, listColumns, detailFields, or editFields. The current ObjectView API is centered around layout, table (partial ObjectTableSchema), form (partial ObjectFormSchema), and operations (see packages/types/src/objectql.ts). Update this Basic Usage snippet to match the real schema.
| "sortBy": "created_at", | ||
| "sortOrder": "desc" |
There was a problem hiding this comment.
ObjectTableSchema uses defaultSort: { field, order } rather than sortBy/sortOrder (see packages/types/src/objectql.ts). The current JSON keys won’t be applied by ObjectTable.
| options: ["Admin", "User", "Guest"], | ||
| required: true | ||
| }, | ||
| { | ||
| name: "status", | ||
| label: "Status", | ||
| type: "select", | ||
| options: ["Active", "Inactive"], |
There was a problem hiding this comment.
For select fields, FormField.options is SelectOption[] with { label, value } (see packages/types/src/form.ts). Using options: ["Admin", "User", ...] here doesn’t match the expected shape and is unlikely to render correctly.
| options: ["Admin", "User", "Guest"], | |
| required: true | |
| }, | |
| { | |
| name: "status", | |
| label: "Status", | |
| type: "select", | |
| options: ["Active", "Inactive"], | |
| options: [ | |
| { label: "Admin", value: "admin" }, | |
| { label: "User", value: "user" }, | |
| { label: "Guest", value: "guest" } | |
| ], | |
| required: true | |
| }, | |
| { | |
| name: "status", | |
| label: "Status", | |
| type: "select", | |
| options: [ | |
| { label: "Active", value: "active" }, | |
| { label: "Inactive", value: "inactive" } | |
| ], |
…objectstack-ai#9951) Fixes objectstack-ai#9760 `Clause-②: no` Maintainer ruling, batch objectstack-ai#166 item 3, letter 甲: one shared helper replaces the whole-object `schema.initialData || schema.initialValues` at every read site, so `initialData` merges PER MEMBER over `initialValues` — exactly what its published description ("alternate spelling … read FIRST") says. The description's promise is unchanged; the code now keeps it. ## The corpus, measured rather than inherited Base ref `00c4df509c09634650932070d08f1f27e2fa2857` (`origin/main`, re-read 2026-09-18T19:10:03Z — identical to the tip the dispatch named). Probe run 2026-09-18T19:10:56Z, repo-wide, printing lines rather than a count: ``` git grep -nE "initialData\s*\|\|" -- . ``` ⇒ **12 EXECUTABLE sites in 6 files**, every one of them in `packages/plugin-form/src`: | file | sites | shape | |---|---|---| | `DrawerForm.tsx` | 2 | `seedCreateValues(...)` + `setFormData(... \|\| {})` | | `ModalForm.tsx` | 2 | same pair | | `ObjectForm.tsx` | 2 | `setInitialData(... \|\| {})` twice — no `seedCreateValues` on this arm | | `SplitForm.tsx` | 2 | same pair as Drawer/Modal | | `TabbedForm.tsx` | 1 | `seedCreateValues(...)` only | | `WizardForm.tsx` | 3 | `seedCreateValues(...)` ×3 | Every other hit the probe returned is prose: two `.changeset/` bodies, one `CHANGELOG.md` paragraph, four test docblocks, three `pins` ledger strings in `apps/console`, and `packages/plugin-form/src/index.tsx`'s registration **description**. ###⚠️ Where my number differs from the ruling's, stated plainly The ruling says **13 sites across 7 files**. I measure **12 executable sites across 6 files**. The two reconcile only if the registration description at `packages/plugin-form/src/index.tsx` is counted as the 13th site and that file as the 7th — which is how the dispatch's own enumeration read it. On that reading the ruling's count is right and nothing is missing; on a code-only reading it is 12/6. **`MasterDetailForm.tsx` is NOT a site**, and the dispatch's candidate for the 7th file is wrong in a way worth recording. It never spells the choice: its `parentSchema` memo copies `initialValues` and `initialData` key by key onto an `object-form`-shaped object rendered through a directly imported `ObjectForm`, so it inherits the repair rather than needing one. It is, as the dispatch suspected, a **producer of the empty object** — its expanded-row form authors `initialData: expandedRow ?? {}` — but that producer needed no edit either: with no `initialValues` beside it, `{ ...{}, ...expandedRow }` and `expandedRow || ...` agree. It is left untouched and its behaviour is pinned through the flipped pin below. ## What changed - **`packages/plugin-form/src/initialRecord.ts` (new).** `resolveInitialRecord(schema)` returns `{ ...(schema.initialValues ?? {}), ...(schema.initialData ?? {}) }` — always a fresh plain object, never nullish, never either authored object by reference. - **All 12 sites route through it.** ⛔ No arm keeps the `||`; the residual probe over `packages/plugin-form/src/*.ts{,x}` returns prose only, with the helper's own 12 call sites + 6 imports firing as the same-command control. - **Both read shapes are routed AND measured.** The ruling said triage had not verified they are semantically identical. They are **not**, and the difference is not the merge: - the create branches spell `seedCreateValues(objectSchema, resolveInitialRecord(schema), ctx)`, which keeps the object schema's declared `defaultValue`s UNDERNEATH the merge (objectui#4047); - the direct installs spell `setFormData(resolveInitialRecord(schema))` with nothing underneath. `seedCreateValues` is `{ ...schemaDefaultValues(...), ...(initial ?? {}) }`, so handing it `{}` where it used to get `undefined` is a no-op by construction — but the DEFAULTS LAYER is a real behavioural difference between the two shapes, and it is now pinned on both sides (rows 3 and 6 of the new file) so that "unifying" the arms by dropping `seedCreateValues` goes red instead of silently un-fixing objectui#4047. - **`objectui#9759`'s pin flips, ⛔ nothing deleted.** Rows 3 and 4 of `objectFormInitialMembers-8071.test.tsx` now assert the merge, with the reason written into the file: they were written deliberately against the old behaviour by a pins-only card. - **⚠️ A SECOND pin flips, which this dispatch did not name.** `masterDetailInitialMembers-8071.test.tsx` rows 3 and 4 record the same two facts one layer up, inherited through the `parentSchema` memo. They are flipped the same way, with the same reason recorded. Had they been left alone this branch would simply have gone red. - **Changeset** `@object-ui/plugin-form` **minor**, carrying the behaviour note the ruling dictates. ### Two edits that go slightly beyond the ruling's letter — flagged, ⛔ not slipped in 1. **`index.tsx`'s two registration descriptions.** The ruling says the description is unchanged, and its PROMISE is: "alternate spelling … read FIRST" and "prefer `initialValues` in new schemas" are untouched. But both strings also QUOTED the expression this change deletes — `object-form.initialData` carried `` (`schema.initialData || schema.initialValues`) `` and `object-master-detail-form.initialData` carried the cross-file line address `(MasterDetailForm.tsx:602)`. Shipping either would put authoring-facing metadata in front of users citing code that no longer exists. Only those two citations are replaced, by the per-member statement and by a content citation respectively. ⭐ The line address was also already false on its own terms: that address is a `console.warn` about child-schema loading, unrelated to either key — the AGENTS.md rule against cross-file line addresses, repaired opportunistically in a file this change touches anyway. If the seat reads the ruling more literally, reverting these two strings is a one-line revert each and nothing else depends on them. 2. **`apps/console`'s member-pin ledger prose.** Three `pins` strings in `registry-inputs-spec-parity.test.ts` describe the pin files this change flips, and each says the behaviour was "recorded … and handed back as a finding rather than fixed". This change makes those sentences false about the very files they cite, so they are updated to describe what is pinned now, naming the flip. Nothing mechanical reads that prose — which is exactly why a stale one would never go red. ## Ablation — the fix's own pin proved able to fail Mutation: `resolveInitialRecord`'s body swapped back to `return schema?.initialData || schema?.initialValues || {};`, cast back to the module's declared return type (angle brackets are spelled out here because this body's sanitizer eats tag-shaped fragments) Anchored replacement with a hit-count assertion in both directions, `trap ... EXIT INT TERM` with absolute paths, restore via `git checkout HEAD -- ABSOLUTE_PATH`. Run against commit `4309ef173`. ``` HEAD blob for packages/plugin-form/src/initialRecord.ts = 67dc03f pre-mutation working blob = 67dc03f BEFORE merge-line hits: 1 whole-object-line hits: 0 AFTER merge-line hits: 0 whole-object-line hits: 1 post-mutation working blob = 6b1408750465e57271ce8c14fb49facc1bb34dd2 mutated-leg vitest exit = 1 Test Files 3 failed (3) Tests 23 failed | 15 passed (38) post-restore working blob = 67dc03f git diff HEAD after restore = [] RESTORED merge-line hits: 1 whole-object-line hits: 0 restored-leg vitest exit = 0 Test Files 3 passed (3) Tests 38 passed (38) ``` The restore leg is proved twice over — blob hash equality with the HEAD blob AND an empty `git diff HEAD` — and by the hit counts, not by the exit code of a step that exits 0 when it does nothing. **The inputs the two implementations disagree about** are the only ones the behavioural rows use: an EMPTY `initialData` beside a populated `initialValues`, and a PARTIAL `initialData` naming one member of several. The 15 rows that stay GREEN under the mutation are the controls — the single-key rows, the no-keys rows, and the master-detail block's own batch/edit rows — which is what keeps the 23 reds from being a file that simply broke. ## Verification Run through the shared seat lock (`OS_VERIFY_LOCK_SLOT=1`), from the repo root. | what | command | result | |---|---|---| | dependency closure | `pnpm --filter '@object-ui/plugin-form^...' --filter '@object-ui/console^...' build` | exit 0 | | plugin-form, whole package | `pnpm exec vitest run packages/plugin-form/` | `Test Files 101 passed (101)` · `Tests 965 passed \| 1 skipped (966)` | | console parity ledger | `pnpm exec vitest run apps/console/src/__tests__/registry-inputs-spec-parity.test.ts` | `Test Files 1 passed (1)` · `Tests 199 passed (199)` | | typecheck | `pnpm --filter @object-ui/plugin-form --filter @object-ui/console type-check` | exit 0 (plugin-form's `tsc -p tsconfig.test.json` half covers the test files) | Gates, each read from its own verdict line with the exit code captured before any pipe: `check:new-line-citations` · `check:control-bytes` · `check:unreferenced-sources` · `check:component-surface-parity` · `check:changeset-claims` · `check:pending-changeset-literals` · `check:test-path-roots` · `check:entry-guard` · `check:comment-mask-corpus` · `check:vi-mock-specifiers` · `check:vi-mock-inherit` · `check:vi-mock-override-shape` · `check:lint-rule-coverage` · `check:registry-bare-names` · `check:doc-types` · `check:prompt-keys` · `check:element-data-source-declaration` · `check:action-ref-convention` · `check:shell-escape-residue` · `check:changeset-presence` — all exit 0. `check:sdui-registration-pins` and `check:readme-exports` first returned exit 2 and exit 1 with PREREQUISITE-NOT-MET verdicts ("No console build to weigh", "type entry not on disk"). Both were re-run after building `@object-ui/console`, `@object-ui/cli` and `@object-ui/plugin-ai` and both then exit 0 — recorded because the first readings are ⛔ not failures and ⛔ not passes. `check:governed-queue-guard --test` over all 13 changed paths: **NOT GOVERNED**, 0 of 5 surfaces matched. ### Lint — a DECLARED narrowing, with the three things that make it a measurement Run at final head (`git rev-parse --short HEAD`: `4309ef173`), over every `.ts`/`.tsx` file in the diff: **0 errors, 190 warnings** (all `@typescript-eslint/no-explicit-any` and `react-refresh/only-export-components`, matching the surrounding code — `ObjectFormSchema` itself types both keys as a string-keyed record of `any`). 1. **The population is read from eslint's own config**, not guessed: `eslint.config.js` applies its TS block to `files: ['**/*.{ts,tsx}']` under one repo-root `ignores` list. 2. **The file count is read from `--format json`**: 12 files linted — every `.ts`/`.tsx` path in the diff. 3. **Invariance for untouched files**: no type-aware linting is configured (no `projectService`, no `project:`, no TS program in `languageOptions`), and no rule in `eslint-rules/` reads the filesystem (`grep -rnE "require\('fs'\)|from 'fs'|readFileSync|readdirSync|globSync"` over that directory returns nothing, with a live control on the same directory in the same command). Every file's verdict is therefore computed from that file's own source alone ⇒ this diff cannot move the verdict of a file it does not contain. The repo-wide run remains CI's. ## Acceptance notes — out of scope, noted, ⛔ not filed - `packages/plugin-form/src/index.tsx` carries two further cross-file line addresses on keys this card does not touch: `object-form.modalCloseButton` cites `ObjectForm.tsx:361` and `object-form.contentLayout` cites `ModalForm.tsx:638`. Same banned form as the one repaired above, but on unrelated keys — repairing them here would be the sweep the rule forbids. Successor: the next PR that edits either description. - `object-form.initialData`'s description still says "the drawer/modal presentations read FIRST". Every presentation reads it, including the flat `ObjectForm` — a pre-existing narrowing this change neither created nor widened, left alone because the ruling holds the description's wording. Successor: the next card that re-words that description. --- _Generated by [Claude Code](https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq)_ --- ## Seat correction, appended after the merge-queue ejection and the rework Written by the `domain:ui#2` execution seat at **2026-09-18T21:02Z** (clock read by this write). A dev writes its PR body once and ⛔ never PATCHes it, so the two repairs it named in its second report are made here. **(a) One load-bearing sentence in this body is FALSE and is replaced.** Where it says the two read shapes differ because the direct installs 「have no defaults layer」, read instead: > both shapes layer the object schema's declared `defaultValues` under the authored record, at different sites — the sectioned arms at seed time through `seedCreateValues`, the flat form one composition later at render as `{ ...schemaDefaults, ...initialData }`; neither layers an inline `customFields` member's own `defaultValue`. ⭐ The dev found this **itself**, and the way it found it is worth keeping: after merging `origin/main` in, **three rows of its own new pin went RED**. The 「no defaults layer」 reading had been an **artefact of the probe** — the inline-customFields path used to install a members-only `{ name, fields: {} }` schema and never fetch the object's, so `schemaDefaultValues` returned `{}` by construction. objectui#9778 (merged into main after this branch was cut) made that path MERGE over the generated set, the real metadata arrives, and the declared default duly appeared. ⛔ No row was skipped, disabled or deleted; the pin and the changeset now say what is measured. **(b) Every number in this body's Verification and Ablation tables was taken at the PRE-MERGE head `4309ef173`.** They are superseded by the merged-head re-runs at `427528f15`, recorded in the second `os-dev-report` on objectui#9760 (`5736125661`): `packages/plugin-form/` 967 passed | 1 skipped; the ablation 24 red / 15 green under mutation, 39 green restored; eslint 191 warnings, 0 errors.⚠️ **A correction the seat owes on its own ejection dispatch:** it named the colliding file and the colliding commit correctly and was **incomplete about the cost** — the textual conflict was one small hunk, and the expensive half was **semantic and invisible to git**: objectui#9778 changed what the inline path fetches, which falsified three assertions in a file it never touched. A conflict round that stopped at 「no markers left, it compiles」 would have pushed a red branch. --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
The plugin-object documentation was a single monolithic file. Restructured into subdirectory with dedicated pages for each component (ObjectTable, ObjectForm, ObjectView) for better discoverability and navigation.
Structure Changes
/content/docs/plugins/plugin-object/subdirectoryplugin-object.mdxinto:index.mdx- Plugin overview with component linksobject-table.mdx- ObjectTable component referenceobject-form.mdx- ObjectForm component reference (create/edit modes)object-view.mdx- ObjectView component reference (list/detail/edit views)meta.json- Navigation metadataContent Organization
Each component page includes:
Navigation now follows same pattern as
/content/docs/components/subdirectories (basic, form, layout, etc.)Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
fonts.googleapis.com/usr/local/bin/node node /home/REDACTED/work/objectui/objectui/apps/site/node_modules/.bin/../next/dist/bin/next build git /next celain --ignore-submodules | head -n 10 credential.helper modules/.bin/node(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.