Codex live projector: a workspaces turn-metadata header preempts the rollout session_meta.cwd, so adding one turns a correct .hypignore drop into a record
Found in PR #477's round-1 review (finding F1, decision D1) and re-confirmed by
execution in round 2. Pre-existing on master: PR #477 neither creates nor
widens it. Filed so it does not live only in an LLP paragraph.
The defect
hypaware-core/plugins-workspace/codex/src/exchange-projector.js:130-131:
const cwd = firstString(codexContext?.cwd, readRecordedCwd(reqBody))
?? (codexContext?.session_id ? rolloutCwd?.resolve(codexContext.session_id) : undefined)
resolveCodexContext folds the selected workspaces key into
codexContext.cwd (:723) before this expression runs, so a present
workspace key satisfies the left side of the ?? and the rollout is never
consulted. When no key matches the request's cwd, selectCodexWorkspace
(:797) substitutes workspacePaths[0], a guess. So on the
ChatGPT-subscription route, the exact route LLP 0083's rollout fallback exists
for, a first-key guess outranks the authoritative session_meta.cwd that Codex
itself wrote at session start.
Verified by execution
Real projector, real shared matcher, one governing .hypignore of class
ignore at /work/ignored, an injected rolloutCwd whose resolve counts its
calls, session_id present so the fallback is genuinely eligible:
| # |
workspaces |
in-band cwd |
rollout session_meta.cwd |
verdict |
rollout consulted |
| 1 |
{/work/clean/proj} |
none |
/work/ignored/real |
RECORDED cwd=/work/clean/proj |
false |
| 2 |
none |
none |
/work/ignored/real |
DROP |
true |
Rows 1 and 2 differ in nothing but the presence of a workspaces header.
Adding one to an otherwise correctly-dropped exchange turns the drop into a
record, and stamps the row with a directory from an unrelated tree.
Byte-identical on origin/master (1555f13), on PR #477's head (0db67e1), and
on the two merged. That is the confirmation that it is pre-existing: PR #477
fixes the case where the request states an explicit cwd, and this is the one
remaining door, where it states none.
The fix, as far as round 1 worked it out
Rank the guess below the rollout, so the order becomes in-band, rollout,
key:
:723 becomes cwd: inBandCwd (drop the firstString(..., workspace?.path)).
- Add a sibling
workspace_cwd: workspace?.path so the key is still carried.
- Add a third
?? codexContext?.workspace_cwd term to the gate expression.
The workspace key must keep supplying a cwd when there is no rollout either,
or the subscription route loses .hypignore coverage outright: the existing test
"the workspace key still supplies the gate cwd when the request states none
(#476)" pins that and must stay green.
Sequencing
The defect is on master and fixable there, so this is not blocked. But
:723 and :130-131 are the exact lines PR #477 rewrites, PR #474 also
rewrites, and PR #462 touches, so whoever takes this should sequence after those
land rather than open a fourth concurrent edit of the same expression. Also
relevant: #479 (the matcher never canonicalizes) is why pathsEqual misses
symlinked spellings in the first place.
Recorded as a stated limit in
llp/0083-codex-live-cwd-from-rollout.decision.md (PR #477, commit 0db67e1);
that paragraph should be trimmed if this is fixed.
Codex live projector: a
workspacesturn-metadata header preempts the rolloutsession_meta.cwd, so adding one turns a correct.hypignoredrop into a recordFound in PR #477's round-1 review (finding F1, decision D1) and re-confirmed by
execution in round 2. Pre-existing on
master: PR #477 neither creates norwidens it. Filed so it does not live only in an LLP paragraph.
The defect
hypaware-core/plugins-workspace/codex/src/exchange-projector.js:130-131:resolveCodexContextfolds the selectedworkspaceskey intocodexContext.cwd(:723) before this expression runs, so a presentworkspace key satisfies the left side of the
??and the rollout is neverconsulted. When no key matches the request's
cwd,selectCodexWorkspace(
:797) substitutesworkspacePaths[0], a guess. So on theChatGPT-subscription route, the exact route LLP 0083's rollout fallback exists
for, a first-key guess outranks the authoritative
session_meta.cwdthat Codexitself wrote at session start.
Verified by execution
Real projector, real shared matcher, one governing
.hypignoreof classignoreat/work/ignored, an injectedrolloutCwdwhoseresolvecounts itscalls,
session_idpresent so the fallback is genuinely eligible:workspacescwdsession_meta.cwd{/work/clean/proj}/work/ignored/realcwd=/work/clean/proj/work/ignored/realRows 1 and 2 differ in nothing but the presence of a
workspacesheader.Adding one to an otherwise correctly-dropped exchange turns the drop into a
record, and stamps the row with a directory from an unrelated tree.
Byte-identical on
origin/master(1555f13), on PR #477's head (0db67e1), andon the two merged. That is the confirmation that it is pre-existing: PR #477
fixes the case where the request states an explicit
cwd, and this is the oneremaining door, where it states none.
The fix, as far as round 1 worked it out
Rank the guess below the rollout, so the order becomes in-band, rollout,
key:
:723becomescwd: inBandCwd(drop thefirstString(..., workspace?.path)).workspace_cwd: workspace?.pathso the key is still carried.?? codexContext?.workspace_cwdterm to the gate expression.The workspace key must keep supplying a
cwdwhen there is no rollout either,or the subscription route loses
.hypignorecoverage outright: the existing test"the workspace key still supplies the gate cwd when the request states none
(#476)" pins that and must stay green.
Sequencing
The defect is on
masterand fixable there, so this is not blocked. But:723and:130-131are the exact lines PR #477 rewrites, PR #474 alsorewrites, and PR #462 touches, so whoever takes this should sequence after those
land rather than open a fourth concurrent edit of the same expression. Also
relevant: #479 (the matcher never canonicalizes) is why
pathsEqualmissessymlinked spellings in the first place.
Recorded as a stated limit in
llp/0083-codex-live-cwd-from-rollout.decision.md(PR #477, commit 0db67e1);that paragraph should be trimmed if this is fixed.