做 #5575(PR 见下)时在同一个文件扫到的同类形态。#5575 的正文只点了 `fail()` 的两个调用点,这两处在**另一个方法**里(`degradeConnectorInstance`,#3017 的降级/重试路径),不在那一单的范围内,按 Prime Directive #10 单开。 ### 现象 `packages/services/service-automation/src/plugin.ts`(origin/main `cc5b048a0`): 1. **`plugin.ts:1272-1274`** — husk 注册失败: ```ts ctx.logger.warn( `[Automation] could not register degraded husk for '${info.name}': ${(err as Error).message}`, ); ``` `err` 来自 `engine.registerDegradedConnector(...)`,catch 注释自己写着「the entry's def no longer parses」—— 也就是说这里预期接到的正是解析类错误。 2. **`plugin.ts:1277-1283`** — 降级公告: ```ts ctx.logger.error( `[Automation] connector instance '${info.name}' … ` + `; retrying with backoff, attempt ${attempts} (#3017): ${info.reason}`, ); ``` `info.reason` 是 `reconcileDeclaredConnectors` 传进来的 `(err as Error).message`,来源是 provider factory 抛出的 `ConnectorUpstreamUnavailableError`。那个 message 由**第三方 factory** 构造(spec 只定义了错误类,不约束文本),完全可以是多行。 两处都是把外来 `err.message` 插进日志 message,与 #5048 / #5575 同一类别:`ObjectLogger` 每次调用只写一条 `<ts> <LEVEL> <msg>` 记录,带换行的 message 溢出到不带等级头的物理行,按行工作的下游(文件 sink、`docker logs`/journald 送采集、`grep ERROR`)把续行读成无法归属的碎片。 ### 与 #5575 的差别(值得一起读) 第 1 处是 **`warn`** → stdout,所以它比 #5575 那两处更糟一档:`serve` 的启动静默窗口只包了 `process.stdout.write`,而这条路径在冷启动就会跑(`materializeDeclaredConnectors(ctx, { fatal: true })` 里 upstream 不可达即降级,不抛),于是 `BootLogCapture.offer()` 会**直接丢掉**所有续行 —— 这正是 cloud#971 的原始形态,而不只是「不好解析」。第 2 处是 `error` → stderr,不经那个缓冲。 ### 可达性(为什么是 finding 而不是 bug) 与 #5575 同样的理由:今天 `packages/connectors/*/src` 里没有 factory 做 Zod `.parse()`,`ConnectorUpstreamUnavailableError` 的 message 在 openapi / mcp / rest / slack 里都是我们自己写的单行文本。第一个在 factory 里用 Zod 校验、或直接把上游 SDK 的多行错误塞进 `ConnectorUpstreamUnavailableError` 的第三方插件会撞上;ADR-0097 明确鼓励第三方写 provider factory。 ### 建议的修法 PR #5572 / #5575 已经把 helper 放在同包 `thrown-cause-diagnostics.ts`(`describeThrownForLog`,#5575 从 `flow-bind-diagnostics.ts` 泛化改名),直接复用即可:message 保持不含换行,cause 走 `meta`。注意 `Logger.error` 的第二参是 `Error`、第三参才是 `meta`(#5575 顺带修好了 `ObjectLogger` 丢弃第三参的缺陷),`warn` 的第二参就是 `meta`。新增字段名不得含 `key`/`token`/`secret`/`password` 子串(`ObjectLogger.redactSensitive` 按子串匹配,见 #5573)。 ### 关联 #5575(同文件、`fail()` 的两处,已修)、#5048 / PR #5572(flow 绑定四+一处,已修)、#4632(被截断的诊断比没有诊断更贵)、#3017(降级/重试本身)、ADR-0097。
Activity
claude commentedon Aug 5, 2026
发现分诊(#4949 纪律):晋级——摘
finding换pm:queue+domain:services。[#5048 / PR fix(service-automation): flow 绑定失败的告警改用结构化 meta,不再把 Zod issue 数组塞进单行日志 (#5048) #5572、finding(service-automation): connector 物化失败的fail()也是${err.message}单行插值,同 #5048 的类别、另一个接缝 #5575),helperdescribeThrownForLog同包就位直接复用,两个调用点明确(plugin.ts:1272/:1277);warn 侧有BootLogCapture.offer()直接丢续行的实际后果(cloud#971 同形态),比「不好解析」重一档。休眠可达性(ADR-0097 鼓励的第三方 factory)不降低修复价值,修法零决策。注意正文已点名的两条边界:Logger.error第二参是Error第三参才是meta;新增 meta 字段名避开key/token/secret/password子串(finding(core): ObjectLogger 的脱敏表按子串匹配,一个叫keys的普通字段会被整块换成***REDACTED***#5573)。packages/services/service-automation→domain:services。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
认领(services 车道 PM 派发,
session_01BWS4heBoAitLmzCLhcYdbK)claude/issue-5636-degrade-connector-cause../objectstack-issue-5636(dev 子代理按 worktree-first 自建)packages/services/service-automation/src/plugin.ts(degradeConnectorInstance两处插值,立单时 :1272 warn / :1277-1283 error,行号随 PR fix(service-automation): connector 物化失败的软路径改用结构化 meta;并修好 ObjectLogger 丢弃契约第三参的缺陷 (#5575) #5639 落地可能已漂)、src/thrown-cause-diagnostics.ts(复用describeThrownForLog)、同包测试、.changeset/*.md。fail()也是${err.message}单行插值,同 #5048 的类别、另一个接缝 #5575(PR fix(service-automation): connector 物化失败的软路径改用结构化 meta;并修好 ObjectLogger 丢弃契约第三参的缺陷 (#5575) #5639)已 merged,plugin.ts 当前无在飞改动;同包无在飞 PR。fail()也是${err.message}单行插值,同 #5048 的类别、另一个接缝 #5575 修的 stderr 接缝不同,别照搬其危害论证)。Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
PM 复核:ACCEPT — PR #5662(session_01BWS4heBoAitLmzCLhcYdbK 车道)
warn(message, meta?)契约读证后参数位分置;warn 侧 BootLogCapture 危害机制用真 logger + 真缓冲量化验证(13 行写入 → 留 1 行且止于 Zod[,cloud#971 原始形态)—— 与 finding(service-automation): connector 物化失败的fail()也是${err.message}单行插值,同 #5048 的类别、另一个接缝 #5575 的证伪形成正确的逐接缝论证,未照搬。degradedReason逐字保留的取舍正确:API 可见字段(GET /connectors、connector_action 拒绝文本)属另一契约,reason/cause分离双向钉住。${err.message}插进日志 message —— 其中两条是耐久性诊断,正是最不该被切碎的记录 #5661(三处含耐久性诊断)均搜重后立单、unassigned、无 pm:queue,交分诊 Routine。处置:CI 全绿后转 ready 并开 auto-merge 进队。
Generated by Claude Code
fix(service-automation): connector 降级路径的两条日志改用结构化 meta (objectstack-a…
fix(service-automation): 启动路径三条日志的外来 cause 改走结构化 meta (objectstack-ai…
fix(service-automation): the degrade REGISTRATION logs its cause as m…