Skip to content

fix(update): qualify activation from an immutable source commit - #4460

Merged
huangruiteng merged 1 commit into
mainfrom
codex/immutable-source-activation-qualification
Sep 15, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
codex/immutable-source-activation-qualification

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

Summary

loopx update check --ref <40-hex-commit> reported

activation_qualification_required
trusted installed-versus-target source lineage is unavailable

for an install that the same pinned SHA had just produced. The qualification read
freshness_source_git_commit from loopx doctor, which only branch refs populate, so a
full-commit install could never prove itself active even though scripts/install.sh had
genuinely installed that commit. On this host the pinned-main steward runtime
(manifest_source_git_commit=…9719dc0d, kind=github_archive, archive_sha256 present)
kept reporting an unproven activation after a successful install.

Behavior

A full commit SHA is its own commit identity, so the qualification now resolves the
selected immutable ref to that commit and compares it with the installed manifest source
commit:

  • match → runtime_active, revision_relation=same, successor.required=false;
  • mismatch → activation_qualification_required with an explicit
    installed source commit does not match the selected immutable source commit reason
    instead of the generic lineage message.

Branch-ref behavior is unchanged, including the existing fail-closed cases for missing
lineage, mismatched repo/ref, and installed_behind / diverged.

Bounded refactor

loopx/self_update.py was at 1515 lines against the 1500-line maintainability ratchet
after the fix. The touched boundary — one decision about whether the installed runtime
proves the selected update source is active — moved to
loopx/activation_qualification.py. loopx/self_update.py returns to 1394 lines and
keeps the schema constant and the not-applicable stub through an explicit import.
loopx/semantics/inventory_v0.json regenerates to source_files: 1176.

Validation

  • pytest tests/test_self_update_runtime_activation.py tests/test_archive_installer_commit_response.py tests/test_doctor_install_freshness.py tests/canary/test_maintainability_ratchet.py -q → 63 passed, 1 skipped
  • loopx canary premerge --from-git-diff --timeout-seconds 300 → gate.status=passed, 19 executed checks, 0 failures, public/private boundary scan clean (7 files)
  • examples/loopx-update-smoke.py covers the matching and mismatching immutable cases; the ratchet and semantic-vocabulary-drift smokes pass

Disclosure

Pinned-commit installs now qualify as runtime_active where they previously failed
closed. docs/product/release-readiness.md and docs/guides/installing-loopx.md state
the new receipt.

`loopx update check --ref <sha>` reported

    activation_qualification_required
    trusted installed-versus-target source lineage is unavailable

for an install that the same pinned SHA had just produced. The qualification
read `freshness_source_git_commit` from `loopx doctor`, which only branch refs
populate, so a full-commit install could never prove itself active even though
the installer had genuinely installed that commit.

A full commit SHA is its own commit identity, so the qualification now resolves
the selected immutable ref to its own commit, compares it with the installed
manifest source commit, and reports `runtime_active` on a match. A mismatch
stays `activation_qualification_required` with an explicit
"installed source commit does not match the selected immutable source commit"
reason instead of the generic lineage message.

Move the qualification into `loopx/activation_qualification.py` while changing
it: `loopx/self_update.py` was at 1485 lines with this fix applied against the
1500-line maintainability ratchet, and this module owns the one decision the
change touches. `loopx/self_update.py` returns to 1399 lines and keeps the
schema constant and the not-applicable stub through an explicit import;
`loopx/semantics/inventory_v0.json` regenerates to `source_files: 1177`.

Disclosure: pinned-commit installs now qualify as `runtime_active` where they
previously failed closed; branch-ref behavior is unchanged. Docs in
`docs/product/release-readiness.md` and `docs/guides/installing-loopx.md` state
the new receipt, and `examples/loopx-update-smoke.py` covers the matching and
mismatching immutable cases.

Validation: 65 passed / 1 skipped across
`tests/test_self_update_runtime_activation.py`,
`tests/test_archive_installer_commit_response.py`,
`tests/test_doctor_install_freshness.py`, and
`tests/canary/test_maintainability_ratchet.py`;
`loopx canary premerge --from-git-diff --timeout-seconds 300` gate=passed with
19 executed checks, 0 failures, and a clean public/private boundary scan.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng
huangruiteng force-pushed the codex/immutable-source-activation-qualification branch from 298caca to f5d2969 Compare September 15, 2026 16:14

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

Reviewed exact head f5d296926c68f6b5053acae158aff26ae4359d3e (rebased onto 2e310d768 after #4458 and #4456 merged). Evidence was executed at this head, not inherited from the earlier 298cacaee head.

动机

loopx update check --ref <40 位 SHA> 对本机这类「按固定 commit 安装」的运行时,永远给不出激活结论。qualification 读取的是 loopx doctor 的 freshness_source_git_commit / manifest_source_freshness_relation,而这两个字段只在 ref 能解析成分支血缘时才有值;用完整 SHA 安装时 target_source_commit 始终是 None,于是落进兜底分支,返回 activation_qualification_required,理由是 trusted installed-versus-target source lineage is unavailable。

这条路径上的最小修复并不小:直接放宽成「manifest 里有 commit 就算激活」会同时接受 manifest ref 指向另一个 commit 的安装,等于削弱身份校验;而把改动留在 loopx/self_update.py 里,文件会到 1515 行,直接撞上仓库自己 1500 行的可维护性棘轮。触发方是真实生产入口:本机管家运行时(release 20260915T144512Z,由 main@9719dc0d4 固定安装)在安装成功后仍然报告「未证明激活」,而这条回执正是 agent 敢不敢声称「这个修复已在运行时生效」的依据。

改动思路

入口是 loopx update check / loopx update plan → build_update_plan,权威输入是 doctor 的 install_freshness(含 manifest_source_repo/ref/git_commit)加上本次选中的 repo/ref。本次把「已安装运行时是否证明选中更新源已激活」这一个判定整体搬进新模块 loopx/activation_qualification.py,由一个函数 runtime_activation_qualification 独占,loopx/self_update.py 只负责计划与呈现。判定边界没有扩大:既有五个分支的优先级、source_identity_matches 先于任何血缘比较、以及所有 fail-closed 结果都原样保留。

对「新引入的状态」这里其实没有任何新状态:decision / runtime_active / successor 仍是 loopx_runtime_activation_qualification_v0 上的既有封闭集合,唯一变化是完整 SHA 这种 ref 会把自身作为 target commit 参与比较,而真正被比较的权威事实仍是已安装 manifest 的 commit。抽出的 105 行是逐字搬移,新增逻辑约 33 行。复用上是「拆出独立 owner」而不是新增并列实现:rg 在 head 上已经找不到任何 _runtime_activation_qualification 的定义或调用者,loox/runtime_activation.py(#4458 刚合并)负责的是安装后的服务重启,与这里不重叠。

具体改动

关键代码讲解

loopx/activation_qualification.py:34 runtime_activation_qualification —— 判定表的唯一 owner。新增的前置步骤只有一处:selected_commit and not target_commit 时,把完整 SHA 作为 target commit,并在与已安装 commit 相同时把关系置为 same。只有当 doctor 没有血缘时才走这一步,所以分支 ref 的既有行为一点没动。分支顺序保持:source_identity_matches 仍然最先裁决,其次是 runtime_active 与 installed_behind / diverged,新增的 elif has_commit_pair and selected_commit 只负责「commit 对得上身份但对不上目标」这一种以前不存在表达方式的组合。

loopx/activation_qualification.py:21 _immutable_source_commit —— 私有判定,要求 ^[0-9a-fA-F]{40}$ 全匹配才视为不可变 ref,并统一小写。分支名、tag、短 SHA 一律返回 None,新路径整体失效。它的存在让「ref 即 commit 身份」这件事有唯一落点,而不是散落在调用方。

loopx/self_update.py build_update_plan —— 只做导入替换:从新模块引入 runtime_activation_qualification 与 RUNTIME_ACTIVATION_QUALIFICATION_SCHEMA_VERSION,not_applicable 兜底仍由同一常量渲染,schema id 与 payload 键保持不变。文件从 1485 行降到 1399 行,棘轮告警被消除而不是新增。

examples/loopx-update-smoke.py:449 test_immutable_source_ref_qualifies_activation —— 用一份 freshness_source_git_commit=None、manifest_source_ref=<SHA> 的 doctor 样本,同时钉住匹配(runtime_active、revision_relation=same、无需 successor)与不匹配(activation_qualification_required 且理由点名 commit 差异)两种回执;旧代码在匹配样本上就会失败。

tests/test_self_update_runtime_activation.py —— 两个新单测分别覆盖「无需远端血缘即解析出 target commit」与「commit 不一致时报出差异」,与既有 test_missing_commit_lineage_never_proves_runtime_active、test_different_selected_source_never_reuses_unrelated_lineage 一起锁住全部拒绝分支。

文档侧在 docs/product/release-readiness.md(Merged Is Not Runtime-Active)与 docs/guides/installing-loopx.md(安装/更新检查)各补一段,说明固定 commit 安装直接按 manifest commit 判定;loopx/semantics/inventory_v0.json 按生成器重新生成到 source_files: 1177。

对主干的风险

最强回归场景是「40 位 hex 的 ref 却不是已安装 commit」,或被误当成 commit 的分支/tag 名。触发条件是 source.ref 匹配 40 位 hex 且 doctor 没有血缘;代码路径上,新分支在 source_identity_matches 与 runtime_active/installed_behind/diverged 之后才可达,最坏结果仍是 fail-closed 的 activation_qualification_required,而不会把未证明的安装升格。若真的出现 40 位 hex 的分支名,最小修复是优先采用 doctor 血缘,而代码已经是这个次序。

可观测面是完整的:decision、.reason、.target_source_commit、.revision_relation、successor 都在 JSON 回执里,markdown 渲染器也会显示 successor 与推荐动作。回滚就是 loopx update --rollback <release-id>,本改动不留持久状态。验证上,同一份合成输入在基线与 head 各跑一次真实公共入口:基线返回 activation_qualification_required + 通用血缘理由 + 「未证明激活」,head 返回 runtime_active / revision_relation=same / 无需 successor;本机已安装运行时也复现了修复前的旧回执。仓库本地门禁 loopx canary premerge --from-git-diff 全绿(19 项、0 失败、改动文件公开/私有边界扫描干净),pytest 65 passed / 1 skipped(Windows-only skip),update smoke、棘轮、语义词汇漂移与 ruff check 均通过。尚未验证的是「装一个由本 commit 构建的 release 后再读一次真实回执」,以及本 head 的远端 CI(复审时仍在跑,合并前必须为绿)。

我的整体评价

在只比较变更代码、不把「全绿」当论证的前提下,这是比例合适的修复:用户可见问题是「固定 commit 安装永远无法自证激活」,机制成本是一个 138 行、单一公共入口的模块加约 33 行解析逻辑,没有新依赖、新 CLI 入口、新配置、新持久状态,反而消掉了一个既有的棘轮违规。基线/head 对比显示只有两条不可变 ref 的行为改变,分支 ref、发散的 ref、跨源选择三类拒绝路径逐字保持不变。类型语义上没有新增枚举值,措辞保持领域中立,判定仍然是机器可读的 decision + successor.required 而不是散文建议;公开协议命名没有暗示比实现更大的权限生命周期(它连写权限都没有)。剩余风险已在上文列明:新 head 的远端 CI 与「安装后再读一次真实回执」。两者之一未落地前,我不会把它当作已激活结论;对于复审,我需要的就是这两项在同一个 head 上的证据。

English verdict: APPROVE at exact head f5d296926c68f6b5053acae158aff26ae4359d3e. A pinned-commit install previously could never satisfy the activation qualification because the target commit was only derivable from branch lineage; the change resolves a full 40-hex ref to its own commit inside the qualification that already owns the decision, keeping every rejection branch fail-closed, and moves that boundary into a 138-line loopx/activation_qualification.py so loopx/self_update.py (1485 → 1399) clears the repository's 1500-line ratchet. Validation: pytest 65 passed / 1 skipped, loopx-update-smoke ok, loopx canary premerge --from-git-diff gate passed (19 checks, 0 failures, clean boundary scan), semantic inventory current at 1177 source files, and a baseline/head probe of the same fixture showing activation_qualification_required → runtime_active with revision_relation=same. No blocking finding; the only open items are remote CI on this exact head and a post-install live readback, neither of which is claimed as evidence here.

@huangruiteng
huangruiteng merged commit f22879d into main Sep 15, 2026
28 checks passed
@huangruiteng
huangruiteng deleted the codex/immutable-source-activation-qualification branch September 15, 2026 16:37
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

后续(非阻塞):activation_qualification.py 的大小写归一化只做了一半

这是对上面 review 5212727715(APPROVE at f5d296926)的补充发现,不改变结论,也不阻塞合并。

loopx/activation_qualification.py:31 明确把选中的 ref 归一化为小写(ref.lower()),:52 的匹配也用 installed_commit.lower() == selected_commit,但 :88 的判定仍是原始字符串比较 installed_commit == target_commit。当已安装 manifest 的 commit 是大写、而选中 ref 是同一个 SHA 的小写形式时(repo/ref 身份都匹配),payload 会自相矛盾。我在这个 head 上独立复现:

installed lowercase  decision=runtime_active                    revision_relation=same successor=False
installed UPPERCASE  decision=activation_qualification_required revision_relation=same successor=True
                     reason=installed source commit does not match the selected immutable source commit

即 revision_relation=same 与「commit 不匹配」的 reason 同时出现,而这正是本 PR 新增的那条 exact-commit 自证路径。触发概率低(archive 安装记录的是 git rev-parse 的小写值),所以是 P3;最小修复是把 :88 一并改成大小写不敏感比较,或补一条大写 install 的负例测试把当前语义固定下来。

另有一处描述精度:PR 正文写 "loopx/self_update.py returns to 1394 lines",那是变基前 head(base 9719dc0d)的真实值;当前 head 是 1399 行(base 前进到 2e310d768 后该文件 base 从 1480 变 1485)。两种情形都低于 1500 的 module_line_limit,只是复核时对不上。

复核环境:exact head f5d296926c68f6b5053acae158aff26ae4359d3e,worktree /private/tmp/pr4460-r20;pytest -q tests/test_self_update_runtime_activation.py tests/test_archive_installer_commit_response.py tests/test_doctor_install_freshness.py tests/canary/test_maintainability_ratchet.py → 65 passed, 1 skipped;examples/loopx-update-smoke.py ok;scripts/generate_semantic_inventory.py --check → up to date。

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.

1 participant