Repository navigation
docs: name both Connect-an-Agent doors in three published pages - #18573
Conversation
Three published pages still routed readers to "Setup -> Connect an Agent", the one app a non-admin cannot open: SETUP_APP declares requiredPermissions: ['setup.access'], and the ruling that made the page reachable for every signed-in user landed as a second navigationContributions entry into the `account` app's grp_account_developer group (packages/mcp/src/connect-ui.ts), not as an ungating of Setup. - content/docs/api/index.mdx: a direct key-minting instruction -> names both doors, matching the wording already shipped in packages/mcp/README.md and content/docs/ai/connect-mcp.mdx. - content/docs/ai/agents.mdx: descriptive summary that defers step-by-step setup to /docs/ai/connect-mcp -> drops the "Setup -> " prefix only. - content/docs/getting-started/build-with-claude-code.mdx: the sentence's subject was "Admins", which a prefix drop cannot fix -> names both doors. Text only. No behaviour, no gate, no authorization change. Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk Co-authored-by: Claude <noreply@anthropic.com>
复核:ACCEPT —— 围栏守住了,三处逐行判也判对了
围栏
⭐ 三处逐行判,第三处是本轮最该留存的一个判断本席读的是
⇒ 这正是派发令要的那件事:逐行判,并说清为什么 —— 而它在第三行上发现了一个「同一种改法在这里不成立」的理由,⛔ 不是三行套同一个模板。 放行判据
|
…O_API_KEY row (objectstack-ai#18959) Fixes objectstack-ai#18143 Clause-②: no ## The remainder — one line, one file This card named **four** sites. PR objectstack-ai#18573 landed three of them; `content/docs/ai/connect-mcp.mdx` belongs to objectstack-ai#17648. What was left is the fourth: the `OS_MCP_STDIO_API_KEY` row in `content/docs/deployment/environment-variables.mdx`, located **by content**, not by the line number the card quotes. | | the cell | |:--|:--| | before | … Mint one from **Setup → Connect an Agent** (or `POST /api/v1/keys`). … | | after | … Mint one from the **Connect an Agent** page — **Account → Developer** for any signed-in user, **Setup → Connect an Agent** for platform admins — or `POST /api/v1/keys`. … | One line in, one line out. It is a table cell in a long Markdown table, so the two-door sentence is compressed to fit: pipe count unchanged (5), row count unchanged (133 `OS_` rows), still a single line. ## Why the old cell was wrong `SETUP_APP` declares `requiredPermissions: ['setup.access']`, and a permissionless principal gets `403 PERMISSION_DENIED` on `/api/v1/meta/apps/setup`. A **direct minting instruction** naming only the Setup door therefore tells a non-admin to take a path they cannot take. Ruling objectstack-ai#16746 (decision batch objectstack-ai#85) delivers the page to them through a `navigationContributions` entry in the **`account`** app — app `account`, group `grp_account_developer` (label **Developer**), item `nav_connect_agent` (label **Connect an Agent**), package id `com.objectstack.account`. The Setup entry **stays** for admins, deliberately. So the fix is **name both doors**, ⛔ not replace Setup with Account — the shape PR objectstack-ai#18142 and PR objectstack-ai#18573 established. The wording here is copied from the two sibling pages rather than invented as a fourth spelling: - `content/docs/api/index.mdx:68-69` — "…from the **Connect an Agent** page in the Console — **Account → Developer** for any signed-in user, **Setup → Connect an Agent** for platform admins." - `content/docs/getting-started/build-with-claude-code.mdx:435-436` — "…lives on the **Connect an Agent** page: **Account → Developer** for any signed-in user, **Setup → Connect an Agent** for platform admins." ## Post-condition probe — written BEFORE the edit, and deliberately NOT "Setup goes to 0" An earlier round's first probe was "`Setup → Connect an Agent` must go to 0 in this file". That probe is **wrong for this card**: the correct end state keeps the Setup door named, so it would read a correct landing as a half-done one. The post-conditions here are about the **Account door appearing alongside**. Every count is taken on a **whitespace-flattened** file, so wrapped prose cannot give a false zero, and every zero is paired with a control from the same population that must hit. | # | reading (flattened) | before | after | post-condition | |:--|:--|--:|--:|:--| | A | this file, `Account → Developer` | 0 | **1** | ≥ 1 — the Account door appears | | B | this file, `Setup → Connect an Agent` | 1 | **1** | ≥ 1 — Setup **stays** named, for admins | | C | CONTROL, this file, `Connect an Agent` unprefixed | 1 | 2 | nonzero both sides — the reader has a pulse | | D | table integrity: `OS_` rows / pipes in the row / lines for that key | 133 / 5 / 1 | 133 / 5 / 1 | unchanged, single line | | E | CORPUS CONTROL over `content/docs/**/*.mdx` (404 files), `Connect an Agent` unprefixed | 10 | 11 | nonzero — the corpus reader has a pulse | Corpus-level close-out: the Setup door is still named in exactly **4** files (unchanged by design), and **every one of the 4 now also names the Account door** — carriers naming the Setup door but not the Account door: **0**. | carrier | `Setup → Connect an Agent` | `Account → Developer` | |:--|--:|--:| | `content/docs/ai/connect-mcp.mdx` | 1 | 1 | | `content/docs/api/index.mdx` | 1 | 1 | | `content/docs/deployment/environment-variables.mdx` | 1 | 1 | | `content/docs/getting-started/build-with-claude-code.mdx` | 1 | 1 | ## Serial constraint — re-measured at hunk level, and it does not bite PR objectstack-ai#18420 (draft, untouched since 2026-09-17T16:16Z) is the only open PR touching this file. Read from its diff: its **only** hunk in this file is `@@ -87,7 +87,7 @@`, the `OS_AUTOMATION_SCHEDULED_WORK_ENABLED` row. This PR changes the row at `:260`. **173 lines apart**, far outside git's three-line context ⇒ no textual conflict. Nothing in objectstack-ai#18420 was touched or coordinated. ## Verification Gate families derived in this worktree from the real change set, not from a hand-written list: `node scripts/pm/dispatch-gates.mjs --commands` (change set: 1 path vs merge base `46559f61c`). - **39 derived families, 39 run, all `exit 0`.** Reconciled with exit codes recorded: `dispatch-gates --repo objectstack-ai/objectstack --ran` ⇒ "39 derived famil(ies) accounted for — 39 run, 0 NOT-MEASURED (a DERIVED zero — all 39 recorded an exit code and none of them is 3)". - Four of them first returned `PREREQUISITE NOT MET` (`exit 3` ×3, plus `check:skill-examples` exit 1 on an unbuilt `client-react` dist) — **not findings**. After `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --filter=@objectstack/client-react --filter=@objectstack/client` (exit 0) all four re-ran at `exit 0`: `check:doc-formula-expressions`, `check:doc-security-posture`, `check:skill-examples`, `check:docs-transcript-drift`. - `pnpm --filter @objectstack/spec build` ran first (exit 0), so `check:docs` read a current tree. - That derivation is **not** a complete account of CI — the artifact-roster, wide-population, pending-changeset and path-scheduled families sit outside it, as the tool says of itself. - Control characters: `grep -naP` over the edited file finds none (exit 1), with a planted positive control proving the reader fires (exit 0, hit). `pnpm check:nul-bytes` exit 0. ### `pnpm lint` — a **proven narrowing**, not a skipped run The repo-wide scan is CI's run. Three pieces of evidence that narrowing excluded nothing: 1. **Population, read from eslint's own config:** every `files:` glob in `eslint.config.mjs` enumerates code extensions (`ts,tsx,mts,cts,js,jsx,mjs,cjs`); the string `mdx` occurs **0** times in that config. `.mdx` is not in the linted population at all. 2. **File count, read from `--format json`:** eslint over the changed file returns **0 results**; the positive control (`scripts/check-nul-bytes.mjs`) returns **1 result** — the reader resolves files and reports. 3. **Invariance for untouched files:** the config enables no type-aware linting for any file (its own header: "this repo runs one `eslint.config.mjs`, which never enables type-aware linting (no `parserOptions.project`, no typed `@typescript-eslint` rules) for ANY file"), so this diff cannot move any untouched file's verdict. ### Changeset: `skip-changeset`, measured Nothing published moves. - 83 tracked manifests; **70** declare `files[]` (the control: the reader resolves `files[]` arrays — e.g. `@objectstack/spec` ⇒ `dist`, `json-schema`, `liveness`, `prompts`, `llms.txt`, `README.md`, `src/**/*.zod.ts`, `CHANGELOG.md`, `api-surface`, `spec-changes.json`). Entries reaching `content/docs/**`: **0**. - Symbol grep over the **2138** files those `files[]` entries actually resolve to: `Mint one from` ⇒ **0**, `Account → Developer` ⇒ **0**; positive control `objectstack` ⇒ **1971** files, so the reader reaches published bytes. - The only consumer of `content/docs/`, `@objectstack/docs` (`apps/docs`), is `private: true` and declares no `files[]`. - The one published manifest whose text mentions `content/docs` (`@objectstack/plugin-webhooks`) does so in its `description` prose about a different page; its `files[]` is `dist`, `README.md`, `CHANGELOG.md`. ## Acceptance notes Out of scope, noted and **not** filed: - The card's four deliberately excluded carriers (`docs/adr/0101-…:104`, `docs/qa/platform-checklist/areas/ai.json:206`, two `.changeset/*.md`) are dated records, left untouched. - This same file carries `Setup → Settings` and `Setup → Authentication`, and the corpus carries 27 other `Setup → X` phrases (Access Control, People, SSO Providers, Datasources, Approvals …). Those name genuinely admin-only surfaces addressed to admins — the Connect-an-Agent defect exists precisely because that one page is **also** delivered to non-admins through the `account` app, which is not true of the others. No defect, and the successor question has an answer: **successor: none** — no PR or reader is routed to them by this change. --- _Generated by [Claude Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_ --- _Generated by [Claude Code](https://claude.ai/code)_ Co-authored-by: Claude <noreply@anthropic.com>
Part of #18143
Clause-②: no
What this changes
Three published documentation pages still sent readers to Setup → Connect an
Agent to reach the Connect-an-Agent page.
SETUP_APPdeclaresrequiredPermissions: ['setup.access'], so that instruction is a dead end forevery non-admin — and the ruling that made the page reachable for them was
delivered by a second
navigationContributionsentry into theaccountapp's
grp_account_developergroup, not by opening Setup up. Each line isjudged on its own; two different fixes result.
content/docs/api/index.mdxcontent/docs/ai/agents.mdx/docs/ai/connect-mcpSetup →prefix onlycontent/docs/getting-started/build-with-claude-code.mdxWhy
agents.mdxgets the prefix drop and not the two-door treatment. Theparagraph sits under a Callout that already reads "Step-by-step client setup
(Claude Code, Claude Desktop,
.mcp.json, API keys) with verification andtroubleshooting lives in Connect an MCP Client. The
summary below covers the architecture." That page carries the authoritative
two-door routing. Naming the page without a door removes the false direction
without duplicating routing into a section that explicitly defers it.
Why
build-with-claude-code.mdxcannot be fixed by a prefix drop. Itssubject was "Admins find …". Deleting
Setup →leaves the sentence stilltelling a non-admin reader that this is somewhere admins go. The subject is the
drift, so the sentence is rewritten and both doors are named.
Wording for the two-door lines is copied from what already shipped in
packages/mcp/README.mdandcontent/docs/ai/connect-mcp.mdx.Deliberately out of this PR
content/docs/deployment/environment-variables.mdx:260carries the fourthinstance of the same drift. It is left untouched here because another PR is
in flight against that file; it will be taken in a follow-up round. That is
why this PR says
Part of #18143and not a closing keyword — Four more shipped docs pages still send a non-admin to "Setup → Connect an Agent", the one app that 403s for them #18143 staysopen for that remaining line.
(
docs/adr/0101-…,docs/qa/platform-checklist/areas/ai.json, and two.changeset/*.mdbodies) are dated records — a ruling, a test checklist andpublished version history. They are history, not drift.
Facts re-verified for this PR
Tree
objectstack-issue-18143, revision340b6dcf74df4f2e931e26f8fb795ce9b5dbf0cb:packages/platform-objects/src/apps/setup.app.ts:47—requiredPermissions: ['setup.access'](control::31name: 'setup').packages/mcp/src/connect-ui.ts— twonavigationContributions:app: 'setup'/group_integrations, andapp: 'account'/grp_account_developer; both itemsid: 'nav_connect_agent',label: 'Connect an Agent'.packages/platform-objects/src/apps/account.app.ts:179-181—id: 'grp_account_developer',label: 'Developer';:186nav_account_api_keys.packages/apps/account/src/index.ts:27—ACCOUNT_APP_PACKAGE_ID = 'com.objectstack.account'.Changeset
skip-changeset, measured rather than assumed. Everyfiles[]array in theworkspace was enumerated (70 packages): the distinct entry set is
CHANGELOG.md | README.md | api-surface | dist | json-schema | liveness | llms.txt | prompts | spec-changes.json | src/**/*.zod.ts.files[]resolvesrelative to each package root and
content/docs/sits above all of them, so noentry can reach it. Firing control for the enumerator: it does report
@objectstack/lintshippingCHANGELOG.md, and@objectstack/specshippingllms.txt/prompts. Symbol check: three distinctive strings from this diff(
copy-paste-ready connect snippet,The in-product entry point,scripts, CI, headless agents) return zero hits acrosspackages/,apps/and
examples/; the same grep overcontent/docs/hits all three lines.Acceptance notes
noted, not filed:content/docs/ai/connect-mcp.mdx:104spells the admindoor as a bullet whose direct link is
/_console/apps/com.objectstack.setup/page/connect_agent,while the pages fixed here spell the same door only as a breadcrumb. Purely a
consistency observation, not a defect, and no in-flight PR or reader path
depends on it. Successor: none.
Verification
Text-only change to three MDX pages; no code, no behaviour, no gate and no
authorization change. Gate families were derived from the diff with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackand run locally; results are in the dispatch report. Repo-wide scans belong to
CI.
Generated by Claude Code