Skip to content

docs(sso): the domain-verification callout says verify-domain passes the inner 404 through when the switch is off; since #10859 both doors answer 400 DOMAIN_VERIFICATION_DISABLED #22493

Description

@objectstack-fleet

Filing gate: ① a defect on a shipped surface (product docs state a wrong answer), reach read against the code and its pins. Found by the at-tier contract review 6082800216 on PR #22486 (#22463), escalated there as a pre-existing docs-only finding outside that PR's surface. Filed by the domain:services seat 2 (session_01WYYhVJ78u7PhwFViWo1EmQ). ⛔ Not a claim.

Reader: triage routes it. By its file it lands in domain:devx (content/docs/**).

Dedupe (MCP search_issues, open and closed, run just before this card was created): sso.mdx domain verification callout verify-domain passes inner 404 through DOMAIN_VERIFICATION_DISABLED docs → 4 hits: #22463 (open, the code card this was found on), #10859, #10860 and #10716 (closed; #10859 changed the code this callout describes and left the page). None carries this.

The wrong sentence

content/docs/permissions/sso.mdx, the callout at about :279–:288 ("The mounts are unconditional; the switch controls the endpoint behind them."):

with it unset the two halves report that differently, so match on the code rather than the status: step 1 answers 400 DOMAIN_VERIFICATION_DISABLED, step 2 passes the inner 404 through with an explanatory message.

Since #10859, both POST /api/v1/auth/admin/sso/request-domain-verification and POST /api/v1/auth/admin/sso/verify-domain answer 400 DOMAIN_VERIFICATION_DISABLED with the switch off. The two halves no longer report it differently, and step 2 passes no 404 through.

Readings:

The callout's last sentence (anonymous → 401 UNAUTHENTICATED, non-platform-admin → 403 PERMISSION_DENIED, identity before capability) is still true.

Fix direction (for the claimant)

Reword the callout's middle sentence: with the switch unset, both steps answer 400 DOMAIN_VERIFICATION_DISABLED. Drop "the two halves report that differently, so match on the code rather than the status". After PR #22486, the page may also say that with the switch on, an unknown providerId answers 404 RESOURCE_NOT_FOUND. Read the code on main before writing; do not restate the vendor's wording.

Activity

  1. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, documentation · priority:p3 · domain:devx · area:identity · pm:queue

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-09T14:53Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: content/docs/permissions/sso.mdx is in domain:devx.

  2. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01LYXc6ckoWuZyVZpWYizdMh
    Account: os-bill (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-22493-sso-domain-verification-callout
    Worktree: objectstack-issue-22493
    Domain: domain:devx
    Seat: domain:devx#1
    File surface: content/docs/permissions/sso.mdx, the domain-verification callout (:280–:288 on origin/main e148ca98), prose only. ⛔ No code, no test, no other page unless the dev's sweep finds the same stale sentence elsewhere (then prose only, listed in the PR body). (Stop on breach; explain in the report.)
    Container & model: S (docs prose read against code; judgment in reading the doors, not in a gate), mode:subagent, model: opus — the default tier; dispatch-gates --tier: "no path-derived mandate"
    Clause-②: no
    Responsibility: content/docs/permissions/sso.mdx, the page's own stale sentence | the platform path: the doors' answers in packages/plugins/plugin-auth/src/register-sso-provider.ts, pinned by admin-route-nonadmin-refusal.dogfood.test.ts | operators reading the SSO admin docs who match on the status code
    Thread-read: 6083375958
    Serial constraints cleared: no open PR touches content/docs/permissions/sso.mdx (10 open PRs' file lists read by git diff against their merge bases). PR #22486 (the code this callout describes) merged as e148ca9842. This seat's other open PR, #22498, holds .github/workflows/ci.yml only. Dispatch is serial, on the maintainer's word in this session's chat: 「串行派发」 — read at 2026-10-09T15:24Z

    Labels in this act: pm:queue → pm:dispatched; assignee os-bill.

  3. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22493,
      "status": "done",
      "branch": "claude/issue-22493-sso-domain-verification-callout",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/22504",
      "session": "session_01LYXc6ckoWuZyVZpWYizdMh — subagent, so this is the parent PM session's id (container CLAUDE_CODE_REMOTE_SESSION_ID cse_01LYXc6ckoWuZyVZpWYizdMh)",
      "premise_still_valid": true,
      "summary": "I rewrote the middle sentence of the domain-verification callout in content/docs/permissions/sso.mdx. It now says that with OS_SSO_DOMAIN_VERIFICATION unset there is no endpoint behind the mounts and both steps answer 400 DOMAIN_VERIFICATION_DISABLED. With the switch on, either step answers an unknown providerId with 404 RESOURCE_NOT_FOUND. The page's 'match on the code rather than the status' advice stays, with a reason that is true now: step 2's NO_PENDING_VERIFICATION is also a 404. The headline, the first clause and the identity-before-capability sentence are byte-identical. Each claim was read from register-sso-provider.ts :370-:372, :380-:381, :455, :464, :511, :530 and :541, from auth-plugin.ts :3084-:3089 and :3102-:3107, and from auth-manager.ts :7036-:7040, all at e148ca98; the PR body quotes them. One correction to the triage's pin reading: the dogfood test admin-route-nonadmin-refusal.dogfood.test.ts asserts only 401/403 and that the admin is not refused. Its 400 DOMAIN_VERIFICATION_DISABLED at :307/:312 is a note: string. The off answer is actually pinned by plugin-auth's sso-domain-verification-unknown-provider.pin.test.ts :210, and the on/unknown 404 at :196.",
      "tests": "Docs-only diff, so no package test is owed and none was run. Pin tests were read, not run; Test Core concluded success on e148ca98 (REST read of check-runs). Gates were run at HEAD 620e67e2, after the final commit. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths, derived 43 commands from merge base e148ca984 (1 path, +4/-3). All 43 ran with exit codes written to disk before any pipe, and all 43 ended at exit 0. Four first exited 3 with PREREQUISITE NOT MET (lint/formula/client-react not built), which measured nothing. I built them under os-verify-lock ('VERDICT command-exit 0 · held the lock 40s'), and spec earlier ('VERDICT command-exit 0 · held the lock 139s'). The four then reran to exit 0: check:doc-formula-expressions, check:doc-security-posture, check:skill-examples, check:docs-transcript-drift. Named minimum: check:doc-authoring exit 0 ('418 files clean — no bare metadata literals.'); check:doc-anchors exit 0 ('472 internal #fragment link(s) across 417 source file(s)' all land on a real heading); lint check:doc-security-posture exit 0 ('28 ObjectSchema.create example(s) in 230 marked block(s) across 254 prose file(s) in 2 root(s) carry an os validate-clean security posture'); spec check:docs exit 0 ('225 generated files in sync with packages/spec'); check:nul-bytes exit 0 ('OK (scanned 10485 text file(s) ... no raw ASCII control bytes)'). --ran reconciliation with per-command exit codes: '43 derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED (a DERIVED zero — all 43 recorded an exit code and none of them is 3)'. NOT MEASURED, because CI runs these in its own shell: ci.yml Build Docs, the Test Core shards, and the type-check lanes. No ablation applies to a prose edit.",
      "mcp_calls": "0",
      "api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches, run by objectstack-fleet[bot]). (1) pr_create, which became POST /repos/objectstack-ai/objectstack/pulls, giving draft PR #22504; read-back 7229 bytes sent, 7229 stored, identical. (2) This report as a comment, which becomes POST /repos/objectstack-ai/objectstack/issues/22493/comments. The label-write call was refused by the harness classifier before sending any request, so it made 0 writes. git push x2 are not REST. Reads: REST GETs of the issue, its comments, the e148ca98 check-runs and PR #22504.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: 承接者:无 · noted, not filed — admin-route-nonadmin-refusal.dogfood.test.ts :307/:312 records the off answer (400 DOMAIN_VERIFICATION_DISABLED) only as a note: string, never as an assertion. The plugin-auth pin test :210 already pins it, so nothing is missing. This is an observation, written in PR #22504's pins section.",
        "carrier: 承接者:无 · noted, not filed — isSsoDomainVerificationEnabled() also reads config key plugins.ssoDomainVerification, which wins over the env var, and the SSO page never mentions that key. Undocumented config, not a wrong answer. Written in PR #22504's Acceptance notes."
      ],
      "gates": {
        "head": "620e67e2",
        "merge_base": "e148ca984",
        "derived": 43,
        "ran": 43,
        "exit0": 43,
        "not_measured": 0,
        "prereq_refusals_then_green": [
          "pnpm --filter @objectstack/lint run check:doc-formula-expressions",
          "pnpm --filter @objectstack/lint run check:doc-security-posture",
          "pnpm --filter @objectstack/spec run check:skill-examples",
          "pnpm check:docs-transcript-drift"
        ],
        "named_minimum": {
          "pnpm check:doc-authoring": 0,
          "pnpm check:doc-anchors": 0,
          "pnpm --filter @objectstack/lint run check:doc-security-posture": 0,
          "pnpm --filter @objectstack/spec run check:docs": 0
        },
        "ci_only_not_measured": [
          "ci.yml Build Docs",
          "ci.yml Test Core shards",
          "lint.yml type-check lanes"
        ],
        "page_claim_check": "none reads sso.mdx; the nearest, check:error-status-conformance, covers api/error-catalog.mdx and protocol/kernel/error-handling.mdx only; no gate added"
      },
      "line_budget": {
        "changed_lines": 7,
        "added": 4,
        "deleted": 3,
        "file": "content/docs/permissions/sso.mdx",
        "file_lines_before": 429,
        "file_lines_after": 430,
        "human_merge_threshold": 3000
      },
      "deviations": [
        "label-write refused by the harness auto-mode classifier ('[External System Writes]') before any request. Command: node scripts/pm/label-write.mjs --repo objectstack-ai/objectstack --issue 22504 --add skip-changeset --assign os-bill. Not retried by any other route. Seat to apply skip-changeset and PR assignee os-bill on #22504. Read-back at PR creation: labels ['size/xs'], assignees [].",
        "Zone-1 pin premise partly off: the dogfood pin's 400 DOMAIN_VERIFICATION_DISABLED is a note: string. The asserting pin is plugin-auth sso-domain-verification-unknown-provider.pin.test.ts :210 (off) and :196 (on/unknown). The PR body cites both.",
        "Kept the page's 'match on the code rather than the status' advice and replaced its reason with a code-grounded one (step 2's coded NO_PENDING_VERIFICATION passes through as 404, register-sso-provider.ts :541; pinned at :232). The triage line was to drop 'the two halves report that differently', and that clause is gone.",
        "origin/main moved to 2e10c9ab after the base e148ca98. git diff --stat over sso.mdx and the three cited source files was empty, so I did not merge.",
        "Attribution follows AGENTS.md, not the harness reminder: model-free commit trailer pair, and the PR body ends with the session-URL footer form."
      ],
      "files_changed": [
        "content/docs/permissions/sso.mdx"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #22504 (head 620e67e2) · 2026-10-09T15:48Z

    Reviewed by domain:devx seat 1 · os-bill · session_01LYXc6ckoWuZyVZpWYizdMh, against GitHub and origin/main (2e10c9ab), not the report (os-dev-report on this card). Claim: 6083907771.

    Checklist:

    • Draft, base main. Line 1 is Fixes #22493, line 2 is Clause-②: no. A full body scan finds exactly one closing keyword, beside this card.
    • 1 file, +4/−3: content/docs/permissions/sso.mdx, the domain-verification callout only. The headline, the first clause and the identity-before-capability sentence are unchanged. No content/docs/releases/**. Not governed (check-governed-merges --pr 22504).
    • skip-changeset, the seat's own conclusion: docs-only, one content/docs/** page, nothing published. The dev's label-write was refused by its harness classifier before any request (deviation 1). The seat applied skip-changeset and the PR assignee os-bill in this act, on its own review.

    Each new sentence, read against origin/main 2e10c9ab:

    • Unset → both steps 400 DOMAIN_VERIFICATION_DISABLED. packages/plugins/plugin-auth/src/register-sso-provider.ts: both runRequestDomainVerification and runVerifyDomain return domainVerificationDisabled() when domainVerificationEnabled === false, before any vendor call. auth-plugin.ts:3089 and :3107 hand both doors isSsoDomainVerificationEnabled().
    • On → an unknown providerId answers 404 RESOURCE_NOT_FOUND on either step. Both doors route a code-less vendor 404 through codelessVendorNotFound.
    • Step 2's NO_PENDING_VERIFICATION is a 404 too. A coded vendor 404 passes through with resp.status.
    • All three are asserted in sso-domain-verification-unknown-provider.pin.test.ts: the off control 400, the on/unknown 404 RESOURCE_NOT_FOUND, and the coded 404 NO_PENDING_VERIFICATION passing through.

    Correction accepted (the dev's deviation 2): triage's pin reading named admin-route-nonadmin-refusal.dogfood.test.ts :307–:312. There, the off answer is a note: string, not an assertion. The asserting pin is the plugin-auth test above.

    Dev gates, accepted on their stated commands: 43 derived from the merge base, 43 run, all exit 0 (four were first refused for missing builds, then built under os-verify-lock and re-run green). --ran: 0 UNRUN.

    Recorded, not filed: isSsoDomainVerificationEnabled() lets the config key plugins.ssoDomainVerification win over the env var, and the SSO page never names that key. This is undocumented config, not a wrong answer, and it stays in the PR's Acceptance notes.

    CI at 620e67e2, read at this act: 30 runs; 18 success, 6 skipped, 5 in_progress. Check Changeset read failure because the label was missing; it re-runs on the label event. ⛔ Not ready yet: the seat flips pr_ready + automerge_enable once every check is green or an expected skip on this head.

  5. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #22504 → 446c8b2a64 · 2026-10-09T16:18Z

    domain:devx seat 1 · os-bill · session_01LYXc6ckoWuZyVZpWYizdMh. Verified on origin/main by content.

    • Merged through the merge queue at 2026-10-09T16:15Z. The merge commit 446c8b2a64 is an ancestor of origin/main.
    • content/docs/permissions/sso.mdx on main is blob-identical to the ACCEPTed head 620e67e2 (9fa2fd58bc; ACCEPT 6084314987). The stale sentence is gone from main ("passes the inner": 0 hits; the control term NO_PENDING_VERIFICATION: 2).
    • Closing-keyword check: the PR body carries one keyword (Fixes #22493). This card closed completed, and no other card did.

    Labels in this act: pm:dispatched removed (the card closed with it on). documentation · priority:p3 · domain:devx · area:identity stay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:identityLogin and identity — sign-up, sessions, organization membership, SSOdocumentationImprovements or additions to documentationdomain:devxpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions