Codex live projector: rank the workspace-key guess below the rollout cwd - #515
Conversation
A `workspaces` turn-metadata key was folded into `codexContext.cwd` before the gate expression ran, so it satisfied the first `??` and the rollout was never consulted. On the ChatGPT-subscription route (the route the rollout fallback exists for, LLP 0083) the request states no cwd, `selectCodexWorkspace` substitutes the first declared workspace, and that guess outranked the `session_meta.cwd` Codex itself wrote at session start. Adding a `workspaces` header to an exchange that was correctly dropped by `.hypignore` turned the drop into a record, and stamped the row with a directory from an unrelated tree. Carry the key apart from the in-band value as `workspace_cwd` and rank it last: in-band, rollout, key. The key keeps its enrichment role and remains the gate cwd when no rollout is found, so the subscription route keeps `.hypignore` coverage. It is offered only when the request stated no cwd at all, not merely when what it stated was unusable, so a substitution still never stands in for a stated value (#476 case c). LLP 0083's stated-limit paragraph is trimmed accordingly, and the two doc paragraphs that described the key as arriving folded into the in-band value are brought back in line. Co-Authored-By: Claude <noreply@anthropic.com>
…e check Two review findings on the workspace-key demotion. LLP 0083's #481 limit ("a row recorded where it used to drop") described only the in-band case. Demoting the key below the rollout widens that same over-record to the whole subscription route: an ignored declared workspace now also loses to a clean rollout `cwd`, where it used to decide the verdict and drop. Say so, rather than leaving a privacy limit narrower on paper than in the code. The demotion also moved the workspace key onto its own `usableInBandCwd` call, and nothing covered it: every #471 case states its bad value in-band, so they all exercise the FIRST call. Replacing the third term with a bare `?? codexContext?.workspace_cwd` passed the entire suite while handing a relative key to the matcher, whose first act is `path.resolve` against the daemon's process cwd. Pinned with a relative key and the governing `.hypignore` at exactly that mistaken base; verified it fails on that mutation. Co-Authored-By: Claude <noreply@anthropic.com>
🤖 neutral review round 1 —
|
Conflict: `usableInBandCwd`'s "what this does not reach" JSDoc paragraph in `exchange-projector.js`, rewritten by both sides. Resolved by keeping both: this branch's "it bounds the SHAPE, never the provenance; the ranking at the call site is what keeps a guess from deciding" framing, master's "#476 closed, a request that states a `cwd` no longer reaches here through the key" and its `@ref LLP 0160#corrections-0083`. Master's closing clause "the key still outranks the rollout fallback (#480)" is the thing this branch changes, so it now reads as the ranking the demotion established. #491's behaviour is untouched: `workspaceCoversCwd`/`isEqualOrDescendant` and the narrowed warn-site comment merged clean and stand. Two prose reconciliations where each side's text asserted the other's pre-merge state: - `llp/0083` #481 limit said widening the refusal to the rollout was blocked on a frequency objection "already open". #491 settled that objection with the ancestor test, so the limit now says widening is unblocked but must reuse 0160's predicate rather than a byte comparison. - `llp/0160` §corrections-0083 called #480 "the open ranking question". It is answered here, so it points at 0083 §workspace-key-ranks-last. Co-Authored-By: Claude <noreply@anthropic.com>
Review round 2 - APPROVE, no findingsRound 2 audit of 1. Round 1's two fixes survived the merge
2. Both mutations still redden on the MERGED tree (re-run here, not taken on trust)Run against
I added a third mutation the round-1 set did not cover, since the merge touched
So all three of the guarantees this PR asserts (the key is shape-checked, the 3. #491 survived intact
4. The reconciled prose is accurate, and the
|
🤖 neutral: #513 and #515 both edit
|
# Conflicts: # llp/0083-codex-live-cwd-from-rollout.decision.md
Triage: safe to ship (branch a)Neutral triage rung, PR #515, head Both review rounds are accounted for:
After round 2, the branch merged
Residual findings at head: zero. Nothing is a blocker, and nothing new is |
The defect
resolveCodexContextfolded the selectedworkspaceskey intocodexContext.cwd, so the key satisfied the first??of the gateexpression and
resolveRolloutCwdwas never reached:On the ChatGPT-subscription route the request states no
cwd, soselectCodexWorkspacesubstitutesworkspacePaths[0], a guess about adirectory the session may never have run in. That guess outranked
session_meta.cwd, the line Codex itself writes at session start, which is theexact value the rollout fallback (LLP 0083) exists to supply. Adding a
workspacesheader to an exchange that was correctly.hypignore-droppedturned the drop into a record, and stamped the row with a directory from an
unrelated tree.
Privacy-relevant: an opted-out tree's exchange is persisted.
Reproduced first
test/plugins/codex-rollout-cwd.test.js, new testa workspaces key does not preempt the rollout session_meta.cwd (#480): onegoverning
.hypignore(classignore) at/work/ignoredthrough the realshared matcher, a call-counting
rolloutCwdstub resolving the thread to/work/ignored/real, no in-bandcwd, and aworkspacesmap naming/work/clean/proj.Before the fix:
After:
The fix
The patch shape worked out as decision D1 in PR #477's round-1 review, per
the maintainer's instruction on the issue.
resolveCodexContextreturnscwd: inBandCwdand a siblingworkspace_cwd: inBandCwd ? undefined : workspace?.path.?? usableInBandCwd(codexContext?.workspace_cwd, ctx).So the order is in-band, rollout, key.
Two properties held deliberately:
more the only
cwdthere is, so the subscription route keeps its.hypignorecoverage rather than failing open. Pinned by the pre-existing test "the
workspace key still supplies the gate cwd when the request states none
(Codex live projector: the workspace-key substitution can feed the .hypignore gate a directory the session never ran in #476)", which stays green, and by a new sibling that pins it with a rollout
resolver present but empty.
cwdat all, notmerely when what it stated was unusable. A relative or blank in-band value is
still a statement about where the session ran, and letting the key stand in
for it is exactly the substitution Codex live projector: the workspace-key substitution can feed the .hypignore gate a directory the session never ran in #476 refuses; "a refused workspace
substitution is logged with hashed paths (Codex live projector: the workspace-key substitution can feed the .hypignore gate a directory the session never ran in #476 case c)" pins that and stays
green.
usableInBandCwdis applied to the key too, as a separate call, so the #471shape checks that used to guard it (it arrived folded into the in-band value)
still do. Warn counts are unchanged: the second call receives
undefinedwhenever the first one had a value to refuse.
Docs
llp/0083-codex-live-cwd-from-rollout.decision.md: the stated-limit paragraphnaming this issue is trimmed and the bullet now carries a
{#workspace-key-ranks-last}anchor stating the three-source order, per themaintainer's instruction. Two paragraphs that described the predicate as seeing
the key instead of the request's
cwdare corrected (it now sees both,separately), in the LLP and in
usableInBandCwd's JSDoc. The symlink paragraphis narrowed rather than deleted: a declared symlinked key now loses to the
rollout, whose value came from
getcwd(2)and is canonical, but still reachesthe gate uncanonicalized when no rollout is found (#479).
One limit is newly written down rather than fixed: a key outranked by the
rollout is discarded silently, because
usage_policy_workspace_cwd_refusedcompares the key against a value therequest stated. Widening it would put a warn on every turn of an ordinary
session whose
session_meta.cwdis a subdirectory of its workspace, which isthe frequency objection already open against that signal (PR #477 F4/D4), so the
two want settling together.
Checks
npm test: 3081 pass, baseline onorigin/masteris 3078 pass, so +3 andnothing regressed. The 8 failures are pre-existing and unrelated (the
leaveorg-attach block); verified identical with the change stashed.
Overlap
Scoped tightly to the precedence bug.
fix/issue-478is editingusableInBandCwd's body in parallel; this change only edits that function'sJSDoc and its call sites, so the two should reconcile cleanly.
Fixes #480