From 519281dee7f9e066a3652bf719a96575b55b9c6d Mon Sep 17 00:00:00 2001 From: cron-bot Date: Sun, 21 Jun 2026 12:21:36 +0000 Subject: [PATCH] refactor(server): pull HubRouter back into server, delete dead wshub pkg [#2195] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit G1 of the god-struct extraction RFC (docs/rfc/godstruct-extraction.md). internal/wshub was a Phase-4a interface-only shell: the 49-field Hub mirror skeleton was deleted in #1741, leaving 6 interfaces of which only HubRouter (14 methods) had a live consumer — internal/server/consumer.go aliased it via `type HubRouter = wshub.HubRouter`. The other five (MessageEnqueuer / CronView / ScratchOps / UploadOps / Auth) had ZERO live references, and wshub.MessageEnqueuer had already rotted out of sync with the real server-package MessageEnqueuer (missing the evictedID return value). Changes: - consumer.go: replace the type-alias with a direct HubRouter interface declaration (14 methods, verbatim). *session.Router still satisfies it structurally; consumer_contract_test.go (var _ HubRouter = (*session.Router)(nil)) guards it unchanged. - delete internal/wshub/types.go (the whole package) — the five dead interfaces go with it. - drop the server→internal/wshub import edge. - clean up stale comment references to the deleted package in cronview.go (×2), dashboard/cronview/cronview.go, backendid.go, and the lint tool's Phase-4 scan assumption. No behavior change, no Hub struct / lock / nodes touched. Both hard gates (TestHubSharesServerNodesState, TestHubShutdown_LockOrderInvariant) and the full internal/server -race suite pass; go build ./... green; no residual production reference to internal/wshub. Adversarial RFC review corrected three stale premises baked into the issues (no compiled field-count test exists; a SECOND hard gate hub_shared_state_test.go exists; handler-grouping conflicts with the existing server-split-phase4 plan) — see RFC §5. --- docs/rfc/godstruct-extraction.md | 103 ++++++++++++++++++ internal/backendid/backendid.go | 2 +- internal/dashboard/cronview/cronview.go | 6 +- internal/server/consumer.go | 47 +++++--- internal/server/cronview.go | 20 ++-- internal/wshub/types.go | 91 ---------------- .../lint-server-handlers/rule_field_block.go | 6 +- 7 files changed, 153 insertions(+), 122 deletions(-) create mode 100644 docs/rfc/godstruct-extraction.md delete mode 100644 internal/wshub/types.go diff --git a/docs/rfc/godstruct-extraction.md b/docs/rfc/godstruct-extraction.md new file mode 100644 index 000000000..a958aec62 --- /dev/null +++ b/docs/rfc/godstruct-extraction.md @@ -0,0 +1,103 @@ +# ARCH-GODSTRUCT — server 包 god-struct 抽取(增量步骤 G1–G4) + +| 字段 | 值 | +| :--- | :--- | +| 状态 | G1 Implemented · G2–G4 Draft(v2,2026-06-21;v2 按对抗性 review 重排步骤 + 订正事实) | +| 作者 | naozhi team | +| 创建日期 | 2026-06-21 | +| 修订日期 | 2026-06-21 | +| 关联代码 | `internal/server/server.go`
`internal/server/wshub.go`
`internal/server/node_accessor.go`
`internal/server/consumer.go`
`internal/wshub/types.go`
`internal/server/shutdown_lock_order_test.go`
`internal/server/hub_shared_state_test.go`
`tools/lint-server-handlers/` | +| 关联设计 | `docs/design/server-split-phase4-design.md`(Phase 0-5 总体计划,**本 RFC 与之协调,见 §0.1**)
`docs/design/server-split-phase4-baseline.md`(字段 baseline) | +| 关联 RFC | `docs/rfc/consumer-interfaces.md`(IoC 接口约束) | +| 关联 issue | #2195 (Hub god-struct + wshub 空壳) · #2192 (nodes 注册表无单一所有者) · #2197 (Server god-struct) · #376/R248-ARCH-6(Hub 子结构锚点) | + +## 0. 摘要 + +三个 issue 指向 `internal/server` 的 god-struct 技术债(#2197 Server、#2195 Hub+wshub、#2192 nodes 注册表)。本 RFC 提出**风险分层、move-only 优先**的增量步骤,每步独立 PR、以编译 + 既有测试为不变量兜底。 + +步骤命名用 **G1–G4**(不用 "Phase 1-5"),以**避免与既有 `server-split-phase4-design.md` 的 Phase 0-5 整数碰撞**(见 §0.1)。 + +**本 RFC 承诺实施的是 G1(wshub 死接口清理)**——经对抗性 review 验证为最隔离、零行为变更、与既有设计方向无冲突的一步。G2–G4 经各自 review 后再分别落地。 + +### 0.1 与既有 `server-split-phase4-design.md` 的关系 + +既有设计文档(v0.6,方案 B)规划了完整的 server 包瘦身(Server 47→≤12 字段、≤5000 行),其推荐合并顺序是 **Phase 0 → 4a/4b/4c(Hub 搬到 wshub 包)→ 1 → 2 → 3a-3f → 5**,且**明确否决了 handler-grouping**:将 god-struct 折叠为「god struct + 12 个 view」是 v0.1 reviewer 一致指出的「换汤不换药」,registration-only handler 的终态是**删成 `routes.go` 局部变量**而非永久子结构(design.md:107)。 + +**本 RFC 的定位 = 既有大计划的补充小步(ORTHOGONAL increments)**,只做不与方案 B 冲突、且能立即落地的低风险清理: + +- ✅ **G1(wshub 死接口清理)** 与方案 B 不冲突——方案 B 的 Phase 4 是把 Hub 搬「进」wshub 包,而 wshub 当前是 Phase 4a 残留空壳(仅剩接口骨架,Phase 4a 的 49-字段 mirror 已于 #1741 删除)。清理死接口 + 把 live 的 `HubRouter` 移回 server,是为方案 B Phase 4 让路的预清理,不是反向。 +- ⚠️ **G4(Server handler-grouping,#2197)** 与方案 B 终态冲突(方案 B 要把 registration-only handler 删成 locals,而非收进子结构)。**因此本 RFC 不推进 G4 的"收进子结构"做法**;#2197 的正解应并入方案 B 的 Phase 5(handler 外移),本 RFC 仅记录此结论,不实施。 + +## 1. 侦察事实(已核实,订正 issue + v1 RFC 的错误前提) + +侦察 + 对抗性 review(git show origin/master + grep + CI config)核实如下,**订正了若干流传的错误前提**: + +1. **不存在编译期"字段计数"测试。** "48/47 字段"是文档数字,实测 origin/master 的 Server/Hub 均 **51 字段**(baseline 文档 47/49 全部 stale)。所谓"awk 字段计数"只是 baseline.md 里的验证命令,非 test。`lint-fact-table` 只比对 markdown 内 prose-vs-speech-table,不数真实字段。 +2. **`internal/wshub` 不是 dead code,但 6 个接口里 5 个是死的。** `types.go` 含 6 个 interface;唯一 live 的是 `HubRouter`(14 方法),被 `consumer.go:44` 用作 `type HubRouter = wshub.HubRouter` type alias,`*session.Router` 结构化满足、受 `internal/session/contract_test.go` 守护。另 5 个(`MessageEnqueuer`/`CronView`/`ScratchOps`/`UploadOps`/`Auth`)零 live 引用;其中 `MessageEnqueuer` 已与 server 包同名 interface 漂移(缺 `evictedID` 返回值),是明确的腐化证据。 +3. **`internal/server` 有两个硬 gate(都 blocking)**,不是 v1 RFC 说的"唯一一个": + - **`shutdown_lock_order_test.go`** — Hub 锁序(`mu ⊃ authMu`、`mu ⊃ eventLog.subMu`),含**硬编码文件列表** `wshubLockOrderFiles`(新 wshub_ 文件不自动覆盖)+ 字面量锚点 `LOCK ORDER CONTRACT (R35-REL2)`。 + - **`hub_shared_state_test.go`** — 用 `reflect.ValueOf(s.hub.nodes).Pointer()` 断言 `Server.nodes`/`Hub.nodes` 是同一 map header、`s.hub.nodesMu == &s.nodesMu` 同一把锁。**G2(nodes 注册表)必须改写此测试**为新不变量(Server/Hub 共享同一 `*nodeRegistry` 实例),不能声称"不触及"。 + - 其余 `lint-fact-table`、`lint-server-handlers` field_block rule 3a 是 CI `continue-on-error: true` 的 **warn/non-blocking**。`tools/check-router-fields`(`-mode fail`,blocking)只作用于 **Router**,不作用于 Server/Hub。 + +## 2. 增量步骤(按风险,每步独立 PR) + +| 步骤 | 范围 | issue | 风险 | 行为变更 | 硬 gate 触及 | 本 RFC 实施 | +| :--- | :--- | :--- | :--- | :--- | :--- | :--- | +| **G1** | 删 `internal/wshub` 5 个死接口,`HubRouter` 移回 server | #2195(②) | 低 | 无 | 无 | ✅ 本次 | +| **G2** | `nodes`/`nodesMu`/`knownNodes` 收敛为单一 owner `*nodeRegistry` | #2192 | 中 | 无 | **改写 `hub_shared_state_test.go`** | 后续 | +| **G3** | Hub 子结构抽取(Subscriber/Broadcast/Send,拆 3 子 PR) | #2195(①) | 高 | 无 | **扩 `wshubLockOrderFiles` + 保 R35-REL2** | 后续 | +| **G4** | Server handler 字段瘦身 | #2197 | — | — | — | ❌ 并入方案 B Phase 5(见 §0.1) | + +### 2.1 G1 详细设计(本次实施) + +**目标**:消除 `internal/wshub` 的腐化死接口,把唯一 live 的 `HubRouter` 收回 server 包,移除 server→wshub 的 import 边。 + +**改动**: +1. 在 `internal/server` 新增(或就近 `consumer.go`)一个直接的 `HubRouter` interface 声明(14 方法,与原 `wshub.HubRouter` 逐字一致),取代 `consumer.go:44` 的 `type HubRouter = wshub.HubRouter` 别名。 +2. 删除 `import "github.com/naozhi/naozhi/internal/wshub"`(consumer.go)。 +3. 删除整个 `internal/wshub` 包(`types.go` 是唯一文件)——其余 5 个接口零 live 引用,直接随包删除。 +4. 清理对 wshub 的 stale 引用: + - `tools/lint-server-handlers/rule_field_block.go:55` 里"Phase 4 后改 scan internal/wshub"的注释假设。 + - `internal/server/cronview.go` / `internal/dashboard/cronview/cronview.go` 中以 wshub 别名模式为设计先例的**注释**(更新为不再引用已删包)。 + - `consumer-interfaces.md` / baseline 对 wshub 作为 Phase 4b landing zone 的引用(加一行说明 Phase 4a 空壳已清)。 + +**必须保**: +- `HubRouter` 的 14 方法集与 `*session.Router` 的实现一致——`internal/session/contract_test.go` 守护;若该 contract test 引用 `wshub.HubRouter`,同步改指 server 包的新声明。 +- 不引入 import cycle:wshub 原本 import dispatch + session,server 已 import 两者,把 `HubRouter` 移进 server 无新边。 + +**验证**: +- `go build ./...` + `go vet ./internal/server/ ./internal/session/`。 +- `go test ./internal/server/ -race`(含 hub_shared_state / shutdown_lock_order — G1 不碰 Hub struct/锁,应原样通过)+ `go test ./internal/session/`(contract_test)。 +- `grep -r "internal/wshub"` 全仓库零生产命中(仅允许 CHANGELOG/RFC 历史提及)。 +- `gofmt`。 + +**回滚**:单 PR,纯接口搬迁 + 删包,可整体 revert。 + +### 2.2 G2–G3 概要(后续 PR,本次不实施) + +- **G2(#2192,中风险)**:新建 `*nodeRegistry`(持 `mu`+`nodes`+`knownNodes`,暴露 `Add`/`Remove`/`NodeByID`/`Snapshot`/`Status`/`Known`)。`Server`+`Hub` 各持 `*nodeRegistry` 指针;`OnRegister`/`OnDeregister` 改调 registry 方法;`nodeAccessor`(读侧雏形)并入/委托。**必须**:① 改写 `hub_shared_state_test.go` 为"共享同一 registry 实例"不变量;② 保 reverseserver owns-check、unregister sync.Pool 复用、health 两路径一致;③ 4 个 dashboard 包的 subset interface 仍被满足(discovery 只要 `HasNodes`+`LookupNode`,agentevents 无 `NodesSnapshot`)。 +- **G3(#2195①,高风险)**:按锚点抽 `SubscriberRegistry`/`BroadcastDispatcher`/`SendCoordinator`,**拆 3 子 PR**。每个必须:① 新文件加入 `wshubLockOrderFiles`;② 保 `LOCK ORDER CONTRACT (R35-REL2)`;③ 新文件带 `WRITES:`/`READS-ALSO:`/`LIFECYCLE-METHOD` marker;④ `-race` 全过。 + +## 3. 不做什么 + +- 不动 `scheduler.go` 拆分(#2193)——无 CI gate、价值低。 +- 不推进 Server handler-grouping 收子结构(G4)——与既有方案 B 终态(删成 locals)冲突,应并入 Phase 5。 +- G1 不碰 Hub struct、不碰 nodes、不碰任何 wshub_*.go 文件、不动锁。 +- 不翻 lint-server 为 fail-mode(独立决策)。 + +## 4. 验证矩阵 + +| 步骤 | 编译 | 既有测试 | 硬 gate | 文档同步 | +| :--- | :--- | :--- | :--- | :--- | +| G1 | `go build ./...` | server -race + session contract_test | 不触及(不碰 Hub/锁/nodes) | wshub 引用清理(lint 注释 + 设计文档一行) | +| G2 | `go build ./...` | server -race + dashboard 各包 | **改写 hub_shared_state_test.go** | baseline nodes 节 | +| G3 | `go build ./...` | server -race | **扩 wshubLockOrderFiles + 保 R35-REL2** | baseline §3 + wshub.go 字段图 | + +## 5. 对抗性 review 留痕(v2) + +v1 RFC 经 3 视角对抗 review,关键修正: +- **[major, 已验证]** 发现第二个硬 gate `hub_shared_state_test.go`(v1 漏,recon 只 grep 了 NumField/TypeOf 未覆盖 reflect.Pointer)→ §1.3 补列,G2 验证矩阵更新。 +- **[major]** v1 的 Phase 1(handler-grouping)与既有 design.md 否决方向冲突 → 降级为 G4 不实施,改以 G1(wshub 清理)为首个落地步骤。 +- **[major]** Phase 整数与既有文档碰撞 → 重命名 G1-G4 + §0.1 明确 ORTHOGONAL 关系。 +- **[minor, 已验证]** `sessionH`/`healthH` 非 registration-only(`healthH.dispatcherMetrics` ctor 后写、`sessionH` 在 server_loops.go 运行时用)→ 印证 handler 不同质,进一步支持不做 grouping。 +- **[nit]** baseline 现已 drift(47/49 vs 实测 51)→ §1 记录;重基线留待真正改 Server/Hub struct 的步骤顺带做,G1 不碰 struct 故不重基线。 diff --git a/internal/backendid/backendid.go b/internal/backendid/backendid.go index 2e18e9f0a..af0fddef7 100644 --- a/internal/backendid/backendid.go +++ b/internal/backendid/backendid.go @@ -1,6 +1,6 @@ // Package backendid centralizes the per-request backend-ID length + charset // gate shared by the HTTP send path (internal/server), the WS handlers -// (internal/server, internal/wshub), and the dashboard cron CRUD endpoints +// (internal/server), and the dashboard cron CRUD endpoints // (internal/dashboard/cron). // // Before R20260607-ARCH-2 (#1893) the identical const + validator was copied diff --git a/internal/dashboard/cronview/cronview.go b/internal/dashboard/cronview/cronview.go index 02ec37cd3..b64e55af9 100644 --- a/internal/dashboard/cronview/cronview.go +++ b/internal/dashboard/cronview/cronview.go @@ -10,8 +10,10 @@ // import server). Hoisting the definition into this leaf package — which // imports nothing internal and is therefore importable from both sides // without a cycle — lets both call sites reference one shape via a type -// alias, mirroring the `HubRouter = wshub.HubRouter` pattern already used in -// internal/server/consumer.go. +// alias. (The server package's HubRouter once used the same leaf-package +// alias trick via internal/wshub; that package was removed in G1 — +// docs/rfc/godstruct-extraction.md / #2195 — and HubRouter is now declared +// directly in internal/server/consumer.go.) // // *cron.Scheduler satisfies CronView implicitly. The interface stays in a // dedicated leaf rather than in the cron package so neither consumer couples diff --git a/internal/server/consumer.go b/internal/server/consumer.go index b1e5efbe0..637a4513a 100644 --- a/internal/server/consumer.go +++ b/internal/server/consumer.go @@ -20,28 +20,45 @@ package server import ( + "context" "time" "github.com/naozhi/naozhi/internal/session" - "github.com/naozhi/naozhi/internal/wshub" ) -// HubRouter is a type alias for wshub.HubRouter. -// -// Phase 4b-router 搬迁(2026-05-28):完整接口定义已搬到 -// internal/wshub/types.go;server 包用 alias 保持向后兼容,所有 *Server -// 字段 / *Hub 字段 / handler 字段 / mock 都不需要改 import 路径。Phase 4b -// 后续刀(subscribe/broadcast/send 方法搬迁)+ Phase 4 全部完成后, -// 本 alias 可移除(届时 server 包直接 import wshub.HubRouter)。 +// HubRouter is the subset of *session.Router that *Hub consumes on the +// WebSocket subscribe / send / interrupt paths. Method list = direct +// h.router. calls (wshub*.go / send.go) PLUS dashboard_scratch.go / +// dashboard_send.go's h.hub.router.* transits where *ScratchHandler / +// *SendHandler intentionally borrow Hub's router handle. 14 methods — +// under the "rethink at >15" threshold from +// docs/rfc/consumer-interfaces.md §7.2. // -// 历史 godoc(pre-Phase 4b-router): +// *session.Router satisfies this implicitly via Go structural typing, +// guarded at CI time by consumer_contract_test.go. // -// HubRouter is the *Hub-only subset of *session.Router. Method list = -// direct h.router. calls (wshub*.go / send.go) PLUS dashboard_scratch.go -// / dashboard_send.go's h.hub.router.* transits where *ScratchHandler / -// *SendHandler intentionally borrow Hub's router handle. 14 methods. -// See docs/rfc/consumer-interfaces.md §3.2.2. -type HubRouter = wshub.HubRouter +// G1 (#2195, docs/rfc/godstruct-extraction.md): this declaration was +// pulled back here from the internal/wshub leaf package, which held only +// a stale Phase-4a interface skeleton (the 49-field Hub mirror was +// deleted in #1741) plus five interfaces with zero live consumers. The +// `type HubRouter = wshub.HubRouter` alias and the whole wshub package +// are removed in the same change. +type HubRouter interface { + GetOrCreate(ctx context.Context, key string, opts session.AgentOpts) (*session.ManagedSession, session.SessionStatus, error) + SessionFor(key string) *session.ManagedSession + Remove(key string) bool + RenameSession(oldKey, newKey string) bool + ResetAndDiscardOverride(key string) + Workspace(chatKey string) string + SetWorkspace(chatKey, path string) + SetSessionBackend(key, backend string) + DefaultWorkspace() string + RegisterForResume(key, sessionID, workspace, lastPrompt string) (effectiveKey string) + InterruptSession(key string) bool + InterruptSessionSafe(key string) session.InterruptOutcome + InterruptSessionViaControl(key string) session.InterruptOutcome + NotifyIdle() +} // ScratchRouter is the *ScratchHandler-only subset of *session.Router. // Closes the Phase 2.5 cleanup item flagged in the consumer.go godoc diff --git a/internal/server/cronview.go b/internal/server/cronview.go index b71f91c43..1583ef97f 100644 --- a/internal/server/cronview.go +++ b/internal/server/cronview.go @@ -6,10 +6,9 @@ // // R20260531070014-ARCH-2 (#1536): that copy and the byte-identical one in // internal/dashboard/session/handlers.go are now both type aliases for the -// single canonical definition in the leaf package internal/dashboard/cronview -// — the only consumer that stays independent is wshub/types.go's CronView -// (HasJob only), a genuinely different shape. Mirrors the -// `HubRouter = wshub.HubRouter` alias pattern in consumer.go. +// single canonical definition in the leaf package internal/dashboard/cronview. +// (The former independent wshub/types.go CronView was removed together with +// the wshub package in G1 — docs/rfc/godstruct-extraction.md / #2195.) package server @@ -29,13 +28,12 @@ type CronView = cronview.CronView // ServerOptions.Scheduler held the concrete *cron.Scheduler — its full ~60 // method surface — even though the server package only ever forwards the // value into already-narrowed interface fields (cronCommandScheduler via -// cronDispatchAdapter, cronview.CronView, wshub.CronView) and calls exactly -// one method on it directly: SetTelemetry (routes.go). This aggregate embeds -// the two consumer interfaces the value is forwarded into plus that one -// direct method, so the field type now advertises only what the server -// actually depends on while *cron.Scheduler continues to satisfy it -// implicitly (pinned by cronview_contract_test.go). Mirrors the wshub Hub -// narrowing to CronView. +// cronDispatchAdapter, cronview.CronView) and calls exactly one method on it +// directly: SetTelemetry (routes.go). This aggregate embeds the two consumer +// interfaces the value is forwarded into plus that one direct method, so the +// field type now advertises only what the server actually depends on while +// *cron.Scheduler continues to satisfy it implicitly (pinned by +// cronview_contract_test.go). // // #1164: the dispatch.CronScheduler embed became the server-local // cronCommandScheduler (cron_dispatch_adapter.go) when dispatch's seam was diff --git a/internal/wshub/types.go b/internal/wshub/types.go deleted file mode 100644 index a647e1cf9..000000000 --- a/internal/wshub/types.go +++ /dev/null @@ -1,91 +0,0 @@ -// Package wshub holds the consumer interfaces the WebSocket Hub depends on -// (server-split-phase4-design v0.6.1 §6.5). -// -// 现状:本包只保留接口契约。HubRouter 等接口有真实消费者—— -// internal/server/consumer.go 通过 `type HubRouter = wshub.HubRouter` -// alias 引用,dispatch 侧 var _ 绑定 MessageEnqueuer。 -// -// 历史:Phase 4a 曾在本包落地一个 49 字段的 server.Hub 镜像骨架 -// (Hub struct + NewHub/Shutdown + placeholder 方法 + 字段计数测试), -// 但 Phase 4b cutover 从未发生、无任何生产 instantiation,已于 #1741 -// 删除(同 #1600 删零消费者骨架的模式)。生产 Hub 实现仍在 -// internal/server/wshub*.go。 -package wshub - -import ( - "context" - "net/http" - "time" - - "github.com/naozhi/naozhi/internal/dispatch" - "github.com/naozhi/naozhi/internal/session" -) - -// MessageEnqueuer is the *dispatch.MessageQueue subset Hub depends on for -// the dashboard-side write path. -// -// satisfies-by: *dispatch.MessageQueue (internal/dispatch/msgqueue.go) -// -// Method set 与 server.MessageEnqueuer 完全一致——Phase 4b 起 server 包 -// 的 wshub_types.go 删除,dispatch 侧 var _ 切到本接口。 -type MessageEnqueuer interface { - Enqueue(key string, msg dispatch.QueuedMsg) (isOwner, enqueued, shouldInterrupt bool, gen uint64) - DoneOrDrain(key string, gen uint64) []dispatch.QueuedMsg - Discard(key string) - Mode() dispatch.QueueMode - CollectDelay() time.Duration -} - -// HubRouter is the *session.Router subset *Hub consumes on the WebSocket -// subscribe / send / interrupt paths. -// -// satisfies-by: *session.Router (implicitly via Go structural typing; -// guarded at CI time by internal/session/contract_test.go). -// -// Method list = direct h.router.* calls in wshub*.go and send.go. 14 -// methods total — under the "rethink at >15" threshold from -// docs/rfc/consumer-interfaces.md §7.2. -// -// Phase 4b-router 搬迁(2026-05-28):本接口从 internal/server/consumer.go -// 完整搬到本包。server.HubRouter 改为 type alias `= wshub.HubRouter` 保持 -// 向后兼容,server 包内的 *Server 字段 / handler 字段类型不需要改。 -type HubRouter interface { - GetOrCreate(ctx context.Context, key string, opts session.AgentOpts) (*session.ManagedSession, session.SessionStatus, error) - SessionFor(key string) *session.ManagedSession - Remove(key string) bool - RenameSession(oldKey, newKey string) bool - ResetAndDiscardOverride(key string) - Workspace(chatKey string) string - SetWorkspace(chatKey, path string) - SetSessionBackend(key, backend string) - DefaultWorkspace() string - RegisterForResume(key, sessionID, workspace, lastPrompt string) (effectiveKey string) - InterruptSession(key string) bool - InterruptSessionSafe(key string) session.InterruptOutcome - InterruptSessionViaControl(key string) session.InterruptOutcome - NotifyIdle() -} - -// CronView is the cron.Scheduler subset Hub uses for cron stub revival -// (R232-ARCH-7). Phase 4b 起从 server.CronView 同步。 -type CronView interface { - HasJob(id string) bool -} - -// ScratchOps is the *session.ScratchPool subset Hub uses for ephemeral -// session opts derivation. Phase 4b 起从 server 包搬过来。 -type ScratchOps interface { - OptsForKey(key string) (any, bool) -} - -// UploadOps is the *uploadStore subset Hub uses for resolving WS-sent -// file_ids. Phase 4b 起从 server 包搬过来。 -type UploadOps interface { - Resolve(fileID string) (path string, ok bool) -} - -// Auth is the AuthHandlers subset Hub uses for nz_anon cookie minting -// (R20260527122801-SEC-2 / #1326). Phase 4b 起从 server 包搬过来。 -type Auth interface { - MintAnonCookie(w http.ResponseWriter, r *http.Request) string -} diff --git a/tools/lint-server-handlers/rule_field_block.go b/tools/lint-server-handlers/rule_field_block.go index aaab6560d..791ae80fe 100644 --- a/tools/lint-server-handlers/rule_field_block.go +++ b/tools/lint-server-handlers/rule_field_block.go @@ -52,8 +52,10 @@ func scanFieldBlockMarkers(serverPkg string) []Violation { continue } name := e.Name() - // Phase 0b 仅扫 wshub*.go (前缀)。Phase 4 抽到 internal/wshub/ 后, - // 调用方改 scanFieldBlockMarkers("internal/wshub") 即可复用。 + // Phase 0b 仅扫 wshub*.go (前缀),即 internal/server 包内的 Hub 实现 + // 文件。NB: the former internal/wshub leaf package (interface-only + // shell) was removed in G1 — docs/rfc/godstruct-extraction.md / #2195; + // the real Hub never moved there, so this prefix scan stays as-is. if !strings.HasPrefix(name, "wshub") { continue }