Skip to content

fix: avoid recursive project state archive - #6205

Merged
huangruiteng merged 2 commits into
loopx-project:mainfrom
Duang777:codex/fix-project-uninstall-root-state-archive-20261011
Oct 12, 2026
Merged

huangruiteng merged 2 commits into
loopx-project:mainfrom
Duang777:codex/fix-project-uninstall-root-state-archive-20261011

Conversation

@Duang777

@Duang777 Duang777 commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • prevent uninstall-project --archive-state from moving a project root or .loopx directory into its own archive path
  • archive only the registered state file when the archive destination is nested under its state directory
  • return explicit would-move-state-file and moved-state-file actions with a warning for that local receipt path

Root cause

_archive_state_directory() always moved state_file.parent. For root-level or .loopx state files, .loopx/archived-project-state is inside that source directory, so shutil.move either rejects a self-move or enters recursive copy fallback.

The new action values remain local to the project-uninstall state_actions receipt. They do not create a shared protocol vocabulary.

Validation

  • red run: the new regression test failed with Cannot move a directory ... into itself
  • .venv/bin/python -m pytest -q tests/test_global_registry_write_serialization.py: 16 passed
  • .venv/bin/ruff check loopx/project_uninstall.py tests/test_global_registry_write_serialization.py: passed
  • project registry I/O manifest check: 291 sites current, 0 unclassified
  • actual CLI run: archived only ACTIVE_GOAL_STATE.md, preserved a project sentinel and the local registry
  • loopx canary premerge --from-git-diff --git-diff-base upstream/main: 5/5 selected canaries passed

No related refactor was needed; the existing project-uninstall owner remains the narrow behavior boundary.

Maintainer Rebase Note (2026-10-12)

Rebased onto main at 5228e06ccae540a47895ce8ba29ee6cc461e0947; new head
c3cc23f7f9329b834f11dd09741ec063f8dc2dfd. The branch's repeated upstream merge
commits were flattened during the rebase; the two fix commits keep their author
and DCO sign-offs, and the net diff is unchanged (3 files, +177/-2).

Conflict resolution: loopx/project_uninstall.py now also carries the newer
retained-owner guard from main; the rebase keeps that guard and places this
branch's nested-destination check after it, so both behaviors hold. The
generated loopx/semantics/project_registry_io_manifest_v1.json was regenerated
against the rebased source (291 sites, 0 unclassified).

Maintainer validation on this head: CI-scope Ruff plus the changed files, the
configured python -m mypy (19 sources), git diff --check,
tests/test_global_registry_write_serialization.py (23 passed), and the related
install/material suites (82 passed). The registry I/O census check reports 291
sites current.

@Duang777

Copy link
Copy Markdown
Collaborator Author

@loopx-agent review

1 similar comment
@Duang777

Copy link
Copy Markdown
Collaborator Author

@loopx-agent review

@mergify

mergify Bot commented Oct 11, 2026

Copy link
Copy Markdown

This pull request has merge conflicts with main and cannot be merged
until they are resolved. Please rebase or merge the base branch, @Duang777.

Choose the remote for the base repository, not an out-of-date fork.
For a fork clone, first inspect git remote -v; upstream must point
to https://github.com/loopx-project/loopx.git. If it is absent, add it
with git remote add upstream https://github.com/loopx-project/loopx.git.
Then run:

git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEAD

For a same-repository clone whose origin points to
https://github.com/loopx-project/loopx.git, use origin instead of
upstream for fetch/rebase. If you prefer merging the base, use
git merge <base-remote>/main and push normally.

Keep the DCO Signed-off-by trailer on every commit when you rebase.
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Oct 11, 2026
@Duang777

Copy link
Copy Markdown
Collaborator Author

Synced upstream/main in d50fe2f7f with a signed merge commit. The conflict resolution preserves both retained-goal state ownership and nested archive protection. Local validation passed: 21 target tests, 10 manifest architecture tests, Ruff, manifest check, and the 5-check premerge gate.

@loopx-agent review

@mergify mergify Bot removed the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Oct 11, 2026

@loopx-agent loopx-agent 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.1-sol; OpenAI; runtime_reported; reasoning_effort=xhigh

REQUEST_CHANGES;精确 head d50fe2f7f1b7605474d92a8739afa7c94692cd9d。

动机

使用项目卸载并选择归档状态文件的维护者。

状态文件放在项目根或 .loopx 时,旧版本试图把整个目录移进其自身归档目录,卸载失败;本修改在普通目录布局下改为只移动记录的状态文件,卸载与回读成功。

普通布局的真实 CLI 和安装 wheel 已完成文件归档并保留项目、其它 Goal 和路由边界;归档目录指向项目外的软链接时仍移动项目或 .loopx,随后因 registry 路径消失失败,这一修复边界尚未闭合。

本批应只修复现有项目卸载的归档边界,不卸载 CLI、不删除其它项目历史、不增加 Goal、lease 或账户权限。

改动思路

继续复用项目卸载 owner、保留 Goal 保护和 registry transaction 即可;目录包含关系属于本地 IO 不变量,不需新的持久状态、配置或并行决策框架。 比较不修复、放松校验和扩大通用框架后,现有 owner 内的 IO 修复最容易验证和回退;输入、作用域和失败保护应一起验收。

当前 PR 可保留普通布局的文件归档分支,但需在同一 owner 补齐词法祖先与物理路径判断,才能保证这条卸载路径不移动项目本身。 Python 在这里是稳定的文件系统/provider IO 边界,未新建通用控制面决策源,无需为语言迁移扩大本批。

具体改动

独立规格 docs/guides/getting-started.md,spec_revision:56560d92db76e3b28a441e1f7ac5b9344fd54f10;使用变更前的合同。getting-started 的项目卸载约束优先于此 PR 新写的范围声明。

  • project-scoped-uninstall:not_met;Disconnect only the current project and retain unrelated project runtime history.
  • dry-run-execute-boundary:implemented;Preview is dry-run; mutations require --execute and direct global-registry uninstall is refused.
  • registered-state-archive:not_met;Archive registered Goal state under .loopx/archived-project-state while selected registration is disconnected.

关键代码讲解

  • _archive_state_directory 保留外部状态与 retained Goal 的原有保护;新增的 resolved 包含分支创建唯一目标,只移动 state_file,并给出已有文件 action 和局部 warning。
  • uninstall_project 仍默认 preview,execute 在既有 canonical writer guard 中先归档,再通过项目 registry transaction 与 source_registry 匹配的 global reducer 删除所选路由。
  • render_project_uninstall_markdown 已能显示文件动作和 warning,CLI 无需重新输入已知信息;没有新增 frontend 或 Lark 配置旅程。
  • 两个参数化测试覆盖根/.loopx 的普通布局,检查 dry-run 无写、文件归档、sentinel 与 registry 保留;manifest 两个写入点仅同步行号,不改变分类或站点集合。

对主干的风险

28 项 focused/IO census pytest、公共 project-uninstall smoke、Ruff、完整语义检查与 291-site manifest 检查通过。冻结 CLI oracle 的普通根目录、.loopx、专用目录、保留 Goal、外部状态、路由不匹配和未开启归档共 8 项通过;外部软链接归档用例失败。专项反例在 source、base 和独立 wheel 都确认根/.loopx 移位、原 registry 消失、global route 未撤销,且原报告归档路径不可读。 对 source、base 与安装 wheel 检查了解释器和模块来源;native filesystem 操作没有替换为成功 mock。初始测试路径错误不计通过;未准备前端 assets 的 wheel 构建先失败,完成正常 npm ci / build:chat 后构建和隔离安装,未绕过包装校验。

软链接反例在 macOS 原生文件系统验证,Windows junction 和并发替换软链接尚未测试;两种布局的破坏无需这些未测条件。此失败在 immutable base 上已存在,但属于本 PR 正在修复的同一归档分支,不能当作无关基线错误忽略。 CI 按当前配置未查询、轮询或等待;作者的外部平台/测试声明不作为本评审的独立证明。语义 advisory 没检测到支持的新词汇载体,完整检查通过但仍保留 45 个 unresolved producer sites,不能由此宣称全树语义安全。

阻塞问题

[P1] loopx/project_uninstall.py:197 只检查 resolve 后的物理包含关系,遗漏外链后的词法祖先。合成项目中将 .loopx/archived-project-state 链到同级外部目录,状态文件分别放在根目录或 .loopx,运行默认预览及 uninstall-project --goal-id sample --archive-state --execute:预览返回 would-move,执行先移走整个项目或 .loopx,随后报 registry 不存在。根布局的项目 sentinel 也被搬走;两布局原 registry 均消失,global registration 未撤销,报告的原归档路径不可读。

这个危险路径在 base 已存在,但直接属于本 PR 的“避免把项目根/.loopx 移进归档目录”目标,不能用基线也失败来免除。最小修复是同时检查实际 resolve 包含与各词法父目录的解析身份,再选择已有文件归档分支或无副作用拒绝。latest main c51ec1a60671edb8ef536ef0651836b9d7642878 的 runtime archive 已采用这一原则;可借鉴技术,不必合并不同卸载权限和效果 owner。补两个实际 CLI 外链回归,断言原 registry、项目与 global route 一致后重新评审。

我的整体评价

普通路径有用,但危险外链布局仍使长期维护与用户恢复中断;完整请求尚未证明,需在当前 PR 修好上述 P1。 继续复用项目卸载 owner、保留 Goal 保护和 registry transaction 即可;目录包含关系属于本地 IO 不变量,不需新的持久状态、配置或并行决策框架。 当前 PR 可保留普通布局的文件归档分支,但需在同一 owner 补齐词法祖先与物理路径判断,才能保证这条卸载路径不移动项目本身。 未来维护整理已检查:补齐既有包含关系即是当前必要的 bounded companion,不需要新的协议/状态框架。 已有阶段恢复经验仅作提示,本结论没有继承历史 verdict,也不声明生产采用、整条 roadmap 关闭或合并授权。

English verdict: REQUEST_CHANGES - exact head d50fe2f. Resolved-only nesting still relocates the project root or .loopx through an outbound archive symlink, then loses the registry path and leaves global registration stale. Reproduced on base, exact head and installed wheel. Ordinary 8 CLI cases and 28 focused tests pass. Add lexical-ancestor protection and real CLI regression before approval; this is an inherited in-scope defect, not a new regression.

Comment thread loopx/project_uninstall.py Outdated
@loopx-agent

Copy link
Copy Markdown
Collaborator

Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=xhigh

English verdict: REQUEST_CHANGES - exact head d50fe2f. Resolved-only nesting still relocates the project root or .loopx through an outbound archive symlink, then loses the registry path and leaves global registration stale. Reproduced on base, exact head and installed wheel. Ordinary 8 CLI cases and 28 focused tests pass. Add lexical-ancestor protection and real CLI regression before approval; this is an inherited in-scope defect, not a new regression.

@Duang777

Copy link
Copy Markdown
Collaborator Author

Addressed the outbound archive symlink finding in 064c2aef4. Project uninstall now checks both the resolved destination and resolved lexical ancestors before deciding whether an entire state directory can move.

Added real CLI coverage for project-root and .loopx state files with .loopx/archived-project-state linked outside the project. Red run: both cases failed after losing registry.json. Green run: both pass, preserve the project, registry, sentinel, and remove the local/global registrations after archiving only the recorded state file.

Validation passed: 23 target tests, 10 registry-I/O architecture tests, public project-uninstall smoke, Ruff, 291-site manifest check, full semantic-vocabulary smoke, and the 5-check premerge gate.

@loopx-agent review

@loopx-agent loopx-agent 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.1-sol; OpenAI; runtime_reported; reasoning_effort=xhigh

精确 head:064c2aef4b84ad8bfd8af204e8b9ce6b72e1fae2。

动机

选择归档状态并断开项目的维护者。 状态文件位于项目根或 .loopx,归档目录也可能是指向外部的软链接;旧版预览/执行错误或搬走项目,本 head 只归档登记文件并保持项目和路由一致。 独立真实 CLI 八个布局全部通过,四个原有坏布局在 immutable base 仍失败;状态内容、其它 Goal、项目 sentinel、预览无写与路由一致性均读回。 只修复现有项目卸载 IO 边界,不移除 CLI、不授予其它项目或 Goal 权限;安装采用与 Windows junction 未在本次验证。

改动思路

Resolved 包含和词法祖先必须一起保护目录移动;既有项目卸载 IO owner 足够,不需新状态或并行 TypeScript 策略。 本 PR 完成现有归档路径的文件移动保护与读回;仅维持现有卸载权限和注册事务。 仍通过既有命令/解析器、权威输入和副作用 owner 完成操作;没有新 Goal、租约、额外状态源或账户权限。正向路径要求得到可回读的结果,反向路径检查真实副作用及恢复,不以单元测试或历史 verdict 代替。可选读取提示不改变机器强制的现有操作约束。

具体改动

独立规格:docs/guides/getting-started.md,不可变修订 56560d92db76e3b28a441e1f7ac5b9344fd54f10。先读取变更前合同和原评审,再判断此 head;条款原文无编号,标识用于对应下列原有要求。

  • project-scoped-uninstall:implemented — Disconnect only the current project and retain unrelated project runtime history.
  • dry-run-execute-boundary:implemented — Defaults to dry-run; actual operation needs --execute and refuses direct shared-global uninstall.
  • registered-state-archive:implemented — Archive this project registered Goal state under .loopx/archived-project-state instead of leaving it in place.

关键代码讲解

  • _archive_state_directory:在既有 external/shared/retained 保护之后检查真实目标包含关系,以及每个词法祖先 resolve 后的身份。嵌套或外链归档只移动登记文件,沿用已有文件 action 和 warning;不搬项目根或 .loopx。
  • uninstall_project:默认预览无副作用,execute 继续使用 canonical writer guard、项目 registry transaction 和 source_registry 匹配的 global route reducer。先归档后断开登记;原定向范围未扩大。
  • 两个参数化布局和两种外链 CLI 测试共152 行,检查文件内容、sentinel、原 registry 和 global route;manifest 两个位置只改行号,site 分类和集合保持。Python 是现有稳定本地文件系统 IO owner,没有新建通用决策源;这次保留局部检查,不拆新的 helper/框架。

对主干的风险

同一独立真实 CLI harness,在固定 base 八场景中根/.loopx/两种 outbound symlink 四项失败,在本 head 八项全部成功;专用状态目录、保留 Goal、外部状态、不启归档的对照保持。每项均 preview→execute→状态内容/项目/原 registry/global route 读回,原生文件系统没有替换为成功 mock。此前 outbound symlink 的 P1 在这一准确 head 得到解决。

23 项 focused 测试通过,7 项 registry IO census 通过,公共 project-uninstall smoke、Ruff、diff/全树语义检查通过。首次误指定不存在的测试文件(0tests)和未准备 TypeScript npm 依赖时3 个 census 失败均保留;按文档 npm ci 后完整7 项 census 通过,未删断言。Windows junction、动态软链接替换和本机安装采用没有测试,不将这些范围或 fleet 效率宣称已验收。没有新增配置或前端旅程;既有 CLI warning/JSON/markdown 能表达实际归档范围。不启 archive flag 的实际默认路径成对保持。

语义与 CI 对齐

复用已有本地文件 action,不创建共享 closed set;advisory 没发现支持的新词汇载体,全树验证与 IO manifest 检查保持。CI 按当前策略未查询、轮询或等待;本地可复验路径才是结论证据。

我的整体评价

修复卸载归档时移动项目本身的风险,后续注册路由能够一致撤销。 Resolved 包含和词法祖先必须一起保护目录移动;既有项目卸载 IO owner 足够,不需新状态或并行 TypeScript 策略。 本 PR 完成现有归档路径的文件移动保护与读回;仅维持现有卸载权限和注册事务。 APPROVE。未发现当前 head 的阻塞问题。当前 bounded 修复与长期状态/路由承诺同向,错误恢复体验改善;不能外推为全平台安装、长期吞吐或 memory 因果收益已证明。旧 review 如仍阻挡合并,单独检查归因和授权,保留讨论,批准不授予合并或 dismissal 权限。

English verdict: APPROVE - 064c2ae; Resolved and lexical containment now protect the project; eight real paired CLI layouts, focused tests, census and static/semantic checks pass. Source-only approval; Windows/installed adoption unverified.

@Duang777

Copy link
Copy Markdown
Collaborator Author

Synced the latest upstream/main (c464cf308) with signed merge commit 54e7cbe26; pushed normally without rewriting history. The only overlapping path was the project-registry I/O manifest, which merged cleanly. The final PR diff remains limited to loopx/project_uninstall.py, its focused tests, and manifest coordinates.

Validation on exact head 54e7cbe26:

  • 23 project-uninstall/global-registry tests passed
  • 10 registry I/O architecture tests passed
  • public project-uninstall CLI smoke passed
  • Ruff passed
  • 291-site project-registry I/O manifest check passed
  • full semantic-vocabulary smoke passed
  • standard premerge gate passed all 5 selected canaries, with no failures or manual holds
  • git diff --check passed; root uv.lock was not touched

No frontend surface changed. Repository policy still requires maintainer merge authority.

@loopx-agent review

@huangruiteng
huangruiteng force-pushed the codex/fix-project-uninstall-root-state-archive-20261011 branch 2 times, most recently from e22a334 to ac0c213 Compare October 12, 2026 06:53
Project uninstall moved state_file.parent as a directory. When the state file lived at the project root or under .loopx, the archive destination was inside that source directory and shutil refused or recursively copied it.

Detect nested archive destinations and move only the recorded state file in that case. This preserves unrelated project files and the project registry while retaining whole-directory archives for isolated state directories.

Signed-off-by: Duang777 <duangjl007@gmail.com>
The prior fix only compared resolved destination paths, so a symlink inside the state directory could hide lexical nesting and trigger a whole-directory move. Check both physical containment and resolved lexical ancestors, with CLI regression coverage for project-root and .loopx state files.

Signed-off-by: Duang777 <duangjl007@gmail.com>
@huangruiteng
huangruiteng force-pushed the codex/fix-project-uninstall-root-state-archive-20261011 branch from ac0c213 to c3cc23f Compare October 12, 2026 06:54

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

Review verdict: APPROVE (maintainer review of a contributor PR)

Reviewer: model_agent (self_reported); model=DeepSeek V4 Flash; provider=DeepSeek.

评审 #6205 fix: avoid recursive project state archive

Review exact head: c3cc23f7f9329b834f11dd09741ec063f8dc2dfd(maintainer rebase 后);合并基线 main:5228e06ccae540a47895ce8ba29ee6cc461e0947;作者原 head:54e7cbe26a97849e2d04978283fcc5e536fd1660。

规范依据(不可变修订): docs/guides/getting-started.md @ 5228e06 — uninstall-project-scope 保留;archive-state-directory 已实现;uninstall-does-not-touch-others 已实现。

动机

Operators and Maintainers who uninstall a project: the person running loopx uninstall-project --archive-state for a Goal whose registered state file sits at the project root or directly under .loopx. Before, the archive step always moved the state file's parent directory, so when the recorded state file lived at the project root or under .loopx the destination .loopx/archived-project-state/... was inside that same directory and shutil.move either refused the self-move or fell back to a recursive copy that could drag unrelated project files or the live registry into the archive; after, a nested archive destination is detected, including through symlinked parents, and only the recorded state file is archived, so unrelated project files and the registry stay in place. uninstall-project --archive-state on a root-level or .loopx state file now returns would-move-state-file in a dry run and moved-state-file with a warning when it archives, the archived tree contains only the recorded state file, the project sentinel and the local registry survive, and isolated state directories still get whole-directory archives. No shared protocol vocabulary, no new CLI command or flag, no change to which Goals are retained, and no change to the external-state or retained-state guards.

改动思路

改动留在既有 project-uninstall owner 内,不新建能力或第二个判定源。_archive_state_directory 在算出 archive destination 之后增加一次嵌套判定:先把 destination 规范化成绝对路径,再同时检查「规范化后的 destination 是否位于 state_dir_resolved 之下」以及「destination 的任一祖先(含符号链接解析)是否就是 state 目录」,只有两条都不成立时才继续移动整个 state 目录。命中嵌套时只 mkdir 目标目录并把注册的 state 文件本身移进去,返回 would-move-state-file / moved-state-file 与一条说明「state 目录包含 archive 目的地,只归档了记录的状态文件」的 warning,从而保证 .loopx/registry.json、项目哨兵文件与其他项目状态都不会被卷进归档。既有 retained-goal 与 external-state 的提前返回分支顺序未变,隔离目录的整目录归档也保持不变;census manifest 只是随行号刷新,291 个 I/O 站点仍全部归类。

关键代码讲解

  • _archive_state_directory(loopx/project_uninstall.py:98):外部 state、缺失目录、共享 state、retained-goal 等既有拒绝/保留分支全部保留,只在 destination 计算之后插入嵌套判定。
  • 嵌套判定(同文件:198–:203):Path(os.path.abspath(destination)) 先规范化路径,destination.resolve().is_relative_to(state_dir_resolved) 覆盖直接嵌套,any(parent.resolve() == state_dir_resolved ...) 覆盖 archive 路径经由符号链接指回 state 目录的情况。
  • 嵌套分支(同文件:204–:222):dry-run 只返回 would-move-state-file,执行时先建目标目录再移动 state_file,并返回 moved-state-file 与限制说明;后续整目录移动分支对非嵌套情况保持不变。
  • uninstall_project(同文件:337):注册表读取与事务调用未改,manifest 中对应行号随之刷新。

具体改动

Maintainer rebase 后 3 文件 +177/−2:loopx/project_uninstall.py +23(import os、嵌套判定与仅移动状态文件的分支)、tests/test_global_registry_write_serialization.py +152(根目录、.loopx、符号链接与隔离目录的正负用例),以及 loopx/semantics/project_registry_io_manifest_v1.json 两条行号刷新。两个提交(caa8952e6e 避免递归归档、c3cc23f7f9 保留符号链接解析)都带 DCO sign-off,作者身份不变;原始分支夹带的多次 upstream merge 在 rebase 中被压平,净差异与 PR 一致。

对主干的风险

最危险的反例是「本该整目录归档的隔离 state 目录被误判为嵌套,只搬走了单个文件」或者反过来「嵌套场景仍然整目录搬走,把 registry 或无关项目文件卷进归档」。当前实现用两条独立判定共同决定嵌套:规范化后的相对关系,以及任意祖先经 resolve() 后等于 state 目录;测试同时覆盖根目录 state、.loopx state、经符号链接指向 state 目录的 archive 路径,以及保持整目录归档的隔离目录场景,dry-run 与执行路径都断言了 action 与 warning。第二类风险是归档语义收窄:文档承诺把「registered Goal state directory」移到 .loopx/archived-project-state/ 下,本次只在目的地自嵌套时改为只移动记录的 state 文件,隔离目录行为不变,也未改动 retained-goal 与 external-state 的既有保护。

关于验证:本 head 的 CI 口径 Ruff、改动的 loopx/project_uninstall.py + 测试文件 Ruff、配置内 python -m mypy(19 sources)、git diff --check 全部通过;tests/test_global_registry_write_serialization.py 23 passed,另有 tests/capabilities/test_material_project_skill_delivery.py 与 tests/test_slash_command_install.py 82 passed;scripts/generate_project_registry_io_manifest.py --check 报告 291 sites current。

我的整体评价

这个切片值得合并:它是一个有边界、可回滚的真实缺陷修复 —— 让 --archive-state 在 state 文件位于项目根或 .loopx 时不再自嵌套移动,也不会把 registry 和无关项目文件卷进归档,同时保留隔离目录的整目录归档与既有保留策略。实现留在既有 owner 内、没有新增词表或 CLI 表面,正负用例与 manifest 校验都到位。没有阻断项。

English verdict: APPROVE - head c3cc23f: a nested archive destination (including through symlinked parents) now archives only the recorded state file with an explicit action and warning, while isolated state directories keep whole-directory archiving; 23 archive-serialization tests plus 82 related install/material tests, the CI-scope Ruff check, the configured mypy run, the diff check and the 291-site registry I/O census all pass at this head.

@huangruiteng
huangruiteng merged commit 38cb407 into loopx-project:main Oct 12, 2026
4 checks passed
@huangruiteng

Copy link
Copy Markdown
Collaborator

Maintainer merge record (admin bypass, owner-authorized self-rebase + self-merge).

  • Exact head: c3cc23f7f9329b834f11dd09741ec063f8dc2dfd; merged as 38cb407edc39752d3ad6b4d414ca5c5255aba177 (base main 5228e06ccae540a47895ce8ba29ee6cc461e0947).
  • Changed surfaces: the project-uninstall state archive helper (nested-destination detection, including symlinked parents), the generated registry I/O census line numbers and the regressions. 3 files, +177/-2.
  • Rebase resolution: main's retained-owner guard is kept ahead of this branch's nested-destination check; the branch's repeated upstream merge commits were flattened and the census was regenerated against the rebased source (291 sites, 0 unclassified).
  • Checks run on this head: CI-scope ruff plus the changed files, the configured python -m mypy (19 sources), git diff --check, tests/test_global_registry_write_serialization.py (23 passed), related install/material suites (82 passed) and the census check. loopx canary premerge --from-git-diff --goal-id loopx-meta passed with 0 failures; change-quality receipt cqr_ddfc33aecd3d981974e2 (status valid). Merge readiness returned ready: true for this head; the merge still required admin rights because the ruleset holds required status checks.

No protocol, capability or persisted-schema change; the two new action values stay local to the project-uninstall state_actions receipt.

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.

3 participants