Skip to content

docs(catalog): add IP-038 a generic fallback is not a typed diagnosis - #5061

Merged
huangruiteng merged 1 commit into
loopx-project:mainfrom
NIU-123370:codex/ip038-fallback-not-a-diagnosis
Sep 25, 2026
Merged

huangruiteng merged 1 commit into
loopx-project:mainfrom
NIU-123370:codex/ip038-fallback-not-a-diagnosis

Conversation

@NIU-123370

Copy link
Copy Markdown
Contributor

Goal And Delivered Outcome

  • Goal/source and gap: [Task]: GH-C37 add IP-038 a generic fallback is not a typed diagnosis #5060, board item GH-C37. The catalog registers what a projection must keep visible (IP-032, IP-033, IP-037) and what a lost transport means (IP-036), but nothing registers the read-side twin: a component publishes a typed reason, the consumer cannot recover it, and the consumer then reports its own observation as the cause. [Bug]: A rejected idle timeout containing a line-break character loses its typed diagnostic #5057 shows that shape is live in this repository rather than hypothetical.

  • Observable before → after, with the validation row that proves it: before, the catalog carried 37 pattern detail sections and 36 classification rows, and none of them named this interaction; after, it carries 38 and 37 with IP-038 present in all three required registrations, and examples/interaction-pattern-catalog-smoke.py reports interaction-pattern-catalog-smoke: ok. The row that proves the entry is load-bearing rather than decorative is regression_parity below: three of the four structural mutations fail, each with its own distinct assertion.

  • Issue/task and intended base: Closes [Task]: GH-C37 add IP-038 a generic fallback is not a typed diagnosis #5060. Base main at 27f0fc93b.

Scope And Continuation

  • Completed scope and remaining work: one entry, four rules, all anchored to shipped code and to tests that already hold them (effect_runtime.py:776-791 and :588, runtime_projection_writer.py:27-31 and :88, subagent_context.ts:31 and :52, plus the guard suite from test(runtime): pin the projection writer's refuse-before-write and readback guards #5044 now on main). Nothing else in the catalog was re-flowed, and no existing entry was edited.

    Deliberately excluded, per the catalog's own maintenance rule against adding an IP for a validation technique, smoke group, release checklist or rollout procedure: this entry carries no canary profile, no runbook and no runner contract. Rule 2 describes a framing obligation on a channel, not a new test group.

  • Slice boundary / successor: complete within this scope. fix(effect-runtime): keep one startup envelope one record #5058 repairs the framing defect that motivated rule 2; this entry does not depend on it and stays correct either way, because rule 2 cites the response reader at :588 as the already-shipped example of the rule.

Validation

  • Tested revision: 8ae03c53c
  • Run state: finished
  • Input classes: none identified for runtime behaviour — this is a documentation-only change; see the rationale under Coverage and gaps.
Check kind Result Public-safe evidence / limitation
static passed loopx check --scan-path docs/concepts/interaction-pattern-catalog.md reports public boundary scan clean: 1 files; git diff --check clean; every file:line quoted in the entry was resolved against main at 27f0fc93b rather than from memory, and the two TS defaults in rule 4 were confirmed at subagent_context.ts:31 and :52.
unit passed python3 examples/interaction-pattern-catalog-smoke.py → ok with the new id registered in the family matrix row, the classification index row and the detail heading.
regression_parity passed Four structural mutations, each applied to a copy of the file and run separately: deleting the detail section fails with pattern rows without a detail heading: ['IP-038']; listing the id under a second family fails with pattern ids listed under more than one family: ['IP-038']; renaming the index id fails with the same missing-heading assertion for IP-039. A fourth mutation, removing the id from the family matrix row, stays green — the matrix registration is convention, not an enforced gate, and is disclosed here rather than counted as evidence.
manual not_applicable No runtime path is added or changed; no dashboard, CLI or agent decision reads this file.
  • Coverage and gaps: the smoke pins exactly the three invariants a catalog entry can violate silently — an index row without its detail section, one id owned by two families, and an id in a family that has no row — so the checks above cover the change surface completely, and the mutation set shows each pin still bites. No runtime behaviour changes, so no test selection beyond the owning smoke was run; the full suite would not observe a doc-only edit.

    Known limitation carried forward from the previous catalog entries: the family matrix row is where a reader discovers which patterns a canary archetype may sample, and nothing fails when an id is omitted from it. This entry is registered there, but that registration is unenforced.

A caller that cannot recover a published reason has to say it could not
recover it. Three merged changes this week showed the same shape from three
different seams: a torn diagnostic stream let an exit status stand in for a
configuration rejection, a projection writer whose readback guard was under
discussion, and host facts that must default to not_observed rather than be
inferred from configured intent.

IP-038 registers the reusable interaction shape in the State And Boundary
family: ask the typed channel before the fallback, frame the reason channel by
the separator the publisher writes, reject by code before any mutation, and
report an unrecovered reason as missing evidence instead of as the cause. Each
rule is anchored to the shipped code and to the tests that already hold it, so
the entry is checkable rather than aspirational.

Distinguished from IP-036 (transport side), IP-037 (a reason that is present
after a capability change), IP-005 (the inverse projection gap) and IP-033 (a
recorded rejection as authority).

Signed-off-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com>

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

English verdict: APPROVE — exact head 8ae03c53c784ec5ff7207c539653109553a538f1 adds one catalog entry whose every cited code/test anchor resolves correctly on current main 3e443ad, and the catalog smoke passes.

动机

catalog 已经有 IP-036(丢失的响应不等于未提交)和 IP-037(退役设置不等于缺失设置),但还没有覆盖读取侧的对应情形:发布方给出了 typed reason,消费方却无法读出,于是用自己的观察(退出码、空读、超时)冒充原因。#5057/#5058 表明这种形态在本仓库确实存在,所以把它登记为独立模式有实际价值,而不是凭空设想。

改动思路

只新增一个条目,并在三处必需位置注册:family matrix、classification 行和详情小节。没有改动其他条目,也没有附带 runbook 或 canary profile,符合 catalog 自身“不为验证技巧单独开 IP”的维护规则。四条规则都锚定到已发布的代码和已有测试上。

具体改动

关键内容讲解

我在当前 main 3e443ad 上逐一核对了引用:

  • effect_runtime.py:776-791:先读 startup stderr 中的诊断,只有返回 None 才落到 runtime_exited_before_ready,准确。
  • effect_runtime.py:588:响应读取按 b"\n" 分帧,准确。
  • runtime_projection_writer.py:27-31 和 :88:写入前两种不同的拒绝,以及写后 readback 失败,准确。
  • subagent_context.ts:31 / :52:live_availability: "not_observed",以及 receipt_observation 只在确实提供 receipt 时才升级,准确。
  • 测试锚点 test_runtime_projection_writer_guards.py 的 :75/:86/:93/:124/:134、agent_context.test.ts 的 :35/:210,以及 test_invalid_idle_timeout_configuration_fails_closed,都存在并与描述相符。

规则 2 的技术论断正确:str.splitlines() 会在 U+0085/U+2028/U+2029 处断行,而 JSON.stringify 不转义这些字符。

uv run --extra test python examples/interaction-pattern-catalog-smoke.py 输出 ok;本 PR 触发的 CI 检查全部通过。

对主干的风险

没有阻塞问题,也不涉及运行时行为。唯一的维护风险是行号锚点会随代码改动失效。main 上的 catalog 已经有 16 处类似锚点,所以这是既有惯例,不算本 PR 的缺陷。不阻塞的建议:以后优先用符号名(函数、测试名)作为主锚点,行号只作辅助。

我的整体评价

APPROVE。这是一个聚焦、可验证、与现有条目边界清楚的文档增量,所有引用在当前 main 上都成立。本 review 不代表我已执行合并。

@huangruiteng
huangruiteng merged commit 8189625 into loopx-project:main Sep 25, 2026
19 checks passed
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.

[Task]: GH-C37 add IP-038 a generic fallback is not a typed diagnosis

2 participants