Skip to content

Codex live projector: a workspaces turn-metadata header preempts the rollout session_meta.cwd, turning a correct .hypignore drop into a record #480

Description

@philcunliffe

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    neutral:fixDelegate this issue to neutral for an autonomous fix attempt (reproduce -> fix -> PR)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions