Skip to content

CRW-178 후속 · 캐시 교체 때 실행 중 작업의 브리지·스킬 경로 실측 반영 - #148

Merged
thisisjun786 merged 4 commits into
devfrom
codex/crw-178-swap-measured-docs
Sep 23, 2026
Merged

thisisjun786 merged 4 commits into
devfrom
codex/crw-178-swap-measured-docs

Conversation

@thisisjun786

@thisisjun786 thisisjun786 commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Change

CRW-178 (#92) left two rows of the cache-lifetime table in docs/plugin-packaging.md labelled as inference. One package replacement on a real host has since measured both, and one of them was wrong:

  • MCP start cwd/args. Before: "a server already running survives … a restart inside that session is expected to fail" (inferred). Measured: about a minute after codex plugin add, the App Server started twelve new bridge processes from the new version directory without restarting itself, and each ran under the bridge record as it stood then. The record was still version 1, so those bridges check no role pair, and ten were still running that way an hour and a half later. That the add caused the restart stays labelled as inferred from timing.
  • Skill reads. Before: "a read against the removed directory is expected to fail" (inferred). Measured: a turn in progress keeps the removed skills root it was given until it ends, compaction included, and the thread's next turn is given the new root. No thread tried to read the removed directory, and the docs say that read was not observed.

Does the policy-free restart recur at the next replacement? From source and an isolated run: not while the record is version 2, its policy file is unchanged, and the new package's declared server reads version 2 as the current launcher does. The launcher finds the Codex home six directories above its own file and reads crw-bridge-mcp.json there; no record field ties it to a version directory or a payload. In an isolated Codex home (real codex-cli 0.154.0 plugin add of one payload, register-mcp writing version 2, then a different payload added over it, which removed the first directory), the new directory's declared launcher, started with only the App Server's six environment variables, handed the bridge the recorded policy: get_capabilities reported allowlist with the recorded digest, and a register-mcp rerun answered record_unchanged. A version-1 record reproduces the host case (presence_only, no roles). No record, a changed policy file, and a rollback to a launcher older than version 2 each exit 2 and start no bridge. The two payloads shipped identical launcher bytes, so a candidate whose declared server differs is outside that measurement, which is why the procedure now probes every candidate before adding it.

Docs only; nothing under plugins/crw/, scripts/ or packages/ changes, so the payload and its version are unchanged.

  • docs/plugin-packaging.md: the two rows; a new subsection, "What one replacement measured"; the supported-range table (column "MCP restart" becomes "MCP bridge") and the paragraph after it; and "Updating safely", rewritten as the operator order for a payload-changing update. The order runs: dry-run register-mcp against the current record; place the Stop fallback; probe the candidate in a throwaway Codex home (register-mcp --codex-home <throwaway> without --apply starts the candidate's declared server with a version-2 record, and the add proceeds only on record_would_create with one cached version declaring codex-thread-bridge); add and re-trust; read back with the dry run, get_capabilities, and a /proc reading of every running bridge's directory and policy digest. It then covers what tasks loaded before the replacement keep, and the first policy registration: a version-1 record cannot take a policy in place, so the choice is when it moves aside, with each order's measured and unmeasured cost. The sentences in "Turning the wired surfaces on", "Adding a skill" and "Update and roll back" that the measurement refines are updated, and a rollback note says an older launcher refuses a version-2 record.
  • docs/runtime-install.md: the registration-order paragraph now says what a bridge started before the registration runs under, for each record state. A new paragraph states the replacement behaviour and the recurrence answer.
  • docs/plugin-transition.md: ## Update points to the payload-update order.

Verification

Local, on this head:

  • python3 scripts/ci/validate.py: ok. It reads local link paths in the three docs, not fragments.
  • A fragment check of the three docs against GitHub heading slugs: 22 links, 0 missing, and a planted bad anchor was caught.
  • git diff --check origin/dev...HEAD is clean, and git diff --name-only origin/dev...HEAD -- plugins/crw scripts packages is empty.
  • python3 scripts/ci/plugin.py: payload 0.4.0+1ed13de2edbb, 218 files, unchanged.
  • python3 scripts/runtime_install.py verify-definition: ok: true.
  • The /proc snippet in step 6 runs as printed (read-only) on the measured host and lists each crw bridge with its version directory and policy state.

The isolated measurement ran in a bwrap sandbox where only the scratch run was writable. It used real codex plugin marketplace add/remove and plugin add, register-mcp from this checkout, the bridge from packages/codex-thread-bridge, and a stub that answers App Server initialize and nothing else. The pre-add candidate probe was measured the same way: a current-launcher candidate answered record_would_create, the 0.4.0 candidate answered launcher_predates_policy, and neither left a record. Receipts are kept with the task record, outside this repository.

Hosted CI and the independent review are reported in the PR conversation, against the exact head each one read.

Risks and remaining work

  • The host restarting bridges at codex plugin add was measured once, on one host. The isolated run shows what a restarted bridge reads, not whether or when the host restarts it.
  • Not measured, and stated as such: what a tool call in flight across the restart sees, what a read of the removed skills root does, what the host does after a bridge refuses to start, and what disabling the package does to loaded threads.
  • Bridges already running without the policy on the measured host stay that way until the host starts them again. Nothing in this repository can do that.

Devin Review

…running tasks

The cache-lifetime table labelled two host-owned rows as inference. One package replacement on a real host measured both: the App Server started bridges again from the new version directory, each under the bridge record as it stood then, and a turn in progress kept the removed skills root until it ended while the next turn was given the new one. The rows, the supported-range table and the prose around them now say so, with measured and inferred kept apart.

An isolated Codex home answers whether the policy-free restart recurs: the record carries no version directory, so a new directory launcher reads the same version-2 record and starts the bridge under its policy; a version-1 record reproduces the host case, and no record, a changed policy file or a pre-version-2 launcher fail closed.

Updating safely now gives the order for a payload-changing update, including a pre-add probe of the candidate package and the first policy registration, and runtime-install and plugin-transition point to it. Docs only; nothing under plugins/crw changes.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

devin-ai-integration[bot]

This comment was marked as resolved.

Review of #148: the hook rerun that refreshes the Stop fallback needs --dest (or --relay-command), as the settings builder refuses without one. Also: step 2 sends a version-1 host to step 8 before step 5, step 5 names the directory to check, the /proc reading says it only sees processes it may read, and the registration order notes the measured replacement needed no restart.
Devin review of #148: the /proc reading matched only the crw marketplace, while the procedure installs crw@<marketplace>, and it skipped bridge processes it could not read. It now parses <marketplace>/crw/<version> under the plugin cache and lists an unreadable bridge process as UNREADABLE. The measured-replacement subsection names the App Server version.
…he payload readback

Review of #148: one process started after the record was written exited before its policy state could be read, so the claim covers the bridges whose state was read. Step 6 now says which directories check-declaration compares.
@thisisjun786

Copy link
Copy Markdown
Owner Author

Merge-readiness handoff for head a68c535249cfa00dcba100c67b0a002e0126660d (base dev 3dde0cfb; GitHub reports the merge state as CLEAN).

  • Hosted CI. CRW CI run 35869274355 (attempt 1, pull_request event) ran on this head. validate, secrets, tests (3.10), tests (3.13), packages (3.11), packages (3.13) and dev-gate succeeded. release-gate was skipped because this is not a main promotion.
  • Devin Review. Devin opened two threads on 3c395c5c, and both are answered and resolved. The marketplace-bound /proc filter was fixed in 4d73ac06. For the note about narrow evidence, the docs already label the restart as measured once, on one host, and cross-version measurement is reported to the issue owner as remaining work. Devin's analysis of this head finished with no new threads.
  • Independent review (fresh context). PASS on this head: no blockers and no reader stumbles. Earlier fresh reviews of 3c395c5c and ff61f02f found a missing --dest in the fallback rerun, the marketplace-bound /proc filter, and a post-registration claim broader than the host evidence. Those were fixed in ff61f02f, 4d73ac06 and a68c5352.
  • Local checks on this head. scripts/ci/validate.py passes. All 22 fragment links in the three docs resolve, and a planted broken anchor is caught. git diff --check is clean. Nothing under plugins/crw, scripts or packages changed: scripts/ci/plugin.py reports the payload unchanged as 0.4.0+1ed13de2edbb (218 files), and verify-definition reports ok: true.
  • GitHub Codex automatic review is disabled for this repository and is not a gate, so its usage-limit notice above is not a finding.

@thisisjun786
thisisjun786 merged commit 57090da into dev Sep 23, 2026
9 checks passed
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