Repository navigation
docs(spec, docs): App.defaultAgent and actions-as-tools name the agent route as the one chat door - #21976
Conversation
…read by the console The docblock said the assistant chat endpoint resolves this agent from context.appName. The cloud AI runtime retired that route; the agent route is the one chat door and the console's surface resolver is the reader. actions-as-tools.mdx drops the retired route from its chat-route callout. Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
…measured Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 138 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 04a85f58e75f24bd57cd468fa264295a925a7bf2 && git checkout 04a85f58e75f24bd57cd468fa264295a925a7bf2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f76c6221acd3997dd778fdd3e8e7d43e0bec4851 7f6089bd504e05ec21401abdedb7c280712bf00c && git checkout -B drift-repro f76c6221acd3997dd778fdd3e8e7d43e0bec4851 && git merge --no-ff 7f6089bd504e05ec21401abdedb7c280712bf00c
node scripts/docs-audit/affected-docs.mjs --json f76c6221acd3997dd778fdd3e8e7d43e0bec4851
|
…opens when it is omitted (objectstack-ai#21981) Fixes objectstack-ai#21973 Clause-②: no ## What changed `ObjectNavItemSchema.viewName`'s describe said `Defaults to "all"`. The console does not do that. The describe now states the console's real rule. One sentence changed, and "Ignored when `recordId` is set." stays: > Default list view to open. When omitted, the console opens the object's default list view, else its first declared list view; `all` names the console's fallback tab, which exists only for an object that declares no list view. Ignored when `recordId` is set. - `packages/spec/src/ui/app.zod.ts`: the describe, plus a short docblock above it naming the objectui code the sentence mirrors. - `content/docs/references/ui/app.mdx`: regenerated with `pnpm --filter @objectstack/spec gen:docs` (the five `viewName` rows). Not edited by hand. - `packages/lint/src/lint-view-refs.ts`: only the header comment's quote of the old sentence (about `:46`) moves. This file was declared on objectstack-ai#6023. No lint logic, rule or test changes. - `.changeset/21973-nav-view-name-default.md`: `@objectstack/spec` `patch`. - ⛔ No schema, type, optionality, default, export or accept-set change. No runtime change, and no objectui file. ## The runtime rule, read at objectui `.objectui-sha` = `0abd4f9f8769fc4c19ad2f96707684876f74c09f` (read-only) - `packages/layout/src/NavigationRenderer.tsx` `resolveHref`: precedence is `recordId`, then `filters`, then `viewName`. With no `viewName`, the entry links to the bare object route, which has no view segment. - `packages/app-shell/src/views/ObjectView.tsx:2151`: `activeViewId = resolvedViewId || defaultViewId || views[0]?.id`. `defaultViewId` (`:2129`) is the first tab whose `isDefault` is set. - `buildViewTabs` (`:936`): it sets `isDefault: true` on the primary `list` and moves that tab to the front. It builds the `fallbackTab`, `{ id: 'all', label: allRecords }`, only when `viewList.length === 0`, which means no defined view and no primary `list`. Saved overlay rows are merged after that, and they can carry their own `isDefault` (a user's set-default). "The object's default list view" covers both cases. - `resolveViewId` (`@object-ui/core`, `utils/resolve-view-id.ts`) matches three ways: exact id, short name retried with the object prefix added, and qualified name retried with the prefix stripped. On a miss it returns `undefined`. `ObjectView` then logs a `console.warn` and falls back to `defaultViewId || views[0]`. The describe leaves this out to stay short. `view-ref-nav-view-missing` in `packages/lint` already refuses an unresolvable name at build time. ## Census: other live text that restates the old default Searched docs (excluding `content/docs/releases/`), skills, examples, `packages/spec` and the rest of the tree for `Defaults to "all"`, `Defaults to 'all'`, and default-`all`-view phrasings. - `content/docs/references/ui/app.mdx`: lines 116, 465, 655, 842 and 1084. Regenerated in this PR. - `packages/lint/src/lint-view-refs.ts:46`: the quote. Moved in this PR. - `packages/lint/src/lint-view-refs.test.ts:240`: a test comment, "The schema documents viewName as 'Defaults to "all"'". It is in the lint lane and asserts nothing about the text, so it is listed here and not edited. - No test asserts the describe text. Searched `*.test.ts`, `*.snap` and `*.json`. - No hit in `skills/**`, `examples/**`, `content/docs/ui/apps.mdx` or `docs/**`. ## Tests and gates (all at head `b53d7b5a34`) - `pnpm --filter @objectstack/spec build`: VERDICT command-exit 0. - `pnpm --filter @objectstack/spec check:generated`: - Before the regeneration: `✗ 1 of 15 artifact(s) stale: content/docs/references/**`. - After: `✓ All 15 generated artifacts are up to date`. - `pnpm --filter @objectstack/spec typecheck`: exit 0. - `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2`: `Test Files 619 passed (619)`, `Tests 18485 passed | 1 todo`. - `pnpm --filter @objectstack/lint typecheck` and `vitest run --maxWorkers=2`: `Test Files 119 passed (119)`, `Tests 5624 passed | 5 skipped`. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 104 commands. All 104 ran and exited 0. - `--ran` reconciliation: `✓ dispatch-gates --ran: 104 derived famil(ies) accounted for — 104 run, 0 NOT-MEASURED`. - The first attempt hit four `PREREQUISITE NOT MET` (exit 3): `check:doc-formula-expressions`, `check:doc-security-posture`, `check:skill-examples` and `check:dual-build-cjs-loads`. Each was re-run green after building what it reads. - NOT MEASURED, because CI owns them: - The six path-scheduled CI jobs that `dispatch-gates` lists (Test Core, Temporal Conformance, Dogfood, Dogfood Verify CLI, Build Core, Build Docs). - The workflow-valued families. - The repo-wide `pnpm lint`. ## Changeset, measured - `@objectstack/spec` ships `dist` and `src/**/*.zod.ts` (`files[]`). After the build, the new describe text is in `packages/spec/dist/ui/index.{js,mjs}`. Positive control: the `recordId` describe is in the same files. The old text has zero hits. - `@objectstack/lint` ships only `dist`, `README.md` and `CHANGELOG.md`. The header comment has zero hits in `packages/lint/dist`. Positive control: `view-ref-nav-view-missing` hits `index.js`, `index.cjs` and `runtime.js`. So lint publishes nothing new and gets no changeset. ## Acceptance notes - **Wording vs. the dispatch.** The dispatch said `all` exists "only for an object that declares no `listViews`". The code's condition is wider than `listViews`: no defined view AND no primary `list`. An object that declares only a default `list` gets no `all` tab, and in the spec `listViews` means a container's *additional* named views. So the sentence says "declares no list view". - **The first sentence is kept.** "Default list view to open." is quoted verbatim in four places: `packages/spec/src/ai/solution-blueprint.zod.ts:174`, `packages/platform-objects/src/apps/setup-users-nav-view.test.ts:13`, `packages/lint/CHANGELOG.md:4743` and `packages/spec/CHANGELOG.md:17669`. Rewriting it would make those quotes stale and step outside the declared file surface. - **`views[0]` and the user's tab order.** `views[0]` follows a per-user tab order (localStorage) and saved `sortOrder` when they exist; otherwise it is declared order. The describe states the metadata rule. - **`lint-view-refs.ts:64`–`:66` is untouched.** It says the schema's `all` "resolves only when the object actually declares it". That is outside the declared quote. It stays true for the lint, because the rule skips objects whose list-view namespace is empty, and those are exactly the objects that get the runtime fallback tab. Carrier: none. - **Serial note.** objectstack-ai#21976 (objectstack-ai#21968) also edits `app.zod.ts`, about 1,200 lines away (near `:1655`). `origin/main` was re-fetched before this PR opened (`dcf3eb494a`). Nothing that landed touches these four files, so no merge was needed. --- _Generated by [Claude Code](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…ed decision in words instead of a tracker number (stage 26) (objectstack-ai#21987) Part of objectstack-ai#20749 Clause-②: no Stage 26 of this card: the next area of class (e), the test strings shipped under `packages/spec/src`, as ruled in `5902360492` on objectstack-ai#20513. This stage takes the first name-ordered `system/` group: the 18 id-bearing test files under `packages/spec/src/system/` from `auth-config.test.ts` to `metadata-form-declared-rows.pin.test.ts`, with `constants/system-names.test.ts` in its path position. Those files carried 91 messages and 97 tracker ids, citing 54 records. All 97 now either state what their record decided, in words (form D), or are dropped where the title already says it. No needle sits in this group. Text only: no assertion, identifier, test count or code comment changes, and no file is renamed. ## Census at the base (`dcf3eb494a`) Instruments: `census10.cjs` (md5 `9d08602ab972b4b8643c90d64d40fa41`), `census.cjs` (md5 `6e42a45a926d375013c32d62f16a296e`), `census-wide.cjs` (md5 `c98410a19529c439adb0afbfb00026a2`) and `dirtable.cjs` (md5 `dda605c54745b4a60cc14c9a686e4eff`), byte-identical to the copies stages 10 to 25 used. A literal counts as a test title when its folded message is argument 0 of a `describe` / `it` / `test` call, `.each` / `.skip` / `.only` chains included. Everything else is an "other" string. The worktree was cut from `origin/main` at `dcf3eb494a`, the claim's base and stage 25's landing. Both instruments read **282 messages / 297 ids in 71 files**, the seat's reading and stage 25's head reading. | directory | files | messages / ids | titles | other | |:--|--:|--:|--:|--:| | `system/` (this PR: 18 of its 34 files) | 34 | 154 / 167 | 128 / 138 | 26 / 29 | | (files directly in `src/`) | 30 | 118 / 120 | 117 / 119 | 1 / 1 | | `ui/` | 5 | 7 / 7 | 0 | 7 / 7 | | `ai/` | 1 | 2 / 2 | 0 | 2 / 2 | | `contracts/` | 1 | 1 / 1 | 0 | 1 / 1 | | **total** | **71** | **282 / 297** | **245 / 257** | **37 / 40** | The group reads **91 messages / 97 ids in 18 files**, the seat's figures file for file: | file (under `system/`) | messages / ids | titles | other | |:--|--:|--:|--:| | `auth-config.test.ts` | 2 / 2 | 2 / 2 | 0 | | `book.test.ts` | 2 / 2 | 2 / 2 | 0 | | `cache.test.ts` | 1 / 1 | 1 / 1 | 0 | | `collaboration.test.ts` | 1 / 1 | 1 / 1 | 0 | | `compliance-families-retirement.test.ts` | 7 / 7 | 4 / 4 | 3 / 3 | | `constants/system-names.test.ts` | 1 / 1 | 1 / 1 | 0 | | `core-service-provider.test.ts` | 1 / 1 | 1 / 1 | 0 | | `data-migration-flag-column-move.pin.test.ts` | 2 / 3 | 2 / 3 | 0 | | `disaster-recovery.test.ts` | 1 / 1 | 1 / 1 | 0 | | `email-config.test.ts` | 2 / 2 | 2 / 2 | 0 | | `email-template-floor-locale-parity.pin.test.ts` | 1 / 1 | 1 / 1 | 0 | | `environment-artifact.test.ts` | 5 / 5 | 5 / 5 | 0 | | `http-server.test.ts` | 2 / 2 | 2 / 2 | 0 | | `i18n-resolver.test.ts` | 53 / 56 | 50 / 53 | 3 / 3 | | `job.test.ts` | 4 / 4 | 4 / 4 | 0 | | `logging.test.ts` | 1 / 3 | 1 / 3 | 0 | | `message-queue-retirement.test.ts` | 3 / 3 | 1 / 1 | 2 / 2 | | `metadata-form-declared-rows.pin.test.ts` | 2 / 2 | 2 / 2 | 0 | | **18 files** | **91 / 97** | **83 / 89** | **8 / 8** | Ten more test files sit in the same name range and carry no id (`constants/platform-object-names`, `constants/platform-tool-names`, `core-services`, `deploy-bundle`, `doc`, `encryption`, `i18n-resolver.nav-label-identity`, `i18n-resolver.object-list-views`, `job-pull-organization`, `license`). The eight "other" strings are expect failure messages, rewritten and declared to the text-only tool: `compliance-families-retirement.test.ts:159` (a template literal), `:210` and `:494`; `message-queue-retirement.test.ts:87` (a template literal) and `:127`; `i18n-resolver.test.ts:3288` (the second leaf of a `+` chain), `:3299` and `:3303`. - **Controls.** Lit: `system/metrics.test.ts` (4 / 5) and `system/translation.test.ts` (12 / 12), outside the group, read the same at the base and at the head. Dark: `i18n-resolver.test.ts` reads 0 at the head while 47 of its lines still carry `#` plus digits: 45 comment lines and the two `(batch objectstack-ai#58)` titles below. Planted in a scratch tree: an id put into the rewritten `i18n-resolver.test.ts` "per-component copy" title reads 1 / 1 (`title:describe`), and an id put into a `job.test.ts` comment reads 0. - **A wider pattern** (any `#` plus digits) reads the same as the gate pattern in 17 of the 18 files at the base. In `i18n-resolver.test.ts` it reads two more: `:1744` and `:1757` end in "(batch objectstack-ai#58)", a two-digit decision-batch label that the gate's three-to-five-digit pattern does not count. Neither literal carries a counted id, so both stay, in the form earlier stages kept batch labels (stage 21's `批 17`, stage 22's `批 14` / `batch 13`). At the head the wider pattern reads 0 in 17 files and those 2 in `i18n-resolver.test.ts`. - **At the head:** 191 messages / 200 ids in 53 files. The 18 files read 0 / 0, `system/` reads 63 / 70 in 16 files, and no other file moved. ## How the area was chosen `system/` is taken in path-ordered file groups near the ~100-id bound, the rule stages 20 to 25 used, with `constants/system-names.test.ts` sorting at `constants/`. Stage 25's cut named this group at 97 ids, and this census reads 97, so no re-cut was needed. `i18n-resolver.test.ts` (56 ids; objectstack-ai#21948 landed into it at `bab76850cd`, and its titles were read at this base) fits one PR and one text-only proof, so it is not split. **Named for the next stages** (cut from the head census, 191 / 200): - **the second `system/` group:** `metadata-form-zod-reconciliation.test.ts` through `worker.test.ts`, 16 files, 63 messages / 70 ids (45 / 49 titles, 18 / 21 other). Its first file alone carries 21 / 24, 17 of them "other" strings (its ledger `why` entries). - The files directly in `src/`, 120, one stage. - The needles: one stage, with an at-tier review. The four colour literals stay, as stage 21 decided. ## What each id became - **23 literals (25 ids)** now state a decision in words. - **11 literals (12 ids)** get their subject back in words, where the number stood for a thing. - **57 literals (60 ids)** drop a number the title already explains. Every cited record was fetched with all its comments through REST, and its decision was read from its ruling, ACCEPT and landing comments: a keyword digest of every record, and full reads wherever the new words carry a decision. 54 records are cited: 51 answer 200 (objectstack-ai#15514 is a PR, merged as `bf1054a4c0`) and 3 answer 404. The three that answer 404 were read from what landed, through the commits endpoint (this checkout is shallow), each found through its CHANGELOG entry or the file's own commit anchor: - **objectstack-ai#10926**, from `d173125fb8` (objectstack-ai#11438): the component-translation `submitLabel` copy key is retired (maintainer ruling 2026-08-22, option A); - **objectstack-ai#12961**, from `901355c3bc` (objectstack-ai#13111): `translatePage` descends into declared `properties.children` ("Ruled 2026-08-29 (option A)"; region-level id wins a collision); - **objectstack-ai#13218**, from `c45d8e6b4e` (objectstack-ai#13641): `walkAddressedPageComponents` is exported as the one addressed-component walk, consumed by `translatePage` and the CLI extractor alike. **The same-id title stage 25 listed:** `book.test.ts:413` "ResolvedBookSchema is the book-tree response contract (objectstack-ai#12038)". Its body parses the real `resolveBookTree()` output, accepts the honest-empty tree and pins each Zod schema type-identical to its interface: a describe-only transcription, which the five-part ruling's implementation plan names (`5434804846`). None of 1C · 2C · 3A · 4A · 5A is pinned, so no letter is named; the title already says the decision, and only the number goes. **"ruled:" appears in two titles,** each on a record that carries a ruling: - `data-migration-flag-column-move.pin.test.ts:72`: "(ruled: one nullable datetime on the flag row)". objectstack-ai#15989's comment `5556979386`, headed "The four questions — ruled", answers Q1 with A: a new nullable datetime field on `DataMigrationFlagSchema`, whose failure mode is absence. objectstack-ai#16185's body restates it ("The mechanism was ruled on objectstack-ai#15989 as A"). - `i18n-resolver.test.ts:4944`: "(ruled: the authored label is the default-locale text)". objectstack-ai#15711's maintainer ruling A, recorded in `5549577889`. **Stated in words** (23 literals): | record | literal (under `system/`) | now reads | the decision | |:--|:--|:--|:--| | objectstack-ai#11739 | `auth-config.test.ts:423` | "AudienceConfigSchema — one closed audience posture, invite_only when undeclared" | One declared posture, a closed vocabulary (`invite_only` / `email_domain` / `open`), `invite_only` when undeclared, as the body records it ruled. | | objectstack-ai#15679 (objectstack-ai#14478 ruling B) | `cache.test.ts:336`, `collaboration.test.ts:1005`, `disaster-recovery.test.ts:235` | "… carry their unit in the key name" / "… carries its unit in the key name" | The `system/` duration keys carry their unit in the key name, stage 25's objectstack-ai#15677 reading. | | objectstack-ai#15513 | `compliance-families-retirement.test.ts:210`, `:494` (expect messages) | "… being undone — the three families were retired whole, none roadmapped" | Maintainer ruling A (`5548577921`): the three compliance-shaped families are retired whole; none is roadmapped. | | objectstack-ai#15989 Q1, via objectstack-ai#16185 | `data-migration-flag-column-move.pin.test.ts:72` | "columns_moved_at — absence is the contract (ruled: one nullable datetime on the flag row)" | Above. | | objectstack-ai#16185 constraint 2 | `data-migration-flag-column-move.pin.test.ts:126` | "the ONE arbiter is unchanged by the new member — read beside it, never inside it" | `isDataMigrationFlagVerified` is not touched; the new field is read in addition to the arbiter, never inside it. | | objectstack-ai#19184 | `job.test.ts:881` | "JobSchema's own @example — copied verbatim, it parses" | The example stops opening with the retired `id`, so a verbatim copy is accepted. | | objectstack-ai#8075 | `message-queue-retirement.test.ts:127` (expect message) | "… being undone — the family had no consumer and carried an inline credential" | Fork (b): exported but unconsumed, the credential-bearing shape is retired whole. | | objectstack-ai#16772 | `i18n-resolver.test.ts:1207` | "translateDashboard — global filters, addressable from a bundle" | Finding B: `dashboards.NAME.globalFilters` becomes a bundle group. | | objectstack-ai#6080 | `:1573` | "per-component copy, keyed by component id" | Page component copy gets a bundle address by component id. | | objectstack-ai#13218 (404) | `:2215` | "walkAddressedPageComponents — the one addressed-component walk, shared by the resolver and the CLI extractor" | What landed in `c45d8e6b4e`. | | objectstack-ai#5377 | `:2652` | "resolveTabLabel — filter-preset tab labels, keyed under `_tabs`" | Tab labels get the `objects.OBJECT._tabs.TAB.label` key and a resolver. | | objectstack-ai#3370 | `:3049` | "translateObject inline actions — served with their `_actions` translations" | Declared action labels are translated, not served as English literals. | | objectstack-ai#3833, objectstack-ai#3847 | `:3131` | "resolveObjectFieldLabels — the declared rich entries, read from the nested translation shape" | objectstack-ai#3833: the nested shape, never the retired flat dialect; objectstack-ai#3847: the rich `label` / `help` / `options` entries the response declares. | | objectstack-ai#7679 | `:3308` | "normalizeSupportedLocales — the declared supportedLocales narrow the advertised set" | `/i18n/locales` reports the app's declared locales, not every loaded bundle. | | objectstack-ai#11287 | `:3732`, `:4134` | "translateFlow — screen-flow copy from the `flows` bundle group" / "resolveFlowScreenTitle — a screen title from the `flows` bundle group" | The resolver half that reads `TranslationData.flows`. | | objectstack-ai#3786 | `:4726` | "… the REST boundary follows — one derived list, no hand-kept copy" | Derive from the one source instead of hand-copying a "keep in sync" list. | | objectstack-ai#14882 | `:4853` | "a declared fallback chain, not a literal `en`, at the resolver" | The chain honours the declared `fallbackLocale`, not a hard-wired `['en']`. | | objectstack-ai#15711 | `:4944` | "… for a default-locale request (ruled: the authored label is the default-locale text)" | Above. | | objectstack-ai#16458 | `:5011` | "resolveMetadataFormSchemaTitles — bundle labels overlaid as JSON Schema titles, repeater rows included" | Item-level property names get a translated title through the array's `items`. | **Subject back in words** (11 literals): - the two "zero holders after objectstack-ai#15513" / "after objectstack-ai#8075" template messages become "after the families' retirement" / "after the family's retirement", stage 25's form; - "[objectstack-ai#15513] ADR-0087 registration" becomes "compliance families retirement — ADR-0087 registration"; - "keeps PR objectstack-ai#15514's fourteen deadline-key registrations" becomes "keeps the earlier deadline-key retirement's fourteen registrations": PR objectstack-ai#15514 retired the fourteen inert deadline keys before the families went whole; - "shipped by plugin-email since objectstack-ai#5087" becomes "shipped by the SMTP transport in plugin-email": objectstack-ai#5087 implemented that transport; - "objectstack-ai#17614 — the published "must stay equal" claim" becomes "EMAIL_TEMPLATE_FLOOR_LOCALE — the published "must stay equal" claim", the constant whose docblock carries it; - "(objectstack-ai#4666 pin)" becomes "(pinned here: a type change the key-level gates cannot see)": objectstack-ai#4666's maintainer ruling chose direction B, fingerprinting defaults only, so the checksum object-to-string type change stays invisible to the key-level gates, and these parses are its gate; - the three "pre-objectstack-ai#20680" / "pre-objectstack-ai#8284" / "pre-objectstack-ai#20731" control titles become "the behaviour before the override rule", the rule their describes name ("the catalog loses to an explicit override"); - "the contract call the objectstack-ai#12961 line left open, made by objectstack-ai#16772" becomes "the contract call the `children`-descent ruling left open, made for tab and accordion panels": the 2026-08-29 ruling (`901355c3bc`) named `properties.children` only, and objectstack-ai#16772 made the `items[].children` call. **Dropped where already stated** (57 literals, 60 ids). A number goes only where the title already says its decision. Examples: `[objectstack-ai#15513]` x2 and `[objectstack-ai#8075]` retirement describes and `[objectstack-ai#4740]`; the four other `[objectstack-ai#15711]` prefixes, once the ruling is stated in the first; the tails `(objectstack-ai#18124)`, `(objectstack-ai#4667)` x2, `(objectstack-ai#12038)`, `(objectstack-ai#4611)`, `(objectstack-ai#4451)`, `(objectstack-ai#5307)`, `(objectstack-ai#14865)`, `(objectstack-ai#4938)`, `(objectstack-ai#5295)`, `(objectstack-ai#16292)`, `(objectstack-ai#19085)`, `(objectstack-ai#20439)`, `(objectstack-ai#4854)`, `(objectstack-ai#21257)`, `(objectstack-ai#10926)`, `(objectstack-ai#12961)`, `(objectstack-ai#5775)`, `(objectstack-ai#20940)` x3, `(objectstack-ai#16772)` x4, `(objectstack-ai#5377)` x2, `(objectstack-ai#5728)` x2, `(objectstack-ai#3847)` x2, `(objectstack-ai#7634)` x4 (one title and three expect messages), `(objectstack-ai#8284)`, `(objectstack-ai#11745)`, `(objectstack-ai#14253)` x6; the `objectstack#14972` tail with its repository qualifier; the ADR-bearing tails `(objectstack-ai#4740, ADR-0049)`, `(objectstack-ai#20680, ADR-0029 D9.2a)`, `(objectstack-ai#20731, ADR-0029 D9.2a)`, `(objectstack-ai#14478, ADR-0087 …)`, which keep their ADR; `pre-objectstack-ai#4740` before "v0 artifact"; and `(objectstack-ai#17782, objectstack-ai#15939, objectstack-ai#14478)` on "logging duration keys → *Ms", whose title shows the rename. The two 404 numbers among them (objectstack-ai#10926, objectstack-ai#12961) go only where the title already states what landed. **No file is renamed.** ## Readers - **Needles:** none. The eight declared strings are assertion failure messages (the second argument of `expect`), none is an expected value. The three `.pin.test.ts` files read source text, and none reads an id: `data-migration-flag-column-move.pin.test.ts` reads `isDataMigrationFlagVerified`'s body for `verified_at`, `blocking` and the absence of `columns_moved_at`; `email-template-floor-locale-parity.pin.test.ts` reads `DEFAULT_TEMPLATE_LOCALE` and "must stay equal"; `metadata-form-declared-rows.pin.test.ts` reads registered form rows. `job.test.ts`'s `@example` pin reads the anchor "@example Metadata Sync Job (Cron)". No title or message in the group is matched against a source docblock or another file's text. - **Test-name filters:** none. No tracked script, workflow or package config passes `-t` / `--testNamePattern` to vitest; the one vitest `-t` hit is a README example under `packages/qa/dogfood` filtering its own fixture. - **Snapshots:** none. No `__snapshots__` directory is tracked under `packages/spec`, and none of the 18 files calls a snapshot matcher. - **Projects:** `compliance-families-retirement.test.ts` and `email-template-floor-locale-parity.pin.test.ts` are in the `repo` project (`packages/spec/vitest.repo-tests.json`); the other 16 run in `local`. The base-versus-head run below takes both projects. - **By substring:** every old literal, its id-bearing fragment and a window around each id (272 needles) was searched with `git grep` at the base, across the tracked tree outside its own file. No gate, doc, filter, snapshot, QA checklist entry or `scripts/check-*.mjs` self-test reads one. The 12 hits are sibling test titles: the three `(objectstack-ai#15679)` titles in this group hit each other (all rewritten here) and the five `(objectstack-ai#15679)` titles of the second `system/` group (`metrics`, `object-storage`, `registry-config`, `tracing`, `worker`), which go with that stage. ## Text-only proof Stage 10's scratch tool (`textonly10.cjs`, md5 `d5e4801dbb4329ab1984da91e92fc47c`) compares base and head file by file on three legs: 1. **Skeleton:** the full AST, with string pieces masked. It must be identical. 2. **Comments:** every comment, byte-equal. 3. **Strings:** each changed string leaf must sit in a test-call title position or on a declared line, must carry a tracker id before, and must carry no `#` plus digits after. This stage declares the eight expect-message lines named above. - **Result:** 18 of 18 files SAME on all three legs, with the per-file counts predicted in writing before any edit. - **Totals:** 91 changed string leaves in 91 literals: 83 titles and 8 declared. The diff's `+` and `-` lines are exactly the 91 planned lines as multisets, and every file keeps its line count. - **Controls (14 of 14 as predicted on the first run, on scratch copies, each anchor hit once):** identifier rename DIFF; numeric literal DIFF; comment edit COMMENT DIFF; a non-title string given an id VIOLATION; a rewritten title given a new id VIOLATION; a title that was id-free at base edited VIOLATION; one title reverted to base SAME; an `it.each` row given an id VIOLATION; an undeclared expect message changed VIOLATION; a title re-split into a `+` chain DIFF; a declared expect message reverted to base SAME; a declared template expect message given a new id VIOLATION; a declared `+`-chain leaf given a new id VIOLATION; a template-literal title given a new id VIOLATION. - **Templates and tables:** no `.each` title and no `$name` placeholder changes. The two template literals change only their text after the `${…}` span. **Test counts:** the 18 files were run at the base, before the edit, and at the head, in the same worktree, with `--project local --project repo`. Both sides read 783 tests in 18 files, all passed, with the same count and status sequence per file in 18 of 18. 354 full test names change, and each changed name equals the base name with the planned replacements applied: 0 mismatches. No full name repeats on either side. No head name carries a gate-pattern id (354 base names did); two head names carry the kept "(batch objectstack-ai#58)". No source escape sits in a planned anchor, so the comparison tool met none. ## Changeset: `skip-changeset` Measured, not assumed: - `npm pack --dry-run` of `@objectstack/spec` lists 2068 files. 0 of the 18 touched files are in it, and no `*.test.ts` at all (`files[]` ships `src/**/*.zod.ts`, not tests). The controls `src/system/job.zod.ts`, `src/system/translation.zod.ts` and `dist/index.mjs` are in it. - In the built `dist/`, two new phrases and an old one each read in 0 files. The control `Unrecognized key` reads in 42. So this PR publishes nothing, and no changeset is added. ## Verification (at `ed2bc649f3`) - `pnpm turbo run build` over all packages: 71 / 71, through the shared verify lock (turbo exit 0, recorded to a file; `VERDICT batch-last-exit 0`). - `@objectstack/spec`: - `vitest run --project local`: 619 files, 18485 passed, 1 todo. - `typecheck`: exit 0, including `check:test-typecheck` (52 files / 246 errors / 135 pinned signatures held). Its program holds all 18 group files, counted by path with `tsc --listFilesOnly -p tsconfig.test.json`. - `check:generated`: all 15 generated artifacts up to date, against the `dist/` the build above wrote. - **Gates:** `dispatch-gates --commands` derived 79 families, the same 79 as stage 25. All 79 exit 0. `--ran` reconciles: 79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN, every family with its exit code recorded. The same 79 derive from `origin/main` `4e4e881427` with this diff applied. The five roster families marked as sharing a directory with this diff (`check:meta-url-spelling`, `check:spec-changes`, `check:authz-resolver`, `check:error-code-casing`, `check:filter-alias-parity`) each exit 0. - **ESLint, a proven narrowing:** `--no-inline-config` over the 18 files reads 0 errors and 0 warnings. The population comes from ESLint's own config: 18 configured, 0 ignored. No file sets `parserOptions.project` or `projectService`, so no untouched file's verdict can move. - `check-governed-merges --test`: NOT governed, 182 changed lines (+91 / -91). - A control-byte scan over the 18 changed files finds none. ## `main` since the base Re-fetched just before this PR opened, `origin/main` was three commits past the base (`4e4e881427`: objectstack-ai#21976, objectstack-ai#21977, objectstack-ai#21352). They touch 490 files, none of the 18 and none under `packages/spec/src/system/`, so `main` was not merged. The census of `4e4e881427` with this diff applied reads 191 / 200, file for file the same as the head. `git merge-tree` onto `4e4e881427` is clean, and none of the 5 open PRs touches any of the 18 files. ## Acceptance notes - **Same-id test titles in this card's later stages** go with those stages: 22 lines, among them the five `(objectstack-ai#15679)` titles of the second `system/` group, `system/translation.test.ts`'s six (`objectstack-ai#16772`, `objectstack-ai#6080`, `objectstack-ai#10926`, `objectstack-ai#21257`, `objectstack-ai#11287`, `objectstack-ai#4667`) and `stack-email-template-locale-floor.test.ts`'s four `objectstack-ai#17614` titles. - **Same-id test titles in other packages** stay: 78 lines in 14 packages (`driver-sql` 13, `cli` 12, `objectql` 10, `rest` 10, `lint` 8, `platform-objects` 6, `plugin-auth` 6, `core` 3, `runtime` 3, `service-i18n` 3, and one each in `client`, `plugin-approvals`, `qa/dogfood` and `sdui-parser`), each package's share under the objectstack-ai#20513 lane children. - **The two "(batch objectstack-ai#58)" titles** (`i18n-resolver.test.ts:1744`, `:1757`) stay: a two-digit decision-batch label outside the gate's pattern, with no counted id in the literal. - **Code comments with live ids** remain in these files, among them the `// objectstack-ai#18124 — step 3 of ruling A on objectstack-ai#18115` header in `auth-config.test.ts`, the `[objectstack-ai#16185]` docblock in `data-migration-flag-column-move.pin.test.ts`, the `[objectstack-ai#17614]` docblock in `email-template-floor-locale-parity.pin.test.ts` and the `// ─── [objectstack-ai#8075]` / `[objectstack-ai#15513]` banners. Code comments are not this card's share. --- _Generated by [Claude Code](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_ Co-authored-by: Claude <noreply@anthropic.com>
…objectstack-ai#21994) Fixes objectstack-ai#21989 Clause-②: no ## What this is This PR adds the curated release page for 17.7.0, `content/docs/releases/v17/17-7.mdx` (1,402 lines). It was written after the publish: npm `latest` moved on 2026-10-06, and `@objectstack/spec@17.7.0` went out at 12:22:14Z. It also wires the page in: - `content/docs/releases/v17/meta.json`: `17-7` comes ahead of `17-6`. - `content/docs/releases/v17/index.mdx`: the status blockquote, the minors warning, the per-release list and the checklist links now name 17.7.0 as current. This is the same shape objectstack-ai#21362 used for 17.6.0. - `scripts/docs-audit/handwritten-docs.json`: the page joins the docs-accuracy audit scope. - `content/docs/releases/v17/17-6.mdx`: one dated correction (2026-10-06) on the anonymous-endpoints known issue. objectstack-ai#21158 was closed as not planned on 2026-10-04. The page follows the 17.6 page's structure: 1. Highlights. 2. What's new: the counts, and the runtime changes that happen silently. 3. Breaking changes and migration, triaged by door and subject. It includes a coverage table that names the section carrying the migration for every ADR-0087 entry added since 17.6.0 (8 conversions, 41 D3 entries). 4. New capabilities. 5. Notable fixes. Security fixes are described only as classes and doors. 6. New in Console: each objectui declared-breaking change, with this repo's answer. 7. The code that shipped but is not listed. 8. The upgrade checklist. Every line is marked *Not exercised.* ## Sources and counts - The version commit `4e4e881427` (objectstack-ai#21352) consumed 323 changesets. 322 are new. One, `748b240` (objectstack-ai#21270), shipped in 17.6.0 and is listed again; the page explains this in its own section. - 69 CHANGELOGs carry a 17.7.0 section, and 48 of them have entries. Their 444 entries (194 minor, 250 patch) de-duplicate to the 323 changesets. - Two commits landed on `main` after the Version Packages PR's last refresh and before it merged: - `8a399b2b15` (objectstack-ai#21977, for objectstack-ai#21923) - `04e776b39a` (objectstack-ai#21976, for objectstack-ai#21968) Both are ancestors of the version commit (exit 0 for each), so the 17.7.0 packages carry their code. But the version commit did not consume their changesets, so no 17.7.0 CHANGELOG line names them. The release-integrity audit named both in a warning. - objectui: the pin moved five times and ends at `0abd4f9f8769`: - `89cad75d5570` (objectstack-ai#21380) - `ab1879721595` (objectstack-ai#21625) - `2e818d0b51ec` (objectstack-ai#21710) - `9dfaca654311` (objectstack-ai#21800) - `0abd4f9f8769` (objectstack-ai#21827) Across 173 commits, 254 changesets were added and 229 of them release something. 65 are declared breaking, plus one commit marked `!`. The Console table answers each. - `PROTOCOL_VERSION` is still 17.0.0. ## Premise corrections - The dispatch named two pin moves ending at `9dfaca654311`. The tree has five, ending at `0abd4f9f8769` (`8832655`, objectstack-ai#21827). The page covers all five. - One new D3 id contains a word that `check:role-word` refuses on docs pages, and release pages are not in its baseline. The coverage table therefore names that entry by its subject and points at `os migrate meta --from 17`. ## Fact-check A second pass checked every cited SHA, PR number, key name and behavioural claim against the commits, changesets and registry entries. It corrected **31 claims** (commit `e4a6280b46`). Two more corrections came earlier, while drafting: - objectstack-ai#21361 is closed; it is not tracking the issue. - There are seventeen `ui-object-*` members, not eighteen. Mechanical checks on the final page: - All 276 cited SHAs resolve, in objectstack or in an objectui clone carrying the final pin's history. - Each of the 216 distinct sha–PR pairs matches its commit subject. - Every printable new D3 id (40) and all 8 conversions appear on the page. - The MDX for 17-7, 17-6 and index compiles with @mdx-js/mdx 3.1.1 and remark-gfm 4.0.1. After the fact-check, `main` gained `1abfc58` (objectstack-ai#21985), which lands the read half of objectstack-ai#21922. It is not an ancestor of the version commit: the test returned exit 1, and the control commit `753e7a1` returned exit 0. So in 17.7.0, the metadata door's reads still serve a stored row under a code-defined datasource name. Commit `8347e0d172` says so in the datasource migration bullet. ## Independent fact-check and fix round An independent, read-only fact-check of head `8347e0d172` returned **FAIL** with 13 findings (record: objectstack-ai#21989 comment 6018402030): 2 wrong facts (a flow's `get_record` node still reads the stored-metadata tables, in projected form), 1 security line that named the filter shapes and depth threshold evading 17.6.0's refusal, 1 breaking change missing from the Breaking section (`0fc8087`, objectstack-ai#21626), 1 link whose label did not match its target, 4 overstatements, 1 missing security fix (`49524f6`, objectstack-ai#21420), 1 missing rollback caveat on `os secret rewrap --apply`, and 2 minor wording issues. All 13 were re-verified against their sources and applied in `a53c972fa7`; none was refuted. A scan for any other line naming a bypass shape of a fixed issue reduced two more lines to their class (`0728cbf`, `fb69825`). ## Measured on `a53c972fa7` - Derived gates: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derives 57 commands; all 57 exited 0 (`--ran`: "57 run, 0 NOT-MEASURED (a DERIVED zero)"). The verdicts include: - check-doc-anchors: 458 links resolve. - check-issue-citations: 305 resolve as a PR, 9 resolve, 15 are cross-repo; every citation this change adds resolves. - `check:role-word`, `check:release-notes` and `check:release-page-status`: OK. - check-release-section-coverage: OK, both plain and with `--strict` (10 minors). - The docs-audit scope check and `check:nul-bytes`: OK. - Docs production build: `TURBO_FORCE=true pnpm turbo run build --filter=@objectstack/docs`, run under the verify lock, reports `Tasks: 2 successful, 2 total` and `Cached: 0 cached`. The built `releases/v17/17-7.html` carries the corrected text and none of the removed text. - Mechanical checks: 231 distinct sha–PR pairs, 0 unresolved, 0 subject mismatches; the MDX of 17-7, 17-6 and index compiles. - Not measured locally: the repo-wide lint and the CI-only families. CI owns them. ## Not in this PR - No changeset. The PR touches only docs and a docs-audit list, nothing a package ships (`skip-changeset`). - Nobody has walked the 17.6.0 → 17.7.0 upgrade. The checklist says so on each line. --- _Generated by [Claude Code](https://claude.ai/code/session_016tKoy8NJa35Yih1FdzrVmn)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #21968
Merge gate: this PR merges only once objectstack-ai/cloud#2651 has merged. That PR removes the three assistant routes. Until it lands, the corrected text would be ahead of the server.
Clause-②: no
What changed
The
App.defaultAgentdocblock and one docs callout namedPOST /api/v1/ai/assistant/chat, a route the maintainer's ruling on objectstack-ai/cloud#2621 retires. Both texts now name the agent route,POST /api/v1/ai/agents/:agentName/chat, as the one chat door.packages/spec/src/ui/app.zod.ts, theApp.defaultAgentdocblock. It no longer says a server route reads the key. It now describes the reader that exists, objectui at the.objectui-shapin0abd4f9f87: the chat dock passes the active app'sdefaultAgentto the console's one surface-to-agent resolver. That resolver honours the value only when it namesaskorbuild(legacy aliases included) and otherwise falls back to the surface default. The resolved agent is then called by name on the agent route, where the:agentNamepath segment selects the agent. The sentence on the barePOST /api/v1/ai/chatroute stays, shortened: it reads no agent, so the key does not scope it. The ADR-0063 surface-binding paragraph and the rule that only the two platform agents resolve are unchanged.content/docs/ai/actions-as-tools.mdx. The in-product chat-route callout lists the agent route only. It keeps its account of how the principal is taken fromreq.userand forwarded astoolExecutionContext.@objectstack/specpatch. Measured: afterpnpm --filter @objectstack/spec build, the new docblock text has 1 hit in each ofdist/ui/index.mjsanddist/ui/index.js, and in thedist/browserbundles. The old route string has 0 hits indist. The positive control, the unchanged ADR-0063 paragraph, has 1 hit indist/ui/index.mjs. The package'sfileslist also shipssrc/**/*.zod.ts. The.d.tsfiles do not carry the docblock.No schema, parse,
.describe(), export, type or accept-set change.check:generatedconfirms that no generated reference moved.How the console reads the key (objectui at
0abd4f9f87, read-only)ConsoleLayout.tsxpassesactiveApp?.defaultAgentto the chat dock, both on desktop and in the mobile sheet.ChatDock.tsx(ChatDockConversation) callsresolveSurfaceAgent('default', { agents, appDefaultAgent: defaultAgent, aiStudioEnabled, canAuthorMetadata }). It posts toapiBase/agents/NAME/chat, with the resolved name URL-encoded.surfaceAgent.ts(resolveSurfaceAgent) runs these steps in order:isBuiltinAgentNameholds. That meansbuild,metadata_assistant,askordata_chat. Any other value is dropped.buildonstudio-build,askeverywhere else.buildrequest falls back toaskwhen AI Studio is off.askrequest becomesbuildfor a principal who may author metadata, when the catalog servesbuild.Census of the three retired routes, and of claims that a server route reads
defaultAgentSearched at
c9761cd2fb(git grep, outsidecontent/docs/releases/):packages/spec/src/ui/app.zod.ts:1658content/docs/ai/actions-as-tools.mdx:275packages/spec/CHANGELOG.md:29568,packages/client/CHANGELOG.md:3498,packages/runtime/CHANGELOG.md:31673packages/spec/liveness/agent.json:3and:70cite cloudroutes/assistant-routes.ts#buildAssistantRoutescloud @cb8ee7ff, where they stay true.planning.maxIterationsis still live throughagent-routes.tsandeval-runner.ts. This lane cannot read cloud to re-anchor them.skills/**,.claude/**,docs/adr/**andexamples/**have zero hits for the three routes. No text incontent/docs,skillsorexamplessays a server route readsdefaultAgent. No test asserts the docblock text, so no needle moves.Tests
All runs are at
7f6089bd50(HEAD). The spec build was taken at4e79442153, whose source is the same; the build-input stamp matched.pnpm --filter @objectstack/spec build: VERDICT command-exit 0.pnpm --filter @objectstack/spec check:generated: exit 0, "All 15 generated artifacts are up to date".pnpm --filter @objectstack/spec run typecheck: VERDICT command-exit 0.vitest run --project localover the sixsrc/ui/app*.test.tsfiles: 6 files, 203 tests passed. This is a declared narrowing. The diff is a comment inapp.zod.ts, so the full spec suite is left to CI.node scripts/pm/dispatch-gates.mjs --ran: 101 derived, 100 run, 1 NOT MEASURED, 0 UNRUN.check:doc-formula-expressions,check:doc-security-posture,check:skill-examples,check:docs-transcript-driftandcheck:lean-entry-closure. After a build of formula, lint, client, client-react and objectql, all five exited 0.check:dual-build-cjs-loads, reason: it needsdistfor the whole workspace (87 packages). As a narrowed stand-in, all 19requireentries of@objectstack/specload. CI'sLint & Repo Gatesruns the full gate.check-changeset-fixed,check:authz-resolver,check:error-code-casing,check:filter-alias-parity,check:meta-url-spellingandcheck:spec-changes.eslint --no-inline-config --format jsonover the two edited source files.app.zod.tsis linted, with 0 errors and 0 warnings. The.mdxfile is outside the population ("no matching configuration").eslint.config.mjsnever enables type-aware linting (it has noparserOptions.project), so this diff cannot move the result for any file it does not touch.Acceptance notes
AiChatPage.tsxalso takes adefaultAgentprop, and resolves it withresolveDefaultAgentName, which is not bounded toask/build. But the console mounts it inapps/console/src/App.tsxwith no props, soapp.defaultAgentnever reaches it there. The docblock therefore describes only the dock path. This is noted, not filed: nothing passes the key down that path. Carrier: none.defaultAgentneeds a server consumer is not this card's question (triage).Generated by Claude Code