Deferred from PR #474's review (round 2 of 2)
PR #474 closed #471 by refusing an unusable in-band Codex cwd before it
reaches the .hypignore gate (usableInBandCwd: non-blank and path.isAbsolute).
Round-2 review established by execution that an adjacent door to the same
hazard is still open, one layer earlier: the value the predicate sees on the
Codex route is usually not the request's cwd at all, it is a workspace key
substituted for it.
The finding
hypaware-core/plugins-workspace/codex/src/exchange-projector.js:773
const workspacePath = workspacePaths.find((key) => pathsEqual(key, cwd)) ?? workspacePaths[0]
resolveCodexContext (:653) calls selectCodexWorkspace(metadata, <in-band cwd>)
and stamps the result as codexContext.cwd (:722). That value is the first
argument to the gate expression at :122, so when the request's cwd matches no
key in the workspaces turn-metadata map, workspacePaths[0] becomes both the
.hypignore gate input and the row's stamped cwd (LLP 0083, "One resolved
cwd, used twice").
Because workspacePaths[0] is normally an absolute path, usableInBandCwd
accepts it. PR #474's predicate does not and cannot see the problem.
Verified by execution
Real projector, real shared matcher (ignoringResolver), one governing
.hypignore of class ignore at /work/ignored:
workspaces map |
request cwd |
result |
row cwd |
{'/work/ignored/proj':{}} |
/work/ignored/proj |
DROP (correct) |
n/a |
{'/work/ignored/proj':{}} |
/work/clean/real |
DROP |
n/a |
{'/work/clean/proj':{}} |
/work/ignored/real |
RECORDED |
/work/clean/proj |
{'/work/ignored/proj':{}} |
sub |
DROP, no cwd_unusable warn |
n/a |
Rows 2 and 3 are the two halves of #471's defect reached through a different
door: row 2 is a false drop for a session no .hypignore covers, row 3 is
the failure the control exists to prevent, a session whose real directory is
ignored being recorded, with a cwd from an unrelated tree stamped on it.
Row 4 shows the workspace substitution runs ahead of #474's predicate, so the
in-band value never reaches it and no usage_policy_cwd_unusable is logged.
Realistic exposure
The single-workspace case, which is the common one, is usually benign: the one
key is the session root, an ancestor of the real cwd, so the substitution is
ancestor-conservative rather than wrong-tree (a deeper .hypignore under the
cwd is still missed, which is fail-open). The wrong-tree verdicts need either a
multi-root session whose cwd sits under a root that is not workspacePaths[0],
or a body-cwd / metadata-key normalization disagreement (pathsEqual only trims
trailing slashes, so symlinked or otherwise non-identical spellings miss). Narrow,
but the same class as #471 and it survives #474 intact.
Why PR #474 did not fix it
The fallback is deliberate and covered by an existing test
(test/plugins/codex-exchange-projector.test.js, "Codex workspace selection
prefers recorded cwd over first metadata key"). It is also load-bearing for
coverage: on the ChatGPT-subscription route the request often carries no cwd
at all, and workspacePaths[0] is then the only in-band source of one, so simply
deleting the substitution removes real .hypignore coverage rather than fixing
anything.
Fixing it therefore requires deciding what codexContext.cwd means when the
workspace map and the request's cwd disagree, which changes row content
(cwd, attributes.codex.workspace), not only the drop decision. That is the
same reason #466 deferred #471 to #471. PR #474 was at its 2-round review budget
and is deliberately one-line-surgical because exchange-projector.js is rewritten
heavily by held PRs #467 and #462.
The decision to take
Pick one:
- Separate the two roles. Keep
workspacePaths[0] for workspace
enrichment (attributes.codex.workspace, git_remote, git_commit,
has_changes) but let it supply the gate/stamp cwd only when it is not
contradicted by an explicit request cwd. Requires resolveCodexContext to
return the two separately.
- Prefer the request's own
cwd on disagreement. One-expression change at
:722; changes the recorded cwd for the cwd-is-a-subdir-of-the-workspace
case from the workspace root to the subdir, so it needs an LLP 0083 amendment
and probably a test update.
- Accept it and say so. Record in LLP 0083 that on the Codex route the
gate's cwd is workspace-mediated and may be an ancestor of, or a sibling
tree to, the session's actual directory. Round-2 review already added a short
note to LLP 0083's decision list pointing here; option 3 is to make that the
final answer.
Either way this should land after #467 and #462 are resolved, since it is in
the neighbourhood they rewrite.
Related
Deferred from PR #474's review (round 2 of 2)
PR #474 closed #471 by refusing an unusable in-band Codex
cwdbefore itreaches the
.hypignoregate (usableInBandCwd: non-blank andpath.isAbsolute).Round-2 review established by execution that an adjacent door to the same
hazard is still open, one layer earlier: the value the predicate sees on the
Codex route is usually not the request's
cwdat all, it is a workspace keysubstituted for it.
The finding
hypaware-core/plugins-workspace/codex/src/exchange-projector.js:773resolveCodexContext(:653) callsselectCodexWorkspace(metadata, <in-band cwd>)and stamps the result as
codexContext.cwd(:722). That value is the firstargument to the gate expression at
:122, so when the request'scwdmatches nokey in the
workspacesturn-metadata map,workspacePaths[0]becomes both the.hypignoregate input and the row's stampedcwd(LLP 0083, "One resolvedcwd, used twice").Because
workspacePaths[0]is normally an absolute path,usableInBandCwdaccepts it. PR #474's predicate does not and cannot see the problem.
Verified by execution
Real projector, real shared matcher (
ignoringResolver), one governing.hypignoreof classignoreat/work/ignored:workspacesmapcwdcwd{'/work/ignored/proj':{}}/work/ignored/proj{'/work/ignored/proj':{}}/work/clean/real{'/work/clean/proj':{}}/work/ignored/real/work/clean/proj{'/work/ignored/proj':{}}subcwd_unusablewarnRows 2 and 3 are the two halves of #471's defect reached through a different
door: row 2 is a false drop for a session no
.hypignorecovers, row 3 isthe failure the control exists to prevent, a session whose real directory is
ignored being recorded, with a cwd from an unrelated tree stamped on it.
Row 4 shows the workspace substitution runs ahead of #474's predicate, so the
in-band value never reaches it and no
usage_policy_cwd_unusableis logged.Realistic exposure
The single-workspace case, which is the common one, is usually benign: the one
key is the session root, an ancestor of the real cwd, so the substitution is
ancestor-conservative rather than wrong-tree (a deeper
.hypignoreunder thecwd is still missed, which is fail-open). The wrong-tree verdicts need either a
multi-root session whose cwd sits under a root that is not
workspacePaths[0],or a body-cwd / metadata-key normalization disagreement (
pathsEqualonly trimstrailing slashes, so symlinked or otherwise non-identical spellings miss). Narrow,
but the same class as #471 and it survives #474 intact.
Why PR #474 did not fix it
The fallback is deliberate and covered by an existing test
(
test/plugins/codex-exchange-projector.test.js, "Codex workspace selectionprefers recorded cwd over first metadata key"). It is also load-bearing for
coverage: on the ChatGPT-subscription route the request often carries no
cwdat all, and
workspacePaths[0]is then the only in-band source of one, so simplydeleting the substitution removes real
.hypignorecoverage rather than fixinganything.
Fixing it therefore requires deciding what
codexContext.cwdmeans when theworkspace map and the request's
cwddisagree, which changes row content(
cwd,attributes.codex.workspace), not only the drop decision. That is thesame reason #466 deferred #471 to #471. PR #474 was at its 2-round review budget
and is deliberately one-line-surgical because
exchange-projector.jsis rewrittenheavily by held PRs #467 and #462.
The decision to take
Pick one:
workspacePaths[0]for workspaceenrichment (
attributes.codex.workspace,git_remote,git_commit,has_changes) but let it supply the gate/stampcwdonly when it is notcontradicted by an explicit request
cwd. RequiresresolveCodexContexttoreturn the two separately.
cwdon disagreement. One-expression change at:722; changes the recordedcwdfor the cwd-is-a-subdir-of-the-workspacecase from the workspace root to the subdir, so it needs an LLP 0083 amendment
and probably a test update.
gate's
cwdis workspace-mediated and may be an ancestor of, or a siblingtree to, the session's actual directory. Round-2 review already added a short
note to LLP 0083's decision list pointing here; option 3 is to make that the
final answer.
Either way this should land after #467 and #462 are resolved, since it is in
the neighbourhood they rewrite.
Related
#decision, LLP 0049, LLP 0050, LLP 0032#codex-repo-root