Skip to content

fix(extensions)!: coordinate removal, activation and shutdown - #361

Merged
leafliber merged 13 commits into
mainfrom
codex/extension-uninstall-lifecycle
Nov 2, 2026
Merged

leafliber merged 13 commits into
mainfrom
codex/extension-uninstall-lifecycle

Conversation

@kwk1001

@kwk1001 kwk1001 commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Previously enabled Worlds could reactivate after uninstall/reinstall, including when their package failed to import. Provider packages also remained available for creating endpoints after uninstall, and shutdown could interrupt package mutations.

This change:

  • Rejects uninstall of mounted Worlds, configured provider modules and the active bot; serializes World removal with activation and removes its runtime contributions.
  • Clears World activation intent on removal while preserving other configuration, and restores it if removal fails. Accepted World IDs are recorded in extensions/world-owners.json, so later import failures retain reliable ownership. Reserved IDs and IDs accepted from another package are excluded from the old owner's cleanup.
  • Withdraws provider modules before package removal, rejects stale endpoint editors and panel requests, and restores registration if removal fails. Endpoint usage is rechecked under the provider write lock.
  • Stops admitting package mutations during shutdown and waits for the active transaction, including rollback. Installation and removal return HTTP 503 after shutdown starts. Supervisors receive cortico:shutdown phases; the forced-exit deadline must begin after waiting-extensions changes to stopping.
  • Keeps per-World environment failures separate from runtime status, so an environment rendering failure does not make a mounted World appear inactive.

Validation: pnpm test --maxWorkers=4 (5,192 passed, 3 skipped), pnpm run typecheck. Tests cover failed-import reinstall, ownership conflicts, failed removal rollback, stale provider editors, both save/removal orderings, and concurrent HTTP installation/shutdown.

Migration: the loader creates the ownership file automatically from accepted exports. A package already broken before this record exists has no recoverable historical ID; cleanup does not infer one from the package name. Uninstall clears only the current deployment's activation intent. In-process coordination does not protect another process sharing the extension directory, nor forced OS termination.

Downstream: Coopanion has local companion changes to honor the shutdown phases and remove its independent 30-second child exit deadline. Its submodule remains unchanged until this PR reaches Cortico main.

Package removal ignored runtime usage and retained activation intent.
World removal now shares the lifecycle queue and preserves configuration
on failure. Tests cover reinstall, rollback, and activation/removal races.
Environment rendering failures preserve the runtime status of every World.

BREAKING CHANGE: uninstall rejects in-use extensions with HTTP 409 and
writes worlds.<id>.enabled=false on successful World removal. Deactivate
before uninstall and explicitly activate after reinstall. Uninstall performs
the configuration migration. The worlds endpoint adds envPromptError;
clients should preserve the returned status when showing that error.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review ✅ Completed 2026-10-31T23:12:09.904068Z 6ea6d22 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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

Vague actions. The PR description says "The lifecycle queue protects the current process."

AGENTS.md §4 AI slop checklist, Vague actions: "State what an operation changes and when it takes effect. Replace phrases such as "touches the disk" or "finishes up" with the actual behavior."

The verb does not say what the queue writes, rejects, or serializes. The checklist's instruction is to replace that phrase with the actual behavior; prefer deletion.

— Cortico PR Gate via Cursor Cloud Agent

Open in Web View Automation 

Sent by Cursor Automation: Cortico PR Gate

Comment thread src/world.ts Resolved
Comment thread docs/extensions.md Resolved
Comment thread src/extensions/strings.ts Resolved
@cursor
cursor Bot requested a review from Phantivia October 31, 2026 23:11
Config options still read startup definitions after inactive World removal. Read the current assembly and return HTTP 409 when activation wins the removal queue. API and lifecycle regressions cover both cases.

BREAKING CHANGE: removed Worlds no longer contribute config options. Clients refresh options after removal; no on-disk migration is required.
Removing a package left its module registered. Withdraw it while the package operation is pending and restore registration on failure. Endpoint validation and captured panel contributions reject stale requests; tests cover both save/removal orderings and rollback.
Import failures lost the ID needed to clear activation, and process shutdown could interrupt package writes. Persist accepted ownership, exclude conflicting IDs, and drain complete transactions before stopping services. Supervisors receive shutdown phases so their deadlines start after the drain. Integration tests cover failed imports, replacement owners, rollback and concurrent HTTP requests.

BREAKING CHANGE: loadExtensions automatically creates extensions/world-owners.json from accepted World exports. Existing packages that already fail import without an ownership record cannot have historical IDs inferred. Shutdown refuses new package mutations with HTTP 503 and waits for active transactions; hosts must honor cortico:shutdown phases before applying forced-exit deadlines.
@kwk1001 kwk1001 changed the title fix(extensions)!: guard uninstall and clear World activation fix(extensions)!: coordinate removal, activation and shutdown Nov 1, 2026

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

Hype and reassurance. The PR description says "so later import failures retain reliable ownership."

AGENTS.md §4 AI slop checklist, Hype and reassurance: "Remove praise for the implementation, subjective modifiers such as "quietly" or "reliably", and unsupported assurances such as "rest assured"."

The checklist's instruction is to remove the subjective modifier; prefer deletion.

The earlier Vague actions finding on "The lifecycle queue protects the current process." no longer applies; that sentence is gone.

— Cortico PR Gate via Cursor Cloud Agent

Open in Web View Automation 

Sent by Cursor Automation: Cortico PR Gate

Comment thread src/boot.ts Resolved
- 生产路径总会传这两项;可选时,手搭的扩展集合与运行状态会退回另一套「归属」「在用」的算法。
- 测试改为显式传入;内建供应商的 enabled 按传入的引用判断来断言。
- 根因:加载时直接 readJsonObject,文件截断、手改或值不是字符串都会抛错,启动失败,报错还写成 config.json 解析失败。
- 现在读不出按空表处理并重写,非字符串值丢掉;写入改为整份写临时文件再 rename,按 retryBusy 退避,失败只记警告。
- 测试覆盖:坏 JSON、非字符串值、读写都失败三种情况下 loadExtensions 照常返回。
- 原来 11 种语言都写「停用后才能卸载」:供应商没有停用这一步,要删除引用它的供应实例;这份部署在用的 bot 包不能卸载。
- zh-Hant、ko 的 shuttingDown 改用文件里其余条目的用词(解除安裝、擴充功能、程序;확장)。
- removeModule 借用的 inUse 写的是「供应实例正被这些部署使用」,列出的其实是引用该供应商的实例名。新增 kindInUse,11 种语言。
- 根因:启动时的记录仍把它记作已加载的供应商,第二次卸载又去撤下已经撤下的模块,providerModule() 抛出未翻译的 Unknown provider module,回 400,包留到重启。
- 本进程卸载过的包跳过撤下,直接删包。测试覆盖卸载、重装、再卸载。
- 根因:回滚用 persist(id, { enabled }),assignDeep 跳过 undefined,原来没写 enabled 的 World 会留下 enabled: false;内存里是合并后的默认值时,又会把默认值写进 config.json。
- 现在分别记下内存与 config.json 里的原值;回滚本身写失败时仍报删包的错。
- 根因:等装卸放在 runShutdown 之前且没有时限,启动器的 35 秒上限也删了。pnpm 卡住,或卸载排在一个 start() 不返回的 World 激活后面时,进程一直不退;等待期间事件照常投递。
- 等待改为 pause 之后的 extensions 步骤,超时记为失败,后面的步骤照常执行;bot.stop() 同样限时。waiting-extensions 在关机开始时发,stopping 在这一步之后发。
- 启动器恢复整体时限:各步上限之和加 2 秒,现为 95 秒。关机阶段的消息名导出为 SHUTDOWN_MESSAGE。
- 测试覆盖:装卸挂住时先暂停投递,60 秒后 extensions 步失败,其余步骤照常完成。
- docs/extensions.md 的「卸载」按现行规则重写,删掉与 providers.md、deployment.md、webui.md 重复的段落,去掉「进程内锁」「此协调」这类没定义的说法。
- worlds.md 把 removeDefinition 挪到写回 enabled 的那段旁边;providers README 补上 removeProviderModule 与 removeModule;owner 头里的 bot.ts、world.ts 标出具体导出。
@leafliber

Copy link
Copy Markdown
Member

先合了一次 main,然后在这个 PR 上补了 8 个提交,把审查里发现的问题直接修掉了:

  • 0a2b368 关机:等装卸改成 pause 之后的 extensions 步骤,限时 60 秒,超时记为失败,后面的步骤照常执行;bot.stop() 同样限时;启动器恢复整体时限,取各步上限之和加 2 秒(95 秒)。原来 pnpm 卡住,或者卸载排在一个 start() 不返回的 World 激活后面时,进程会一直不退。waiting-extensions 仍在关机开始时发,stopping 在等待结束或超时后发,消息名导出为 SHUTDOWN_MESSAGE。
  • ebd9566 world-owners.json 截断、手改或值不是字符串时,按空表处理并重写,不再让启动失败;写入改成一次 rename,按 retryBusy 退避,写不进只记警告。
  • 8eda876、8c782bf 占用提示按 World、供应商、bot 分成三条,11 种语言都改了:供应商要删除引用它的供应实例,在用的 bot 包不能卸载。hub 的 removeModule 改用新的 kindInUse,列出的是供应实例名。
  • 7f416f2 同一进程里卸载后又装回的供应商,再次卸载时不再报 Unknown provider module。
  • d73e0cd removeDefinition 删包失败时,enabled 原来没写就删掉,不再留下 enabled: false,也不会把合并后的默认值写进 config.json。
  • f10fef7 worldOwners、isProviderUsed 改为必填,去掉了退回旧算法的分支。
  • 6ba1a68 文档:「卸载」按现行规则重写,重复的段落只留一处;src/providers/README.md 补上撤下流程。

新增的测试在改动前都会失败。squash 时提交说明我会整理一下,写清供应商 enabled 的含义变了、忙碌时的状态码从 400 改成 409。#590 可以按 waiting-extensions 至多约 62 秒给等待设上限。

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

The earlier Jargon finding on 「进程内锁」 in docs/extensions.md, the Accuracy finding on a single “deactivate” recovery string, and the Vague actions finding on “package mutations have settled” in src/boot.ts no longer apply; those sentences are gone or split by kind.

— Cortico PR Gate via Cursor Cloud Agent

Open in Web View Automation 

Sent by Cursor Automation: Cortico PR Gate

Comment thread src/bot.ts
*/
const SHUTDOWN_BUDGET_MS = {
pause: 2_000,
extensions: 60_000,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A 60-second shutdown wait arrives without a derivation for that value.

CONTRIBUTING.md, "Limits and guards": "The value carries its derivation from the path it protects, naming what produces the load and what drains it."

SHUTDOWN_BUDGET_MS.extensions is 60_000. runShutdown and Bot.stop wait on extensions.close() with this budget. A pnpm that never returns (the new shutdown test) or close() blocked on removeWorld behind a start() that does not return will sit on that wait. The PR description does not mention 60 seconds. Commit 0a2b368 states the hang and the number; it does not say what produces 60 seconds of load or what drains it.

Add the evidence and the derivation to the PR description, or remove the hunk.

— Cortico PR Gate via Cursor Cloud Agent

@leafliber
leafliber merged commit 3ad3a3f into main Nov 2, 2026
4 checks passed
@leafliber
leafliber deleted the codex/extension-uninstall-lifecycle branch November 2, 2026 11:41
leafliber added a commit to Pal-AI-Lab/Coopanion that referenced this pull request Nov 2, 2026
- Pal-AI-Lab/Cortico#361:/api/extensions 中供应商的 enabled 改为有模型连接在用,原来是已加载;运行中的扩展卸载回 409,提示按类型说明;关机时先暂停投递,等正在进行的装卸至多 60 秒;/api/worlds 带 envPromptError。
- tests/console-api.test.ts 锁上 envPromptError:环境提示词渲染失败的 World 仍是 active,envPrompt 为空。
leafliber added a commit to Pal-AI-Lab/Coopanion that referenced this pull request Nov 2, 2026
- 已卸载的条目只当包状态显示,不再沿用旧的 World 运行信息:没有运行控件、实时状态灯和配置入口,标红留在「待生效」直到重启;新安装的条目显示「已安装,待重启」;停用 World 时侧栏入口收起。
- 卸载前检查运行状态,确认后重新读取清单再查一次;运行中的 World 与被模型连接占用的供应商不能删除,提示按类型说明。切回「扩展市场」时重读清单,删掉最后一个连接后删除按钮随之可用。
- World 清单读取失败时保留扩展清单里已知的运行状态;Core 报告单个 World 的 envPromptError 时,原因从状态标签旁打开。
- vendor/cortico 升到 Cortico main 3ad3a3f(Pal-AI-Lab/Cortico#361):供应商的 enabled 改为有模型连接在用,卸载与关机协调,envPromptError。tests/console-api.test.ts 锁上 envPromptError。

Co-authored-by: kwk1001 <kwk1111234@gmail.com>
Co-authored-by: Fiber <leaf@airo.cc>
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.

2 participants