Skip to content

feat(B-0156): Phase 6 .py policy CI gate — no-python-files lint#3949

Merged
AceHack merged 1 commit into
mainfrom
otto-cli/b0156-phase6-no-python-lint-2026-05-16
May 16, 2026
Merged

feat(B-0156): Phase 6 .py policy CI gate — no-python-files lint#3949
AceHack merged 1 commit into
mainfrom
otto-cli/b0156-phase6-no-python-lint-2026-05-16

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 16, 2026

Summary

Smallest safe slice of B-0156 (TypeScript standardization). Closes Phase 6 — .py policy CI enforcement.

Substrate-drift discriminator at session start confirmed Phases 1-5 = DONE (all six named non-install .sh files already ported to .ts and deleted; tools/profile.ts, tools/peer-call/amara.ts, tools/peer-call/ani.ts, tools/hygiene/{snapshot,check}-github-settings.ts, tools/hygiene/check-tick-history-shard-schema.ts all exist). Phase 6 (.py policy enforcement) was the only outstanding acceptance bullet.

What lands

  • tools/lint/no-python-files.ts — TS+Bun lint (per Rule 0: no .sh outside install graph). Walks the tree, hard-excludes references/upstreams, .venv, __pycache__, site-packages, tools/lean4/.lake, node_modules, bin/obj. Reads tools/lint/no-python-files.allowlist for explicit exceptions. Exit 0 clean / 1 flagged / 2 allowlist-missing.
  • tools/lint/no-python-files.allowlist — starts empty (current repo state: 0 .py files in our scope, matching the row's audit baseline). Legitimate exceptions land here with reason comments.
  • tools/lint/no-python-files.test.ts — 9 bun test unit tests against synthetic trees (clean / flagged / allowlisted / each hard-exclude segment / --list mode / comment-line handling / missing-allowlist).
  • .github/workflows/gate.yml — new lint-no-python-files job adjacent to lint-no-empty-dirs, same shape (3-min timeout, install toolchain, run lint). No untrusted input in run: lines.
  • Backlog row — Phase 6 marked DONE; last_updated: 2026-05-16.
  • Tick sharddocs/hygiene-history/ticks/2026/05/16/2157Z.md.

Focused checks

Check Outcome
bun test tools/lint/no-python-files.test.ts 9 pass / 0 fail / 15 expect() calls
bun tools/lint/no-python-files.ts (real repo) OK (0 allowlisted, 0 flagged) — exit 0
bun tools/lint/no-empty-dirs.ts (regression check after adding new files) green
js-yaml parse of .github/workflows/gate.yml 17 jobs, lint-no-python-files present
git ls-tree HEAD | wc -l vs origin/main (broken-commit canary) 53 vs 53, OK

Test plan

  • CI gate.yml runs lint-no-python-files step — green on this PR (0 .py files in scope)
  • No regression on existing lint-no-empty-dirs or other lint jobs
  • Backlog index regen produced byte-identical docs/BACKLOG.md (verified locally; no diff)

Composes with

  • B-0156 acceptance criterion 4 (.py policy lint added to gate.yml)
  • Rule 0 (TS over bash for non-install scripts) — implementation IS TS, not the inline find bash the row had drafted
  • no-empty-dirs.ts template — same allowlist + hard-exclude + posix-rel + bun-test patterns

🤖 Generated with Claude Code

Smallest safe slice of B-0156 (TypeScript standardization). Phases
1-5 were already DONE per the row (all six named .sh files ported
to .ts and deleted); Phase 6 (.py policy enforcement) was the only
outstanding acceptance bullet.

- tools/lint/no-python-files.ts — TS+Bun lint (Rule 0: no .sh
  outside install graph). Walks the tree, hard-excludes
  references/upstreams, .venv, __pycache__, site-packages,
  tools/lean4/.lake, node_modules, bin/obj. Reads
  tools/lint/no-python-files.allowlist for explicit exceptions.
  Exit 0 clean / 1 flagged / 2 allowlist-missing.
- tools/lint/no-python-files.allowlist — starts empty (current
  repo state: 0 .py files in our scope, matching the row's
  audit baseline). Legitimate exceptions land here with reason
  comments.
- tools/lint/no-python-files.test.ts — 9 bun tests against
  synthetic trees (clean, flagged, allowlisted, each hard-exclude
  segment, --list mode, comment-line handling, missing-allowlist).
- .github/workflows/gate.yml — lint-no-python-files job adjacent
  to lint-no-empty-dirs, same shape (3 min timeout, install
  toolchain, run lint). No untrusted input in run: lines.
- docs/backlog/P1/B-0156-...md — Phase 6 marked DONE; last_updated
  bumped to 2026-05-16.

Focused checks: 9/9 unit tests pass; real-repo lint reports
"0 allowlisted, 0 flagged"; no-empty-dirs regression green;
gate.yml parses cleanly (17 jobs, new job present).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 16, 2026 21:59
@AceHack AceHack enabled auto-merge (squash) May 16, 2026 21:59
@AceHack AceHack merged commit bd99605 into main May 16, 2026
35 of 36 checks passed
@AceHack AceHack deleted the otto-cli/b0156-phase6-no-python-lint-2026-05-16 branch May 16, 2026 22:02
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a Bun/TypeScript CI lint gate for B-0156 Phase 6 to prevent first-party .py files outside approved exclusions/allowlist.

Changes:

  • Adds no-python-files lint tool, allowlist, and Bun tests.
  • Wires the lint into gate.yml.
  • Updates backlog/tick documentation for the completed phase.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tools/lint/no-python-files.ts Implements the .py file scanner and policy enforcement.
tools/lint/no-python-files.test.ts Adds unit tests for lint outcomes and exclusions.
tools/lint/no-python-files.allowlist Documents the explicit allowed .py path list.
.github/workflows/gate.yml Adds the CI job that runs the new lint.
docs/backlog/P1/B-0156-typescript-standardization-non-install-scripts-aaron-2026-05-01.md Marks Phase 6 complete and updates status details.
docs/hygiene-history/ticks/2026/05/16/2157Z.md Records the implementation tick for this phase.
Comments suppressed due to low confidence (1)

tools/lint/no-python-files.ts:62

  • This second spawnSync("git", ...) has the same SonarJS issue as the repoRoot() call: TypeScript tools in this repo suppress sonarjs/no-os-command-from-path with a short rationale when intentionally invoking git from PATH. Without that, the new tool is likely to break the TypeScript lint gate.
  const result = spawnSync("git", ["check-ignore", "--stdin"], {

];

function repoRoot(): string {
const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {

function makeRepo(): string {
const root = mkdtempSync(join(tmpdir(), "no-python-files-"));
spawnSync("git", ["init", "-q", root], { encoding: "utf8" });
);
for (const f of flagged) process.stderr.write(` ${f}\n`);
process.stderr.write("\n");
process.stderr.write("Per B-0156 (Aaron 2026-05-01): TS is preferred over Python in our codebase.\n");
Comment on lines +229 to +230
const ignored = new Set(gitCheckIgnore(candidates));
const filtered = candidates.filter((f) => !ignored.has(f));
Comment on lines +12 to +14
# .venv, node_modules, __pycache__, site-packages, .lake are hard-
# excluded by the script itself (HARD_EXCLUDE_PREFIXES /
# HARD_EXCLUDE_SEGMENTS) and do NOT need to be listed here.
Comment on lines +883 to +884
# references/upstreams, .venv, __pycache__, site-packages, .lake
# are hard-excluded by the script itself.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 72ab8dc69c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (isHardExcluded(rel)) continue;
if (e.isDirectory()) {
stack.push(full);
} else if (e.isFile() && e.name.endsWith(".py")) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Detect Python extensions case-insensitively

findPythonFiles() only flags names ending with lowercase .py, so a committed file like scripts/tool.PY or tool.Py is treated as non-Python and the new CI job passes even though policy intent is to block Python files. This creates a straightforward bypass of the enforcement introduced in this commit; normalize the filename case (or use a case-insensitive suffix check) before testing the extension.

Useful? React with 👍 / 👎.

AceHack added a commit that referenced this pull request May 16, 2026
Per-tick shard documenting:
- Cron sentinel re-arm at session start (catch 43 prevention)
- Substrate-drift discriminator on B-0156 (audit stale; Phases
  1-5 effectively complete; Phase 6 landed via #3949)
- Smallest safe slice picked: tools/profile.test.ts
- 8/8 tests pass verification trace
- Pure-git tier — PR creation deferred to post-rate-limit-reset

operative-authorization: aaron 2026-05-14: "- **Devil-pole** (edge-runner drive): keep pushing, discover, go hard, never-be-idle"

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.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.

2 participants