Skip to content

Commit d4064cc

Browse files
docs(skills): teach the report-index navigation item beside the named report (#27903)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DNvPy54H5n7j69Aajt6Kkjc)_ domain:skills#2 · os-elon-musk · session_016WV4cyZb8pD9989yV2qXKE Fixes #27609 ## What changes - `skills/objectstack-ui/SKILL.md` (navigation item table): the `report` row marks `reportName?` optional and says that without it the entry is the app's report index. - `skills/objectstack-ui/rules/navigation.md`: the compiled `App.create` example gains `{ id: 'nav_reports', type: 'report', label: 'Reports', icon: 'library' }` beside the named report, with a comment that the console adds no index entry itself. Paid for by shortening the `filters`-slice comment above it, which restated the Run Modes table and decision rule further down the same file. - `skills/objectui/guides/app-composition.md` (added to the file surface by the seat's claim amendment): the route table gains `{type:'report'}` → `/reports`. Paid for by dropping the Contract Reminders' parenthetical list of target keys, which restated the table's first column. - `skills/objectstack-ui/evals/views-apps-actions-pages.json`: case 10 asks for an app's Reports entry and expects `{ type: 'report' }` with no `reportName`. - `scripts/check-skills-token-ratchet.mjs`: one row raised (the eval file), under the standing ruling quoted verbatim in the row comment. - Changeset: `@objectstack/skills` patch. ## Why PR #27566 (`bf658d1b8`) made `ReportNavItemSchema.reportName` optional (`packages/spec/src/ui/app.zod.ts:687`): absent, the entry opens the app's report index (`resolveHref` → `${basePath}/reports`, `ui/packages/layout/src/NavigationRenderer.tsx:1013`), and the console stopped injecting an "All reports" row, so an app shows the index only where its navigation declares it. The published skills still taught only the named form, so an agent following them shipped an app with no report index entry. No other skill page claims an injected "All reports" row (grep of `skills/**` for "All reports" / "report index": none before this PR). ## Verification Token measurements (`node scripts/check-skills-token-ratchet.mjs`, ceil(bytes/4)): | File | Before | After | Ceiling | |:--|--:|--:|:--| | `skills/objectstack-ui/SKILL.md` | 3840 | 3848 | 3851 (unchanged) | | `skills/objectstack-ui/rules/navigation.md` | 2271 | 2273 | 2273 (unchanged) | | `skills/objectui/guides/app-composition.md` | 1078 | 1078 | 1079 (unchanged) | | `skills/objectstack-ui/evals/views-apps-actions-pages.json` | 2009 | 2108 | 2009 → 2108 | The eval row moves under the maintainer ruling of 2026-10-27, verbatim: 「A:先削重复,余额按实测提高」. The file's cases share no sentence, so there was no duplicate to cut first; the ceiling is pinned at the measurement (8432 bytes). Gates: `node scripts/pm/dispatch-gates.mjs --ran` on `8af37ee5`: 65 derived families accounted for, 61 run and green, 4 NOT MEASURED (exit 3, prerequisite: whole-tree `dist/`): `check:dts-closure`, `check:dual-build-cjs-loads`, `check:lean-entry-closure`, `check:sourcemap-no-sources-content`. `check:skill-examples` (274 prose examples type-check, the edited `os:check` block included) and `check:doc-formula-expressions` were green after building their prerequisites. `pnpm --dir ui exec vitest run scripts/` was red once on the first run (`spec-symbol-ledger-plugin-detail-7265.test.ts`), green alone on both this head and `main`, and green on a full rerun (169 files, 4902 tests). `eslint` on the edited script: clean. Clause-②: no Metadata-protocol: none ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (No spec change.) - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; eslint on the edited script passes (full `pnpm lint` left to CI). - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. - [x] 6. `packages/spec` not touched. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. - [x] The *Before Submitting* list in CONTRIBUTING.md is done. ## 维护者速读 - 改了什么:技能教会报表导航项的「索引」写法:`{ type: 'report' }` 不写 `reportName` 就打开该应用的报表索引(`/reports`)。 - 为什么:#27566 之后控制台不再自动注入「All reports」行,应用只在导航声明处才有报表索引入口;技能只教具名报表,AI 照着写会漏掉索引入口。 - 改动面:objectstack-ui 的导航表一行与导航示例一项,objectui 路由表一行,新增 eval 第 10 例;无 schema、键或行为变更。 - token:三个文档文件都在原上限内(删去同文件重复句付账);eval 文件上限 2009→2108,依据 2026-10-27 裁决「A:先削重复,余额按实测提高」。 - 验证:65 族 gates 中 61 族绿,4 族因整树未构建为 NOT MEASURED;skill-examples 274 例类型检查通过。 - 落地:欠 CONTRACT_REVIEW_TIER 契约复审,落地为 Tier H(待受管批准人批准)。 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016WV4cyZb8pD9989yV2qXKE --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1199dff commit d4064cc

6 files changed

Lines changed: 35 additions & 8 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): objectstack-ui and objectui teach the report-index navigation item beside the named report
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** The skills taught only the named report item, `{ type: 'report', reportName }`. Since PR #27566 a `type: 'report'` item with no `reportName` opens the app's report index (`/reports`), and the console no longer adds an index entry on its own, so an agent following the skills shipped an app with no report index entry.
11+
- **Now.** The `objectstack-ui` navigation item table marks `reportName` optional and says what omitting it opens; `rules/navigation.md` shows the index entry beside the named one and says the console adds none itself; the `objectui` app-composition route table lists `{type:'report'}` → `/reports`. A new eval case (10 in `evals/views-apps-actions-pages.json`) asks for an app's report index entry and expects `{ type: 'report' }` with no `reportName`.
12+
- No schema, key or behaviour changes.

‎scripts/check-skills-token-ratchet.mjs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,8 +1123,13 @@ export const CEILINGS = new Map([
11231123
// spells its own `must_not_contain` string (-2 bytes). 7154 -> 8034 bytes -> 2009 tokens,
11241124
// ceiling +220, pinned AT the measurement. Same ruling as the line above (2026-10-27, tap A:
11251125
// 「A:先削重复,余额按实测提高」).
1126-
// ceil($(wc -c < skills/objectstack-ui/evals/views-apps-actions-pages.json) / 4) == ceil(8034 / 4)
1127-
['skills/objectstack-ui/evals/views-apps-actions-pages.json', 2009],
1126+
// 2009 -> 2108 (#27609): case 10, the app's report index entry — `{ type: 'report' }` with no
1127+
// `reportName` (PR #27566), which the console no longer adds on its own — +398 bytes; the
1128+
// file's cases still share no sentence, so there was no duplicate to cut (0 bytes).
1129+
// 8034 -> 8432 bytes -> 2108 tokens, ceiling +99, pinned AT the measurement. Same ruling as
1130+
// the lines above (2026-10-27, tap A: 「A:先削重复,余额按实测提高」).
1131+
// ceil($(wc -c < skills/objectstack-ui/evals/views-apps-actions-pages.json) / 4) == ceil(8432 / 4)
1132+
['skills/objectstack-ui/evals/views-apps-actions-pages.json', 2108],
11281133
['skills/objectstack-upgrade/evals/protocol-major-upgrade.json', 1033],
11291134

11301135
// the remaining skills' eval notes

‎skills/objectstack-ui/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ second at load, per ADR-0019 (app-as-consumer-unit) D3. More audiences are
168168
| `group` | `label`, `icon`, `expanded`, `children[]` | Collapsible group of items |
169169
| `object` | `objectName`, `viewName?`, `recordId?`, `filters?`, `label`, `icon` | Link to an object list, a named view, a record deep-link, or a `filters` slice on the bare data surface |
170170
| `dashboard` | `dashboardName`, `label`, `icon` | Link to a dashboard |
171-
| `report` | `reportName`, `label`, `icon` | Link to a report |
171+
| `report` | `reportName?`, `label`, `icon` | A report; no `reportName` = the app's report index |
172172
| `page` | `pageName`, `label`, `icon` | Link to a custom Page |
173173
| `url` | `url`, `label`, `icon` | External or custom URL |
174174
| `action` | `actionDef` (`{ actionName, params? }`), `label`, `icon` | Run an action instead of navigating |

‎skills/objectstack-ui/evals/views-apps-actions-pages.json‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@
9090
"must_contain": ["disabled:", "disabledReason"],
9191
"must_not_contain": ["description:", "disabledMessage", "disabledTooltip", "visible:"]
9292
}
93+
},
94+
{
95+
"id": 10,
96+
"prompt": "Give the crm app a Reports entry listing all reports it ships.",
97+
"expected_output": "`{ type: 'report' }` with no `reportName` is the app's report index; the console adds none unless navigation declares it.",
98+
"files": [],
99+
"assertions": {
100+
"must_contain": ["type: 'report'"],
101+
"must_not_contain": ["reportName:"]
102+
}
93103
}
94104
]
95105
}

‎skills/objectstack-ui/rules/navigation.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ export const CrmApp = App.create({
3030
{ id: 'nav_opportunity', type: 'object', objectName: 'opportunity', label: 'Opportunities', icon: 'target' },
3131
// Open a specific named view instead of the object default:
3232
{ id: 'nav_pipeline', type: 'object', objectName: 'opportunity', viewName: 'pipeline_kanban', label: 'Sales Pipeline', icon: 'columns-3' },
33-
// One-off parameterized slice — lands on the bare data surface
34-
// (`/:objectName/data`, objectui ADR-0055) with removable URL filter
35-
// chips, NOT anchored to a saved view. Don't author a view for these:
33+
// One-off slice on the bare data surface (Run Modes, below):
3634
{ id: 'nav_my_open', type: 'object', objectName: 'opportunity', filters: { owner_id: '{current_user_id}', status: 'open' }, label: 'My Open Deals', icon: 'user-check' },
3735
{ id: 'nav_dash', type: 'dashboard', dashboardName: 'sales_dashboard', label: 'Sales Dashboard', icon: 'chart-bar' },
3836
{ id: 'nav_report', type: 'report', reportName: 'opportunities_by_stage', label: 'Opps by Stage', icon: 'bar-chart-3' },
37+
// No `reportName` = the app's report index; the console adds none itself:
38+
{ id: 'nav_reports', type: 'report', label: 'Reports', icon: 'library' },
3939
],
4040
},
4141
{

‎skills/objectui/guides/app-composition.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ source of truth for that mapping.
1919
| `{type:'object', objectName, filters}` | `/:objectName/data?filter[k]=v` | Bare data surface. No saved-view tab bar; conditions render as removable chips; full filter/sort/group toolbar; "Save as view" is the exit |
2020
| `{type:'dashboard', dashboardName}` | `/dashboard/:name` | Dashboard renderer |
2121
| `{type:'report', reportName}` | `/report/:name` | Report renderer |
22+
| `{type:'report'}` | `/reports` | The app's report index; none unless navigation declares it |
2223
| `{type:'page', pageName}` | `/page/:name` | Bare SDUI rendering only -- no object shell, so view switching, actions and record routing are hand-assembled in the page schema |
2324
| `{type:'url', url}` | external | `target` controls the tab |
2425
| `{type:'component', componentRef, params?}` | `/component/:ns/:name?…` | `componentRef` is colon-joined (`metadata:resource`); `params` ride as querystring. `metadata:*` refs are special-cased onto the `/metadata[/:type[/:name]]` routes |
@@ -30,8 +31,7 @@ source of truth for that mapping.
3031

3132
- Nav items are a **discriminated union on `type`**. There is no `path` and no
3233
`kind` — those keys are ignored by `resolveHref` and rejected/stripped at
33-
save. Emit the typed target field for the chosen type (`objectName`,
34-
`pageName`, `dashboardName`, `reportName`, `url`, `componentRef`).
34+
save. Emit the chosen type's target field.
3535
- Object-item targets are exclusive, not ordered (spec `objectNavTargetExclusivity`):
3636
`filters` is mutually exclusive with `recordId`/`viewName`; `runAction` is refused with
3737
`recordId` (it composes with `viewName` or `filters`); `recordId` + `viewName` is tolerated.

0 commit comments

Comments
 (0)