From 2e8b86e9d7284d3a35dadc344e01754ca66aedb9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 00:33:11 +0000 Subject: [PATCH 1/2] docs(ui,protocol): document the six variants that only ever got name-checked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the #4177 variant/doc gate, using the signal it produced. That gate passes a variant if the page contains either `: ''` or a bare `` `variant` ``, and its header says plainly that the second form is loose: a pass means the page says the word, not that it documents the thing. Six variants were passing on the loose form alone — the gate's own "weak pass" list, and a precise worklist. apps.mdx enumerated nine navigation item types but only documented six. `report`, `action` and `component` shared one sentence — "the spec also defines …" — while the other six each had a section with an example. They now have their own, and the intro no longer implies a second tier of half-supported types. The `action` section earns its place: `actionName` lives inside a nested `actionDef` block, not at the top level, which is exactly the shape a reader guesses wrong. Both it and `component` note which sub-object stays OPEN by design (`actionDef.params`, `component.params`) — the sibling contract owns those, and after #4165 everything around them is strict. knowledge.mdx had a table naming `object` / `file` / `http` and no example of any, so the per-kind keys (`contentFields` vs `prefix` vs `urls`) appeared nowhere. Added one example each. Every example was parsed against the real schema before committing — NavigationItemSchema and KnowledgeSourceKindSchema, six for six. Writing docs from memory is how the region example in #4218 ended up using an `id` key that does not exist; not repeating that. Anchored-form coverage for these two unions: 9/12 → 12/12. check:variant-docs, check:docs, check-doc-authoring and check:strictness-ledger all green; content/docs/references unchanged. Refs #4001, #4177 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0147tNF4Snk7Ry1KGt4a5PY4 --- content/docs/protocol/knowledge.mdx | 28 +++++++++++++++++ content/docs/ui/apps.mdx | 48 ++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/content/docs/protocol/knowledge.mdx b/content/docs/protocol/knowledge.mdx index 06988e69ed..105ec9519b 100644 --- a/content/docs/protocol/knowledge.mdx +++ b/content/docs/protocol/knowledge.mdx @@ -119,6 +119,34 @@ Three source kinds: | `file` | A folder in `IStorageService` | Onboarded PDFs, uploads | | `http` | A list of remote URLs | External docs, RSS, sitemaps | +`kind` is the discriminator, and each kind carries its own keys — a key from one +kind is rejected on another: + +```typescript +// object — content comes from the named fields of each matching record +source: { + kind: 'object', + object: 'kb_article', + contentFields: ['title', 'body'], // at least one, required + metadataFields: ['category', 'author'], // optional, carried onto the document + where: { status: 'published' }, // optional ObjectQL filter +} + +// file — every object under a storage prefix +source: { + kind: 'file', + prefix: 'knowledge/handbook/', + mimeTypes: ['application/pdf', 'text/markdown'], // optional; omit to take all +} + +// http — an explicit URL list (each must be a valid URL) +source: { + kind: 'http', + urls: ['https://docs.example.com/guide', 'https://docs.example.com/faq'], + userAgent: 'ObjectStack-KnowledgeBot/1.0', // optional +} +``` + Every source binds to a named **adapter id** (e.g. `'ragflow'`, `'memory'`). The adapter id is resolved at runtime by the plugin that registers itself with that name. Adapter config (endpoint, dataset id, diff --git a/content/docs/ui/apps.mdx b/content/docs/ui/apps.mdx index 8d694a86e9..1276d19e52 100644 --- a/content/docs/ui/apps.mdx +++ b/content/docs/ui/apps.mdx @@ -57,7 +57,11 @@ const crmApp = { ## Navigation Items -The navigation tree supports nine item types, combined to create rich menu structures. The most common are shown below (`object`, `dashboard`, `page`, `url`, `group`, `separator`); the spec also defines `report`, `action`, and `component` items. +The navigation tree supports nine item types, combined to create rich menu structures: `object`, `dashboard`, `page`, `url`, `report`, `action`, `component`, `group` and `separator`. Each is documented below. + +`type` is the discriminator: a value outside that list is rejected with the full +set of valid ones, and every other key is checked against the branch you picked +rather than against all nine. ### Object Navigation @@ -121,6 +125,48 @@ Groups items into collapsible sections with children: } ``` +### Report Navigation + +Links to a saved report: + +```typescript +{ id: 'nav_pipeline', type: 'report', label: 'Pipeline Report', reportName: 'sales_pipeline', icon: 'file-bar-chart' } +``` + +### Action Navigation + +Runs an action instead of navigating to a surface. The reference lives in a +nested `actionDef` block — `actionName` is **not** a top-level key on the item: + +```typescript +{ + id: 'nav_import', + type: 'action', + label: 'Import Records', + icon: 'upload', + actionDef: { + actionName: 'bulk_import', + params: { objectName: 'account', mode: 'upsert' }, + }, +} +``` + +`actionDef` accepts only `actionName` and `params`. `params` itself is **open by +design** — the action owns its own parameter contract, so the app schema does +not validate what goes inside it. + +### Component Navigation + +Renders a registered component. `componentRef` is a component-registry key, not +a file path: + +```typescript +{ id: 'nav_directory', type: 'component', label: 'Directory', componentRef: 'metadata:directory', icon: 'contact' } +``` + +`params` is handed to the component as props and is **open by design** — the +props are the component's own contract. + ### Separator A visual divider in the navigation list. It renders no target and carries no From ac129a29352dd4700665888e61a885f7465eb13e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 00:35:19 +0000 Subject: [PATCH 2/2] chore: add the empty changeset this docs-only PR still needs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check Changeset failed: the gate wants a changeset on every PR, and its message says how — "an empty changeset is fine for changes that release nothing". I had asserted in the PR description that a docs-only change needs no changeset. That was wrong about this repo's convention: 72 empty changesets already exist, and they are how documentation work records itself without bumping a package. Added one in that form (empty frontmatter, description only). The alternative the message offers — a `skip-changeset` label — would suppress the record instead of writing it, which is the wrong trade for a change that has something worth saying in the release notes. Refs #4001, #4177 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0147tNF4Snk7Ry1KGt4a5PY4 --- .changeset/document-weak-pass-variants.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .changeset/document-weak-pass-variants.md diff --git a/.changeset/document-weak-pass-variants.md b/.changeset/document-weak-pass-variants.md new file mode 100644 index 0000000000..93d666ca1f --- /dev/null +++ b/.changeset/document-weak-pass-variants.md @@ -0,0 +1,4 @@ +--- +--- + +docs(ui,protocol): document the six navigation / knowledge-source variants that the #4177 variant/doc gate was only ever name-checking. `apps.mdx` enumerated nine navigation item types but gave `report`, `action` and `component` a single shared sentence instead of sections; `knowledge.mdx` named `object` / `file` / `http` in a table with no example of any, leaving the per-kind keys undocumented. Each example was parsed against the real schema (`NavigationItemSchema`, `KnowledgeSourceKindSchema`) before landing. Documentation only; releases nothing.