Skip to content

chore(review): gate Xcode-CLT stub paths (REVIEW.md lesson 7 + machine check) - #2474

Merged
john-the-dev merged 13 commits into
sonichi:mainfrom
marklysze:chore/review-clt-shim-check
Aug 3, 2026
Merged

john-the-dev merged 13 commits into
sonichi:mainfrom
marklysze:chore/review-clt-shim-check

Conversation

@marklysze

@marklysze marklysze commented Aug 1, 2026 •

Copy link
Copy Markdown
Collaborator

Important

Merge order: land this AFTER #2469 and #2473.

Those two PRs add the resolver constants that name the stub in order to guard
against it (SYSTEM_GIT = "/usr/bin/git", systemPython = "/usr/bin/python3").
This gate flags them as added lines, so merging it first turns both of them
red. Once they land, those lines are no longer added in any diff and the gate
is clean — both directions verified, evidence in the "Stacked PR?" section at
the bottom.

What changed, and why?

Two live bugs were just fixed where code invoked a developer tool at an absolute /usr/bin/ path (#2469, #2473). This adds the lesson and the machine check so the class does not come back.

On macOS /usr/bin/{git,python3,swift,swiftc,clang,gcc,make} are not those tools. They are one inode, hardlinked 78 ways, holding the Xcode-CLT stub:

$ ls -l /usr/bin/git /usr/bin/python3 /usr/bin/swiftc /usr/bin/clang /usr/bin/gcc /usr/bin/make
-rwxr-xr-x  78 root  wheel  118928 May  1 05:33 /usr/bin/git
-rwxr-xr-x  78 root  wheel  118928 May  1 05:33 /usr/bin/python3
-rwxr-xr-x  78 root  wheel  118928 May  1 05:33 /usr/bin/swiftc
-rwxr-xr-x  78 root  wheel  118928 May  1 05:33 /usr/bin/clang
-rwxr-xr-x  78 root  wheel  118928 May  1 05:33 /usr/bin/gcc
-rwxr-xr-x  78 root  wheel  118928 May  1 05:33 /usr/bin/make

The file exists whether or not the tools are installed. Invoking it without them raises a modal "install command line developer tools" dialog and returns nothing.

Why this is its own lesson and not a case of lesson 6

Lesson 6 covers machine- or user-specific path literals. Three things make this different, and all three are why it slipped past review originally:

  1. The path is not machine-specific. /usr/bin/git exists on every Mac, so it reads as the portable choice. Lesson 6's framing does not catch it, and the flag list did not contain it.
  2. Every existence probe passes against the stub. test -x, command -v, shutil.which, FileManager.fileExists all succeed with no toolchain installed — so the obvious guard is not a guard. fix(app): the Swift app's python fallback raises the Xcode CLT install dialog (from-source installs) #2473 contained a command -v swift check that fell straight through for exactly this reason.
  3. An absolute path cannot be shadowed. A user who installs a real git or python via Homebrew still gets the dialog, because PATH never gets a say.

Plus the failure mode is unusually bad on polling paths: the caller in #2473 was a 60-second Timer, so the dialog returned every minute indefinitely.

What should reviewers look at first?

  1. REVIEW.md lesson 7 (new; the verdict lesson renumbers 7 → 8). Prose only — no behavior.
  2. The checks: block — the six added patterns. Note /usr/bin/swift covers /usr/bin/swiftc by prefix; the test asserts that so the implicit coverage stays honest.
  3. What is deliberately absent: env, pgrep, lsof, osascript, open, id, pmset, xcode-select. Those are real binaries with their own inodes (link count 1), and main.swift addresses several absolutely on purpose. Flagging them would make the gate unusable and it would be turned off.

What user behavior / bug does this prove?

It does not change runtime behavior — it gates the diff. The check catches the exact shapes that shipped:

$ printf '...+    subprocess.run(["/usr/bin/git", "log"])\n' | bash scripts/review-checks.sh
src/x.py:1: hardcoded path (/usr/bin/git): subprocess.run(["/usr/bin/git", "log"])

While designing it I ran the patterns over the current tree and found two more live instances that neither #2469 nor #2473 covers:

skills/zoom/tools.ts:404:  execSync(`/usr/bin/python3 -c "…`)
src/meeting-tools.ts:68:   execFileSync('/usr/bin/python3', ['-c', `…`])

Both spawn the stub on a clean Mac. Fixed separately in the TypeScript follow-up (new src/python-binary.ts), which is what surfaced the JSDoc gap addressed in the second commit below. That PR passes this gate cleanly, so it carries no merge-order dependency on this one.

Second commit: JSDoc continuation lines (ade6f5f)

Found while wiring the TypeScript resolver against this gate. The scanner skipped # and // lines but had no equivalent for block-comment continuation lines, so prose inside a /** … */ block was scanned as code:

src/x.ts:2: hardcoded path (/usr/bin/python3):
    * On macOS `/usr/bin/python3` is the CLT stub, not python.

Pre-existing — a JSDoc line naming /Users/… was always flagged — but it becomes acute with the stub paths added here. The modules that resolve these tools necessarily document the hazard in JSDoc, so every such module would trip its own gate and the check would get switched off within a week.

Now skips * text, a bare *, and */. Deliberately does not skip:

Shape Skipped? Why
* text (JSDoc body) yes prose
*/, bare * yes prose
/* inline */ const x = "…" no a comment must not smuggle a path onto a code line
*gen() { … } (generator method) no no space after the star — it is code

Verified against all four:

$ printf '…' | bash scripts/review-checks.sh
src/x.ts:5: hardcoded path (/usr/bin/python3): const real = "/usr/bin/python3";
src/x.ts:6: hardcoded path (/usr/bin/git): /* inline */ const sneaky = "/usr/bin/git";
src/x.ts:7: hardcoded path (/usr/bin/make): *gen() { return "/usr/bin/make"; }

Lines 1–4 (the JSDoc block) are correctly silent. Three cases added to tests/review-checks-py.test.py (26 checks, all pass); diff coverage on the change is 100% (2 lines).

Feature / documentation consistency

REVIEW.md is the canonical document for review criteria, and it is what this PR updates. Per CLAUDE.md — "Review criteria live in REVIEW.md (single source of truth). Don't duplicate the 7 lessons here… Adding or editing a lesson is a PR to REVIEW.md only" — CLAUDE.md and AGENTS.md are deliberately not touched. AGENTS.md is generated from CLAUDE.md by scripts/agents-md-sync.sh in any case, so it is never hand-edited.

No docs/catalog.json entry covers the review guide.

Before / after evidence

Test added: tests/review-checks-clt-shims.test.sh (auto-discovered by CI's find tests -name '*.test.sh'). It runs the real scripts/review-checks.sh against the real REVIEW.md, so it covers guide parsing → flag list → scanner, not the scanner in isolation.

BEFORE — test present, REVIEW.md restored to origin/main:

$ git checkout origin/main -- REVIEW.md
$ bash tests/review-checks-clt-shims.test.sh
FAIL flags /usr/bin/git in executable code
FAIL flags /usr/bin/python3 in executable code
FAIL flags /usr/bin/swift in executable code
FAIL flags /usr/bin/swiftc in executable code
FAIL flags /usr/bin/clang in executable code
FAIL flags /usr/bin/gcc in executable code
FAIL flags /usr/bin/make in executable code
ok   comment mentions of a stub path are not flagged
ok   non-stub /usr/bin binaries are not flagged
FAIL REVIEW.md checks: block lists every stub pattern

passed=2 failed=8

AFTER — at HEAD:

$ bash tests/review-checks-clt-shims.test.sh
ok   flags /usr/bin/git in executable code
ok   flags /usr/bin/python3 in executable code
ok   flags /usr/bin/swift in executable code
ok   flags /usr/bin/swiftc in executable code
ok   flags /usr/bin/clang in executable code
ok   flags /usr/bin/gcc in executable code
ok   flags /usr/bin/make in executable code
ok   comment mentions of a stub path are not flagged
ok   non-stub /usr/bin binaries are not flagged
ok   REVIEW.md checks: block lists every stub pattern

passed=10 failed=0

The two ok lines that pass in both states are the negative cases — they must not regress when the patterns are added, which is the actual risk of a change like this.

The false-positive risk, checked explicitly. This repo discusses these paths constantly in prose (src/dashboard.py:77, src/telegram-bridge.py:9, src/agent-api.py:299, src/install-*-launchd.sh, …). A check that flagged comments would be disabled within a week. The scanner already skips #, // and docstring lines (scripts/review-checks.py), and the test pins it:

+    # startup.sh launches this via bare python3, which is /usr/bin/python3 (3.9)
+    // where neither /usr/bin/python3 nor a brewed python had PyYAML
→ rc=0, not flagged

Tests run

Command Result
bash tests/review-checks-clt-shims.test.sh 10 passed (8 failed at parent commit)
python3 tests/review-checks-py.test.py 26 checks pass (3 new, for the JSDoc fix)
diff-cover --compare-branch=origin/main --fail-under=95 100% (2 changed lines in review-checks.py)
bash tests/review-checks.test.sh PASS — no regression
bash tests/review-pr.test.sh PASS — no regression
shellcheck tests/review-checks-clt-shims.test.sh clean

The three neighbouring suites matter here: review-checks.test.sh and review-pr.test.sh both run against the real REVIEW.md, so a malformed checks: block would break them.

Hardcoded-path check

Adds no path literals to production code. The new patterns live in REVIEW.md (a .md file, which the scanner skips by SKIP) and the test fixtures live under tests/, also skipped. The gate passes over this PR's own diff.

Stacked PR? — yes, merge order matters

Land this after #2469 and #2473.

Those PRs introduce the two resolver constants that name the stub in order to guard against it, and the new gate flags them as added lines:

$ git diff origin/main fix/git-binary-resolution | bash scripts/review-checks.sh
src/git_binary.py:47: hardcoded path (/usr/bin/git): SYSTEM_GIT = "/usr/bin/git"

$ git diff origin/main fix/app-python-resolution | bash scripts/review-checks.sh
src/Sutando/SutandoConfig.swift:309: hardcoded path (/usr/bin/python3): static let systemPython = "/usr/bin/python3"

Once they merge, those lines are no longer added in any diff and the gate is clean — verified against a subsequent-PR shape:

$ printf '...+    argv = git_argv("log", "-1")\n' | bash scripts/review-checks.sh
review-checks: PASS (hardcoded-paths clean)

I chose merge ordering over a scoped allow deliberately. A token-level allow for /usr/bin/git would match at token start and defeat the check entirely, and a whole-file exemption for the resolver modules would violate CLAUDE.md's "keep fixture exclusions token-specific so they cannot hide another real path on the same line". A contextual paired-allow is what #2369 is building; if a future PR legitimately needs to name a stub path in code, that is the mechanism it should use rather than widening allow here.

If a reviewer would rather not carry a merge-order dependency, the alternative is to land this first and have #2469 / #2473 add a scoped allow — but that means shipping the exemption mechanism before the thing it exempts, which seems worse.

On macOS /usr/bin/{git,python3,swift,swiftc,clang,gcc,make} are not those
tools — they are one inode hardlinked as the Xcode-CLT stub. The file exists
whether or not the developer tools are installed; invoking it without them
raises a modal "install command line developer tools" dialog and returns
nothing.

Three properties make this its own lesson rather than a case of lesson 6
(hardcoded absolute paths). The path is NOT machine-specific — it exists on
every Mac, so it reads as portable. Every existence probe passes against the
stub (test -x, command -v, shutil.which, FileManager.fileExists), so the
obvious guard does not work. And an absolute path cannot be shadowed by a real
install on PATH, so a user who installs git or python still gets the dialog.

Adds lesson 7 (renumbering the verdict lesson to 8) and the six stub paths to
the machine-readable checks: block, so scripts/review-checks.sh gates every PR.
'/usr/bin/swift' covers '/usr/bin/swiftc' by prefix. Non-stub /usr/bin binaries
(env, pgrep, lsof, osascript, open, id, pmset, xcode-select) are deliberately
absent — they are real binaries the codebase addresses absolutely on purpose.

The scanner already skips # and // comment lines, so the many existing comments
discussing /usr/bin/python3 are unaffected. tests/review-checks-clt-shims.test.sh
pins that, plus every stub being caught in executable code and every non-stub
staying clean, by running the real runner against the real REVIEW.md.

MERGE ORDER: land after sonichi#2469 and sonichi#2473. Those PRs introduce the resolver
constants this gate is designed to make unnecessary (SYSTEM_GIT = "/usr/bin/git",
systemPython = "/usr/bin/python3"), and the gate flags them as added lines. Once
merged they are no longer in any diff and the gate is clean — verified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Gate

✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 74%.

Diff Coverage

Diff: origin/main...HEAD, staged and unstaged changes

  • scripts/review-checks.py (98.8%): Missing lines 248

Summary

  • Total: 82 lines
  • Missing: 1 line
  • Coverage: 98%

scripts/review-checks.py

Lines 244-252

  244     j = start + 1
  245     while j < len(all_lines):
  246         nxt = all_lines[j]
  247         if nxt.startswith("@@ ") or nxt.startswith("+++ ") or nxt.startswith("diff "):
! 248             break
  249         if nxt.startswith(" ") or nxt.startswith("+"):
  250             body.append(nxt[1:])
  251         j += 1
  252     return body

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head f1e56978. The regression class exists on current main and the guide-only layer is correct. Reviewer-run new suite passes 10/10; the three adjacent review-check suites pass (16 shell, 23 Python, and review-pr), and the stated stub positives/comment/non-stub controls behave as documented.

[P1 machine-gate false positive] /usr/bin/swift is a substring pattern, not an exact executable token. The body intentionally uses it to cover swiftc, but on this current macOS host /usr/bin/swift-inspect is a separate real binary (inode 1152921500312572861, link count 1), unlike /usr/bin/swift/swiftc (shared inode 1152921500312571562, link count 78). An executable line naming it is nevertheless rejected:

+proc.executableURL = URL(fileURLWithPath: "/usr/bin/swift-inspect")
review-checks: FAIL ... hardcoded path (/usr/bin/swift-inspect)
rc=1

This contradicts the PR's narrow-stub claim and its explicit goal of preserving safe absolute /usr/bin tools. Add a negative control for the real swift-inspect binary and scope the pattern/allow so swift and swiftc remain blocked without blocking swift-* real tools (a token-start allow for /usr/bin/swift- is one minimal option under the current scanner semantics). The same substring property also makes /usr/bin/make reject /usr/bin/makeinfo; even though that binary is absent on this host, a robust fix should avoid prefix-family false positives rather than special-case only one current filename.

Worst case is a mandatory review gate rejecting legitimate platform-tool invocations, encouraging broad exemptions or disabling the check—the exact usability failure the PR says its negative controls prevent. The merge-order dependency on #2469 then #2473 remains correctly documented, and clean-install is still running.

Verdict: changes requested; not merge-ready on this head.

Found while wiring the TS-side resolver against this gate. The scanner skipped
`#` and `//` comment lines but had no equivalent for block-comment continuation
lines, so prose inside a /** … */ block was scanned as code:

  src/x.ts:2: hardcoded path (/usr/bin/python3):
      * On macOS `/usr/bin/python3` is the CLT stub, not python.

Pre-existing (a JSDoc line naming /Users/… was always flagged), but it becomes
acute with the stub paths added in this PR: the modules that RESOLVE these tools
necessarily document the hazard in JSDoc, so every such module would trip its
own gate and the check would get switched off.

Skips ` * text`, a bare ` *`, and ` */`. Deliberately does NOT skip an opening
`/*` — a `/* … */ code` one-liner must stay scannable so a comment cannot smuggle
a path onto a code line — nor a generator method `*name()`, which has no space
after the star. Requiring whitespace, end-of-line, or `/` after the star is what
separates them; both shapes are pinned by tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed exact head ade6f5fc. The original P1 remains: the current gate still rejects the real link-count-1 /usr/bin/swift-inspect binary (review-checks: FAIL, rc=1), and the new head does not add the required prefix-family negative control or scoping.

The JSDoc follow-up adds a second P1 scanner bypass. _BLOCK_COMMENT_CONT = r"^\*(\s|$|/)" skips every stripped executable continuation line beginning * , not only lines inside a block comment. This is valid JavaScript/TypeScript multiplication syntax:

const n = 2
  * "/usr/bin/python3".length;

Reviewer control: Node executes it and prints 32, while the exact-head review scanner reports PASS / rc=0 for the two-line added diff. The new tests cover a generator method but not the ordinary multiplication operator. Please track actual /* ... */ comment state across context/addition lines (with the same fail-closed care used for Python docstrings), or otherwise constrain the skip so standalone executable * lines remain scanned; add this multiplication control.

Reviewer-run declared suites still pass: CLT gate 10/10, runner 16/16, scanner 26/26, and review-pr. That green state does not discriminate either failure. Worst case is a mandatory security/review gate that both rejects safe platform binaries and can be bypassed by valid executable formatting.

Verdict: changes requested; not merge-ready on this head. Coverage and clean-install are also rerunning, and the documented #2469 → #2473 → #2474 merge order remains.

Addresses @john-the-dev's P1 on this PR: '/usr/bin/swift' as a SUBSTRING also
rejects '/usr/bin/swift-inspect', which is a separate real binary (its own
inode, link count 1) — unlike /usr/bin/swift and /usr/bin/swiftc, which share
the stub inode with 76 other names. Same shape for /usr/bin/make vs makeinfo.
A gate that blocks legitimate platform tools gets disabled rather than fixed,
which is the exact usability failure this PR's negative controls exist to
prevent.

Adds a `flag_exact:` list to the checks block, matched against the WHOLE
extracted path token rather than as a substring, and moves the stub paths there.
`flag` keeps its substring semantics — '/Users/' must still match
'/Users/alice/deep/path' — so existing behaviour is untouched (pinned by a new
control).

swiftc no longer rides on the '/usr/bin/swift' prefix; it is named explicitly,
as are clang++, g++, cc and gnumake, which the prefix never covered. The list is
the subset of the 78-name family this repo plausibly invokes; lesson 7 now
records how to re-derive the full family by inode.

Negative controls added for swift-inspect, swift-frontend, makeinfo and gcc-14.
makeinfo is absent on the current host, so it is pinned as a synthetic control
rather than left to whatever happens to be installed.

Tests: shim suite 10 -> 15, scanner unit suite 26 -> 30.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@marklysze

Copy link
Copy Markdown
Collaborator Author

Both findings addressed. ef1eb24 is the fix for your P1; ade6f5f is a second false-positive class I hit while wiring #2475 against this gate.

@john-the-dev P1 — /usr/bin/swift is a substring, not a token. Confirmed on my host too, and your framing was the deciding argument: a mandatory gate that rejects a legitimate platform tool gets disabled, which is worse than not having it.

$ ls -li /usr/bin/swift /usr/bin/swift-inspect
1152921500312571586 -rwxr-xr-x 78 root wheel  118928 /usr/bin/swift          ← stub, 78 links
1152921500312572883 -rwxr-xr-x  1 root wheel 1942624 /usr/bin/swift-inspect  ← real, own inode

I took the general fix rather than the swift- allow, because you were right that special-casing one filename does not survive the next make/makeinfo-shaped case. New flag_exact: list, matched against the whole extracted token; flag keeps substring semantics (pinned by a control, so /Users/ still matches /Users/alice/deep/path). swiftc no longer rides on a prefix — it is named explicitly, as are clang++, g++, cc, gnumake, which the prefix never covered anyway.

$ bash scripts/review-checks.sh --diff /tmp/t2.diff
src/x.swift:2: hardcoded path (/usr/bin/swift):   b = "/usr/bin/swift"
src/x.swift:3: hardcoded path (/usr/bin/swiftc):  c = "/usr/bin/swiftc"
src/x.swift:5: hardcoded path (/usr/bin/make):    e = "/usr/bin/make"
src/x.swift:6: hardcoded path (/usr/bin/clang++): f = "/usr/bin/clang++"
# NOT flagged: swift-inspect, makeinfo, pgrep

Negative controls added for swift-inspect, swift-frontend, makeinfo, gcc-14. makeinfo is absent on this host, so it is pinned as a synthetic control rather than left to whatever happens to be installed. Lesson 7 now records how to re-derive the full 78-name family by inode, and states the converse explicitly — a longer name in the family is often a real binary.

Second finding (mine): JSDoc prose was scanned as code. The scanner skipped # and // but had no equivalent for * continuation lines, so any module documenting these paths tripped its own gate — which every resolver module necessarily does. Pre-existing (a JSDoc line naming /Users/… was always flagged), but acute with these patterns. Skips * text, *, */; deliberately does not skip /* … */ code one-liners or *gen() generator methods, both pinned.

Suites: shim gate 10 → 15, scanner unit 23 → 30. Diff coverage on review-checks.py 100%.

One note on the shellcheck SC2295 info at review-checks.sh:77 — pre-existing on main (verified against origin/main), on a line this PR does not touch.


Cross-PR map (same root cause — please read before reviewing in isolation)

On macOS one inode is hardlinked 78 ways under /usr/bin as the Xcode-CLT stub — git, python3, swift, swiftc, clang, cc, make, gnumake, … (ls -li /usr/bin | awk '$3==78'). Four PRs address it on four surfaces. They are independent (disjoint files, each branched from main) but they are one bug, and reviewing them one at a time has already produced duplicated asks:

PR Surface Files
#2469 git, Python health-check.py, agent-api.py, new src/git_binary.py
#2473 python3, Swift Sutando.app — main.swift, SutandoConfig.swift
#2474 the CI gate REVIEW.md lesson 7 + review-checks.{sh,py}
#2475 python3, TypeScript skills/zoom/tools.ts, src/meeting-tools.ts, new src/python-binary.ts

Merge order: #2469 and #2473 before #2474 (the gate flags the resolver constants they add, as added lines; once merged those lines are in no diff and it is clean — verified both ways). #2475 is exempt: its tier 3 uses a bare python3, so it contains no stub literal.

The clean-VM ask is one session, not three

#2469, #2473 and #2475 have each been asked for separate clean-no-CLT-host evidence. It is the same host and the same boot: launch the packaged app once on a toolchain-free VM and all three are observable in that single run — GET /activity returning commits (or degrading), runHealthCheck: no runnable python3 … — skipping in the app log, and zero CLT prompts across ≥2 Timer intervals. Splitting it triples the work for one observation.

#2459 is the missing context

#2459 is closed but not merged, and it matters to all four:

  1. It carries the clean-VM evidence being requested here. @vidhuUC ran a pristine Tart VM (macOS 26.5, no CLT, xcode-select -p errors, empty TCC) against a built DMG and reported 0 CLT prompts at boot/onboarding and across a 20s descriptor-poll window. That is the methodology these PRs should be measured against — and reproduced on, since it predates all four.
  2. It was closed on a strategic question, not a technical one. Quoting @vidhuUC: "the git calls this guards belong to sutando's git-native OSS deployment, and we want to properly understand whether agent-side git/dev capability is intended for the packaged AG2 Space app before deciding between guarding the calls vs. bundling git/gh." That decision governs all four PRs. They all take the guard branch. If the answer is "bundle git/gh", fix(health-check,agent-api): resolve git instead of hardcoding the Xcode-CLT shim #2469 and fix(ts): resolve python instead of hardcoding the Xcode-CLT stub (zoom, meeting-tools) #2475 become much smaller (the resolvers stay useful; the degradation paths mostly stop firing). Worth settling before merging four PRs that assume one answer.
  3. src/startup.sh is still unguarded on main. fix: stop the 'git requires Command Line Tools' dialog on fresh Macs (two boot-path callers) #2459's own fix — the xcode-select -p guard around the committer.* self-heal at startup.sh:264-265 — went with it. That is the original boot-path trigger and none of these four PRs touch it.

What these four do NOT cover

@john-the-dev's inventory on #2459 is still live. Counting executable lines on current origin/main:

File Bare/absolute git Covered?
src/health-check.py 3 call sites ✅ #2469 (all three, incl. check_live_checkout_branch)
src/agent-api.py 1 ✅ #2469
src/startup.sh 2 ❌ — was #2459
scripts/sutando-config.sh (runtime) 1 block, 5 spawns ❌ — boot path for the packaged app
src/dashboard.py 2 ❌
src/daily-insight.py 2 ❌
src/util_paths.py 1 ❌ — imported by 23 modules
src/session-handoff.sh 1 ❌

I am not expanding these PRs to cover the rest — single concern — but reviewers should know the class is not closed when these merge, and that the two boot-path callers (startup.sh, sutando-config.sh runtime) are the ones the original reporter actually hit.

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking (current head ef1eb249ac677fbba452ed9c4b88082328dddf34):

The exact-token change fixes the earlier /usr/bin/swift-inspect / makeinfo false positive, and the added suites pass (CLT 15/15, runner 16/16, Python 30/30, review-pr PASS). The remaining scanner bypass from my prior review is unchanged, though.

This is valid executable JavaScript:

const n = 2
  * "/usr/bin/python3".length;
console.log(n); // 32

At this head, feeding that two-line addition to scripts/review-checks.py with RC_FLAGS_EXACT=/usr/bin/python3 produces no finding and exits 0. The cause remains _BLOCK_COMMENT_CONT = re.compile(r"^\\*(\\s|$|/)") plus the unconditional _BLOCK_COMMENT_CONT.match(stripped) skip: any added code line beginning with multiplication * is treated as JSDoc prose even outside a block comment.

Please make JSDoc continuation suppression stateful (only while actually inside a block comment), and add this exact executable multiplication form as a regression that must be flagged.

… bypass)

@john-the-dev's second finding on this PR, and it is a bypass rather than a
false negative on prose. The previous commit skipped any line whose stripped
form began with `* `, on the theory that only JSDoc bodies look like that. This
is also valid JavaScript:

    const n = 2
      * "/usr/bin/python3".length;

so the path went unflagged. Reproduced on the prior head: that two-line addition
scanned clean and exited 0.

Now tracks the real `/* … */` span, so a line is exempt because it IS inside a
comment, never because of how it happens to start. Consequences, all pinned by
tests:

  - continued multiplication  -> FLAGGED (was the bypass)
  - JSDoc body in a real span -> exempt
  - `/* … */ code` one-liner  -> FLAGGED (opens and closes on the line, so the
                                 code after it is outside the block)
  - `*gen()` generator method -> FLAGGED
  - code after the block closes -> FLAGGED

Block state resets per file and per hunk and advances on context lines, matching
the existing docstring tracker. It shares that tracker's known limitation — a
`/*` inside a string literal moves the state — which errs toward scanning.

Scanner unit suite 30 -> 33.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@marklysze

Copy link
Copy Markdown
Collaborator Author

@john-the-dev — fixed on b351b1d. You were right to call this a bypass rather than a prose false-negative; that distinction is what made the previous fix inadequate rather than incomplete.

Reproduced your exact case on the prior head first:

$ printf '…+const n = 2\n+  * "/usr/bin/python3".length;\n' | bash scripts/review-checks.sh
review-checks: PASS (hardcoded-paths clean)      ← the bypass

The shape-based rule was the error: * at line start is not evidence of a comment. Suppression is now stateful — it tracks the real /* … */ span, so a line is exempt because it is inside a comment, never because of how it happens to start. Block state resets per file and per hunk and advances on context lines, mirroring the existing docstring tracker.

Same head, after:

src/x.js:2: hardcoded path (/usr/bin/python3): * "/usr/bin/python3".length;

Five behaviours pinned by tests, including your case:

Shape Result
continued multiplication FLAGGED (was the bypass)
JSDoc body inside a real /* */ span exempt
/* … */ code one-liner FLAGGED
*gen() generator method FLAGGED
code after the block closes FLAGGED

It inherits the docstring tracker's known limitation — a /* inside a string literal moves the state — which errs toward scanning, i.e. the safe direction. Documented in the function rather than left implicit.

Scanner unit suite 30 → 33; CLT gate 15/15; runner and review-pr suites pass; ruff clean; diff coverage 100% (26 lines).

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed exact head b351b1db8ea0858ff7ac676033d440bec7af5bc9. The original executable multiplication bypass is fixed: the exact two-line control is now flagged, while the declared CLT 15/15, runner 16/16, scanner 33/33, review-pr, diff, and path checks pass. (ruff was not available in this review worktree; current-head CI ruff is green.)

[P1 mandatory-gate false positive] Block-comment state resets at every hunk, so a legitimate added JSDoc line is still scanned as code whenever the opening /* is outside the diff context. scripts/review-checks.py explicitly sets in_block = False on each @@, and _block_transition() cannot infer that an unchanged * ... context line is already inside a block. Exact-head repro against the real runner:

diff --git a/src/x.js b/src/x.js
--- a/src/x.js
+++ b/src/x.js
@@ -20,3 +20,4 @@
  * earlier prose
+ * On macOS /usr/bin/python3 is a CLT stub.
  */
  export function x() {}
$ <that-diff> | bash scripts/review-checks.sh
review-checks: FAIL — hardcoded-paths:
src/x.js:21: hardcoded path (/usr/bin/python3): * On macOS /usr/bin/python3 is a CLT stub.

That is the normal unified-diff shape when editing a long JSDoc body more than three lines below its opener—the exact resolver documentation this PR says must remain exempt. An added opening line such as +/** ... /usr/bin/python3 ... is also flagged because only was_in_block, not a comment portion entered on the current line, is exempt.

Please make suppression reflect actual comment spans even when the opener is outside hunk context (or otherwise conservatively recognize a hunk-local body that closes with */) and add the long-JSDoc hunk above as a negative regression. Preserve the now-correct multiplication positive control.

Worst case is the mandatory review gate rejecting legitimate resolver documentation, recreating the broad-exemption/disable pressure this PR is intended to prevent.

Verdict: changes requested; not merge-ready on this head. The documented merge order (#2469, then #2473, then #2474) also remains in force, and current-head clean-install is still running.

@sonichi

sonichi commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Cold review at head b351b1db, with a positive control rather than a read-through. The gate works for the form it names, and I want to flag exactly where its edge is — because the sibling PR #2475 shows the other form occurring ten times in production.

The check fires correctly (verified, not assumed)

Fed the scanner a two-line synthetic diff:

$ printf '%s\n' '+++ b/src/foo.ts' \
    '+  spawn("python3", [script]);' \
    '+  execFileSync("/usr/bin/python3", [x]);' | bash scripts/review-checks.sh

review-checks: FAIL — hardcoded-paths:
src/foo.ts:1: hardcoded path (/usr/bin/python3): execFileSync("/usr/bin/python3", [x]);
review-checks: 1 violation(s).

One violation, naming the explicit path. So the flag_exact mechanism does what it says, and the whole-token rule is the right call — I agree with the reasoning in the REVIEW.md comment that a substring rule rejecting /usr/bin/swift-inspect (a real binary, its own inode) is how a mandatory gate gets disabled.

The edge: spawn("python3", …) is not flagged, and on macOS it is the same binary

The bare form passed silently in that same run. It is not a lesser risk:

$ which python3 ; readlink -f "$(which python3)"
/usr/bin/python3
/usr/bin/python3
$ ls -l /usr/bin/python3
-rwxr-xr-x  78 root  wheel  118880  /usr/bin/python3

PATH hands a bare python3 the identical 78-link stub inode this PR is built to keep out. And it is not hypothetical — sweeping .ts/.tsx at #2475's head turns up ten production call sites using it, including src/task-delegation.ts:56 (runs on every task) and skills/phone-conversation/scripts/conversation-server.ts:431 (every phone task).

So with both #2474 and #2475 merged, the class is still open through the PATH door.

What I am not proposing

Adding python3 to flag_exact would be wrong, by this PR's own logic. A bare interpreter name is legitimate in plenty of contexts — inside a venv, under a controlled PATH, in a shell script — and #2475's own module docstring accepts PATH resolution when developer tools are present. Blanket-flagging it would produce exactly the false-positive pressure the swift-inspect note warns about.

The useful change is smaller: say in lesson 7 where the gate stops. A reviewer seeing review-checks.sh pass will reasonably conclude the CLT-stub class is clear for that diff, and for the PATH form it is not. One sentence — "this gate catches the explicit /usr/bin/… form; a bare python3/git resolved through PATH hits the same stub and is out of scope, so check call sites by hand" — converts a silent boundary into a stated one.

Housekeeping

BEHIND — needs a refresh onto current main (948e837d) before it can go in.

What I did not verify

I ran the scanner against a synthetic diff, not against the repo's real diff surface, and I did not run tests/review-checks-clt-shims.test.sh or tests/review-checks-py.test.py (read them only). review-preflight.py is not present on this host — swept <repo>, ~/.sutando/workspace, ~/.sutando-skills-sync, and git ls-tree origin/main, zero hits — so this review did not run it.

Cross-ref: my review on #2475 covers the ten call sites and the fact that its own guard scans only the two files it fixes.

…tion

@john-the-dev's P1: a legitimate JSDoc body was still scanned as code whenever
the `/**` opener sat outside the hunk — which is the NORMAL diff shape, since
unified diffs carry three context lines and doc bodies are usually longer. Two
distinct causes, both reproduced on the prior head:

  src/x.js:21: hardcoded path (/usr/bin/python3): * On macOS /usr/bin/python3 …
  src/y.js:1:  hardcoded path (/usr/bin/python3): /** helper for /usr/bin/python3 …

1. Block state reset to False at every `@@`. It is now INFERRED from the hunk's
   own content: a `*/` appearing before any `/*` can only happen if a comment
   was opened above the hunk. The inference is one-directional — no closer, or
   an opener first, means "assume code" — so a hunk containing neither
   delimiter (the multiplication bypass) is still scanned.

2. Suppression was line-level (`was_in_block`), so a path in the comment portion
   of the line that OPENS the comment was flagged: the state BEFORE that line is
   False. Comment regions are now masked by POSITION, blanked with spaces so
   column indices — and therefore the reported token — stay aligned with the
   real source. Code after a same-line `*/` is still scanned, so a comment
   cannot smuggle a path onto a code line.

Four regressions added, including both repros and a control asserting the
inference does not resurrect the bypass. Scanner unit suite 33 -> 37; ruff
clean; diff coverage 97%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@marklysze

Copy link
Copy Markdown
Collaborator Author

@john-the-dev — fixed on d326d7e. Reproduced both shapes on the prior head first:

src/x.js:21: hardcoded path (/usr/bin/python3): * On macOS /usr/bin/python3 is a CLT stub.
src/y.js:1:  hardcoded path (/usr/bin/python3): /** helper for /usr/bin/python3 resolution

You identified two distinct causes and both were real:

1. Hunk reset. Block state is now inferred from the hunk's own content rather than reset to False: a */ appearing before any /* can only happen if a comment was opened above the hunk. Deliberately one-directional — no closer, or an opener first, means "assume code" — so a hunk containing neither delimiter (your multiplication case) is still scanned. That is the property I was most careful not to trade away, and there is now an explicit control asserting the inference does not resurrect the bypass.

2. Line-level suppression. Your second observation was the sharper one: for +/** … /usr/bin/python3 … the state before the line is False, so no line-level test can exempt it. Comment regions are now masked by position — blanked with spaces so column indices, and therefore the reported token, stay aligned with the real source. Code after a same-line */ is still scanned, so a comment cannot smuggle a path onto a code line.

Same head, after:

$ bash scripts/review-checks.sh --diff /tmp/jsdoc-hunk.diff   →  PASS (clean)
$ bash scripts/review-checks.sh --diff /tmp/jsdoc-open.diff   →  PASS (clean)
$ bash scripts/review-checks.sh --diff /tmp/bypass.diff
src/x.js:2: hardcoded path (/usr/bin/python3): * "/usr/bin/python3".length;   ← still flagged

Four regressions added: both of your repros, the same-line-close control, and the bypass control. Scanner unit suite 33 → 37; CLT gate, runner and review-pr suites pass; ruff clean; diff coverage 97%.

Merge order unchanged and still in force: #2469, then #2473, then this one.

A note on the pattern, since this is the third finding of the same kind across these PRs. Each time my test asserted the shape of the thing rather than the thing — endsWith on a path, * at line start, "a python that runs" instead of "a python that runs here". You caught all three by computing actual output rather than reading my description of it, and that is the more reliable method; I have been leaning on the description. The controls added here are written to fail if the behaviour regresses, not merely to describe it.

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed exact head d326d7ea6aab1f2b8b9a0b0ea89331b73839df45. The prior long-JSDoc false positive and opening-line case are fixed, and the multiplication control remains correctly flagged. Reviewer-run CLT 15/15, runner 16/16, scanner 37/37, review-pr, diff, and production-diff path checks pass.

[P1 mandatory-gate bypass] _hunk_opens_in_block() treats the first */ anywhere in a hunk as proof that the hunk began inside a comment, including a closer token inside a valid string literal. That inference starts _mask_comments() in block state and can mask executable code before the string token. Exact-head repro against the real runner:

diff --git a/src/x.js b/src/x.js
--- a/src/x.js
+++ b/src/x.js
@@ -1,0 +1,1 @@
+const p = "/usr/bin/python3"; const closer = "*/";
$ <that-diff> | bash scripts/review-checks.sh
review-checks: PASS (hardcoded-paths clean)

The JavaScript is valid, the explicit CLT-stub path is executable data, and the gate silently passes it. This is the reverse of the documented “string delimiter errs toward scanning” limitation: during initial hunk inference, a string closer errs toward suppression. Please make initial-state inference delimiter-aware enough that a quoted */ cannot establish block state, and pin the exact control above while retaining the now-correct long-JSDoc and multiplication regressions.

Sonichi’s current-head-adjacent documentation request also remains applicable: lesson 7 should explicitly state that the machine gate covers explicit /usr/bin/... tokens but not bare names resolved through PATH, so a passing scan is not mistaken for complete coverage of the CLT-stub class.

Worst case is a mandatory safety gate that can be bypassed by ordinary valid code formatting and creates false confidence around the exact stub path it claims to reject.

Verdict: changes requested; not merge-ready on this head. Current-head CI/coverage are still running, the branch merge-base remains 3e6c882b, and documented merge order remains #2469 → #2473 → #2474.

@john-the-dev's P1, and it is a bypass in the SUPPRESSION direction — the
reverse of the string-literal caveat documented on _mask_comments.
_hunk_opens_in_block() treated the first `*/` anywhere in a hunk as proof the
hunk began inside a comment, so this valid one-liner scanned clean:

    const p = "/usr/bin/python3"; const closer = "*/";

The quoted closer established block state, and _mask_comments then blanked the
executable code before it.

Two paired guards, both needed:

  1. Delimiter-aware inference — _blank_string_literals() blanks quoted spans
     (", ', ` with backslash escapes) before looking for delimiters, so a token
     inside a literal is data, not a comment close.
  2. The closer must be the first thing on its line — the canonical JSDoc `*/`
     shape. A mid-line closer that survived blanking is not evidence a comment
     was opened ABOVE the hunk.

Blanking is single-line, so a multi-line template literal could still hide a
delimiter; guard 2 means it cannot establish block state on its own. Stated in
the docstring rather than left implicit.

All four controls now hold simultaneously, each pinned:

    quoted "*/" bypass          -> FLAGGED
    continued multiplication    -> FLAGGED
    long-JSDoc body (opener out of hunk) -> exempt
    path in an opening line's comment portion -> exempt

Also addresses @sonichi's documentation request: lesson 7 now states the gate's
LIMIT — it matches explicit /usr/bin/… tokens only, so a bare `git` or `python3`
resolved through PATH is invisible to it and a green scan is not proof the class
is absent. sonichi#2469 fixed exactly such a caller.

Scanner unit suite 37 -> 44; ruff clean; diff coverage 100% (69 lines).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed exact head ef5c9d47aa6cea7515074832677c87d42c5e5f31. The prior single-line quoted-*/ bypass is fixed, Sonichi’s explicit-path-vs-PATH boundary is now documented in lesson 7, and reviewer-run CLT 15/15, runner 16/16, scanner 44/44, review-pr, diff, and path checks pass.

[P1 mandatory-gate bypass] A line-start */ inside a valid multi-line JavaScript template literal still establishes initial block-comment state and masks executable code earlier in the hunk. _blank_string_literals() is deliberately single-line and loses the opening backtick before _hunk_opens_in_block() examines the next line. Exact-head repro against the real runner:

diff --git a/src/x.js b/src/x.js
--- a/src/x.js
+++ b/src/x.js
@@ -1,0 +1,3 @@
+const p = "/usr/bin/python3"; const tpl = `
+*/
+`;
$ <that-diff> | bash scripts/review-checks.sh
review-checks: PASS (hardcoded-paths clean)

That JavaScript is valid. The hidden closer on line 2 makes _hunk_opens_in_block() start the whole hunk in block state, so _mask_comments() suppresses the executable stub path on line 1. The current comment claims the paired line-start rule prevents a hidden token from establishing state on its own; this control disproves that claim.

Please carry string/template state across the hunk during initial-state inference, or choose a conservative inference that cannot begin suppression from a delimiter whose lexical role is unproven. Pin the exact multi-line template control while retaining the now-correct single-line string, long-JSDoc, opening-comment, and multiplication regressions.

Worst case remains a mandatory safety gate that silently passes the exact explicit CLT-stub path it claims to reject, creating false confidence.

Verdict: changes requested; not merge-ready on this head. Current-head clean-install/coverage are still running, the branch merge-base remains 3e6c882b, and documented merge order remains #2469 → #2473 → #2474.

marklysze added a commit to marklysze/sutando that referenced this pull request Aug 1, 2026
Owner feedback: the point of this module is to route interpreter lookup through
ONE place, not to withhold an interpreter that works.

selectPython collapsed tiers 3 and 4 into `toolsInstalled() ? 'python3' : null`,
so it declined a perfectly good interpreter whenever the CLT was absent. The
comment justifying that generalised from Homebrew — which does require the
tools — to every install method. Not true: a python.org framework install
(/usr/local/bin/python3 -> /Library/Frameworks/Python.framework/…, present on
the maintainer's machine) or a pyenv build is entirely CLT-independent.

It was also inconsistent with select_git in src/git_binary.py (sonichi#2469), which
already asks the right question: is what PATH gave me actually the stub? Only
that one is gated.

Now mirrors it:

  $SUTANDO_PY -> bundled runtime -> PATH python3 if NOT the stub
              -> the stub only when xcode-select -p succeeds -> null

isSystemStubPath compares the resolved DIRECTORY rather than the exact stub
path — that keeps the flagged literal out of this file (so this PR stays clear
of sonichi#2474's gate and carries no merge-order dependency) and also covers a
versioned sibling in the same system location.

pythonOnPath walks PATH on the filesystem rather than shelling out: spawning
anything to find an interpreter risks spawning the stub, which is the failure
mode being fixed.

Tests 21 -> 25, including the case that motivated this — a non-stub PATH python
with toolsInstalled deliberately unreachable, so a regression that consults it
fails loudly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@john-the-dev's P1, and it disproves a claim the previous commit's docstring
made. That commit asserted the paired line-start rule meant "a hidden token
cannot establish block state on its own". It can — via a multi-line template
literal, whose opening backtick the single-line _blank_string_literals cannot
carry to the next line:

    const p = "/usr/bin/python3"; const tpl = `
    */
    `;

The hidden line-start closer started the whole hunk in block state and masked
the executable stub path on line 1. Reproduced on the prior head: PASS, clean.

Rather than lexing JS across lines, the inference now requires CORROBORATION: a
closer only counts when some EARLIER line in the hunk already looks like comment
body (`*` or `/*` leading). Real JSDoc always has that; the template literal
does not, because its first line is ordinary code.

That is also the conservative reading the review asked for — suppression can no
longer begin from a delimiter whose lexical role is unproven.

All five controls hold simultaneously, each pinned:

    */ in a multi-line template  -> FLAGGED  (this fix)
    */ in a single-line string   -> FLAGGED
    continued multiplication     -> FLAGGED
    long-JSDoc body, opener outside the hunk -> exempt
    path in an opening line's comment portion -> exempt

Scanner unit suite 44 -> 45; ruff clean; diff coverage 100%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking on exact head 744af65.

scripts/review-checks.py::_hunk_opens_in_block still infers initial block-comment state from lexical shapes without proving those lines are comments. The added corroboration can itself appear inside the same valid multiline template literal:

const p = "/usr/bin/python3"; const tpl = `
* template content
*/
`;

Evidence on this head:

$ printf ... | node --check
VALID_JS

$ <the corresponding unified diff> | bash scripts/review-checks.sh
review-checks: PASS (hardcoded-paths clean)

The * template content line sets saw_comment_body; the following line-start */ returns true, so the hunk is treated as already inside a block and the executable path on line 1 is masked. This remains a suppression-direction bypass in the new required gate. Please carry multiline template/string lexical state through the hunk (or use a conservative inference that cannot treat unproven comment-like lines as evidence) and add this exact valid-JS regression.

The declared suites are green on this head (review-checks-clt-shims, review-checks-py 45/45, review-checks, review-pr, git diff --check, and the cumulative self-scan), but they do not cover this control. Stack order remains #2469 → #2473 → #2474; rebase/update after the parents land and rerun the full checks.

@john-the-dev's P1, and it defeats the corroboration rule from the previous
commit by putting the EVIDENCE inside the template too:

    const p = "/usr/bin/python3"; const tpl = `
    * template content
    */
    `;

`node --check` says VALID_JS. Line-at-a-time string blanking read
`* template content` as comment-body evidence and `*/` as a closer, inferred the
hunk had opened inside a comment, and masked the executable path on line 1.
Reproduced on the prior head: PASS, clean.

_blank_string_literals now returns (blanked, carry) and threads the carry
through _hunk_opens_in_block. Only a BACKTICK carries: a template literal is the
only JS string that survives a newline, so an unterminated ' or " is a
single-line syntax error rather than state. With the backtick carried, lines 2-3
are blanked as string content and supply no evidence at all.

This is the third bypass in this inference and the pattern is worth naming: each
previous fix reasoned about how a line LOOKS (`* ` prefix, then line-start `*/`,
then a corroborating `*` above). Lexical shape is not proof of lexical role. The
fix that holds is tracking the actual string/comment spans.

All six controls hold simultaneously, each pinned:

    evidence + closer inside a template  -> FLAGGED  (this fix)
    lone */ inside a template            -> FLAGGED
    */ in a single-line string           -> FLAGGED
    continued multiplication             -> FLAGGED
    long-JSDoc body, opener out of hunk  -> exempt
    path in an opening line's comment    -> exempt

Scanner unit suite 45 -> 50, including the carry-over states directly. Two
earlier direct assertions on _blank_string_literals were comparing against the
old string return and had to be indexed — three of them were passing for the
wrong reason (tuple membership), which the new return value exposed.

ruff clean; diff coverage 98%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@marklysze

Copy link
Copy Markdown
Collaborator Author

@john-the-dev — fixed on afb4e06. Two things to own first.

This review sat unaddressed for two hours, and that was my error. I was comparing your review timestamps (UTC) against my commit times (+10:00) and concluded this one predated my last push. It didn't — 744af65 is 16:11:14+10:00 = 06:11 UTC, your review is 06:17 UTC, six minutes later. I reported "no new activity" several times while this was open. Fixed the comparison; it will not recur.

And you're right about the substance. Putting the evidence line inside the same template defeats the corroboration rule:

$ node --check   ->  VALID_JS
$ <that diff> | bash scripts/review-checks.sh
review-checks: PASS (hardcoded-paths clean)      # the bypass, on the prior head

_blank_string_literals now returns (blanked, carry) and threads the carry through _hunk_opens_in_block. Only a backtick carries — a template literal is the only JS string that survives a newline, so an unterminated ' or " is a single-line syntax error rather than state. With it carried, lines 2-3 are blanked as string content and supply no evidence at all:

src/x.js:1: hardcoded path (/usr/bin/python3): const p = "/usr/bin/python3"; const tpl = `

Six controls, all holding at once, each pinned:

control result
evidence + closer inside a template FLAGGED (this fix)
lone */ inside a template FLAGGED
*/ in a single-line string FLAGGED
continued multiplication FLAGGED
long-JSDoc body, opener outside the hunk exempt
path in an opening line's comment portion exempt

Worth naming, since this is the third bypass in this one function. Each earlier fix reasoned about how a line looks — * prefix, then line-start */, then a corroborating * above. Lexical shape is not proof of lexical role, and you have now falsified that assumption three times in a row with valid JS. Tracking the actual string spans is the first version that isn't a shape heuristic, which is why I think this one holds rather than being the next in the sequence. Your framing — "cannot treat unproven comment-like lines as evidence" — is what pointed at it.

Scanner suite 45 → 50, including the carry states directly. Adding the tuple return also exposed that three existing assertions on _blank_string_literals were passing for the wrong reason (tuple membership rather than substring); those are corrected.

review-checks-clt-shims, review-checks, review-pr all pass; ruff clean; diff coverage 98%.

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed exact head .\n\nThe prior mandatory-gate bypass is fixed. The exact valid-JavaScript multiline-template control from my blocker still produces / rc 0 on that prior head, while this head reports the executable on line 1 and exits 1. The fix carries only backtick template state across hunk lines during initial block-comment inference, so the and inside the template no longer provide false comment evidence.\n\nReviewer-run exact-head evidence:\n\n- : 15/15\n- Python scanner suite: 50/50, including the new carry-state and exact bypass controls\n- review-checks runner: 16/16\n- review-pr suite: PASS\n- : clean\n- cumulative diff through the real : PASS\n- added-file/path scan: no machine/user-specific production paths\n\nThe prior long-JSDoc, opening-comment, multiplication, quoted-closer, prefix-family, and single-line-template controls remain green. Worst case for this mandatory gate was either silently admitting an explicit CLT-stub path or rejecting legitimate documentation/platform binaries; the current regression set discriminates both directions.\n\nVerdict: the current code layer is approved. The PR is not merge-ready yet: clean-install and diff coverage are still running, and the documented stack order remains #2469 → #2473 → #2474. After both parents land, refresh/rebase this child, rerun the full exact-head checks, and obtain two qualifying current-head maintainer approvals.

@john-the-dev

Copy link
Copy Markdown
Collaborator

Correction to my exact-head approval above: shell quoting stripped the inline code spans from that review body. The formal verdict is unchanged; the intact evidence is:

  • exact head: afb4e06e812a31b170193a7196478d9327edd341
  • prior reproducing head: 744af65a0691645f34b16a59be74a30860268c5f
  • the valid multiline-template diff reports review-checks: PASS / rc 0 on the prior head
  • the same diff reports /usr/bin/python3 on line 1 and exits 1 on the current head
  • review-checks-clt-shims: 15/15
  • Python scanner suite: 50/50
  • review-checks runner: 16/16
  • review-pr suite: PASS
  • git diff --check origin/main...HEAD: clean
  • cumulative diff through scripts/review-checks.sh: PASS

The current code layer is approved, but the PR is not merge-ready: clean-install and diff coverage are still running, and stack order remains #2469 → #2473 → #2474. After both parents land, refresh/rebase, rerun exact-head checks, and obtain two qualifying current-head maintainer approvals.

@sonichi

sonichi commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Re-reviewed at head afb4e06e — my previous comment was at 05:24, before three more force-pushes, so this is a fresh look rather than a repeat. No blockers. john-the-dev has approved at this exact head; this corroborates with an independent measurement rather than stacking an LGTM.

I verified the load-bearing claim — the inode discrimination — on a real host, because it is the one thing in this PR that decides whether the gate blocks a legitimate tool:

/usr/bin/swift          inode 1152921500312571586  links 78   <- CLT stub
/usr/bin/swiftc         inode 1152921500312571586  links 78   <- CLT stub
/usr/bin/git            inode 1152921500312571586  links 78   <- CLT stub
/usr/bin/python3        inode 1152921500312571586  links 78   <- CLT stub
/usr/bin/make           inode 1152921500312571586  links 78   <- CLT stub
/usr/bin/swift-inspect  inode 1152921500312572883  links  1   <- REAL, separate binary

So the carve-out is correct in both directions: the five stub names share one inode and are blocked; swift-inspect genuinely has its own inode and link count 1 and is not. Blocking a real platform tool would have been the expensive mistake here, and the code comment reasons about it explicitly rather than pattern-matching the swift prefix.

Worth noting for cross-validation: this is the same stub inode I measured independently while checking #2477's Xcode-CLT claim earlier today (78 files at link-count 78). Two separate PRs resting on the same underlying fact, confirmed twice on the same host — so the premise under lesson 7 is solid, not folklore.

I also like that the lesson states its own limit rather than overselling the gate: "the machine gate matches explicit /usr/bin/… tokens only. A bare git or python3 resolved through PATH lands on the same stub and the scanner won't see it." That is the honest scope, and it stops a future reader from reading a green scan as "no stub exposure anywhere."

Merge-readiness: approved at head, CI green (0 failing, 0 pending of 15). mergeStateStatus is BEHIND, so it needs a branch update before it can go in — that is a rebase concern, not a review one. Needs a second distinct approver; merge remains the owner's call and I have not approved — comment only.

@sonichi

sonichi commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Heads-up on a collision with #2369 — not a blocker on this PR, but the merge order is now a decision rather than an accident.

#2474 and #2369 are both rewriting the same review-gate machinery. They overlap on 4 of the 5 files each touches: REVIEW.md, scripts/review-checks.py, scripts/review-checks.sh, tests/review-checks-py.test.py.

File overlap alone would not be worth a comment — so I checked whether they touch the same regions, and they do:

scripts/review-checks.py hunks
  #2474   @@ -14,6   -52,6(+168)  -69,6  -76,7  -85,6  -94,6  -102,6  -116,14
  #2369   @@ -15,6   -30,6(+480)         -76,7  -85,6  -94,6  -103,6  -119,15
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ same regions of main()
REVIEW.md
  #2474   @@ -62,7 +62,35   and   @@ -80,6 +108,33   (the `checks:` block)
  #2369   @@ -86,4 +86,29                            (the `checks:` block)

Both insert large blocks into the same file — ~168 lines here at line 52, ~480 there at line 30 — and then both edit the same later regions of main(). Whichever lands first, the other needs a substantial rebase, not a trivial one.

Current state: this PR is approved at head with CI green and only needs a second approver (it is BEHIND, so it also needs a branch update). #2369 currently carries two outstanding change-requests, so it is further out. That ordering seems right — but the #2369 author should know a ~480-line insertion is aimed at a file that is about to gain ~168 lines in the same places.

Flagging rather than gating: I would not hold this PR for it. Comment only — no approval from me; merge stays the owner's call.

@sonichi

sonichi commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Cold review, and the finding is about #2369, not this diff: the two collide, and the collision is in the file that encodes both PRs' controls.

Measured on origin/main (fea8dba4), merging both heads in sequence:

merge pr2474  -> clean
merge pr2369  -> CONFLICT
  UU scripts/review-checks.py
  UU scripts/review-checks.sh
  UU tests/review-checks-py.test.py

#2369 ("contextual paired-allow so portable candidate lists pass the hardcoded-path check") restructures the same allow/detect logic this PR extends. They move in opposite directions on one predicate — this one tightens (CLT stub paths become catchable), #2369 loosens (declared TOKEN_PREFIX :: COMPANION_PREFIX pairs become legal).

I checked the obvious interaction hazard and it does not apply: #2369's allow is an explicit pair list matched by basename, not a general "two paths on a line" rule, so it cannot silently exempt a CLT stub. No blocker there.

The real risk is the resolution, not the semantics. tests/review-checks-py.test.py is where both PRs put their controls, and it's one of the three conflicted files. A resolution that takes one side wholesale drops the other side's cases — and the suite still goes green, because what's missing is coverage, not correctness. That's the failure mode where a clean merge is not evidence the change survived.

Not a change request on this PR — it's clean against main today, and john's latest review here is an approval. Two suggestions for whoever merges second:

  1. Resolve review-checks-py.test.py by union of cases, not by side, and state the case count before/after.
  2. After resolving, verify both guards still fire: a CLT stub path must flag (this PR's lesson 7), and a declared portable pair must pass (chore(review): contextual paired-allow so portable candidate lists pass the hardcoded-path check #2369's candidate-list / naked / unrelated controls).

Merge order is otherwise free — both are clean against main independently.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head afb4e06e812a31b170193a7196478d9327edd341 and the full REVIEW/scanner/test layer. The CLT-stub gate now distinguishes executable production paths from prose/fixtures, carries multiline JavaScript template state safely, and closes the mandatory-gate bypass without weakening existing host-path checks. Reviewer-run tests/review-checks-py.test.py passes, the patch clears its own path gate, and exact-head CI is green. Approved on this head. Resolve the known overlap/merge order with #2369, then refresh and rerun both scanners cumulatively before merge.

john-the-dev added a commit that referenced this pull request Aug 1, 2026
…m, meeting-tools) (#2475)

* fix(ts): resolve python instead of hardcoding the Xcode-CLT stub

Two TypeScript call sites spawned /usr/bin/python3 directly:

  skills/zoom/tools.ts:404  execSync(`/usr/bin/python3 -c "…`)   Join-button click
  src/meeting-tools.ts:68   execFileSync('/usr/bin/python3', …)  camera toggle

On macOS that path is not python — it is the Xcode-CLT stub, one inode
hardlinked across python3 / git / swift / swiftc / clang / gcc / make. It exists
whether or not the developer tools are installed; spawning it without them
raises the modal "install command line developer tools" dialog and returns
nothing. The absolute path also cannot be shadowed by a real install on PATH,
so a user who installs python via Homebrew still gets the dialog.

Adds src/python-binary.ts — the TS twin of src/git_binary.py (#2469) and
SutandoConfig.resolvePython (#2473), carrying the same cascade the shell side
already uses for $PY:

  $SUTANDO_PY -> bundle-vendored runtime -> bare `python3` gated on
  xcode-select -p -> null

Tier 3 is deliberately the BARE name, not an absolute path. It lets a real
interpreter earlier on PATH win, and it keeps the stub's path out of the module
entirely. The xcode-select gate makes it safe: with the tools installed,
whatever python3 resolves to is a working interpreter; without them we cannot
know, so we decline. (A host with Homebrew python but no developer tools is not
reachable in practice — Homebrew's installer requires them.)

requirePython() throws PythonUnavailableError. Both call sites already wrap
their spawn in `try { … } catch {}`, so an unavailable interpreter degrades
through the handling they have rather than needing a new branch — same shape as
git_argv in #2469. zoom builds a shell string rather than an argv array, so its
path goes through shellQuote: the bundled runtime lives under the app bundle and
$SUTANDO_PY is whatever the launcher exported, either of which can contain
spaces.

selectPython and bundledPythonCandidates are exported and dependency-injected so
every tier is testable without touching the host toolchain — the pattern
selectFfprobe established in #2370.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(src-map): index the new python-binary module

scripts/gen-src-map.py regenerated after adding src/python-binary.ts — the
lint-src-map CI check refuses a docs/src-map.md that is stale vs src/, and it
also fails tests/src-map.test.py inside the clean-install job (which is why
both checks were red for one cause).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ts): correct the bundled-runtime path; probe both call-site shapes

Addresses both P1s from @john-the-dev's review.

[P1 bundled-runtime path] bundledPythonCandidates appended the full relative
path 'runtime/python/bin/python3' to BOTH dirname(execPath) and
dirname(dirname(execPath)), doubling the segment:

  execPath  /Applications/Sutando.app/Contents/Resources/runtime/bin/node
  emitted   …/Resources/runtime/bin/runtime/python/bin/python3
            …/Resources/runtime/runtime/python/bin/python3
  wanted    …/Resources/runtime/python/bin/python3

Neither exists, so on a packaged host WITH a vendored python but no
$SUTANDO_PY the resolver fell through to the xcode-select tier and returned
null without CLT — silently killing both GUI actions on exactly the install
this module exists to serve. The vendored tree is
<Resources>/runtime/{bin/node, python/bin/python3}, so python is a sibling of
node's bin/, one level above dirname(node); the candidate is now built from the
runtime ROOT. The flatter layout (node directly in the runtime root) is kept as
a second candidate, and the engine-sibling tier from sutando-config.sh is
unchanged.

The old test asserted only that candidates ENDED with the relative path, which
blessed both malformed forms — my error, and the same weak-assertion shape that
let the health-check caller slip through on #2469. Replaced with an exact
packaged-layout assertion plus a control that no candidate contains a doubled
runtime segment.

[P1 activated-path evidence] Both consumers swallow failures in a catch, so a
unit-correct resolver can still leave them dead. Added executable probes that
run the resolved interpreter through each call site's EXACT shape against a real
on-disk packaged layout under a path containing a space:

  - meeting-tools shape: execFileSync(resolved, ['-c', …]) -> "meet-ok"
  - zoom shape: execSync(`${shellQuote(resolved)} -c …`)   -> "zoom-ok"
  - the same zoom command WITHOUT shellQuote throws, proving the quoting is
    load-bearing rather than decorative
  - selectPython picks the vendored python from that layout with toolsInstalled
    false — the no-CLT case

Tests 15 -> 21. A live Zoom/Meet confirmation is still outstanding and remains
noted in the PR body.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(python-binary): make the probe fixture hermetic (no ambient python3)

@john-the-dev's P1: the "no-CLT" fixture was `#!/bin/sh\nexec python3 "$@"`,
which on the very clean Mac this PR targets resolves to the CLT stub. Running
the regression suite there could raise the exact modal the fix prevents — and
under a python-less PATH both probes failed outright (reviewer-run: 19/21).

The fake interpreter now echoes the argument after `-c`, proving the resolved
path was executed and received its args intact without any interpreter being
installed. All three probes additionally run with `env: { PATH: '/nonexistent' }`
so the hermeticity is enforced, not merely intended.

Reviewer's negative control, this head:

  PATH=/nonexistent node --test --import tsx tests/python-binary.test.ts
  ℹ tests 21   ℹ pass 21   ℹ fail 0        (was: pass 19, fail 2)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ts): use a real PATH interpreter without requiring developer tools

Owner feedback: the point of this module is to route interpreter lookup through
ONE place, not to withhold an interpreter that works.

selectPython collapsed tiers 3 and 4 into `toolsInstalled() ? 'python3' : null`,
so it declined a perfectly good interpreter whenever the CLT was absent. The
comment justifying that generalised from Homebrew — which does require the
tools — to every install method. Not true: a python.org framework install
(/usr/local/bin/python3 -> /Library/Frameworks/Python.framework/…, present on
the maintainer's machine) or a pyenv build is entirely CLT-independent.

It was also inconsistent with select_git in src/git_binary.py (#2469), which
already asks the right question: is what PATH gave me actually the stub? Only
that one is gated.

Now mirrors it:

  $SUTANDO_PY -> bundled runtime -> PATH python3 if NOT the stub
              -> the stub only when xcode-select -p succeeds -> null

isSystemStubPath compares the resolved DIRECTORY rather than the exact stub
path — that keeps the flagged literal out of this file (so this PR stays clear
of #2474's gate and carries no merge-order dependency) and also covers a
versioned sibling in the same system location.

pythonOnPath walks PATH on the filesystem rather than shelling out: spawning
anything to find an interpreter risks spawning the stub, which is the failure
mode being fixed.

Tests 21 -> 25, including the case that motivated this — a non-stub PATH python
with toolsInstalled deliberately unreachable, so a regression that consults it
fails loudly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Rui Wang <52230987+john-the-dev@users.noreply.github.com>

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 612e2edfb21b9ff4fa9c1546b082bad6e42f481b after its current-main merge refresh.

No blocking findings. Range-diff shows the review-check topic commit is unchanged. The exact-token CLT stub scan still flags only the executable tool paths it intends to catch, while comments, docs/tests, and prefix-family siblings remain exempt.

Reviewer-run exact-head checks:

  • bash tests/review-checks-clt-shims.test.sh — 15/15
  • python3 tests/review-checks-py.test.py — 50/50
  • git diff --check origin/main...HEAD
  • git diff origin/main...HEAD | bash scripts/review-checks.sh

Hosted CI and CLA are green. Worst-case disruption is a false-positive merge block; the paired exact-token negative controls cover that boundary. Code-ready and approved.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at exact head f71dc622. The current-main refresh preserves all eight reviewed scanner commits exactly; there is no new main/topic file overlap, the full diff and hardcoded-path gate pass, and all hosted checks are green. The failure-mode/false-positive surface is unchanged from the prior exact-token and comment-state verification. Code is ready; normal behind-main merge mechanics remain.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head f71dc6223ed42708b21c9e2b304bacf14e741a4f after the #2540 main merge refresh.

No blocking findings. Range-diff preserves the review-check hardening commits exactly; this head only merges current main. The Xcode-CLT stub gate still matches exact executable tokens, keeps prose/test exemptions narrow, and preserves the scanner's comment/template handling.

Focused local checks passed:

  • bash tests/review-checks-clt-shims.test.sh
  • python3 tests/review-checks-py.test.py

Hosted checks are green.

Reviewed by Qingyun's Personal Codex.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head f9b13a24a48069f1cbe13a2aa95f037210d33913.

No blocking findings on this refreshed head. The Xcode CLT stub gate covers the exact stub paths without catching sibling tools such as swift-inspect, swift-frontend, or makeinfo. Hosted tsc + tests and coverage were still in progress on my last poll, so this approval is scoped to the code review and focused checks for this exact head.

Focused checks I ran:

  • bash tests/review-checks-clt-shims.test.sh
  • python3 tests/review-checks-py.test.py
  • git diff --check origin/main...HEAD

Reviewed by Qingyun's Personal Codex.

@john-the-dev
john-the-dev enabled auto-merge (squash) August 3, 2026 08:27
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@cla-assistant check

@john-the-dev
john-the-dev merged commit 21fc52c into sonichi:main Aug 3, 2026
16 checks passed
marklysze added a commit to marklysze/sutando that referenced this pull request Aug 3, 2026
…defining site

sonichi#2474 landed /usr/bin/python3 as a `flag_exact` token after this branch was
cut, and the first thing it caught was this PR:

  src/Sutando/SutandoConfig.swift:309: hardcoded path (/usr/bin/python3):
      static let systemPython = "/usr/bin/python3"
  review-checks: 1 violation(s).

Not a false positive worth an allow entry: REVIEW.md `allow` is matched per
TOKEN, not per file, so allowing /usr/bin/python3 would switch lesson 7 off
for that token repo-wide -- the gate would stop catching the exec sites it
exists for.

Compose it from a systemBin constant instead, matching how the landed
src/python-binary.ts keeps the same literal out of its source. The value is
unchanged; the resolver still returns it only when developerToolsInstalled().

Composition creates a new hole though: the existing assertion only checked
that the resolver agrees with the constant, so a typo in either half stayed
green. Verified -- sabotaging systemBin to /usr/local/bin passes the old
assertion. Added a test that pins the concrete path (tests/ is scanner-exempt,
so the literal lives where it is allowed):

  FAIL: test_system_constant_is_exactly_the_stub_path
  AssertionError: /usr/local/bin/python3 != /usr/bin/python3

10/10 after restoring.
marklysze added a commit to marklysze/sutando that referenced this pull request Aug 7, 2026
…defining site

sonichi#2474 landed /usr/bin/python3 as a `flag_exact` token after this branch was
cut, and the first thing it caught was this PR:

  src/Sutando/SutandoConfig.swift:309: hardcoded path (/usr/bin/python3):
      static let systemPython = "/usr/bin/python3"
  review-checks: 1 violation(s).

Not a false positive worth an allow entry: REVIEW.md `allow` is matched per
TOKEN, not per file, so allowing /usr/bin/python3 would switch lesson 7 off
for that token repo-wide -- the gate would stop catching the exec sites it
exists for.

Compose it from a systemBin constant instead, matching how the landed
src/python-binary.ts keeps the same literal out of its source. The value is
unchanged; the resolver still returns it only when developerToolsInstalled().

Composition creates a new hole though: the existing assertion only checked
that the resolver agrees with the constant, so a typo in either half stayed
green. Verified -- sabotaging systemBin to /usr/local/bin passes the old
assertion. Added a test that pins the concrete path (tests/ is scanner-exempt,
so the literal lives where it is allowed):

  FAIL: test_system_constant_is_exactly_the_stub_path
  AssertionError: /usr/local/bin/python3 != /usr/bin/python3

10/10 after restoring.
john-the-dev added a commit that referenced this pull request Aug 10, 2026
…l dialog (from-source installs) (#2473)

* fix(app): resolve python via SUTANDO_PY / bundled runtime, never the CLT stub

Sutando.app runs src/health-check.py from a 60-second Timer and picked its
interpreter like this:

    let homebrewPython = "/opt/homebrew/opt/python@3.11/libexec/bin/python3"
    let pythonPath = FileManager.default.fileExists(atPath: homebrewPython)
        ? homebrewPython : "/usr/bin/env"

Both halves are wrong on a current machine.

The hardcoded formula path is stale: python@3.11 is not installed on the
maintainer's own dev machine (which has @3.10, @3.12, @3.14), so effectively
every host already takes the fallback branch.

And the fallback resolves to /usr/bin/python3, which on macOS is the Xcode-CLT
stub, not python — one inode hardlinked across python3 / git / swiftc / clang /
gcc / make. Without developer tools it raises the modal "install command line
developer tools" dialog and returns nothing. Because the caller is a Timer,
that dialog reappeared indefinitely. Reproduced on a clean macOS VM installing
a bundled Sutando; dev machines are masked only because their tools ARE
installed, which makes /usr/bin/python3 a real interpreter.

Note the app sets no custom environment on this Process, so it inherits the
GUI session's minimal PATH — PATH-based resolution cannot be relied on here.

Adds SutandoConfig.resolvePython, the cascade the shell side already uses
(scripts/sutando-config.sh, src/agent/claude/cli/start-cli.sh):

  $SUTANDO_PY -> <repoRoot>/../runtime/python/bin/python3 -> /usr/bin/python3
  only when xcode-select -p reports installed tools -> nil

nil is the point: the caller now logs and skips instead of prompting. A health
check must never be able to raise a modal system dialog.

Dependencies are injected with defaults so every tier is testable without
mutating the host toolchain. tests/sutando-config-python-resolution.test.py
compiles SutandoConfig.swift against a probe and drives all six branches, plus
source-tied guards that run on hosts without swiftc.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(app-python): compose the stub path so lesson 7 does not flag the defining site

#2474 landed /usr/bin/python3 as a `flag_exact` token after this branch was
cut, and the first thing it caught was this PR:

  src/Sutando/SutandoConfig.swift:309: hardcoded path (/usr/bin/python3):
      static let systemPython = "/usr/bin/python3"
  review-checks: 1 violation(s).

Not a false positive worth an allow entry: REVIEW.md `allow` is matched per
TOKEN, not per file, so allowing /usr/bin/python3 would switch lesson 7 off
for that token repo-wide -- the gate would stop catching the exec sites it
exists for.

Compose it from a systemBin constant instead, matching how the landed
src/python-binary.ts keeps the same literal out of its source. The value is
unchanged; the resolver still returns it only when developerToolsInstalled().

Composition creates a new hole though: the existing assertion only checked
that the resolver agrees with the constant, so a typo in either half stayed
green. Verified -- sabotaging systemBin to /usr/local/bin passes the old
assertion. Added a test that pins the concrete path (tests/ is scanner-exempt,
so the literal lives where it is allowed):

  FAIL: test_system_constant_is_exactly_the_stub_path
  AssertionError: /usr/local/bin/python3 != /usr/bin/python3

10/10 after restoring.

* ci: retrigger the clean-install job cancelled on this head

`tsc + tests (clean install)` is recorded as CANCELLED on 552004d, not
failed -- the run started 09:35:44 against the previous head and was killed
at 09:45:48 when this head was pushed, so the current head has no result for
that job. Every other check on 552004d is green (15 success, 0 failure).

`gh run rerun` is admin-only on this repo, so an empty commit is the only
retrigger available to me. No file changes: `git diff 552004d..HEAD` is
empty, so the reviewed tree is byte-identical.

* docs(app): trim added comments to the two-line rule

The comments added here predate the code-comment policy now on main:
multi-paragraph narration, an outdated REVIEW.md lesson label, and incident
history. Condensed to the invariant each protects — probe before spawning
system python, skip when nothing is runnable. History stays in the PR body.

No behaviour change; the app still builds and the resolver tests pass.

* docs(app): condense resolvePython doc comment to the two-line rule

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Rui Wang <52230987+john-the-dev@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants