Codex plugin installation (experimental); shell hooks load the vault - #22
Merged
Merged
Conversation
Render the commands, agents and skills into adapters/codex/ as 30 namespaced skills plus hooks.json, shipped in the projectstore-codex shell with canonical and compatibility plugin manifests. The installer stages a marketplace under CODEX_HOME, runs Codex's CLI and verifies the cache by version and digest. Maintainer: [email protected]
The host's PLUGIN_ROOT names the shell's root, and pluginRoot() took it for the core's. In a shell the core sits beneath it, in node_modules/projectstore/, so the first Codex install's hooks, run by hand the way Codex runs them, said "vault load failed — Layout not found" in every session; in the first it sat beneath the welcome, which was all the user saw. No test had run a rendered hook from a built tree. - pluginRoot(): a host root that CONTAINS the running core is a shell, and the core answers from its own location. A root equal to the core or unrelated to it is returned as named, so Claude Code is unchanged. Compared as real paths - A shells test builds each hook-rendering shell from the packed core and fires every rendered hook through sh -c: three SessionStarts must orient on the bound vault, and a PreCompact must name the story just written. It fails with the parent's harness.mjs. A resolver test pins contained, equal, unrelated, prefix-sibling and symlinked roots - Codex is experimental again: verified is null, with the reason beside it — the 2026-09-30 run exercised one skill and not the hooks. Both READMEs and the harness page say so, and the page claims only what was measured - The install command reads the version from package.json instead of naming rc.2's tarball, and a test refuses a versioned tarball name in any shipped document - 516 tests pass (from 514) Closes F1 and F2 of the 2026-10-03 review in "The Codex shell's plugin root: .codex-plugin, the rendered surfaces, and the first Codex install" (PS-HARNESS). The rest of that review, and the live run that alone sets verified, stay open there. Maintainer: [email protected]
The previous commit's correction to docs/harnesses.md made a new claim: that
all 759 captured firings came from the inline form. The spike recorded 757
inline firings across SessionStart, UserPromptSubmit, PreToolUse, PostToolUse
and Stop, plus 2 from a file-site hooks/hooks.json. PreCompact has never been
observed firing on Codex, and neither has the ${PLUGIN_ROOT} form selected
through extensions.com.openai.
- docs/harnesses.md says that, and says the hand-run probe used zsh -lc:
that machine's default shell, started the way Codex's runner starts a
hook on its main branch (<default shell> -lc), unconfirmed on 0.153.4
- verified_reason names where the bar is defined (docs/harnesses.md) and
what derives the label from it (contract 16 of the generation spec); it
drops the validator claim, which F5 of the 2026-10-03 review in "The
Codex shell's plugin root: .codex-plugin, the rendered surfaces, and the
first Codex install" (PS-HARNESS) has yet to substantiate, and names what
a live run must exercise before the block is set
- The built-shell hook test starts each hook in a temporary HOME, so only
the payload's cwd can name the project
Maintainer: [email protected]
One session's hooks run concurrently: parallel tool calls each fire PreToolUse. writeSessionState truncated the pointer file in place, so two writers left the shorter JSON followed by the longer one's tail. CI run 37116511756 failed on exactly that: "Unexpected non-whitespace character after JSON at position 139". While the file is torn, the status line reports the session state unreadable. readSessionState answers null, so the next write keeps only its own patch, and the rest of the pointer is lost for good. - writeSessionState and writeAnchorOffer publish through writeFileAtomic (a temp file, then a rename), so a reader gets the old bytes or the new ones, never an empty or torn file - The concurrency test now parses the pointer while 24 writers race, with a positive control. Against the old write it failed 9 of 15 local runs; with the fix it fails none - A deterministic test asserts that both files are new inodes after a rewrite, which an in-place write cannot satisfy - The comments that kept counters out of writeSessionState now give the reason that still holds: an unlocked read-modify-write loses concurrent updates, and the last rename wins Maintainer: [email protected]
layout-legacy named the package's shell for every install. Run for a git-marketplace user, the shell registered projectstore@projectstore-npm and turned the git copy off for the checkout, so /plugin update stopped reaching it. The maintainer decided on 2026-10-03 that the move keeps the user's channel; the normative text is contracts 7 and 12 of "The project-level layout: .projectstore/, harness overlays, state, and the migration from .claude/", as amended that day. - layoutRemedy names the move in the channel of the copy the project runs: the session's host-cache root, else this project's newest present enabled registry copy (own row first, never another checkout's; a 0.27.x copy is told to update first), else the running root. installChannel compares real paths. agents-in-binding names the same command - Both remedy forms pass the new --no-register (plan, install, upgrade), so the move calls no host command by construction. A relocated host home rides along as a command prefix - A registration the run leaves out sets the render root at its own row in the plan loop; read after the loop, it changed no item - While the move is pending, the startup line offers the move alone, and the re-stamp offer needs our status-line wiring. layout-legacy names every legacy file that keeps it alive, the welcome marker included - The move's cleanup removes a legacy launcher when the status-line slot is foreign or empty, and keeps one any settings file the host reads still runs, compared by file identity - S1: publicItem strips private fields per step kind, so plan --json again shows the layout steps' from and files - S2: portablePayloadRoot keeps doctor from reporting "<core> is not a portable plugin root" in a project that merely contains .codex/; a shell naming a root that is not one marks the plan incomplete - README "Upgrading", commands/doctor.md and update_instructions give both forms; the README adds the way back, the agents block's placement, the projectstore- skill prefix and what uninstall leaves in AGENTS.md - 530 tests pass (from 517) Closes S1 and S2 of the 2026-10-03 review in "The Codex shell's plugin root: .codex-plugin, the rendered surfaces, and the first Codex install" (PS-HARNESS), which the maintainer set to land before the rc.3 tag. Maintainer: [email protected]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands the Codex plugin installation, still experimental, together with the two fixes its review required before it could land.
Commits
feat(codex): add portable plugin installation: Codex's work, carried ontomainwith its tree unchanged (byte-identical to18ff8be). It renders the 30 namespaced skills andhooks.jsonintoadapters/codex/and ships them in the privateprojectstore-codexshell, with canonical and compatibility plugin manifests. Its installer stages a marketplace underCODEX_HOME, drives Codex's CLI, and verifies the cache by version and digest.fix(codex): let shell hooks load the vault; relabel Codex experimental: closes F1 and F2 of the 2026-10-03 review.F2: the shell's hooks could not load the vault
Measured on the real install's cache (
codex-cli 0.153.4, the dev build0.28.0-rc.2+codex.dev.15c346cc0791). The installed SessionStart hook was run the way Codex runs it, withzsh -lcandPLUGIN_ROOTset to the shell root. It failed withvault load failed — Layout not foundin every session. In the first session the failure sat beneath the welcome, which was all the user saw.The cause:
pluginRoot()took the host'sPLUGIN_ROOT, which is the shell root, for the core root, while the core sits beneath it innode_modules/projectstore/.The fix: if a host root contains the running core,
pluginRoot()treats that root as a shell and resolves from the core's own location. A root that equals the core, or is unrelated to it, is returned as before, so the Claude Code path is unchanged; the reviewer compared hook outputs against the parent commit.The new test builds every shell that renders hooks from the packed core and fires every rendered hook through
sh -c:It fails on the parent commit and passes now. A unit test covers the resolver's cases: a root that contains the core, one equal to it, an unrelated one, a sibling with the same prefix, a trailing separator, and a symlink.
F1: Codex was labelled supported
harnesses/codex.jsonhasverified: nullagain, with averified_reason: the 2026-09-30 run exercised one skill, not the hooks.docs/harnesses.md, the README tagline, the README's Codex and shells paragraphs, and the shell's README now all say experimental.package.jsonat run time instead of naming rc.2's tarball. A test refuses a versioned tarball name in any shipped document.Verification
npm run release:checkpasses: adapters, guard, shells, 516/516 tests (514 before), andnpm pack(149 files).["projectstore-claude"]. The Codex shell stays private.Still open
These are tracked in the vault story The Codex shell's plugin root: .codex-plugin, the rendered surfaces, and the first Codex install (PS-HARNESS):
verified.