Skip to content

docs(spec, docs): App.defaultAgent and actions-as-tools name the agent route as the one chat door - #21976

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21968-default-agent-chat-door
Oct 6, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21968-default-agent-chat-door

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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.defaultAgent docblock and one docs callout named POST /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, the App.defaultAgent docblock. It no longer says a server route reads the key. It now describes the reader that exists, objectui at the .objectui-sha pin 0abd4f9f87: the chat dock passes the active app's defaultAgent to the console's one surface-to-agent resolver. That resolver honours the value only when it names ask or build (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 :agentName path segment selects the agent. The sentence on the bare POST /api/v1/ai/chat route 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 from req.user and forwarded as toolExecutionContext.
  • Changeset @objectstack/spec patch. Measured: after pnpm --filter @objectstack/spec build, the new docblock text has 1 hit in each of dist/ui/index.mjs and dist/ui/index.js, and in the dist/browser bundles. The old route string has 0 hits in dist. The positive control, the unchanged ADR-0063 paragraph, has 1 hit in dist/ui/index.mjs. The package's files list also ships src/**/*.zod.ts. The .d.ts files do not carry the docblock.

No schema, parse, .describe(), export, type or accept-set change. check:generated confirms that no generated reference moved.

How the console reads the key (objectui at 0abd4f9f87, read-only)

  • ConsoleLayout.tsx passes activeApp?.defaultAgent to the chat dock, both on desktop and in the mobile sheet.
  • ChatDock.tsx (ChatDockConversation) calls resolveSurfaceAgent('default', { agents, appDefaultAgent: defaultAgent, aiStudioEnabled, canAuthorMetadata }). It posts to apiBase/agents/NAME/chat, with the resolved name URL-encoded.
  • surfaceAgent.ts (resolveSurfaceAgent) runs these steps in order:
    1. It keeps the value only if isBuiltinAgentName holds. That means build, metadata_assistant, ask or data_chat. Any other value is dropped.
    2. With no kept value, the surface default applies: build on studio-build, ask everywhere else.
    3. A build request falls back to ask when AI Studio is off.
    4. An ask request becomes build for a principal who may author metadata, when the catalog serves build.
    5. The result is matched against the live catalog, alias-aware. If nothing matches, the catalog's platform default is used.

Census of the three retired routes, and of claims that a server route reads defaultAgent

Searched at c9761cd2fb (git grep, outside content/docs/releases/):

Hit Lane Disposition
packages/spec/src/ui/app.zod.ts:1658 spec moved with this card
content/docs/ai/actions-as-tools.mdx:275 docs moved with this card
packages/spec/CHANGELOG.md:29568, packages/client/CHANGELOG.md:3498, packages/runtime/CHANGELOG.md:31673 release-owned listed, not edited. These are historical entries.
packages/spec/liveness/agent.json:3 and :70 cite cloud routes/assistant-routes.ts#buildAssistantRoutes spec, ledger evidence listed, not edited. They name the handler file, not a route path, and are anchored at cloud @cb8ee7ff, where they stay true. planning.maxIterations is still live through agent-routes.ts and eval-runner.ts. This lane cannot read cloud to re-anchor them.

skills/**, .claude/**, docs/adr/** and examples/** have zero hits for the three routes. No text in content/docs, skills or examples says a server route reads defaultAgent. No test asserts the docblock text, so no needle moves.

Tests

All runs are at 7f6089bd50 (HEAD). The spec build was taken at 4e79442153, 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 local over the six src/ui/app*.test.ts files: 6 files, 203 tests passed. This is a declared narrowing. The diff is a comment in app.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.
    • Five gates first refused with PREREQUISITE NOT MET (exit 3): check:doc-formula-expressions, check:doc-security-posture, check:skill-examples, check:docs-transcript-drift and check:lean-entry-closure. After a build of formula, lint, client, client-react and objectql, all five exited 0.
    • NOT MEASURED: check:dual-build-cjs-loads, reason: it needs dist for the whole workspace (87 packages). As a narrowed stand-in, all 19 require entries of @objectstack/spec load. CI's Lint & Repo Gates runs the full gate.
    • The six artifact-roster gates flagged under my paths also exited 0: check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity, check:meta-url-spelling and check:spec-changes.
  • Lint, narrowed: eslint --no-inline-config --format json over the two edited source files.
    • Population, read from eslint's own answer: app.zod.ts is linted, with 0 errors and 0 warnings. The .mdx file is outside the population ("no matching configuration").
    • Invariance: eslint.config.mjs never enables type-aware linting (it has no parserOptions.project), so this diff cannot move the result for any file it does not touch.

Acceptance notes

  • objectui's AiChatPage.tsx also takes a defaultAgent prop, and resolves it with resolveDefaultAgentName, which is not bounded to ask / build. But the console mounts it in apps/console/src/App.tsx with no props, so app.defaultAgent never 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.
  • Whether defaultAgent needs a server consumer is not this card's question (triage).

Generated by Claude Code

claude added 2 commits October 6, 2026 09:07
…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>
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 4 documentable anchor(s).

4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/actions-as-tools.mdx (via /api/v1/ai/agents/:agentName/chat (route, a path literal in a comment in AppSchema))
  • content/docs/ai/index.mdx (via /api/v1/ai/agents/:agentName/chat (route, a path literal in a comment in AppSchema))
  • content/docs/getting-started/quick-reference.mdx (via AppSchema (symbol, a top-level const))
  • content/docs/protocol/kernel/error-handling.mdx (via /api/v1/ai/agents/:agentName/chat (route, a path literal in a comment in AppSchema))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v14.mdx (via AppSchema (symbol, a top-level const))
  • content/docs/releases/v17/17-0.mdx (via AppSchema (symbol, a top-level const), /api/v1/ai/chat (route, a path literal in a comment in AppSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 138 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f76c6221acd3997dd778fdd3e8e7d43e0bec4851 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 04a85f58e75f24bd57cd468fa264295a925a7bf2 — the merge of head 7f6089bd504e05ec21401abdedb7c280712bf00c into base f76c6221acd3997dd778fdd3e8e7d43e0bec4851, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs f76c6221acd3997dd778fdd3e8e7d43e0bec4851 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:ui tooling labels Oct 6, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 6, 2026 10:30
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 6, 2026 10:30
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit 04e776b Oct 6, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21968-default-agent-chat-door branch October 6, 2026 11:11
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…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>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…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>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(spec, docs): App.defaultAgent's docblock and actions-as-tools.mdx name POST /api/v1/ai/assistant/chat, a route cloud retired (cloud#2621)

2 participants