Skip to content

fix(services): in-system copy says "this system" or drops the noun, not "this environment" (cloud#3002 ruling A) - #24407

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-24368-services-this-system-copy
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-24368-services-this-system-copy

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24368

Clause-②: no
Basis: copy only. Every HTTP status, error code, settings key and response shape stays as it was. Only the human-readable text moves.

What changed

This PR covers the domain:services part of the census on the card: plugin-auth, plugin-security and service-settings. Each string follows the rule in triage 6252585695, the one 6250167038 set out for objectstack-ai/cloud#3002 ruling A. "Made here, not from a package" becomes "authored in this system". "The system the caller is in" drops the noun, or says "this system" when the noun is the subject of the sentence. A deployment or configuration scope keeps its word. The other lanes went to #24404 (cloud-connection, mcp), #24405 (platform-objects) and #24406 (packages/spec). This PR does not touch them.

Lines are on 3777e26dc. They are the same lines as on d79dc4883, because every edit replaces one line with one line.

The strings the claim lists

# Site Before After Sense, and the reading it gets
1 plugin-auth/src/audience-posture.ts:476, SELF_REGISTRATION_CLOSED (sign-up refusal) "Self-registration is closed on this environment (audience posture invite_only). …" "Self-registration is closed (audience posture invite_only). …" The system the caller is in. The noun is dropped.
2 audience-posture.ts:487, EMAIL_DOMAIN_NOT_ALLOWED "Self-registration on this environment is limited to approved email domains, …" "Self-registration is limited to approved email domains, …" The same. The noun is dropped.
3 auth-manager.ts:2527, 403 ENV_ACCESS_DENIED (the /oauth2/authorize gate) "You are not authorized to sign in to this environment." kept A deployment addressed from outside. See "Kept, and why" below.
4 auth-plugin.ts:3505, OAuth self-service 503 SERVICE_UNAVAILABLE "OIDC provider is not enabled on this environment" "OIDC provider is not enabled" The system the caller is in. The noun is dropped, as in the runtime's "MCP server is not enabled".
5 last-admin-guard.ts:893, maxScan overflow refusal "… or raise the guard's maxScan if this environment really is that large." "… if this system really is that large." The system the caller is in. The noun is the subject, so it reads "this system".
6 last-admin-guard.ts:1278, the walled-posture remedy appended to both emptiness refusals "… will NOT give this environment an administrator. …" "… will NOT give this system an administrator. …" In-system, so "this system". The same clause opens "This deployment runs a WALLED tenancy posture". That noun is a configuration scope and keeps its word.
7 last-admin-guard.ts:1304, refusal when admin_full_access is switched off "Refusing this …: this environment recognises NO administrator because its …" "… this system recognises NO administrator because its …" In-system, so "this system".
8 last-admin-guard.ts:1354, refusal for dangling grants "Refusing this …: this environment recognises NO administrator, and this is not …" "… this system recognises NO administrator, and this is not …" In-system, so "this system".
9 last-admin-guard.ts:1510, a logger.warn line (the user-row halves) "[LastAdminGuard] refused a … that would have left this environment with no unbanned administrator (target: …)" "… left this system with no unbanned administrator (target: …)" A log line, changed. An operator reads it, but its noun means the system's administrator population, the same thing the refusal thrown next to it tells the caller. It is not a deployment or configuration scope, and triage counted it in the in-system set. So it follows the refusal, and says "this system" so the two lines read alike.
10 register-sso-provider.ts:385, 400 DOMAIN_VERIFICATION_DISABLED "Domain verification is not enabled for this environment (set OS_SSO_DOMAIN_VERIFICATION)." "Domain verification is not enabled (set OS_SSO_DOMAIN_VERIFICATION)." The system the caller is in. The noun is dropped, as the runtime half did for the switched-off MCP 404. The switch stays named.
11 plugin-security/src/packaged-permission-set-lock.ts:337–:338, PackagedPermissionSetLockedError diagnostic message (403 NOT_OVERRIDABLE) "… is declared by package '…' and is locked in this environment — editing it here would silently fork it …" "… is declared by package '…' and is locked — editing it here would silently fork it …" In-system. The noun is dropped, because the "here" that follows already says where. userMessage is unchanged.
12 packaged-permission-set-lock.ts:363, PackagedPermissionSetProvenanceUnknownError diagnostic message "… cannot be saved right now: this environment could not determine whether …" "… this system could not determine whether …" The noun is the subject, so it reads "this system". Its userMessage already says "the system couldn't confirm".
13 service-settings/src/manifests/auth.manifest.ts:92, Authentication › Audience group help "Who may become a user of this environment's apps. …" "Who may become a user of this system's apps. …" In-system: an admin reads this in Setup. Later in the same text, "unless the deployment turns it off" names the configuration scope and keeps its word.
14 manifests/mail.manifest.ts:58, Mail › Provider group help "Choose how this environment sends outbound email." "Choose how this system sends outbound email." In-system, so "this system".
15 manifests/sms.manifest.ts:22, SMS › Provider group help "Choose how this environment sends outbound SMS." "Choose how this system sends outbound SMS." In-system, so "this system".
16 translations/en.ts:27, settings.mail.groups.provider.description as row 14 as row 14 The bundle copy of row 14.
17 translations/en.ts:139, settings.auth.groups.audience.description as row 13 as row 13 The bundle copy of row 13.

Three strings the census missed, read and changed in place

The census grepped one line at a time. These three split the phrase across a string concatenation (… this + environment …), so it never saw them. A multiline search found them in the same file and in the same functions as rows 9 and 7–8. I changed them under os-dev's bounded in-place rule, because all four of its conditions hold: they are the same defect class, the fix is mechanical and the rule above fixes its shape, the claim already names this file, and the same gates verify them. In the two refusals, "administer the environment" in the same sentence follows too, so neither sentence names the system twice in two ways.

# Site Before After
18 last-admin-guard.ts:1516–:1519, the user-row refusal thrown right after row 9's warning "… that is the last administrator this environment has that is not already banned, … would leave nobody able to administer the environment or restore anyone's access …" "… this system has … administer the system or restore anyone's access …"
19 last-admin-guard.ts:1612–:1613, a logger.warn line (the standing halves), the twin of row 9 "… that would have left this environment with no unbanned administrator (losing: …)" "… left this system with no unbanned administrator (losing: …)"
20 last-admin-guard.ts:1619–:1624, the standing refusal "… who is the last administrator this environment has that is not already banned. … nobody would be able to administer the environment …" "… this system has … administer the system …"

The other locales of rows 13–15

zh-CN, ja-JP and es-ES carry the same three keys. The only change is the noun, which now uses the spellings the console's locale packs already use under the same ruling (objectstack-ai/objectui PR 12959 and PR 12962: zh 本系统, ja このシステム, es este sistema). No other word in those translations changed.

Key zh-CN ja-JP es-ES
settings.mail.groups.provider.description 选择此环境如何发送邮件。 → 选择本系统如何发送邮件。 この環境の送信方法を選択します。 → このシステムの送信方法を選択します。 … correo saliente este entorno. → … correo saliente este sistema.
settings.sms.groups.provider.description 选择此环境如何发送外发短信。 → 选择本系统如何发送外发短信。 この環境が送信 SMS を… → このシステムが送信 SMS を… Elige cómo envía este entorno los SMS salientes. → … este sistema …
settings.auth.groups.audience.description (first sentence) 谁可以成为本环境应用的用户。 → 谁可以成为本系统应用的用户。 この環境のアプリの… → このシステムのアプリの… … de las aplicaciones de este entorno. → … de este sistema.

en.ts has no settings.sms namespace. In English, the SMS help comes from the manifest literal (row 15). This PR does not add the key.

Kept, and why

  • auth-manager.ts:2527 (row 3). This refusal exists only where the host sets oidcAuthorizeGate. The option's contract (auth-manager.ts:726–:735) calls it the "OIDC OP authorization gate (cloud-as-IdP app-assignment)". The cloud control plane uses it so that "a cloud user may only obtain a code for an env client … of an org they belong to". It also says: "Unset (open editions / self-host …) = allow". Nothing in this repository sets the option. Only oidc-authorize-env-gate.dogfood.test.ts installs one, as a fixture. So the text is shown by the cloud, acting as OIDC provider, to a cloud user who is already signed in, and its noun names the environment that user asked to sign in to. That is the deployment addressed from outside, where triage keeps the cloud noun (its reason for cli). "This system" would tell that user they may not sign in to the cloud itself. Dropping the noun would tell an already signed-in user they may not sign in. The census counted this string in-system from a code reading, so I report the different reading here and in the report rather than choosing silently.
  • last-admin-guard.ts:1300 and :1350: two logger.warn lines, "refused a … in an environment whose …" and "… in an environment with no administrator that is NOT a fresh install". They use an indefinite "an environment" to describe a state, not "this environment". They are not in the census and not in this card's phrase. Noted only.
  • Source comments: every * / // hit of the phrase in the three packages.
  • plugin-security/src/__tests__/legacy-catalog-objects.testkit.ts:366: a test fixture that freezes a retired object's field description.
  • Release-owned history: plugin-auth/CHANGELOG.md (:610, :14237); .changeset/pre/22463-sso-domain-verification-unknown-provider.md:9, which quotes the old 400 as its "Before"; and content/docs/releases/v17/17-3.mdx (:18, :183, concept prose "an environment's apps").
  • The other environment nouns in the service-settings locales are configuration scope and keep their words: the env-var source label, "deploy env vars take precedence", "production environment", "regulated environments", the per-environment storage prefix.

Readers of the changed strings, measured

git grep over the whole tree (packages, docs, docs/qa, content/docs, skills, examples, .changeset) for each string's fragments, plus the sibling objectui checkout at 2ec9e2a:

  • Tests pin only substrings that survive the change: /recognises NO administrator/ (last-admin-guard.test.ts:1979, :2098), /last administrators?/i in 46 toThrow / toMatch assertions across the last-admin-guard* and grant-readers-* suites, 'Self-registration is closed' (audience-posture.test.ts:606, and a not-contain at audience-bootstrap-seam.test.ts:185), and the lock errors' class plus the set name, package id and reason in message (packaged-permission-set-lock.test.ts:229–:272). No test pins the changed noun, so no pin moved and no new prose was copied into a test.
  • oidc-authorize-env-gate.dogfood.test.ts:271 pins the code ENV_ACCESS_DENIED. That string is kept anyway.
  • No consumer parses the text. The console maps sign-up refusals by code (objectui signUpRefusalMessages.ts), and it reads Settings help from the shipped bundles.
  • No docs/qa/platform-checklist item, doc page or skill quotes any of these strings.

Mechanism hypotheses from the dispatch, measured

  • Line positions on d79dc4883: confirmed. All 17 listed sites sit at the lines the claim gives.
  • The re-grep reconciliation: the one-line grep finds 16 of the 17 listed sites and nothing else. The 17th, packaged-permission-set-lock.ts:337–:338, splits the phrase across two lines, and the census listed it as a range. The multiline search for a split phrase finds that one and three more strings, rows 18–20, which the list missed. After this PR, the only non-comment, non-test hit of this environment in the three packages is row 3, and the split-phrase search finds none.
  • "Move the other locales with check:i18n / check:i18n-stale-fill": falsified. Both gates walk the generated *.generated.ts bundle sets that i18n-extract.config.ts configs produce. service-settings has no extract config, and its four locale files are hand-written. At 3777e26dc, check:i18n reports "OK (10 package(s) …)" and check:i18n-stale-fill reports "scanned 11 bundle set(s)". Neither log names service-settings. So no repo tool moves these values. The route the ruling intends is the noun swap above, in the ruling's own per-locale spellings, not a new translation. Its coverage guard, settings-translation-coverage.test.ts, checks presence only and stays green.

Verification (head 3777e26dc, on d79dc4883; origin/main had not moved)

All builds and tests ran through scripts/pm/os-verify-lock.sh.

  • turbo run build --filter=!@objectstack/docs --concurrency=2: "Tasks: 76 successful, 76 total" (3 cached). VERDICT command-exit 0.
  • @objectstack/plugin-auth: vitest run --maxWorkers=2 reported "Test Files 163 passed (163) · Tests 3145 passed | 10 skipped (3155)". typecheck exited 0 (tsc, the examples project, and check:test-typecheck: "10 file(s) / 88 error(s) / 23 pinned signature(s) held").
  • @objectstack/plugin-security: reported "Test Files 187 passed (187) · Tests 3738 passed | 45 skipped (3783)". typecheck exited 0 (tsc, the scripts project, and check:test-typecheck: "0 file(s) / 0 error(s)").
  • @objectstack/service-settings: reported "Test Files 51 passed (51) · Tests 881 passed (881)". typecheck (tsc --noEmit) exited 0.
  • Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 67 commands from the 13 changed paths. All 67 ran after the final commit, each exit code captured before any pipe, and all 67 exited 0. --ran: "Run reconciliation — 67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN." Selected verdict lines: check:doc-authoring "19057 customer-facing string(s) … clean"; check:nul-bytes "OK (scanned 12249 text file(s) …)"; check-empty-changeset "1 declaring changeset(s) added"; check-adr-0087-registration "this PR adds no declared-breaking changeset"; check:dual-build-cjs-loads "111 … 69 …" entries/packages, over its floors.
  • pnpm exec eslint --no-inline-config over the 12 changed .ts files: exit 0, no output.
  • No ablation. The change is copy, and no test pins the changed noun.
  • .changeset/24368-services-this-system-copy.md: patch on @objectstack/plugin-auth, @objectstack/plugin-security and @objectstack/service-settings, all three published (files: dist, README.md, CHANGELOG.md). It starts with Clause-②: no.

Acceptance notes


Generated by Claude Code

…ot "this environment"

Refusals, two guard warnings and Setup help text in plugin-auth,
plugin-security and service-settings, read inside the running system,
follow the per-string rule: "this system", or drop the noun. The OIDC
authorize-gate refusal keeps its noun: only the cloud control plane
installs that gate, and there it names the environment being signed in to.

Copy only: no status, error code, settings key or response shape moves.

Claude-Session: https://claude.ai/code/session_01Nk17Z26X5DB59iQztDBADH
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Oct 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/plugin-auth, @objectstack/plugin-security, @objectstack/service-settings, touching 6 documentable anchor(s).

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

  • content/docs/api/client-sdk.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))
  • content/docs/api/error-catalog.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))
  • content/docs/api/index.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))
  • content/docs/permissions/authorization.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))
  • content/docs/permissions/sso.mdx (via DOMAIN_VERIFICATION_DISABLED (literal, a string literal in domainVerificationDisabled))
  • content/docs/protocol/kernel/error-handling.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))
  • content/docs/ui/print-pages.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))

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

  • content/docs/releases/v17/17-0.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))
  • content/docs/releases/v17/17-3.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))
  • content/docs/releases/v17/17-4.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))
  • content/docs/releases/v17/17-7.mdx (via SERVICE_UNAVAILABLE (literal, a string literal in registerAuthRoutes))

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
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • 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 — 28 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 2a1e48a1c104f533186b13fc0f19d9e60e567ce3 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from fa491536744aa7c4ba4153ee7c31bd635de3623b — the merge of head 3777e26dc067b2383761c1281d16a95bfc1971d9 into base 2a1e48a1c104f533186b13fc0f19d9e60e567ce3, 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 fa491536744aa7c4ba4153ee7c31bd635de3623b && git checkout fa491536744aa7c4ba4153ee7c31bd635de3623b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2a1e48a1c104f533186b13fc0f19d9e60e567ce3 3777e26dc067b2383761c1281d16a95bfc1971d9 && git checkout -B drift-repro 2a1e48a1c104f533186b13fc0f19d9e60e567ce3 && git merge --no-ff 3777e26dc067b2383761c1281d16a95bfc1971d9

node scripts/docs-audit/affected-docs.mjs --json 2a1e48a1c104f533186b13fc0f19d9e60e567ce3

⚠️ 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 2a1e48a1c104f533186b13fc0f19d9e60e567ce3 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:services seat 1 (#6021) · marchtian · session_01Nk17Z26X5DB59iQztDBADH · 2026-10-18T23:27Z · head 3777e26dc0. The seat's own review at TIER_DEFAULT. --tier read no path-derived mandate, and the diff touches no published schema or governed text: message copy only. It lands through the merge queue once every check on this head is green.

  • Shape: a draft PR to main. Its first line is Fixes #24368, and that is the only closing keyword in the body. Clause-②: no is at a line start in the body and in the changeset. Card and PR assignee: marchtian. Not governed: check-governed-merges reads 0 of 13 paths, and 91 changed lines ≤ 3000.
  • The rule (6250167038, 6252585695), each site read by the seat in the diff:
    • Noun dropped where it is "the system the caller is in": the two self-registration refusals, the OIDC 503, the DOMAIN_VERIFICATION_DISABLED refusal, and the packaged-set lock's diagnostic.
    • "this system" where the system is the subject: the last-admin refusals and their two log lines, the provenance refusal, and the three Setup help texts with their locale copies.
    • Unchanged: every status, code, key and shape, and each string's deployment or configuration clause ("This deployment runs a WALLED tenancy posture", "unless the deployment turns it off").
    • Locale copies change the noun only, to the spellings the console's packs already carry under the same ruling (本系统 / このシステム / este sistema).
  • The seat's ruling on the dev's open question: auth-manager.ts:2527 (403 ENV_ACCESS_DENIED, "You are not authorized to sign in to this environment.") is kept (A).
    • The seat read the producer on main. The refusal fires only where the host sets oidcAuthorizeGate, which auth-manager.ts:726–:735 documents as the cloud control plane's cloud-as-IdP hook: "Unset (open editions / self-host …) = allow". Nothing else in packages/ sets it.
    • Its reader is a cloud user signing in to an environment from outside. That is the case triage kept for cli: "the deployment addressed from outside, where the cloud noun is the right one" (6252585695). The rule's keep clause decides it.
    • Triage confirmed the census in bulk, so this one reading is named here for triage, should it read the string otherwise.
  • Pins: no test pins the changed noun. The existing prose pins (recognises NO administrator, the last-administrator assertions, "Self-registration is closed", and the lock errors' class with name, package and reason) all hold, and none was moved.
  • Gates on this head: 34 checks succeeded and 6 were skipped by design (Build Docs, Console Pin Gate, Dogfood Regression Gate, Dogfood Verify CLI, Packed-tarball smoke, Temporal Conformance: the roster set). There were no failures, and the required contexts are green. Locally the dev derived 67 families on this head (the seat's 67 at d79dc4883), ran all 67, and all exited 0 (--ran: 0 NOT-MEASURED, 0 UNRUN). The tests and typechecks of all three packages passed.
  • Accepted deviations:
    • Three strings the census missed (last-admin-guard.ts :1516–:1519, :1612–:1613, :1619–:1624) were split across a concatenation. They are in a file the claim holds, of the same class, and were changed in place. "administer the environment" in the same sentences followed, so no sentence names the system both ways.
    • The locale files are hand-written, outside check:i18n's generated bundle sets. Noun-only edits there do not count as new translations.
  • Out-of-scope findings, with their carriers:

Generated by Claude Code

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 size/s tooling

Projects

None yet

2 participants