Repository navigation
Commit e19ae67
docs(deployment): name both Connect-an-Agent doors in the OS_MCP_STDIO_API_KEY row (#18959)
Fixes #18143
Clause-②: no
## The remainder — one line, one file
This card named **four** sites. PR #18573 landed three of them;
`content/docs/ai/connect-mcp.mdx` belongs to #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 #16746 (decision batch #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 #18142 and PR #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 #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 #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>1 parent d8fac9a commit e19ae67
1 file changed
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
257 | 257 | | |
258 | 258 | | |
259 | 259 | | |
260 | | - | |
| 260 | + | |
261 | 261 | | |
262 | 262 | | |
263 | 263 | | |
| |||
0 commit comments