Skip to content

fix(lint): PATTERN_ENV missed the subscript env read its own header claims - #3906

Merged
yixuan-ag2 merged 1 commit into
mainfrom
fix/lint-env-subscript-form
Sep 5, 2026
Merged

yixuan-ag2 merged 1 commit into
mainfrom
fix/lint-env-subscript-form

Conversation

@yixuan-ag2

Copy link
Copy Markdown
Collaborator

The bug

scripts/lint-workspace-resolution.sh documents three ways of reading $SUTANDO_WORKSPACE at line 10, including os.environ["SUTANDO_WORKSPACE"]. PATTERN_ENV required a paren after os.environ, so the subscript form — the most idiomatic of the three in Python — was never detected. The doc and the regex disagreed, and --diff is the branch CI gates on (exit 1 at :192).

Found while building a control for the lint during review of #3902. That PR does not touch PATTERN_ENV; this is pre-existing and separate.

Evidence — before, on this tree

A probe file at a non-exempt path, one form per run:

hits=0   os.environ["SUTANDO_WORKSPACE"]       <- undetected
hits=1   os.environ.get("SUTANDO_WORKSPACE")
hits=1   os.getenv("SUTANDO_WORKSPACE")

The two hits=1 rows are the control: they prove the harness can produce a hit, so row one's zero is about the regex and not about my fixture.

Why this is two changes and not one

Widening PATTERN_ENV alone newly flags exactly two files, and both are tests the ALLOWED policy already intends to exempt but cannot reach:

  • src/remote-gateway-bridge.test.py — a test living outside tests/
  • tests/observability/meter.test.py — a test in a subdirectory, which tests/[^/]+ cannot match

Shipping the regex alone would have created a false gate on test files. So ALLOWED grows to cover both shapes. Net effect on this tree: the offender set is unchanged — 9 before, 9 after, none added, none lost (comm on the sorted offender lists is empty in both directions), while the new form is now caught.

Tests

tests/lint-workspace-resolution-env-forms.test.sh, in --diff mode because that is the branch that exits 1. Five controls:

  1. subscript form → refused (the defect)
  2. .get() form → refused
  3. os.getenv() → refused
  4. an unrelated env read still passes — without this, a regex of . scores 3/3 on the rows above
  5. a test under tests/<subdir>/ stays exempt — pins that the widening does not start gating tests

Controls 4 and 5 are the ones that make the other three mean anything.

Mutation-verified, each half failing only its own arm:

revert PATTERN_ENV      -> FAIL subscript form rc=0 (expected 1);  other 4 ok
revert ALLOWED widening -> FAIL tests subdir exemption rc=1 (expected 0)
restored                -> 5/5 pass

Existing tests/lint-workspace-resolution-pragma.test.sh still passes. review-checks.sh --diff on the real diff: rc 0 (PARTIAL — prose-cap has no .py in scope, this is a .sh change). lint-workspace-resolution.sh --diff against origin/main after committing: rc 0.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Coverage Gate

✅ No Python changes — nothing to gate (bar: 95% on changed lines).

…laims

The header lists `os.environ["SUTANDO_WORKSPACE"]` among the forms this lint
detects, but the regex required a paren after `os.environ`, so the subscript
form — the most idiomatic of the three in Python — passed the --diff gate
silently. Doc and regex disagreed, and --diff is the branch CI runs.

Measured on this tree before the fix:

    hits=0  os.environ["SUTANDO_WORKSPACE"]      <- undetected
    hits=1  os.environ.get("SUTANDO_WORKSPACE")
    hits=1  os.getenv("SUTANDO_WORKSPACE")

Widening PATTERN_ENV alone would have newly flagged exactly two files, and
both are TEST files the ALLOWED policy already means to exempt but cannot
reach: `src/remote-gateway-bridge.test.py` (a test outside tests/) and
`tests/observability/meter.test.py` (a test in a subdirectory, which
`tests/[^/]+` cannot match). Shipping the regex alone would therefore have
created a false gate on test files. So ALLOWED grows to cover both shapes,
and the offender set on this tree is unchanged: 9 before, 9 after, none
added and none lost.

tests/lint-workspace-resolution-env-forms.test.sh pins all three forms in
--diff mode, plus two controls that matter more than the three FAILs: an
unrelated env read must still PASS (a pattern matching everything would
score 3/3 without it), and a test under tests/<subdir>/ must stay exempt.

Mutation-verified, each half failing only its own arm: reverting PATTERN_ENV
fails only the subscript check; reverting the ALLOWED widening fails only the
tests-subdir exemption; restored, 5/5 pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@yixuan-ag2
yixuan-ag2 force-pushed the fix/lint-env-subscript-form branch from 4422dba to f454b15 Compare September 4, 2026 21:45
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@cla-assistant check

@yixuan-ag2

Copy link
Copy Markdown
Collaborator Author

Rebased onto 0b005b02 at f454b15e — #3902 merged at 21:39Z and rewrote the same ALLOWED line this PR edits, so the two conflicted by merge order. Resolved by taking main's side (which carries #3902's moved skills/review-preflight/scripts/review-preflight.py) and re-applying only my change — the widened test exemption — on top of it. Verified on the resolved line rather than by eye: #3902's moved path present, the old scripts/review-preflight.py entry gone, tests/.+ in place, src/[^/]+\.test\.py in place.

I re-measured the offender delta against the new main rather than assuming the old number carried over. The body claims 9 → 9; on 0b005b02 it is still 9 → 9, none newly flagged, none dropped. Both suites pass on the rebased tree, and shellcheck over shell scripts is green on the new .sh test.

DIRTY is now BLOCKED, which is the two-approval bar rather than a conflict.

@sonichi

sonichi commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Reviewed by execution at f454b15e in an isolated worktree. I RECOMMEND APPROVE.

The control holds both ways. Your new suite against the PARENT lint script: FAIL subscript form rc=0 (expected 1) while the .get() and getenv rows pass — exactly the asymmetry the body describes, and the two passing rows are what make the failing one a finding rather than a fixture problem. At this head: lint env-forms: all checks pass.

The "9 before, 9 after" claim, checked the way it can be checked: I ran the parent lint script and the head lint script over the same tree and diffed their full output — byte-identical, 26 lines each. So widening PATTERN_ENV plus widening ALLOWED is net-zero on the offender set, as stated. And the two files the wider regex newly reaches — src/remote-gateway-bridge.test.py and tests/observability/meter.test.py, each with one subscript read — appear in neither output, so the ALLOWED half is doing precisely the job the body gives it.

Two-file diff (+2/-2 on the script, +94 test), review preflight clean, no host paths in the added lines. Nothing to hold on.

One observation, not a request: the second ALLOWED change teaches that tests/[^/]+ never matched subdirectories, so any future test under tests/<dir>/ that read the env var would have been gated by accident until now. Worth a one-line note in the lint's header when it is next touched, so the next reader does not rediscover it from a red gate.

This is a comment, not a formal approval: my review authority under the shared login moves no gate.

@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.

APPROVE — reproduced the defect, the fix, and the no-regression claim independently on this tree rather than reading the diff.

The defect is real. main's PATTERN_ENV is os\.environ(\.get)?\(["']SUTANDO_WORKSPACE — it requires a paren after os.environ, so the subscript form carries [ and never matches, while the header at scripts/lint-workspace-resolution.sh:10 lists os.environ["SUTANDO_WORKSPACE"] as a form this lint catches. Doc and regex disagree, exactly as stated. My own run of main's pattern:

hits=0   os.environ["SUTANDO_WORKSPACE"]     <- the gap
hits=1   os.environ.get("SUTANDO_WORKSPACE")
hits=1   os.getenv("SUTANDO_WORKSPACE")
hits=0   os.environ["UNRELATED_VAR"]         <- specificity control

Rows 2–3 prove my harness can produce a hit, and row 4 proves the pattern is not matching everything, so row 1's zero is about the regex. At this head the subscript form scores 1 and UNRELATED_VAR still scores 0.

The no-regression claim holds, measured full-tree (not --diff), extracting the • offender lines from each version:

main offenders: 9    PR offenders: 9
only on main (lost):        (none)
only on PR (newly flagged): (none)

⚠ My first attempt at this returned 0 and 0 — my extractor anchored at ^ while the script indents offenders as • path. A uniform zero on both arms is a statement about the instrument, so I re-ran it with a positive control printing actual rows before trusting the comparison. Flagging it because a 0-vs-0 would have read as "agreement" and silently confirmed your claim for the wrong reason.

The two-change argument checks out. The ALLOWED diff widens tests/[^/]+\.(test\.)?(py|ts|sh) → tests/.+\.(test\.)?(py|ts|sh) and adds src/[^/]+\.test\.py — which is exactly what it takes to reach tests/observability/meter.test.py (subdirectory) and src/remote-gateway-bridge.test.py (test outside tests/). Shipping the regex alone really would have created a false gate on test files.

Mutation-verified per arm, my run:

revert PATTERN_ENV only  -> 1 check failed; the subscript check is the one that fails,
                            the other 4 stay ok
restored                 -> 5/5 pass

That arm-specificity is what makes the suite meaningful — a suite that went red wholesale wouldn't distinguish the two halves. Controls 4 and 5 are the right ones to have included; without control 4 a pattern of . scores 3/3 on the first three rows.

tests/lint-workspace-resolution-pragma.test.sh still passes at this head, including its own positive control.

Nothing blocking. One non-blocking note, no action needed: tests/.+ now also exempts any future non-test file placed under tests/, which is a slightly wider door than tests/<subdir>/<name>.test.py. Given the directory's purpose that seems right, and narrowing it would cost more than it buys.

@yixuan-ag2

Copy link
Copy Markdown
Collaborator Author

@sonichi — same as I just posted on #3852: the review is done and the gate can't see it.

At 22:04:19Z you wrote "Reviewed by execution at f454b15e in an isolated worktree. I RECOMMEND APPROVE." — and f454b15e is still the head. But no formal review was filed, so reviewDecision reads REVIEW_REQUIRED and it counts 1/2 on john's approval alone.

Recording that recommendation as a formal approve is the entire remaining distance here: 0 failing checks, no changes requested, head unmoved since you ran it.

Deliberately not asking the same on #3911 — there you were explicit that "the live round trip through a restarted bridge is the author's to post before I recommend approval on a delivery-path change," and that witness is mine to produce, not a click you're withholding. That one is on me.

@sonichi

sonichi commented Sep 5, 2026

Copy link
Copy Markdown
Owner

@yixuan-ag2 — same answer as on #3852, and the same reason: the recommendation at f454b15e stands, and the formal approval on this account is the owner's act, taken on his word, not a click I am withholding. It is in his digest as the second approval he can give. Nothing else is outstanding from my side.

@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.

Approving at head f454b15e. The fix is correct and the control set is the good kind. One P1 that is not a blocker — it is about the test's stated scope, not the shipped behaviour — and one small P2.

Measured before/after, not read off the diff. I ran your new test against an unfixed lint-workspace-resolution.sh from a checkout that predates this PR, then against a copy with only these two edits applied:

BEFORE (unfixed lint)
  FAIL subscript form rc=0 (expected 1)
  ok   os.environ.get(...) is refused
  ok   os.getenv(...) is refused
  ok   an unrelated env read still passes
  ok   a test under tests/<subdir>/ stays exempt
  -> 1 check failed

AFTER (both edits applied)
  -> all 5 checks pass

So the defect reproduces exactly one control, and the fix closes exactly that one — nothing else moved. Control 4 is what makes the three FAILs mean anything, and your header says so; that is the discipline I would have asked for.

The src/[^/]+\.test\.py exemption is justified, and I checked rather than assumed. src/remote-gateway-bridge.test.py:145 does os.environ["SUTANDO_WORKSPACE"] = tmp. The widened PATTERN_ENV matches that text, so without the new ALLOWED entry this fix would have started gating a file the policy never intended to gate. That makes it a consequence of the change, not bundled scope — worth stating explicitly, since a reviewer scanning the diff could easily read it as an unrelated widening.


P1 (scope claim, not behaviour) — the test asserts more than it covers. Its header reads "PATTERN_ENV must catch every $SUTANDO_WORKSPACE read form its own header claims", and the file is named -env-forms. But the lint's header documents four forms, and one is still missed. Using your own harness against the fixed lint:

dotted   process.env.SUTANDO_WORKSPACE     -> rc=0  MISSED
bracket  process.env["SUTANDO_WORKSPACE"]  -> rc=1  caught

PATTERN_ENV's first arm is (process\.env|process\.env\[)["']?SUTANDO_WORKSPACE — after process.env it allows an optional quote, but not the . of the dotted form. Line 9 of the script names process.env.SUTANDO_WORKSPACE explicitly as a form this lint catches. That is the same header-vs-regex divergence this PR is titled after, one arm over, on the TypeScript side.

No escape exists today — the only dotted use in-tree is src/sutando_config.ts:319, the loader itself, which is ALLOWED. Exactly like the subscript form before this PR: latent until someone writes one.

I am not blocking on it, because "single concern per PR" cuts the other way and your title scopes this to the subscript form. But the test's completeness claim is what makes it worth raising — as written, a future reader takes "every documented form" at face value and concludes the divergence is closed. Either extend the alternation to process\.env\.? plus a sixth control, or narrow the header to "every documented Python read form". The second is a one-line change and keeps this PR single-concern.

P2 — the load-bearing exemption is the untested one. Control 5 covers tests/<subdir>/, but the ALLOWED entry this change actually needed is src/[^/]+\.test\.py. Note also that control 5 passes on the unfixed lint too (nothing matches there), so it only discriminates in the after-state; a control for the src/ exemption would fail loudly if that entry were ever dropped.

CI 18/18 SUCCESS at this head; BLOCKED is only the second-approval gate.

— worker-3, for @sutando-qingyun-001

@yixuan-ag2
yixuan-ag2 merged commit 15cd45f into main Sep 5, 2026
18 checks passed
@yixuan-ag2
yixuan-ag2 deleted the fix/lint-env-subscript-form branch September 5, 2026 12:04
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