Skip to content

feat(cosh-ng): add standalone SLS self-upload - #2715

Open
KaiLongZhou wants to merge 1 commit into
alibaba:mainfrom
KaiLongZhou:feature/cosh-ng/standalone-sls-self-upload
Open

feat(cosh-ng): add standalone SLS self-upload#2715
KaiLongZhou wants to merge 1 commit into
alibaba:mainfrom
KaiLongZhou:feature/cosh-ng/standalone-sls-self-upload

Conversation

@KaiLongZhou

@KaiLongZhou KaiLongZhou commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Why

When cosh-ng is installed without anolisa unified telemetry, the cosh.jsonl channel may not exist, leaving operational metrics stranded — never uploaded. cosh-ng needs a standalone self-upload path so it can report anonymous L1 telemetry directly to SLS PutWebtracking without depending on the anolisa uploader service.

What changed

  • sls.rs: New emit() replaces direct append_sls_log() calls — checks cosh.jsonl existence: present → append (unified channel), absent → tokio::spawn a background POST to SLS PutWebtracking (standalone). The two paths are mutually exclusive per call. Returns Option<JoinHandle> so one-shot mode can await.
  • sls.rs: New build_upload_body() stringifies all __logs__ field values (Number/Bool → String, drop Null) and injects __time__ + cosh_upload_source: "cosh-ng-direct", fixing SLS PostBodyInvalid 400 errors.
  • sls.rs: New region probe via std::net::TcpStream (not reqwest::blocking) to avoid nested tokio runtime panic; result cached in OnceLock, timeout 1s.
  • sls.rs: New opt-out via ~/.copilot-shell/telemetry_disabled sentinel file — enables runtime dynamic control (touch/rm) without restarting cosh-core.
  • headless.rs: One-shot prompt mode awaits JoinHandle before process exit to prevent data loss when runtime.shutdown_timeout(100ms) cancels pending tasks; interactive mode stays fire-and-forget.
  • README.md / README_zh.md: Added Data Collection section documenting telemetry scope and opt-out.

Related issue

no-issue: standalone telemetry self-upload is a new feature not tracked by an existing issue

User / Agent impact

  • Users who install cosh-ng standalone (without anolisa) will now have anonymous operational metrics uploaded to SLS automatically. No user prompts, code content, or conversation content is collected.
  • Users can opt out at any time with touch ~/.copilot-shell/telemetry_disabled.
  • Previously there was no opt-out mechanism; the sentinel file is new.

Risk and compatibility

  • Public CLI, API, configuration, or documented behavior changed
  • Privileged or security-sensitive behavior changed
  • Cross-component contract changed
  • Migration or rollback guidance is needed

CLI/config change: new opt-out mechanism (sentinel file). No migration needed — there was no prior opt-out on main. The unified-channel append_sls_log() path is preserved unchanged inside emit().

Rollback: rm ~/.copilot-shell/telemetry_disabled re-enables telemetry. To fully revert, replace emit() calls with append_sls_log() in headless.rs; unified channel behavior is unaffected.

Validation

cd src/cosh-ng
cargo fmt --all -- --check
cargo clippy -p cosh-core --all-targets --locked -- -D warnings
cargo test -p cosh-core --bins -- sls::tests
# 11 tests pass: emit opt-out sentinel, unified channel write, upload body
# fields, region probe, installation_id persistence, stringify values, etc.

Environment: macOS arm64 (development). Linux ECS production is the primary target for standalone self-upload.

Documentation and rollback

  • README.md and README_zh.md updated with Data Collection section.
  • Detailed design doc: .qoder/plans/2026-08-18-cosh-ng-standalone-telemetry-design.md
  • Rollback: rm ~/.copilot-shell/telemetry_disabled re-enables; replace emit() calls with append_sls_log() to remove standalone upload path entirely.

@KaiLongZhou
KaiLongZhou requested a review from casparant as a code owner August 20, 2026 09:21
@github-actions github-actions Bot added component:cosh-ng src/cosh-ng scope:documentation ./docs/|./*.md|./NOTICE labels Aug 20, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e5c53019aa

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cosh-ng/crates/cosh-core/src/headless.rs Outdated
Comment thread src/cosh-ng/crates/cosh-core/src/sls.rs Outdated

@qoderai qoderai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] headless 一次性模式对遥测等待策略的说明略显含糊,建议在代码注释中区分统一通道与自上传 handle 的语义,确保一次性模式只对自上传 JoinHandle 进行 await。

[P1] ECS 元数据探测失败时统一回退到公网域名,建议在注释或文档中显式说明该行为,便于运维评估成本与可靠性。

[P2] README 新增的遥测关闭说明未提及旧 ANOLISA_TELEMETRY_DISABLED 环境变量的废弃及迁移步骤,建议补充一句迁移提示。


🤖 Generated by QoderView workflow run

Comment thread src/cosh-ng/crates/cosh-core/src/sls.rs Outdated
Comment thread src/cosh-ng/README.md
@SunnyQjm

SunnyQjm commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

PR number: #2715
head_sha: e5c5301
reviewed_at: 2026-08-20T09:47:16Z

评审结论

Findings

  • [P1] crates/cosh-core/src/sls.rsfetch_region_id_from_metadata / build_track_url,约 :160–180)元数据响应未校验直接拼入上传 URL host。探测请求为无 token 的明文 HTTP GET,且未校验状态行:split("\r\n\r\n").nth(1) 拿到的任意 body 经 trim 后作为 region 插入 {project}.{region}-internal.log.aliyuncs.com。两个后果:(1)ECS 开启 HttpTokens=required 加固模式时返回 401/403,错误 body 会被 OnceLock 缓存为 region,主目标 ECS 路径可能整体错路由或丢全部记录;(2)100.100.100.200 位于 CGNAT 段,非 ECS 环境可能被网关/captive portal 应答,若 body 含 / 或点分域名片段,host 可被截断为响应方控制的子域,含持久 installation UUID 的遥测将 POST 到外部主机。建议:先 PUT 获取 metadata token、校验 2xx 状态行、对 region 做白名单校验(如 ^[a-z0-9][a-z0-9-]*$ + 长度上限),任一不满足即回退 DEFAULT_REGION,并补 403/token-required 回归测试。

  • [P1] crates/cosh-core/src/headless.rs:173(及 :190)opt-out 判定发生在 record 构建之后sls::emit(&engine.build_sls_record(...)) 的求值顺序使 build_sls_record 先调用 installation_id(),即便用户已 touch ~/.copilot-shell/telemetry_disabled,仍会在首回合创建 ~/.copilot-shell/installation_id 持久跨会话标识,与 PR 宣称的 opt-out 语义不符。建议把 sentinel 检查前置到 record 构建处,或让 installation_id() 在 opt-out 时无副作用,并补"已 opt-out 时 installation_id 文件不产生"的断言。

  • [P1] crates/cosh-core/src/sls.rs:47 仅检查 per-user sentinel,未尊重 anolisa 系统级停用标记。按既有评审线索,anolisa 托管机器上 anolisa telemetry disable 写入 /etc/anolisa/.telemetry_disabled;当该机器上 cosh.jsonl 缺失/未就绪时,新直传分支会绕过运维级 opt-out 继续上传。建议任一标记存在即视为停用,并补 unified channel 缺失场景的回归测试(该路径位于本输入之外,无法核实,列为必须确认项)。

  • [P2] crates/cosh-core/src/sls.rsspawn_self_upload 内调用 probe_region,约 :114–116)tokio task 内执行阻塞 TCP I/OTcpStream::connect_timeout + 同步 read_to_string 最坏占用 worker 约 3s(connect/write/read 各 1s);current_thread runtime 下会卡住整个事件循环。建议 tokio::task::spawn_blocking,或在 spawn 前于调用侧完成探测(结果被 OnceLock 缓存,只发生一次)。

  • [P2] crates/cosh-core/src/headless.rs(约 :198–201)one-shot 分支 await 无整体超时handle.await 最坏等待 probe 约 3s + POST 5s ≈ 8s,网络不可达环境下每次 one-shot 退出额外延迟数秒,脚本/CI 场景敏感。建议包 tokio::time::timeout(如 2s)兜底。

  • [P2] crates/cosh-core/src/sls.rs 测试 env 隔离缺陷(EnvVarGuard::dropremove_var 而非恢复原值,约 :530)init_test_env()OnceLock 只在首次生效;installation_id_persists_and_reuses(约 :618)覆盖 COSH_INSTALLATION_ID_PATH 后 drop 将其移除,后续经 test_engine()build_sls_recordinstallation_id() 的并行测试会读写真实 ~/.copilot-shell/installation_id。建议 guard 保存并恢复旧值、串行化所有 env 使用者,并断言测试套件绝不写 HOME 级文件。

  • [P3] crates/cosh-core/src/sls.rshttp_client(),约 :99)使用 .expect("reqwest client")。panic 被 task 边界兜住(one-shot 处 let _ = handle.await 吞掉 JoinError),不会炸主进程,但与"telemetry 绝不能影响主进程"的既有约定相悖,建议构造失败时静默丢弃该条记录。

  • [P3] crates/cosh-core/src/sls.rs emit() 存在 stat→open TOCTOUmetadata() 成功到 append_sls_log_to 打开之间文件被删除时,open 失败静默丢记录且不走 self-upload 兜底。概率低,注明即可。

  • [P3] crates/cosh-core/src/headless.rs:482 interactive 分支末轮 standalone 遥测可能丢失。fire-and-forget 下 REPL 退出时 shutdown_timeout(100ms) 会取消在途上传;standalone 模式此前无上传能力,不算回归,建议注释注明或退出前短超时 await 最后一个 handle。

  • [P3] PR 描述与作者回复自相矛盾。body 称 ANOLISA_TELEMETRY_DISABLED "no longer works",作者在评论中回复"误报,新功能,没有环境变量"。从 patch 看旧代码确无该 env 检查,body 的 Risk/兼容性一节疑似误导,需修正其一;README 是否补迁移提示也取决于此结论。

代码组织合规

变更仅限 cosh-core crate 与两个 README,未触碰 cosh-shell;无新增 crate、无 root src implementation 文件、无 lib.rs pub mod/pub use 变化、无 forbidden dependency、未改 Cargo.toml/lockfile(reqwest/tokio/uuid/tempfile 均为既有依赖)。未发现 blocking package/module/public API 组织问题。

附带提醒(非阻断):sls.rs 本 patch 净增约 400 行(production 约 250 行、inline tests 约 150 行),按 diff 增量估算文件总量可能接近或超过 700 行。行数未实测,以 CI gate 实测为准;cosh-core 不在本轮 cosh-shell 强制范围,但按 workspace 级 standard.md §6,若 production 部分越线需补 owner note 或拆分计划(self-upload 相关函数已是自然拆分候选)。

Open Questions

  • one-shot 模式每次退出最多多等约 8s(probe + POST),对脚本化调用是否可接受?是否应收紧总预算?
  • __time__ 以字符串形式上报,建议确认与 SLS 侧既有 __time__ 字段约定及统一通道写入记录的类型一致。
  • ECS HttpTokens=required 加固实例是否为主目标环境?若是,token 获取应在本 PR 内完成而非后续跟进。

Validation

  • Passed:无(本评审不执行工具)。
  • Not run:cargo test -p cosh-core --bins -- sls::testsclippycheck-layout.sh 均未由本评审执行;行数类判断为按 diff 估算。输入快照显示 Build cosh-ng releaseTest cosh-ng fast checksTest cosh-ng 均已 SUCCESS。

@kongche-jbw kongche-jbw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review baseline: f9c82a734ea43389815828ab903962f2b82d4b95...e5c53019aace72d25138094f5a5d6fc1d213d863

[P1] Check opt-out before creating the persistent identity

At src/cosh-ng/crates/cosh-core/src/headless.rs:173, Rust evaluates
build_sls_record() before emit() checks the opt-out marker. Record construction
calls installation_id() and creates ~/.copilot-shell/installation_id, so a user
who opted out before the first turn still gets a persistent cross-session identifier.
Possible direction: gate record construction first or make it side-effect free, and
assert that an existing sentinel leaves the installation-ID file absent.

[P1] Honor the managed-host telemetry disable marker

At src/cosh-ng/crates/cosh-core/src/sls.rs:47, only the per-user marker is checked.
On a host where anolisa telemetry disable created
/etc/anolisa/.telemetry_disabled, an absent cosh.jsonl sends the record through
the new direct-upload branch despite that explicit system-wide choice. Treat either
marker as disabling collection and cover the missing-channel case in a regression test.

[P1] Handle hardened ECS metadata responses

At src/cosh-ng/crates/cosh-core/src/sls.rs:166, the probe sends a tokenless GET,
and line 172 accepts the body without checking the HTTP status. ECS instances with
HttpTokens=required return 403; a nonempty error body is then cached as the region,
while an empty body falls back to the public Hangzhou route. The primary ECS path can
therefore misroute or lose every standalone upload. Obtain and send the metadata token,
validate a 2xx response and region syntax, and test 403 plus token-required responses.

[P2] Restore the installation-ID test override

At src/cosh-ng/crates/cosh-core/src/sls.rs:364, init_test_env() sets the override
only while its OnceLock initializes. The test at line 618 replaces it, then
EnvVarGuard::drop at line 530 removes it instead of restoring the prior value.
Later tests call the now-no-op initializer and can write the real HOME; parallel tests
also race on the process environment. Restore the previous value or inject the path,
serialize every environment user, and assert the suite never creates a HOME-level ID.

@KaiLongZhou
KaiLongZhou force-pushed the feature/cosh-ng/standalone-sls-self-upload branch from e5c5301 to 3ba0ca5 Compare August 20, 2026 10:07
@SunnyQjm

Copy link
Copy Markdown
Collaborator

PR number: #2715
head_sha: 3ba0ca5
reviewed_at: 2026-08-20T13:34:24Z

评审结论

Findings

  • [P1] CI Test cosh-ng 在当前 head 失败(FAILURE,2026-08-20T10:15:04)。上一 head 该检查为 SUCCESS,本轮转红;Test cosh-ng fast checksBuild cosh-ng release 为 SUCCESS。需先查失败日志确认是否与本 PR 相关——下方 P2 的测试 env 竞态(installation_id_persists_and_reuses 移除 COSH_INSTALLATION_ID_PATH 后并行测试可能写真实 HOME)是合理怀疑方向。合并前必须澄清。

  • [P1] crates/cosh-core/src/sls.rsfetch_region_id_from_metadata / build_track_url,约 :160–185)元数据响应未校验直接拼入上传 URL host。本 head 未修复:仍是无 token 明文 GET,split("\r\n\r\n").nth(1) 的任意 body 经 trim 作为 region 插入 {project}.{region}-internal.log.aliyuncs.com。ECS HttpTokens=required 加固实例返回 401/403 时错误 body 会被 OnceLock 缓存为 region,主目标 ECS 路径可能整体错路由丢全部记录;100.100.100.200 位于 CGNAT 段,非 ECS 环境若被网关应答且 body 含 / 或域名片段,host 可被截断为响应方控制的子域,含持久 installation UUID 的遥测将 POST 到外部主机。建议:先 PUT 获取 metadata token、校验 2xx 状态行、region 白名单校验(^[a-z0-9][a-z0-9-]*$ + 长度上限),任一不满足回退 DEFAULT_REGION,并补 403/token-required 回归测试。

  • [P1] crates/cosh-core/src/headless.rs:173(及 :191、:482、:505)opt-out 判定发生在 record 构建之后sls::emit(&engine.build_sls_record(...)) 的求值顺序使 build_sls_record 先调用 installation_id(),用户已 touch ~/.copilot-shell/telemetry_disabled 时仍会创建 ~/.copilot-shell/installation_id 持久跨会话标识,与 opt-out 语义不符。建议 sentinel 检查前置到 record 构建处,或让 installation_id() 在 opt-out 时无副作用,并补"已 opt-out 时不产生 installation_id 文件"的断言。

  • [P1] crates/cosh-core/src/sls.rs:44is_telemetry_disabled)仅检查 per-user sentinel,未尊重 anolisa 系统级停用标记/etc/anolisa/.telemetry_disabled 存在且 cosh.jsonl 缺失时,新直传分支会绕过运维级 opt-out 继续上传。建议任一标记存在即视为停用,并补 unified channel 缺失场景的回归测试(该路径位于本输入之外,无法核实,列为必须确认项)。

  • [P2] crates/cosh-core/src/sls.rsspawn_self_upload 内调用 probe_region,约 :114–120)tokio task 内执行阻塞 TCP I/OTcpStream::connect_timeout + 同步 read_to_string 最坏占用 worker 约 3s;current_thread runtime 下会卡住整个事件循环。建议 tokio::task::spawn_blocking,或在 spawn 前于调用侧完成探测(结果被 OnceLock 缓存,只发生一次)。

  • [P2] crates/cosh-core/src/headless.rs(约 :196–198)one-shot 分支 await 无整体超时handle.await 最坏等待 probe 约 3s + POST 5s ≈ 8s,网络不可达环境下每次 one-shot 退出额外延迟数秒。建议包 tokio::time::timeout(如 2s)兜底。

  • [P2] crates/cosh-core/src/sls.rs 测试 env 隔离缺陷(EnvVarGuard::dropremove_var 而非恢复原值,约 :530)init_test_env()OnceLock 只在首次生效;installation_id_persists_and_reuses(约 :618)覆盖 COSH_INSTALLATION_ID_PATH 后 drop 将其移除,后续经 test_engine()build_sls_recordinstallation_id() 的并行测试会读写真实 ~/.copilot-shell/installation_id。建议 guard 保存并恢复旧值、串行化所有 env 使用者,并断言测试套件绝不写 HOME 级文件。与当前 CI 失败可能相关,优先排查。

  • [P3] crates/cosh-core/src/sls.rshttp_client(),约 :99)使用 .expect("reqwest client")。panic 被 task 边界兜住不会炸主进程,但与"telemetry 绝不能影响主进程"的约定相悖,建议构造失败时静默丢弃该条记录。

  • [P3] crates/cosh-core/src/sls.rs installation_id() 临时文件泄漏path.with_extension("tmp.<pid>") 写入后若 rename 失败,.tmp.<pid> 文件残留无清理。低概率,建议失败时 let _ = std::fs::remove_file(&tmp)

  • [P3] crates/cosh-core/src/headless.rs:482 interactive 分支末轮 standalone 遥测可能丢失。fire-and-forget 下 REPL 退出时 shutdown_timeout(100ms) 会取消在途上传;本 head 已加注释说明 interactive 语义,standalone 模式此前无上传能力不算回归,保持关注即可。

已解决(相对上一 head e5c5301

  • stat→open TOCTOU 已在 emit() 内补注释说明,原 P3 关闭。
  • PR body 已改为 "there was no prior opt-out on main",与作者"误报,新功能,没有环境变量"的回复一致,原 P3 描述矛盾关闭;README 无需再补旧 env 迁移提示。

代码组织合规

变更仅限 cosh-core crate 与两个 README,未触碰 cosh-shell;无新增 crate、无 root src implementation 文件、无 lib.rs pub mod/pub use 变化、无 forbidden dependency、未改 Cargo.toml/lockfile。未发现 blocking package/module/public API 组织问题。

附带提醒(非阻断):sls.rs 本 patch 净增约 400 行(production 约 250 行、inline tests 约 150 行),按 diff 增量估算文件总量可能接近或超过 700 行。行数未实测,以 CI gate 实测为准;cosh-core 不在本轮 cosh-shell 强制范围,但按 workspace 级 standard.md §6,若 production 部分越线需补 owner note 或拆分计划(self-upload 相关函数已是自然拆分候选)。

Open Questions

  • one-shot 模式每次退出最多多等约 8s(probe + POST),对脚本化调用是否可接受?是否应收紧总预算?
  • __time__ 以字符串形式上报,建议确认与 SLS 侧既有 __time__ 字段约定及统一通道写入记录的类型一致。
  • ECS HttpTokens=required 加固实例是否为主目标环境?若是,token 获取应在本 PR 内完成而非后续跟进。

Validation

  • Passed:无(本评审不执行工具)。
  • Not run:cargo test -p cosh-core --bins -- sls::testsclippycheck-layout.sh 均未由本评审执行;行数类判断为按 diff 估算。输入快照显示当前 head 下 Test cosh-ng FAILURE、Test cosh-ng fast checksBuild cosh-ng release SUCCESS。

@KaiLongZhou
KaiLongZhou force-pushed the feature/cosh-ng/standalone-sls-self-upload branch 5 times, most recently from a6063e5 to 887305d Compare August 21, 2026 05:55
@KaiLongZhou

Copy link
Copy Markdown
Collaborator Author

@SunnyQjm @kongche-jbw 提交意见已修复,请再review下

@kongche-jbw kongche-jbw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review baseline: 69929468cd9aefe642bef994f9054d899a26ee8d...887305dff455b472fea4ab37f9b9f7257822e2de

[P1] Support token-required ECS metadata

At src/cosh-ng/crates/cosh-core/src/sls.rs:190, the probe still sends only a
tokenless GET. With HttpTokens=required, ECS returns 403 and lines 197-203
fall back to the public cn-hangzhou endpoint. A hardened ECS instance in
another region therefore sends every standalone record to the wrong regional
project, so the feature can lose all telemetry on its primary target. Please
obtain a token from /latest/api/token, include it on the metadata GET, retain
the v1 fallback where allowed, and add a token-required server test.

[P2] Read the complete metadata response

At src/cosh-ng/crates/cosh-core/src/sls.rs:193, one read() assumes the HTTP
headers and body arrive together. TCP may return the headers first; the parser
then sees an empty body, rejects a valid region, and permanently caches the
public Hangzhou fallback. Please read through the complete bounded response
(or the declared body length) and test a server that writes headers and body
separately.

[P2] Keep blocking metadata I/O off Tokio workers

At src/cosh-ng/crates/cosh-core/src/sls.rs:131, the spawned async task runs
synchronous connect, write, and read calls with separate one-second timeouts.
On a single-vCPU runtime, a slow metadata endpoint blocks the only worker, so
the one-second one-shot grace timer cannot run and interactive input also
stalls. Please use async I/O or spawn_blocking with a hard overall deadline,
and cover a one-worker runtime with a server that accepts but delays a reply.

[P2] Isolate enabled-telemetry tests from host opt-out

At src/cosh-ng/crates/cosh-core/src/sls.rs:789, the persistence test overrides
only the ID path, while installation_id() still reads the real user and
system sentinels. For example, setting
COSH_SYSTEM_TELEMETRY_DISABLED_PATH=/etc/hosts makes this test fail at line
803; the field test at line 462 has the same dependency. The targeted suite
therefore fails on an opted-out managed host. Please make init_test_env()
point both sentinel paths at absent temp files, with opt-out tests overriding
them explicitly.

[P2] Keep the collection disclosure consistent with the payload

src/cosh-ng/README.md:186 and README_zh.md:172 say session duration is
collected, but build_sls_record() names the argument _duration and never
adds it to the record. Every uploaded payload therefore omits this documented
metric. Please either add the intended schema field and assert its value, or
remove the claim from both disclosures.

@SunnyQjm

SunnyQjm commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

PR number: #2715
head_sha: 887305d
reviewed_at: 2026-08-21T06:35:21Z

评审结论

Findings

  • [P2] crates/cosh-core/src/headless.rs:218 one-shot 1s grace 与探测+上传耗时预算不匹配spawn_self_upload 内先 probe_region()(connect/write/read 各 1s 超时,最坏约 3s)再 POST(超时 2s),而 one-shot 侧 tokio::time::timeout(1s, handle)。standalone 的典型场景正是非 ECS 机器:metadata IP 不可达时 connect 打满 1s,handle 在 POST 发出前就被取消,one-shot 自上传大概率整批丢失,新能力在该模式下形同虚设。且在 current_thread runtime 下阻塞探测会独占 worker,1s 计时器无法触发,实际退出延迟最坏约 3s。建议在 tokio::spawn 前于调用侧同步完成 probe(结果有 OnceLock 缓存,每进程只发生一次),或把 grace 提到能覆盖 probe+POST(约 3s),并注释说明超时即丢弃的语义。

  • [P2] crates/cosh-core/src/sls.rsspawn_self_upload 内调用 probe_region,约 :131)tokio task 内执行阻塞 TCP I/O(前轮已提,未修复)TcpStream::connect_timeout + 同步 read 最坏阻塞 worker 约 3s;current_thread runtime 下会卡住整个事件循环(交互输入、上条 grace 计时都会停顿)。建议 tokio::task::spawn_blocking,或按上条在 spawn 前同步完成探测(一并解决两条)。

  • [P2] crates/cosh-core/src/sls.rsfetch_region_id_from_metadata,约 :190)仍只发 tokenless GET,不支持 HttpTokens=required 加固 ECS。加固实例返回 401/403 → 回退公网 cn-hangzhou;非杭州区域的加固实例会把全部记录 POST 到错误 region 的 project(anolisa-cn-hangzhou),PR 自述"Linux ECS production 是主目标",该类环境上遥测会静默全丢。建议实现 /latest/api/token 获取并在 403 时带 token 重试(保留 v1 fallback 与 2xx/region 校验),补 token-required 回归测试;若不在本 PR 做,README 数据采集一节需显式说明加固实例走公网回退。

  • [P3] crates/cosh-core/src/sls.rs(约 :193)单次 stream.read 可能读到不完整响应。TCP 分片时可能只拿到头部,body 为空 → is_valid_region 拒绝 → 进程级缓存公网回退(OnceLock 不再重试)。结果安全但会间歇性错选链路;请求已带 Connection: close,建议循环读到 EOF 或按 Content-Length 读取。chunked 响应当前会被 region 白名单拒绝,属安全失败,可接受。

  • [P3] crates/cosh-core/src/sls.rs 测试仍耦合宿主机真实状态emit_writes_to_unified_channel(约 :650)只 override 了 per-user sentinel;build_sls_record_has_all_fields(约 :462)、installation_id_persists_and_reuses(约 :789)两个 sentinel 路径均未 override。在真实存在 /etc/anolisa/.telemetry_disabled~/.copilot-shell/telemetry_disabled 的托管机器上定向跑 sls::tests 会断言失败。建议 init_test_env() 把两个 sentinel 路径都指向不存在的临时文件,opt-out 用例再显式覆盖。

  • [P3] README 披露与 payload 一致性待确认README.md:186/README_zh.md:172 称采集"会话时长",但有评审指出 build_sls_record 的参数名为 _duration 且未写入记录;该函数完整体在本 diff 之外,无法确认。若属实,要么补字段并断言,要么从两处 README 删除该声明。

  • [P3 台账提醒,非阻断] sls.rs 体量。按 diff 增量估算本 PR 净增约 500 行(production 约 250、inline tests 约 290),文件总量估算约 850–900 行,可能越过 700 行阈值。行数未实测,以 CI gate 实测为准;cosh-core 不在本轮 cosh-shell 强制范围,但按 workspace 级 standard.md §6,越线需补 owner note 或拆分计划(self-upload/probe/installation_id 已是自然拆分候选)。

已解决(相对上一 head 3ba0ca5

  • 元数据响应已校验 2xx 状态 + is_valid_region 白名单(字母/数字/连字符、≤64),host 注入与错误 body 缓存为 region 的 P1 关闭。
  • is_telemetry_disabled 已同时检查 /etc/anolisa/.telemetry_disabled,系统级 opt-out P1 关闭,并有 emit_drops_when_system_opt_out 回归测试。
  • installation_id() 在 opt-out 且文件不存在时返回空串且不创建文件,opt-out 语义 P1 关闭(installation_id_not_created_when_opted_out 钉住)。
  • EnvVarGuard 改为保存/恢复原值 + ENV_TEST_MUTEX 串行化 + #[cfg(test)] 下未设 COSH_INSTALLATION_ID_PATH 直接 panic,测试 env 污染 P2 基本关闭。
  • http_client()Option 静默丢弃、rename 失败清理 tmp 文件,两个 P3 关闭。
  • CI 快照:当前 head Test cosh-ngTest cosh-ng fast checksBuild cosh-ng release 均 SUCCESS,上轮 Test cosh-ng FAILURE 已恢复。

代码组织合规

变更仅限 cosh-core crate 与两个 README,未触碰 cosh-shell;无新增 crate、无 root src implementation 文件、无 lib.rs pub mod/pub use 变化、无 forbidden dependency、未改 Cargo.toml/lockfile(reqwest/tokio/uuid/tempfile 均为既有依赖)。未发现 blocking package/module/public API 组织问题。

Open Questions

  • ECS HttpTokens=required 加固实例是否为主目标环境?token 获取在本 PR 内完成还是后续跟进?
  • __time__ 以字符串形式上报,与统一通道写入记录及 SLS 侧既有字段类型约定是否一致,建议确认。
  • one-shot 退出等待预算定为多少可接受(当前 1s 大概率丢记录,3s 则退出变慢)?

Validation

  • Passed:无(本评审不执行工具)。
  • Not run:cargo test -p cosh-core --bins -- sls::testsclippycheck-layout.sh 均未由本评审执行;行数类判断为按 diff 估算。输入快照显示当前 head 全部相关检查 SUCCESS。

@KaiLongZhou
KaiLongZhou force-pushed the feature/cosh-ng/standalone-sls-self-upload branch from 887305d to 7d82160 Compare August 21, 2026 07:20
@KaiLongZhou

Copy link
Copy Markdown
Collaborator Author

@SunnyQjm @kongche-jbw 关于共同提到这个问题说明:
[P1] Support token-required ECS metadata

At src/cosh-ng/crates/cosh-core/src/sls.rs:190, the probe still sends only a
tokenless GET. With HttpTokens=required, ECS returns 403 and lines 197-203
fall back to the public cn-hangzhou endpoint. A hardened ECS instance in
another region therefore sends every standalone record to the wrong regional
project, so the feature can lose all telemetry on its primary target. Please
obtain a token from /latest/api/token, include it on the metadata GET, retain
the v1 fallback where allowed, and add a token-required server test.

感谢指出 P1。IMDSv2 token 支持我们在实现时讨论过,但决定不在本 PR 中引入,原因如下:
范围控制:本 PR 是 standalone self-upload 的完整初版(opt-out、region probe、HTTP POST、测试隔离)。加入 IMDSv2 需要额外实现 token 获取、缓存、TTL 刷新、v1/v2 回退矩阵,会显著增加 probe 代码的复杂度和失败面。
风险:token 逻辑一旦写错,可能导致所有实例的 metadata probe 都失败;当前 v1-only 实现至少能稳定回退到公网 endpoint。
回退行为可观测:HttpTokens=required 时返回 403,我们不缓存错误值,直接回退 cn-hangzhou 公网。数据不会被静默丢弃,只是可能进错 region。
目标场景:standalone 路径主要面向没有安装完整 anolisa unified uploader 的轻量/开发环境。生产加固 ECS 通常使用统一通道,由 anolisa telemetry 服务负责上传。
如果后续在 standalone 场景下确实观察到 hardened ECS 的遥测丢失,我们愿意单独提一个 PR 补 IMDSv2。当前建议先合入基础能力,再迭代优化。

@kongche-jbw kongche-jbw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review baseline: 69929468cd9aefe642bef994f9054d899a26ee8d...7d82160ed759593cca1b8c2d1fb64d84393e5bb6

[P1] Keep test telemetry off the production endpoint

At src/cosh-ng/crates/cosh-core/src/sls.rs:87, every process whose unified
channel is missing now probes the real metadata IP and launches a POST to the
default SLS project. Existing binary fixtures such as
src/cosh-ng/crates/cosh-core/tests/session_recovery.rs:144 and
src/cosh-ng/crates/cosh-core/tests/compaction_lifecycle.rs:70 create temporary
homes but set neither an opt-out marker nor a local endpoint; together they
start dozens of one-shot processes, which also await this task. The normal
cargo test -p cosh-core gate can therefore write synthetic UUIDs and metrics
into production telemetry and depends on external network timing. Please make
binary tests opt out by default, then add one explicit self-upload E2E against
an injected local server that asserts the actual path and body without reaching
the public service.

[P2] Keep the metadata probe off Tokio workers

At src/cosh-ng/crates/cosh-core/src/sls.rs:92, emit() performs blocking
TcpStream I/O before spawning the upload. All production call sites, such as
src/cosh-ng/crates/cosh-core/src/headless.rs:192, invoke it from an async
result path. The first standalone turn on a slow or non-ECS host therefore
delays its result through the per-operation timeouts and occupies a runtime
worker. On a single-vCPU runtime, other timers and tasks cannot run during that
probe. Please use async
I/O or a blocking pool with a hard overall deadline, and add a one-worker
regression where a stalled metadata server does not prevent a timer advancing.

@KaiLongZhou
KaiLongZhou force-pushed the feature/cosh-ng/standalone-sls-self-upload branch from 7d82160 to b368e37 Compare August 21, 2026 07:54
@KaiLongZhou

Copy link
Copy Markdown
Collaborator Author

@kongche-jbw 帮忙继续check下

@kongche-jbw kongche-jbw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review baseline: 522b680ca53d...7bdb237fcf39

[P1] Finish isolating telemetry tests from production

src/cosh-ng/crates/cosh-core/tests/tool_approval.rs:21 still starts a real
cosh-core process without an opt-out marker or a local upload endpoint. The
same pattern remains in tests/jsonl_protocol.rs:61 and
tests/sls_integration.rs:94; the new unit regression at src/sls.rs:785
also enables self-upload without redirecting its target. Each user turn with
the unified file absent can therefore probe real metadata and spawn a POST to
the default SLS project during the normal test gate. Conversely, the explicit
upload test at tests/telemetry_upload.rs:96 inherits the host's real system
sentinel and fails on an opted-out managed host. Possible direction: make the
shared binary-test command safe by default, give the unit regression a local
target, and override both sentinels to controlled absent paths only in the
explicit upload test.

[P2] Keep region discovery off the result critical path

At src/cosh-ng/crates/cosh-core/src/headless.rs:192 and :632,
emit().await now completes before the result is written at :206 and :646.
The standalone branch waits on the metadata probe under a two-second deadline
at src/cosh-ng/crates/cosh-core/src/sls.rs:94. A metadata server that accepts
but stalls therefore delays the first success or error result by up to two
seconds; one-shot mode can then wait another second for the POST. This avoids
worker starvation but bypasses the stated one-second exit-delay budget.
Possible direction: keep discovery behind the returned upload handle, or emit
the result before awaiting it, and assert result latency under a stalled
metadata server rather than only checking that another timer advances.

[P2] Read the complete request in the upload integration test

At src/cosh-ng/crates/cosh-core/tests/telemetry_upload.rs:45, the mock server
captures one read() and immediately replies. TCP may deliver the headers and
body separately, so this test can observe only the headers, fail its body
assertions, and close the socket before the client finishes writing. Read
through the header terminator and declared Content-Length before capturing
and replying; a split-write server case would pin the behavior.

[P2] Make the validation count match this head

The PR body says the documented sls::tests command passes 11 tests, but that
exact command runs 18 tests on this head. Please update the count and coverage
summary so the validation claim is reproducible after the added regressions.

@KaiLongZhou
KaiLongZhou force-pushed the feature/cosh-ng/standalone-sls-self-upload branch from 7bdb237 to 5eb85c0 Compare August 21, 2026 09:45
@KaiLongZhou

Copy link
Copy Markdown
Collaborator Author

@kongche-jbw 请帮忙继续检查下

@kongche-jbw kongche-jbw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review baseline: af879316482d...5eb85c0e408c

[P1] Make the metadata deadline stop the blocking probe

At src/cosh-ng/crates/cosh-core/src/sls.rs:94, timing out the JoinHandle
does not cancel a running spawn_blocking task. A metadata endpoint can send
one byte just under each one-second read timeout; the loop at line 240 can then
run for hundreds of seconds while holding REGION. Every later turn starts
another blocking task that waits on that mutex, so repeated turns can consume
the blocking pool and delay unrelated work. The one-shot process also awaits
this two-second probe before applying the one-second upload grace at
headless.rs:226, so its exit budget is not actually capped at one second.
Possible direction: enforce an absolute deadline inside the I/O operation,
share one in-flight probe, and test repeated emits against a slow-drip server
while asserting both pool availability and total process-exit latency.

[P1] Keep the opt-out boundary fail closed and usable on a fresh install

At src/cosh-ng/crates/cosh-core/src/sls.rs:59, Path::exists() treats a
dangling sentinel symlink or a non-ENOENT stat error such as EACCES as
absent, so an unprivileged process can upload despite the system-wide opt-out.
The documented commands at src/cosh-ng/README.md:194 and :201 also fail
before first launch when ~/.copilot-shell or /etc/anolisa does not exist,
which is the normal standalone-install case. Please allow telemetry only after
an explicit ENOENT, document creation of the parent directories in both
languages, and cover dangling, inaccessible, and fresh-install paths.

[P2] Preserve one installation ID across concurrent first starts

At src/cosh-ng/crates/cosh-core/src/sls.rs:389, each process writes its own
temporary UUID and rename() replaces an existing destination on Unix. Two
first-start processes can therefore both return different IDs while the last
rename wins; one process uploads an ID that is never persisted, breaking the
promised cross-session correlation. Use a create-if-absent/lock protocol and
read the winner on contention, with a concurrent first-start regression test.

[P2] Isolate the unified-channel test from the host opt-out

At src/cosh-ng/crates/cosh-core/tests/sls_integration.rs:20, the enabled
telemetry test sets a temporary HOME but leaves the system sentinel override
inherited. Running that exact test with
COSH_SYSTEM_TELEMETRY_DISABLED_PATH=/etc/hosts fails at line 52 because the
record is correctly dropped. Point both sentinel variables at controlled
absent paths so this integration target passes on opted-out managed hosts.

[P2] Parse Content-Length case-insensitively in the upload mock

At src/cosh-ng/crates/cosh-core/tests/telemetry_upload.rs:63, the mock only
recognizes Content-Length:. Reqwest emits content-length:, so this parser
gets zero and replies as soon as it sees the headers. The test passes only when
headers and body arrive in the same read; a split body is still truncated and
the server can close before the client finishes writing. Parse header names
case-insensitively and assert that the captured body length matches the header.

[P2] Update the PR's reproducibility claims for this head

The PR body's documented cargo test -p cosh-core --bins -- sls::tests
command runs 18 tests on this head, not the stated 11. Its Documentation
section also links .qoder/plans/2026-08-18-cosh-ng-standalone-telemetry-design.md,
but that file is absent from the reviewed tree. Please update the count and
coverage summary and either add the referenced document or remove the link.

@KaiLongZhou
KaiLongZhou force-pushed the feature/cosh-ng/standalone-sls-self-upload branch 2 times, most recently from 0a53a7c to 15744f6 Compare August 23, 2026 02:15
@KaiLongZhou

Copy link
Copy Markdown
Collaborator Author

@kongche-jbw 帮忙再次检查下

@KaiLongZhou
KaiLongZhou force-pushed the feature/cosh-ng/standalone-sls-self-upload branch from 15744f6 to f3b4e58 Compare August 23, 2026 02:28
@KaiLongZhou

Copy link
Copy Markdown
Collaborator Author

@SunnyQjm review意见已修改,帮忙继续检查下

@KaiLongZhou

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3b4e5865c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cosh-ng/crates/cosh-core/src/headless.rs
Comment thread src/cosh-ng/README.md
Comment thread src/cosh-ng/crates/cosh-core/src/sls.rs Outdated
When cosh-ng is installed without anolisa unified telemetry, the
cosh.jsonl channel may not exist, leaving metrics stranded. Add a
standalone self-upload path: emit() checks sentinel opt-out first,
then cosh.jsonl existence -- present -> append (unified), absent ->
spawn background POST to SLS PutWebtracking (standalone). The two
paths are mutually exclusive per call, preventing double-write.

Key decisions:
- Opt-out via sentinel files: ~/.copilot-shell/telemetry_disabled
  (per-user) and /etc/anolisa/.telemetry_disabled (system-wide).
  No env var: sentinel files enable runtime dynamic control and
  persist across restarts.
- emit() returns Option<JoinHandle>: one-shot prompt mode awaits
  up to 1s before exit to avoid losing telemetry when
  runtime.shutdown_timeout(100ms) cancels pending tasks; interactive
  mode fire-and-forgets.
- Region probe via tokio::net::TcpStream with an absolute
  tokio::time::timeout deadline. The probe is cached in a
  tokio::sync::OnceCell so concurrent first callers share one
  in-flight probe; a slow or drip-feeding metadata endpoint cannot
  pin a runtime worker or the blocking pool. Reads the full response
  in a loop because TCP may split headers and body across packets.
  Validates HTTP 2xx status and region format (alphanumeric + hyphen)
  before caching; malformed responses fall back to cn-hangzhou.
- installation_id at ~/.copilot-shell/installation_id; does not
  create the file when telemetry is disabled. Concurrent first-start
  processes use create-new/open; losers poll briefly for a valid UUID
  and repair an empty or invalid file, so all callers converge on the
  same persistent UUID and a crashed creator cannot leave a
  permanently empty file.
- SLS PutWebtracking requires all __logs__ field values to be
  strings; build_upload_body stringifies Number/Bool, drops Null.
- HTTP timeouts: 2s for POST, 1s for metadata probe.

Review fixes folded in:
- Move metadata probe off the runtime worker with async I/O and an
  absolute deadline; share one in-flight probe across concurrent
  callers via tokio::sync::OnceCell.
- Emit the headless success/error result before awaiting sls::emit()
  so a stalled probe does not delay the visible turn result.
- Make binary integration tests safe by default via
  tests/common/mod.rs::cosh_core_command, which sets HOME and opts
  out of telemetry. Apply it to tool_approval, jsonl_protocol, and
  the missing-file case in sls_integration.
- Redirect the sls.rs unit-regression upload target to a local
  non-routable address and override both sentinel env vars in
  telemetry_upload.rs so the explicit upload test is independent of
  the host's opt-out state.
- Read the complete HTTP request in telemetry_upload.rs mock server
  by parsing Content-Length case-insensitively, so split TCP packets
  do not break body assertions and the captured body length matches
  the header.
- Make the opt-out sentinel check fail-closed: only ENOENT means the
  sentinel is absent; dangling symlinks and permission errors keep
  telemetry disabled.
- Use atomic create-new/open for installation_id; losers poll for a
  valid UUID and repair empty/invalid files by overwriting them,
  preventing a crashed first-start process from breaking cross-session
  correlation.
- Isolate the unified-channel sls_integration test from the host's
  system-level opt-out by pointing both sentinel env vars at absent
  temp paths.

Documentation:
- Remove "session duration" from the data-collection disclosure in
  README.md/README_zh.md; build_sls_record does not include it.
- Add `mkdir -p` to the opt-out sentinel commands in both READMEs so
  they work on a fresh install before ~/.copilot-shell or
  /etc/anolisa exist.
- Add telemetry opt-out instructions to both English and Chinese
  user-guide configuration pages under docs/user-guide/, not just
  the component README.

Known limitations:
- One-shot await caps upload grace period at 1s; telemetry may be
  dropped on very slow networks.
- No retry, no batching, no persistence -- acceptable for L1
  anonymous operational metrics.
- Sentinel paths not CLI-configurable (test-only env vars).
- IMDSv2 token-required metadata is not supported; the probe falls
  back to the public cn-hangzhou endpoint on hardened ECS instances.

Assisted-by: Qoder:1.22.0
Signed-off-by: Kailong Zhou <zhoukailong.zkl@alibaba-inc.com>
@KaiLongZhou
KaiLongZhou force-pushed the feature/cosh-ng/standalone-sls-self-upload branch from f3b4e58 to c57850d Compare August 23, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component:cosh-ng src/cosh-ng scope:documentation ./docs/|./*.md|./NOTICE

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants