Repository navigation
fix(OMN-17800): the privilege-dropped co-install stops inheriting root's CWD - #3165
Conversation
…t's CWD `runuser -u <owner> -- env HOME=...` changes UID, GID and HOME. It does not change the inherited working directory. Under the .201 root reconcile cron that directory is /root at mode 0700, so the provider co-install became the operator while still standing in root's home, and uv -- which discovers its configuration by walking UP from the working directory -- refused with error: failed to open file `/root/uv.toml`: Permission denied (os error 13) on 67 consecutive hourly ticks. The co-install is the first step of the venv repair and forces the lock pass after it, so one unreadable directory took out venv:omnibase-infra, venv:omnibase-core and venv:omnimarket together -- the exact three DID_NOT_MOVE surfaces in the live receipt. It was the only as_owner-wrapped package operation in the file without a `cd`; lines 406, 589 and 629 were already `(cd "$project" && as_owner ...)`. It is also the second defect on that line, after OMN-17383 fixed the inherited PATH there and left the inherited directory. $OMNI_HOME rather than $INFRA_DIR: this is a `uv pip install`, and from inside the project uv would newly discover pyproject.toml's [tool.uv] override-dependencies -- the layer beneath, which this install exists not to re-resolve. The workspace root carries no uv config on either host. Second defect, found while proving the first: reconcile-host.sh wrote the receipt's array separator as sep=",\n". Bash does not interpret \n in a plain double-quoted assignment, and printf does not interpret escapes in a %s ARGUMENT, so every receipt this script has produced on BOTH hosts carried a literal backslash-n and failed json.loads. The existing receipt test missed it because its workspace yields one surface, and a separator is untested until something is separated. check_reconciler_privilege.py gains Part 7, failing the build on any as_owner-wrapped uv sync or co-install that does not set a working directory. CLAUDE.md rule 5: the gate ships with the fix it protects. Red first: 6 failed, 2 passed against origin/dev@4053dc3c0, including the receipt's JSONDecodeError at the same line-8 signature as the live .201 file. Green after: 154 passed across all 13 reconciler test files. Evidence-Ticket: OMN-17800
There was a problem hiding this comment.
Hostile Reviewer — adversarial findings (OMN-17492)
Models succeeded: glm-review
Models failed: codex
New finding threads: 0
Deduped (already posted on this PR): 0
Nit-level findings suppressed: 2
The model is the FINDER, never the gate: merge is gated only by the
deterministic Hostile Review Thread Gate, which blocks while
hostile-reviewer threads are unresolved. Resolve each thread after
addressing (or rejecting, with a reply) its finding.
Findings not anchored to a changed file
-
[MAJOR] hostile-reviewer (glm-review)
Gate detector keys on fragile substrings, not on uv package operations | The new Part 7 check decides whether an as_owner-wrapped line needs a working directory using
_UV_INVOCATION.search(...) and " sync" in executableand_INSTALL_INVOCATION.search(...) and "--execute" in executable. These are substring tests against the joined logical line, not a parse of the uv subcommand. A futureuv sync --frozenvariant matches, butuv pip installwithout--execute, auv pip sync, or an install script invResolve this thread when addressed — the
Hostile Review Thread Gateblocks while hostile-reviewer threads are unresolved (OMN-17492). -
[MAJOR] hostile-reviewer (glm-review)
Any occurrence of 'cd' in the line satisfies the working-directory rule |
_WORKDIR_SET = re.compile(r"(?<![\w.$-])cd\s")is searched against the whole joined executable line. A line containingcdanywhere (in a quoted string argument, in a dead branch such asx && cd y || as_owner ..., or in a comment that survives joining) passes the check even when the privilege-dropped invocation itself never runs inside a chosen directory. The gate therefore provides weaker protection than the narrative claims, anResolve this thread when addressed — the
Hostile Review Thread Gateblocks while hostile-reviewer threads are unresolved (OMN-17492). -
[MINOR] hostile-reviewer (glm-review)
Co-install CWD chosen to OMNI_HOME relies on an unverifiable invariant | The fix places the dropped-privilege child in
$OMNI_HOMEon the argument that 'the workspace root carries no uv configuration on any host'. Nothing in the diff enforces or tests that invariant; if a uv.toml or pyproject.toml later appears in OMNI_HOME, the install silently changes resolution behavior again, exactly the failure mode this change exists to prevent. The gate also accepts anycdtarget, so a future drift to$INFRA_DIRResolve this thread when addressed — the
Hostile Review Thread Gateblocks while hostile-reviewer threads are unresolved (OMN-17492). -
[MINOR] hostile-reviewer (glm-review)
Backslash assertion will false-positive on legitimately escaped content |
assert "\\n" not in rawrejects any receipt whose JSON encoding contains the two-character sequence backslash-n, which json.dumps produces for any detail string containing a newline or a backslash followed by n (e.g. a Windows-style path or a verbatim error message). The receipt is now valid JSON, and JSON escapes are the normal mechanism; asserting their absence conflates the historical bug with valid encoding. | Evidence: `assertResolve this thread when addressed — the
Hostile Review Thread Gateblocks while hostile-reviewer threads are unresolved (OMN-17492). -
[MINOR] hostile-reviewer (glm-review)
Mutation test asserts gate message routed to stderr without verifying the contract |
test_the_gate_rejects_...asserts'without setting a working directory' in result.stderr. The gate's main() output stream is not asserted anywhere in this diff; if a refactor moves failure reporting to stdout, the test fails for the wrong reason and the diagnosis cost lands on whoever refactors. The test also depends on the exact wording of a user-facing message, coupling CI to prose. | Evidence: `assert result.returncoResolve this thread when addressed — the
Hostile Review Thread Gateblocks while hostile-reviewer threads are unresolved (OMN-17492). -
[MINOR] hostile-reviewer (glm-review)
Tests import private helpers from other test modules | The new test module pulls
Workspace,_run,_stub,_make_clone,_advance_origin,_lock,_make_uv_shim, and_Workspacefrom two sibling test files. This makes three modules refactor-locked together; renaming a helper in the OMN-17307 test breaks this file, and pytest collection order/caching assumptions now span modules. The underscore-prefixed names were never a public contract. | Evidence: `from tests.scripts.test_reconcile_host_omn17307Resolve this thread when addressed — the
Hostile Review Thread Gateblocks while hostile-reviewer threads are unresolved (OMN-17492).
|
| Surface | Meaning | Blocks merge? |
|---|---|---|
| Review threads | Per-finding, posted by the reviewer | No (informational) |
Hostile Review Thread Gate |
Deterministic: unresolved hostile-reviewer threads exist | Fails until resolved (not yet a required context) |
degraded verdict |
Fewer than 2 models succeeded (infra) | No |
Powered by omniintelligence.review_pairing.cli_review — multi-model adversarial review: qwen3-review, qwen3-review-b, glm-review (OMN-8468/OMN-8524/OMN-17492)
#8157) * evidence: OCC companion pass 1 for OmniNode-ai/omnibase_infra#3165 * evidence: OCC companion self-bind for #8157 --------- Co-authored-by: node-occ-companion-effect <occ-companion-effect@omninode.ai>
OMN-17800 — the privilege-dropped co-install stops inheriting root's CWD
What
scripts/reconcile-host.sh/scripts/reconcile-workspace-venvs.shrun the workspaceco-install after dropping privileges from root. The dropped child kept root's working
directory (
CWD=/root), souvwalked up from/rootand tried to read/root/uv.toml,which the unprivileged user cannot open — the reconcile step failed with a permission
denial that had nothing to do with the workspace being reconciled.
This change makes the privilege-dropped co-install enter the workspace directory itself
rather than inheriting the caller's CWD, and adds
scripts/check_reconciler_privilege.pyplus
tests/.../test_reconcile_cwd_and_receipt_omn17800.pyso the invariant is assertedrather than assumed.
Why
The failure is deterministic under the root-cron reconcile path: root's CWD is
/root,/root/uv.tomlis0600 root:root, and the dropped user gets EACCES on every run. Thereceipt JSON emitted by the reconcile step recorded the failure without naming the cause,
so the same denial had to be re-diagnosed each time.
d2771b641tests/ci/test_reconcile_cwd_and_receipt_omn17800.py(355 lines added) asserts theco-install CWD and the receipt-JSON shape.
Evidence-Ticket: OMN-17800
Evidence-Source: OCC#8157