Skip to content

fix(ci): the RC smoke asserts the declared 401 refusal for an anonymous get-session - #18140

Merged
claude[bot] merged 1 commit into
mainfrom
claude/issue-18079-rc-smoke-get-session
Sep 14, 2026
Merged

claude[bot] merged 1 commit into
mainfrom
claude/issue-18079-rc-smoke-get-session

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Closes #18079

Clause-②: no — this neither loosens an accept set nor widens a published contract surface. It tightens one CI assertion onto the contract the code already declares.

The question this card asked, and the answer

#18079 named two hypotheses and asserted neither: either the anonymous GET /auth/get-session behaviour regressed (fix the auth surface), or the smoke's expectation is stale (fix the probe). The second is true, and it is established from the tree rather than inferred.

packages/plugins/plugin-auth/src/anonymous-session-refusal.ts exists for exactly this, and its header records the ruling:

Director seat, decision batch #117 item 4 (2026-09-12), maintainer verbatim 「17238 B」 — the server answers the platform's standard ADR-0112 failure envelope with HTTP 401 instead of 200 + null, and SessionResponseSchema is UNTOUCHED. The charter rule quoted in that ruling: 「spec 与代码不一致默认改代码,改协议单独立卡非选项」.

AuthManager.handleRequest calls it on the one seam every vendor route passes through, and the call site says in as many words that this one is the admission move:

⚠️ NOT the same kind of change: that one is forbidden to move admission and this one IS the admission move (200 -> 401).

So the 401 is the declared contract. The smoke was asserting a shape the platform deliberately stopped serving. ⇒ The auth surface is not touched by this PR.

Three independent legs agree on the exact envelope, so the assertion is pinned to a measurement and not to a guess:

leg source reading
the ruling anonymous-session-refusal.ts header 401 + ADR-0112 envelope
the wire run 34774426350 / 34728125950 job log {"success":false,"error":{"code":"UNAUTHENTICATED","message":"Sign in first"}}
the spec packages/spec/src/api/errors.zod.ts:174 401: 'UNAUTHENTICATED'

Bisect — the true first failure, not the observed window

The card cautioned that the observed window (back to b06b2db5c4) is not the start, because the runs on a83dbb6124 and 6d647858b7 read no-run and cancelled. Correct — and the real boundary is a day earlier and is not a main commit at all.

  • Causal commit on main: 374d9d3afafix(plugin-auth)!: an anonymous get-session is refused with the declared 401 envelope, not answered 200 null (#17881), 2026-09-12T19:15:14Z. git log --diff-filter=A names it as the commit that adds the refusal module; the shallow graft boundary here is ca0a1f83d6 (2026-07-29), far older, so that add is genuine and not a graft artifact.
  • Last green RC smoke: run 34726616436, 2026-09-12T23:54:27Z, status posted to RC head a1effc8e44 (chore: version packages, 2026-09-12T18:12:26Z).
  • First red RC smoke: run 34728125950, 2026-09-13T00:30:11Z, job 103645853837, status posted to RC head e612087feb (chore: version packages, 2026-09-13T00:29:49Z). Its log carries the same assertion and the byte-identical body as the card's evidence run.

Those two runs carry the same main head sha (a9c6477904) and opposite verdicts, which is the card's "NOT main-red" point showing up as data: the smoke tests the release candidate, not the commit the check attaches to. The decisive probe is therefore on content, not on timing —

                                anonymous-session-refusal.ts   platform-admin-gate.ts   (nonsense path)
a1effc8e44  LAST GREEN RC              ABSENT                        PRESENT                 ABSENT
e612087feb  FIRST RED RC               PRESENT                       PRESENT                 ABSENT

The firing control is present in both trees (the probe reaches them) and the nonsense control is absent in both (the probe can say no). 374d9d3afa (19:15:14Z) falls inside the interval between those two chore: version packages commits (18:12:26Z → 00:29:49Z), so the two readings cross-validate.

What changed

One file, scripts/publish-smoke.sh:

  1. The anonymous probe now expects 401 and is paired with assert_body '.error.code == "UNAUTHENTICATED"'. The status alone would be satisfied by an origin check, a rate limiter or any later guard while measuring nothing — the same reasoning the SELF_REGISTRATION_CLOSED probe below it is already written under, and what this file's header means by "every assertion here is an HTTP status plus a code this repo owns and publishes".
  2. A comment pinning why, naming fix(plugin-auth)!: an anonymous get-session is refused with the declared 401 envelope, not answered 200 null #17881, auth.me() returns the literal null for an anonymous caller, which no value of its declared SessionResponse can express #17238 and the ruling, so the next reader does not "fix" it back — the card asked for this explicitly.
  3. The header's declared-contract table updated to match.

The signed-in probe is deliberately untouched and still asserts 200: the refusal seam converts only a 200 whose body is exactly null, so that answer is byte-identical to before.

This is the #14000 move repeated — that card re-pinned this same script to the declared contract for SELF_REGISTRATION_CLOSED rather than touching auth runtime code, and left a standing ⛔ against relaxing an assertion back toward 200. This change runs the other way (200 → 401), which is the direction that prohibition protects.

Verification

  • bash -n scripts/publish-smoke.sh → exit 0.
  • The new jq filter, tested against the byte-exact body from job 103645853837 and two controls: observed body → exit 0; a 401 carrying INVALID_ORIGIN → exit 1; the old null → exit 1. So the assertion accepts the real refusal and rejects both a foreign guard and the retired shape.
  • The full derived gate family for this diff — 26 commands from node scripts/pm/dispatch-gates.mjs --commands — all exit 0, including check:bash32-floor and check:nul-bytes.
  • Control-character self-scan over the edited file: no matches.
  • ⚠️ The smoke itself is NOT MEASURED locally. It packs ~70 tarballs, installs and builds a project outside the workspace and boots a dev server; that is far past this container's foreground budget and its shared-verification discipline. The real verdict is the next publish-smoke / packed-tarballs status on the release-candidate head.

Reverse-read, both directions

Which currently-true sentence does this make false? Inside this file, the header's → 200 (anonymous) row — updated in the same diff, so the file does not contradict itself. Outside it, none: I grepped get-session across packages, scripts, docs, content and .github and no other statement depends on the smoke asserting 200.

Which currently-false sentence does it make true? Two. The file's own claim that it asserts "the DECLARED first-run contract" was false for this row and is now true. And the status text Fresh install of the release candidate: auth + CRUD green, which this gate has been unable to post since 2026-09-13T00:30Z, becomes reachable again — that is the release-blocking half.

Zero results reported as such: no other consumer of the smoke's expectation exists; no docs page restates it.

Noted separately

The same grep found that #17881 moved the wire answer but left the client SDK still documenting -> 200 null (packages/client/src/index.ts:1474) and a test double still modelling it. That is domain:services, not this lane, and it is filed on its own card — see #18139. It is not addressed here.

Merge channel

⚠️ The dispatch brief expected this fix to land in .github/workflows/publish-smoke.yml and warned that such a PR cannot be armed by the PM seat (HTTP 422, token lacks workflows). That premise does not hold — the probe lives in scripts/publish-smoke.sh, the workflow's driver script, and the workflow YAML contains no assertion at all. This diff touches no path under .github/workflows/, so that caveat does not apply to it. It also touches no governed surface under Prime Directive #14.

No changeset: the changed file is a CI driver shipped by nothing. Measured rather than assumed — the root package is private: true, and of the 70 package manifests declaring files[], zero name this path. The skip-changeset label carries that, not this sentence.


Generated by Claude Code

…us get-session

The packed-tarball release-candidate smoke asserted that an anonymous
`GET /api/v1/auth/get-session` answers 200. Since #17881 the platform
deliberately answers 401 with the ADR-0112 envelope
(`code: UNAUTHENTICATED`), ruled by the director seat in decision batch
 #117 item 4 on card #17238: better-auth's `200` + literal JSON `null` is a
value no `SessionResponse` can express, so `auth.me()` resolved outside its
own declared type; the code moved and the published schema did not.

The smoke was therefore asserting a contract the product deliberately stopped
serving. This re-pins the probe to what the code declares — the same move
 #14000 made for `SELF_REGISTRATION_CLOSED` — and pairs the status with the
envelope `code`, so a 401 from an origin check, a rate limiter or a later
guard cannot keep the probe green while measuring nothing.

The signed-in `get-session` probe is untouched and still asserts 200: the
refusal seam converts only a 200 whose body is exactly `null`.

Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Co-authored-by: Claude <noreply@anthropic.com>
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 14, 2026
@claude
claude Bot marked this pull request as ready for review September 14, 2026 06:07
@claude
claude Bot enabled auto-merge September 14, 2026 06:07
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

复核通过 —— 已 undraft 并武装(auto_merge: true)

domain:devx 执行 PM 席 · session_012GKcPZbMoGq7WPzKLfRBTU · 复核对着 GitHub 与 origin/main 做,⛔ 不对着报告做

⭐ 先更正本席自己:派单简报的文件面写错了

简报(以及本席写的 Claim:File surface)把探针的位置写成 .github/workflows/publish-smoke.yml错的。 本席独立重测:

探针 读数
grep -n "get-session|expected HTTP|Auth probes" .github/workflows/publish-smoke.yml 1 行,且是注释(:10 描述 first-run flow)—— 该 workflow 不含任何断言
grep -n "get-session" scripts/publish-smoke.sh(origin/main) :804 匿名探针 200 · :935 登录态探针 200断言在 shell 驱动里

⇒ 由此,简报里那条"本 PR 触及 .github/workflows/** ⇒ 本席武装不了(HTTP 422)⇒ 等人工合"的告诫对本 PR 不成立。本 PR 只改 scripts/publish-smoke.sh 一个文件,可以武装,已经武装
⭐ 这正是本席对每个 dev 的那条要求("简报是主张,不是事实;发现假的就报回来")兑现的一次 —— 记录在此,⛔ 不改简报把痕迹抹掉。

假设判定:假设 2 成立,本席独立复验

⛔ 没有采信报告的结论,重读了树:

  • packages/plugins/plugin-auth/src/anonymous-session-refusal.ts origin/main 上存在,其模块头记着裁决:总监席决策批次 🔗 Broken links detected in documentation #117 项 4(2026-09-12),维护者原文 「17238 B」 —— 服务端以 ADR-0112 失败信封 + 401 作答,SessionResponseSchema 不动,并引了宪章规则「spec 与代码不一致默认改代码,改协议单独立卡非选项」。
  • packages/spec/src/api/errors.zod.ts:174401: 'UNAUTHENTICATED'

401 是已声明的契约,冒烟断言的是平台已经故意停止提供的形状。⇒ 修探针,⛔ 不碰 auth 面 —— 这一条本 PR 做到了(diff 只有一个文件)。

⭐ 这次改动里最值钱的一处,是状态码本身不作为断言

probe "GET /auth/get-session (anonymous — must be REFUSED)" 401 …
assert_body '.error.code == "UNAUTHENTICATED"' …

⭐ 理由写在代码旁边而不是只写在 PR 里:401 也可能来自 origin 检查、限流、dev server 前面的代理,或日后新加的任何守卫 —— 那些都会让探针保持绿,却不再测量匿名会话契约。信封 code 才是把它钉住的那一半,而 UNAUTHENTICATED 是本仓自己拥有并发布的码(ADR-0112 派生映射),⛔ 不是 vendor 内部符号。这是把"绿"与"在测量"分开,正是本卡所属那一族缺陷的解药。

同时核过的:

  • 登录态探针未动 —— 分支上 :1171 仍是 200 + 原断言,与 main 逐字一致 ✅
  • assert_body 的签名是 jq -e "$1" "$BODY",紧跟在 probe 之后读的就是该次响应体 ✅ 与文件内既有用法同形
  • 头部"声明契约表"同笔更新为 401 ✅ —— ⛔ 没有留下一句仍写着 200 的旧话
  • 无 changeset ⇒ 打的是 skip-changeset 标签(⛔ 不是正文一句话)✅
  • check-clause2-carriers --pair 18140两载体一致、diff 无放宽征兆,exit 0 ✅
  • CI:33 项,RED: none(19 success / 11 skipped / 3 in_progress);未挂 needs:contract-review
  • 不触及任何治理面 ✅

附带发现 #18139:本席已独立验真,⇒ 交回 domain:services

dev 报的"客户端 SDK 仍写着 -> 200 null"经本席在 origin/main 上重测属实:

  • packages/client/src/index.ts:1474 —— GET /api/v1/auth/get-session (anonymous) -> 200 null
  • packages/client/src/organization-get-active-member-addressing.test.ts:40:88 —— 测试替身仍以 false 建模「匿名 ⇒ /get-session 答字面 null

空对照读 0,所以这两处是真读数。⭐ 其中替身那一半是更有后果的:一个仍在模拟产品已不再产生的服务端答复的 fake,会让依赖它的用例在真实行为变更后继续绿。
#18139 已由 dev 不带标签立卡(正确 —— domain:* 是分诊的字段),本席 ⛔ 不代打标签、⛔ 不代修:那是 domain:services 的面。

⚠️ 一条明确的 NOT MEASURED,接受并转述

dev 声明没有真跑冒烟本身(要打 ~70 个 tarball、在工作区外装并构建一个项目、再起 dev server,超出本容器前台预算)。⭐ 这是正确的做法:exit 3 = 前提未满足 = 未测量,⛔ 不许拿一个没跑过的东西当"验证过"。
⇒ 真正的判词是 RC head 上下一次 publish-smoke / packed-tarballs 的状态⚠️ 而按本卡自己的读数,那条 check run 会挂在某个 main 提交上却不测那棵树(它测的是 RC)—— ⛔ 谁都不要把它的红点读成 main 红,#14000 记的就是这个陷阱。


Generated by Claude Code

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

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants