Skip to content

Retain pinned hook bundles across plugin cache updates - #106

Open
adit-chandra wants to merge 3 commits into
mainfrom
adit-chandra/codex/retained-hook-bundles
Open

adit-chandra wants to merge 3 commits into
mainfrom
adit-chandra/codex/retained-hook-bundles

Conversation

@adit-chandra

@adit-chandra adit-chandra commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Nonblocking failure handling keeps the agent usable, but a running session should continue executing the hook version it loaded even after an update removes that plugin cache directory.

Summary

Pin each generated portable hook command to a manifest covering its adapter, configuration, helper files and other bundle contents. Prepare a private copy at SessionStart (without executing authored code), or on first invocation. Validate and atomically publish complete bundles into a durable store outside host plugin caches; concurrent invocations share only the exact verified digest.

Loaded commands retain their behavior across upgrades, rollback and same-version replacement. Corrupt or unavailable bundles skip nonblockingly with a deduplicated diagnostic. There is no latest-version fallback or automatic eviction: safe cleanup needs lifetime information for idle/resumable sessions. Dependencies must be bundled and resolved relative to __file__; writable hook state belongs elsewhere.

Stack: depends on #105. #107 separates installation from session activation in the update workflow. This change covers portable declarations, not authored native JSON or the managed trace runtime.

Validation

  • Focused hook suite: 72 passed, including complete-bundle retention after cache deletion, concurrent installation, interrupted staging, tampering, rollback, same-version replacement and signal delivery.
  • Generated launcher exercised with Python 3.9 locally; CI now includes that smoke test.
  • Repository-wide Ruff lint/format and ty passed.
  • Full suite: 1,066 passed. The 72 focused hook tests also passed after incorporating the base PR's isolated Python imports.
  • These are generated-command subprocess tests, not live desktop-host acceptance or a marketplace release.

@adit-chandra
adit-chandra marked this pull request as ready for review October 23, 2026 01:06
adit-chandra added a commit that referenced this pull request Oct 23, 2026
## Motivation

When a marketplace update removes a plugin version still referenced by a
running session, Python exits with code 2 before the hook adapter
starts. Codex interprets that infrastructure failure as an intentional
hook block, potentially hiding the result of a command that already ran.

## Summary

Embed a Python launcher in portable hook commands so it survives
deletion of plugin files. Portable context hooks now report bounded
failure diagnostics and exit successfully; native JSON hooks retain
their authored blocking semantics. Execution remains in-process so
cancellation reaches handlers.

Stack: this PR targets `main`; #106 adds retained runtime bundles on
top, and #107 separates installation from current-session activation.

## Validation

- Full suite: 1,053 passed.
- Focused hook suite: 59 passed, covering deleted adapters, entrypoints
and helpers across Claude, Codex and Cursor; script exit 2; and native
blocking behavior.
- Repository-wide Ruff lint/format and ty passed (using the worktree
environment).
- Generated commands exercised in local subprocesses; live desktop-host
acceptance and marketplace publication are not part of this PR.
Base automatically changed from adit-chandra/codex/hook-failure-isolation to main October 23, 2026 01:08

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

Codex Bug Bot

Found one regression: valid relative entrypoint spellings can cause portable hooks to be skipped on every host.

1 inline comment(s) below.

"schema": 1,
"host": target,
"hook": asset.id,
"entrypoint": f"{asset.id}/{spec.entrypoint}",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Medium] Normalize the entrypoint before storing it in the manifest. HookDefinition accepts paths such as ./handler.py and sub//handler.py, which previously executed successfully, but the manifest file keys are normalized by pathlib while this field preserves the original spelling. Consequently, read_manifest rejects these valid bundles as "incomplete hook manifest", and both preparation and ordinary invocations silently skip the hook on all targets. Construct this field with (Path(asset.id) / spec.entrypoint).as_posix() and add coverage for these accepted spellings.

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