Skip to content

fix(dsh): mirror every canonical credential shape in the observer - #7733

Open
JasonBuildAI wants to merge 1 commit into
loopx-project:mainfrom
JasonBuildAI:codex/agent-v3r7
Open

JasonBuildAI wants to merge 1 commit into
loopx-project:mainfrom
JasonBuildAI:codex/agent-v3r7

Conversation

@JasonBuildAI

@JasonBuildAI JasonBuildAI commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Goal And Delivered Outcome

Impact. Anyone who enables the DSH shadow observer (packages/dsh-loopx-plugin, the only producer of reliability_diagnostics ledger bytes) can have a live provider credential written in cleartext into the durable diagnostic ledger. The capability README promises the opposite — "Absolute local paths and credential-like tokens fail closed without reaching ledger bytes" — and the accepted RFC names "producer-side public-safety rejection before the first ledger append" as its P0 checkpoint. The producer implemented that control as a hand copy that had already lost most of the canonical owner's shapes.

Beneficiary. Operators running the DSH observer, and anyone they hand a diagnostic ledger or receipt to: the ledger is a whole-file export ("no filtering of negative records") and the read-only projection also surfaces tool_name from the summary.

Scale. Seventeen distinct values across four arm classes. A 38-value differential against the canonical loopx/public_safe_text.py owner at upstream/main found 17 that the producer accepted and appended while the Python owner refuses them:

Arm class Values the producer wrote in cleartext
Provider issuers GitHub fine-grained PAT, any 16-19 character classic PAT, AWS AKIA/ASIA, Slack xoxb-/xoxp-, Google AIza, Stripe rk_live_, npm, PyPI
Labeled assignments api_key:, access_token:, refresh_token:, password:, secret:
Label alternative the api arm of (?:access|api|secret)[_-]?key
Local path any Windows drive path outside users / documents and settings

Author Declaration

  • Written by: model agent (Claude-family model via Qoder), implementing at a human operator's request under their direction and review.

AI disclosure: this change was drafted with AI assistance at my request, under my direction and review.

Implemented against

  • Specification and revision: loopx/capabilities/reliability_diagnostics/README.md ("Every provider applies the equivalent recursive public-safe value contract before its first append ... credential-like tokens fail closed without reaching ledger bytes") and docs/architecture/rfcs/long-running-agent-reliability-diagnostics-governed-delivery-v0.md P0 checkpoint ("producer-side public-safety rejection before the first ledger append"), both at 225698b31; the canonical shape text is loopx/public_safe_text.py SECRET_LIKE_SURFACE_PATTERN / LOCAL_PATH_SURFACE_PATTERN at the same revision.
  • Criteria:
Criterion (spec clause) Disposition Symbol / path Test or command
Credential-like tokens fail closed before the first append implemented SECRET_LIKE_SURFACES, packages/dsh-loopx-plugin/src/observer.ts observer.spec.ts → refuses every canonical credential arm before the first append
Absolute local paths fail closed before the first append implemented LOCAL_PATH_SURFACES, same file same test, windows_drive_outside_user_profile row
Every provider applies the equivalent contract (i.e. the same shape set as the canonical owner) implemented the two shape lists above test_typescript_mirror_equals_the_canonical_credential_arms, test_typescript_mirror_equals_the_canonical_local_path_surfaces
Ingest re-validates every line (second boundary) not_met by design, unchanged loopx/cli_commands/reliability_diagnostics.py pre-existing tests/capabilities/test_reliability_diagnostics.py
Envelope, stats, ledger layout and activation contract unchanged implemented — tests/capabilities/test_reliability_diagnostics_dsh_provider.py (30 passed), full plugin suite parity below
  • Self-check before submission: ran the three typecheck projects, the observer spec at base and head, the full plugin suite at base and head, the capability pytest files, ruff check, and a 38-value cross-language differential before/after. Deliberately left out: the POSIX/UNC/home-relative canonical path arms are mirrored for parity but are not reachable through the producer's own compact-token filters; the file-URL arm stays broader than the canonical file:// because narrowing it would loosen a check that already rejects file:/…. Two residual differences are documented rather than fixed: JavaScript's ASCII-only \b makes the mirror stricter than the Unicode-aware Python boundary for a token glued to a non-ASCII letter, and the parity test compares the arm list in order, so reordering it reads as a failure even though it changes no verdict.

Scope And Continuation

  • Completed scope and remaining work: the producer's two shape lists now equal the canonical owner's, and one test fails whenever either side moves without the other. The remaining work in this class belongs to [Architecture]: two modules both claim to own "private-looking text" #5136 (one shared detection contract instead of per-surface copies); this change does not attempt that consolidation.
  • Slice boundary / successor: complete within this scope. No successor task is needed for the DSH producer.

Validation

  • Tested revision: 7c96eea8d (rebased onto 225698b31)
  • Run state: finished
  • Input classes: synthetic, public_fixture
Check kind Result Public-safe evidence / limitation
unit passed tests/capabilities/test_reliability_diagnostics_dsh_provider.py → 30 passed. With the base observer.ts restored in place, the two new tests fail (2 failed / 28 passed), so they are not vacuous.
unit passed packages/dsh-loopx-plugin/tests/observer.spec.ts → 12 passed, 1 failed. The failure is is off unless one exact goal, session, and run identity are declared, a pre-existing Windows-only path-resolution assertion (the fixture's POSIX /tmp ledger directory is rewritten drive-qualified on Windows); the same single test fails identically at the base revision (11 passed, 1 failed).
unit passed Full plugin vitest suite: base 9 failed / 198 passed (207), head 9 failed / 199 passed (208) — the same nine pre-existing Windows-host failures (POSIX fixture paths, child-close timing, symlink/permission), plus the one new passing test. No regression.
static passed tsc -p tsconfig.host.json --noEmit, tsc -p tsconfig.client.json --noEmit, tsc -p tsconfig.tests.json --noEmit → exit 0 each.
static passed ruff check tests/capabilities/test_reliability_diagnostics_dsh_provider.py → All checks passed.
regression_parity passed 38-value differential, same candidates, canonical Python owner vs the shipped TypeScript producer: base 17 values accepted-and-written, head none — the only remaining verdict differences are six values the producer drops earlier than the guard, so neither side leaves ledger bytes. An independent reviewer re-ran a 926-value boundary/casing/unicode differential and found zero fail-open divergences.
manual passed Mutation check: the two new parity tests fail against the base observer.ts and pass at head.
integration failed loopx canary premerge --from-git-diff --git-diff-base upstream/main selected the dsh-loopx-plugin risk profile and failed on this Windows host for two reasons that both reproduce at the base revision: the quality phase runs the same full vitest suite (9 pre-existing Windows-only failures, base 9 failed / 198 passed), and the package / runtime phases stop at spawnSync ...\node_modules\.bin\dsh ENOENT, which the same smokes hit identically at base. The four direct checks (diff hygiene ×3, RFC bilingual) and py_compile passed. Hosted dsh-plugin.yml runs typecheck and the suite on its own Ubuntu and Windows runners.

Frontend / Visual Evidence

  • UI impact: none

Type of Change

  • Bug fix

LoopX Area

  • Capability or extension (providers, adapters, skills)

Technical Direction

Shared-authority RFC fixture impact

N/A

Boundary Checklist

  • Neither the diff nor this PR body/comments/attachments disclose private state, credentials, raw traces or verifier output, internal links, or local machine paths (including .loopx/, .codex/goals/, and live ACTIVE_GOAL_STATE.md).
  • I did not duplicate maintainer-owned benchmark work unless a maintainer split out a public issue for it.
  • I kept the change scoped to the linked issue/task.
  • Existing English/Chinese RFC pairs (including ledger entries) both have a diff, and the local premerge check python3 scripts/check_rfc_bilingual.py --base origin/main --head HEAD passes, or no paired RFC changed.
  • I completed the visual evidence section for UI changes, or marked UI impact none.
  • Every commit includes a DCO Signed-off-by trailer (git commit -s).

The DSH shadow observer is the only writer of `reliability_diagnostics`
ledger bytes, and its documented contract is that credential-like tokens
fail closed before the first append. It enforced that contract with a
hand-written copy of the canonical owner's shapes that carried two of the
nine provider-issuer arms and four of the labeled arms, raised the GitHub
floor from 16 to 20, and accepted only two directory names under a Windows
drive.

A 38-value cross-language differential at upstream/main found seventeen
values the canonical `loopx/public_safe_text.py` owner refuses and the
producer accepted and wrote in cleartext: a GitHub fine-grained PAT and any
16-19 character classic PAT, AWS access and session key ids, Slack bot and
app tokens, a Google API key, a Stripe restricted key, npm and PyPI tokens,
`api_key:`, `access_token:`, `refresh_token:`, `password:` and `secret:`
labels, and any drive path outside `users` / `documents and settings`.

Spell every arm byte-identically to the canonical owner and add a parity
test that rebuilds the canonical alternation from the mirror's own arms, so
an arm added, narrowed or invented on one side fails in the suite that runs
on both. No envelope, stats, ledger or activation contract changes.

Fixes loopx-project#7729

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
JasonBuildAI added a commit to JasonBuildAI/loopx that referenced this pull request Oct 30, 2026
…7733

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>

@songoow songoow 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.

Reviewer: model_agent · GPT-6 Astra Pro · OpenAI · self_reported

动机 / Motivation

显式启用 DSH shadow observer 收集运行诊断的操作者需要保证诊断文件本身不包含凭据或本地私有路径。例如工具名或来源字段意外携带无标签 npm 凭据,旧 TypeScript producer 会先写入本地 ledger,后续 Python ingest 拒绝也无法撤回已落盘原值;修改后 producer 在写入前拒绝该事件。独立 Node 探针使用真实 appendLedgerLines 写磁盘:十类危险样例中基线九类落盘,head 全部拒绝;三个安全工具名仍写入,错误 Session 被拒绝,默认关闭时没有访问任何 Host hook。范围是默认关闭的被动诊断 producer 安全边界;不启用 observer,不增加调度或执行权,也不宣称完成 live C1、长期保留或性能资格。

改动思路 / Approach

独立发布的 DSH 插件不能在运行时导入仓库 Python 模块,因此保留镜像有部署依据;镜像语义由 canonical owner 与跨语言模式比较约束,拒绝发生在真实落盘边界之前。当前 PR 保留为 producer guard 补齐及 parity/ledger 回归,不把默认关闭的观察者变成 Core 决策者,也不把此检查点表述为完整诊断能力上线。不修改会保留已经实际复现的泄漏;只在更晚的消费者拒绝也不能撤回已写入或公开的原值。修复放在现有 owner,正常输入沿原有路径继续处理,不增加重复状态、配置同步或人工确认步骤。对相关四个凭据 PR 的有界比较确认,它们面向不同目的地,能够共享识别规则而保持各自拒绝或遮盖策略。

具体改动 / Changes

完整审阅范围:3 files, +171/-7; producer +40/-7, focused TS regression +59 and Python parity checks +79. 原始问题依据为 https://github.com/loopx-project/loopx/issues/7729,文本版本 body-sha256:7e944eec7d6034df82a16b94a996c3347b42255a5b375f1795d230b3d46cb93f;维护者接受的共享识别、目的地策略边界见 #5136。OBS-7729-1:implemented; OBS-7729-2:implemented; OBS-7729-3:implemented,对应识别完整性、兼容控制和真实输出边界,未把更大路线图当成此 PR 的完成条件。

关键代码讲解

  1. LOCAL_PATH_SURFACES / SECRET_LIKE_SURFACES(packages/dsh-loopx-plugin/src/observer.ts:76):补齐 canonical secret arms 和路径形状,保留既有 file: 的更保守处理;ASCII token admission 约束住跨语言边界差异。

  2. ShadowObserver.record(packages/dsh-loopx-plugin/src/observer.ts:571):序列化前对整个 envelope 做递归安全检查,拒绝计数不把原值写入 ledger;flush 前再次检查。

  3. apply / resolveShadowObserverConfig(packages/dsh-loopx-plugin/src/observer.ts:667):仍需完整 exact Goal、Session 与 run identity;未配置直接返回,单独插件加载不等于激活。

对主干的风险 / Risks

验证资格阻断:当前结论为 REQUEST_CHANGES;没有发现需要修改生产代码的 finding。 当前 chat-bundle-browser (1) 出现delegation-search-browser-smoke.mjs rows等待超时。虽然源码与调用路径检查支持它与本次补丁分离,现行 review policy 仍要求同一检查在 immutable base 与 exact head 上执行并取得相同归一化失败签名;本轮未完成这组浏览器配对,不能把静态排除或一般 merge hold 替代该条件。最小补充:在相同隔离 fixture 上,对immutable base225698b3和当前head运行同一个delegation-search检查,比较失败身份/等待点;也可提交已有相同输入、revision和实际执行绑定的base/head产物。 若配对确认是既有问题,即可据已有完整代码/真实消费者证据重评,无需为了本 review 改动无关业务代码。

未发现阻断性代码问题。canonical 启发式仍有精度边界;JavaScript与Python的Unicode word boundary并非完全等价,现有ASCII字段限制下差异趋向拒绝。未运行完整已安装DSH/真实模型会话或live C1。实际生效调用链为 Cordis observer row -> apply/resolveShadowObserverConfig -> ShadowObserver.observeSessionEvent -> record/flushBatch -> appendLedgerLines -> reliability diagnostics ingest。最需要反证的是“模式正确,但在落盘/发布之后才被调用”;我使用同一个合成 corpus 跑 immutable base 与当前 head 的真实消费端,且把新版本的无泄漏 oracle 放到基线确认会失败,避免只测试新 helper 自洽。

PASS:.venv/bin/python -m pytest -q tests/capabilities/test_reliability_diagnostics_dsh_provider.py tests/capabilities/test_reliability_diagnostics.py,133 passed in 6.43s。完整安装的 DSH/Vitest 宿主套件未在本地运行;这里的独立 Node 试验直接调用生产 observer 和真实文件 append,未伪造 guard 或持久化结果。base/head 消费探针均有可重放脚本及原始结果,未调用真实模型、凭据或操作者状态。安全输入和拒绝后的继续处理均单独检查;Rejected observer event is counted; worker execution is not stopped. Subsequent safe events are accepted by the same observer.

语义与 CI 对齐

复用 existing vocabulary,没有新增工作状态分类或扩大 authority。远端 JUnit 中 heartbeat budgets、quota reason、native restart、skill links 等失败已逐条读取。针对涉及的 owner,在225698基线和当前head各执行100项:均62 passed/38 failed,38个失败身份与规范化 message 完全一致。已有 chat-bundle-browser 的 delegation-search 超时也已定位,browser bundle 不装载本次默认关闭的 DSH observer;未把该超时宣称为已解决,整体 CI/合并资格继续独立保留。这些红灯没有被改写成通过;已复现的基线失败与本次改变的 invariant 分开归因,未完成的平台/浏览器复验和合并门槛继续明确保留。

当前失败的具体归因与审阅范围

本次当前浏览器红灯是 delegation-search 的 rows 等待超时。已比较整个 dashboard 树、整个 loopx 生产树、steward fixture 与测试启动器的 Git 对象,base/head 完全相同;这条 Node/Python/browser 路径不加载独立 Cordis observer 包。本 PR 的必要证明是 observer 本地落盘前 guard、显式 Session 绑定和关闭侧零 Host hook,均已直接执行通过;这个因果边界值得保留,但不能替代政策要求的同一失败检查配对执行。

pytest、checks 与 merge-gate 是现有 workflow 的上游结果汇总,不视作额外独立根因;对应 Python/Windows/浏览器当前失败分别按上述具体路径处理。这次因当前CI归因证据不足而暂不批准;未更改或绕过远端必需状态检查。

我的整体评价 / Verdict

对 exact head 7c96eea8df378618c508c7bbe120e2c335bf4fa3 给出 REQUEST_CHANGES,原因仅为上述当前CI归因资格缺口,未发现代码finding。长期重复输出的凭据暴露风险改善,普通任务和后续读取流程保留,代码体量与已证实的问题相称。已检查 observer、独立 Cordis 注册行和诊断 ledger 消费端;没有页面、布局或可见交互变更,视觉复验不适用。未运行页面布局截图;此 PR 没有布局或交互变更。默认关闭与显式配置两侧均直接验证,观察者不能进入 worker 或 scheduler 决策。建议维持当前最小 owner 修复范围,未来精度调整集中在 canonical 模式与公共 corpus;补齐该配对归因后可复用本次已完成的代码与消费者验证;不要求扩大产品修复范围。

English verdict: REQUEST_CHANGES — current required CI attribution is incomplete. No blocking code finding was identified; provide the same browser check at immutable base and exact head, or equivalent verifiable existing paired artifacts, without relaxing its assertions or changing unrelated code.

@JasonBuildAI

Copy link
Copy Markdown
Contributor Author

归因补齐:必需检查在未改动的 exact head 上重跑通过,无代码改动

评审本身没有 code finding,我也没有改动生产代码或测试。exact head 仍为 7c96eea8df378618c508c7bbe120e2c335bf4fa3(base 仍为 225698b313f3bcd5c967220013b4a2254c586792)。下面补齐你要求的执行证据;其中第 1 条比 base/head 配对更直接,第 2、3 条是同一失败类别的既有执行。

1. 同一 exact head 的两次执行(决定性)

对同一个 run 41010988719 用 rerun-failed-jobs 重跑(attempt 2),没有改动任何 commit:

attempt job 结果 证据
1 chat-bundle-browser (1) failure npm run smoke:delegation-search 06:59:06 启动,06:59:45 在 smoke/delegation-search-browser-smoke.mjs:152 调用的 rows(同文件 :56)抛 TimeoutError
2 chat-bundle-browser (1) success 同一步 00:34:39 启动,00:34:47 输出 PASS: packaged search, pre-pagination match, count, ...

同一 sha、同一 workflow blob、同一 runner 标签、同一隔离 fixture,唯一差别是执行时序。attempt 2 中 chat-bundle-browser (1)(2)(3) 与 checks 均为 success。

这直接说明:rows(13) 超时不是该 revision 的属性,也不可能由本 diff 引入。

2. base 侧与「同输入」的既有执行

base 225698b3 上同一 job 的实际执行(两个 PR 都未改动 apps/presentation/dashboard 与 examples/personal-workspace-browser,即运行的是与 base 完全相同的 dashboard 树):

执行 检查输入 chat-bundle-browser (1) 完成时间 (UTC, 10-30)
#7731(base 225698b31) dashboard/examples 未改动 success 06:40:40
#7732(base 225698b31) dashboard/examples 未改动 success 06:53:19
main 26f73056c dashboard 子树 585f02c7…、examples 子树 a8474f2a…、workflow blob f95fe722… 与 base 逐字一致 success 10:10:34

即 base 树在本次 head 失败前 6 分钟刚通过同一个检查;head 侧失败发生在 06:59:45。

3. 同一失败行号在别的 PR 上重现

#7744(head eaea1d4ed)的 chat-bundle-browser (1) 于 10-30 13:44:25 在同一 :152 → rows(:56)抛 TimeoutError,与本次失败身份完全相同。该失败点位于烟测最后一段「changed-snapshot More」(fixture('report-window') 后按 Enter 分页并断言 rows(13)),该文件与 conversation-requests.tsx 均不在本 diff 内,属于 dashboard 烟测的间歇失败类别,需要 dashboard owner 单独跟踪;本 PR 不做无关改动。

4. 差异范围

225698b3...7c96eea8d = 3 files, +171/−7,全部在 packages/dsh-loopx-plugin/** 与 tests/capabilities/**,不进入 dashboard/browser 路径,也不进入 loopx/web/chat 构建路径。

5. 当前 CI 状态

attempt 2 已绿:changes、chat-bundle、chat-bundle-browser (1)(2)(3)、checks、dashboard-browser ×3、dashboard-acceptance、kernel-static-checks、kernel-contracts、typescript-core ×3、typescript-coverage、stage2c ×4、windows-powershell、node-minimum-compatibility。

仍在排队:test-shard (1)–(6)。它们的失败就是你已逐条配对确认过的同一组既有失败(涉及 owner 在 base/head 各 100 项:62 passed / 38 failed,失败身份与归一化 message 完全一致),本回复不把它们改写成通过,也不据此声称合并资格。

6. 关于 policy 要求的配对

现行配对要求的目的是把一个失败归因为既有问题。本次的情况不是「head 侧存在无法归因的失败」,而是该必需检查在未改动的 exact head 上重跑后通过,且 base 树在相邻时间同样通过;第 1 条是同一 revision 上的直接反证,不是静态排除。我没有放宽断言、没有跳过检查、没有改动无关业务代码。

如果你仍需要一份 base 侧失败产物,我可以对 base 225698b3 的 Python Tests 取消态运行做一次完整重跑(或临时 base 控制分支)后回报,请直接说明。

请求:exact head 仍为 7c96eea8d,请据此重评。


English verdict: no code change was needed and none was made. The required chat-bundle-browser (1) check is green on the unchanged exact head 7c96eea8d: attempt 2 of the same run (rerun-failed-jobs) passed the identical npm run smoke:delegation-search step that timed out in attempt 1 at delegation-search-browser-smoke.mjs:152 (rows, :56). The same base dashboard/examples/workflow inputs passed on PRs #7731 and #7732 (both based on the immutable base 225698b3) and on main 26f73056c, and the identical :152 timeout also hit unrelated PR #7744 — this is an intermittent dashboard smoke failure, not a property of this revision. The diff is 3 files, +171/−7, all in packages/dsh-loopx-plugin/** and tests/capabilities/**. Please re-review the unchanged head.

@JasonBuildAI
JasonBuildAI requested a review from songoow October 31, 2026 00:45

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: the DSH shadow observer's producer-side public-safety check misses 7 of 9 provider credential shapes and narrows the drive-path arm

2 participants