oct.git.quality_gate module#

Purpose#

Orchestrate the Phase 4B unified quality gate: lint + format dry-run + secrets detection on a scoped set of files. Returns a structured QualityGateResult that downstream consumers (oct git check, oct git commit, hooks, MCP) can inspect and render.

Responsibilities#

  • Resolve file scope (staged / changed / all) via oct.core.git.

  • Run the linter on Python files via oct.linter.oct_lint._lint_single_file().

  • Run the formatter in dry-run mode via oct.formatter.oct_format.format_file().

  • Run secrets detection (content + filename pattern) via oct.linter.oct_lint.check_no_hardcoded_secrets() and oct.core.exclusions.NEVER_EXPORT_FILE_PATTERNS.

  • Optionally run pytest via subprocess (--include-tests).

  • Support --fix two-pass mode: detect → fix → re-verify.

Diagnostics#

Domain: GIT Levels:

L2 — lifecycle: gate start/end, scope resolution L3 — details: per-pass summary counts L4 — deep trace: per-file check outcomes

Contracts#

  • This module does not import click. It is a pure orchestrator.

  • It calls existing library functions (linter, formatter, exclusions) directly; it never shells out to oct lint or oct format.

  • The only subprocess call is for pytest (--include-tests), which is a distinct tool boundary.

  • run_quality_gate() never raises on check failures; it returns a QualityGateResult with the findings. Only infrastructure errors (missing git repo, broken imports) may propagate.

class oct.git.quality_gate.PreCommitResult(ok: bool, exit_code: int, errors: list, branch: str, effective_profile: str, staged_files: list, pb_advisory: str = '')[source]#

Bases: object

OI-527: structured outcome of the three pre-gate checks that run before oct git commit executes the quality gate.

When ok is True the caller proceeds to the quality gate using staged_files; the other fields carry context the command needs regardless (branch, effective_profile). When ok is False the caller emits errors on stderr (or as JSON) and exits with exit_code.

pb_advisory is a protected-branch advisory string that must be echoed whether or not the check blocked the commit; callers treat it as a side-channel warning.

branch: str#
effective_profile: str#
errors: list#
exit_code: int#
ok: bool#
pb_advisory: str = ''#
staged_files: list#
class oct.git.quality_gate.QualityGateResult(files_checked: int = 0, lint_violations: int = 0, format_violations: int = 0, secrets_findings: list = <factory>, test_failures: int = 0, duration_ms: int = 0, exit_code: int = 0, per_file: list = <factory>)[source]#

Bases: object

Structured output from a quality-gate run.

Every field is JSON-serialisable (primitives, lists of tuples/dicts). Field names must match the attribute access in oct.git.policy.apply_profile_policy().

duration_ms: int = 0#
exit_code: int = 0#
files_checked: int = 0#
format_violations: int = 0#
lint_violations: int = 0#
per_file: list#

Per-file dicts suitable for --json rendering.

secrets_findings: list#

List of (path_str, line, reason) tuples.

test_failures: int = 0#
oct.git.quality_gate.pre_commit_checks(project_root: Path, branch: str, message: str, force_commit: bool, profile: str, octrc: dict) PreCommitResult[source]#

OI-527: run the three pre-gate checks and collapse their outcomes into a single PreCommitResult.

The helper replaces the four sequential early-return blocks that previously lived in git_commit_cmd (commit-message validation, staged-files presence, protected-branch guard). Exit codes and side effects match the original CLI behaviour byte-for-byte:

  • exit_code == 3 — invalid commit message (skipped when force_commit is True).

  • exit_code == 4 — no staged files.

  • exit_code == 1 — protected-branch block.

oct.git.quality_gate.run_quality_gate(project_root: Path, scope: Literal['staged', 'changed', 'all'], profile: Literal['proto', 'compact', 'strict'], include_tests: bool = False, fix: bool = False, sandbox: bool = False) QualityGateResult[source]#

Orchestrate lint + format-dry-run + secrets on the scoped files.

When fix=True, runs in two passes:

  1. Detect pass: run the gate with fix=False to record the initial state.

  2. Fix pass: run lint and format with fix_mode=True to apply auto-fixes.

  3. Re-verify pass: run the gate again with fix=False. The returned result reflects the post-fix state.

Secrets are never auto-fixed (no safe auto-fix for a hardcoded password). The --fix flag does not bypass the always-blocking secrets rule.

Parameters:
  • project_root – Resolved project root containing .octrc.json, tests/, etc.

  • scope"staged" checks only the git index; "changed" checks staged + unstaged + untracked; "all" checks every Python file.

  • profile – Lint profile: "proto" / "compact" / "strict".

  • include_tests – If True, also run pytest as a subprocess (exit code 4 on fail).

  • fix – If True, attempt auto-fix of lint/format violations, then re-check.

  • sandbox – OI-517: when True and include_tests is True, the pytest subprocess runs under the MCP sandbox environment — sanitised env, no network side-channels via PYTHONPATH, output capped.

oct.git.quality_gate.run_quality_gate_on_files(project_root: Path, files: list[Path], profile: Literal['proto', 'compact', 'strict'], include_tests: bool = False, fix: bool = False, sandbox: bool = False) QualityGateResult[source]#

Run the quality gate on an explicit file list.

Identical contract to run_quality_gate() but accepts a pre-resolved list of files instead of computing one from a scope string. This is the entry point used by F1.3 per-subproject fan-out at workspace-level commit, where the workspace-wide staged set is grouped into subproject buckets and each bucket runs under its own profile.

Parameters:
  • project_root – Resolved project root for octrc / linter-context / secrets config loading. For a per-subproject call, pass the subproject’s path; for a workspace-level call, pass the workspace root.

  • files – Pre-filtered list of files to lint / format / scan. May include non-Python files (passed through to the secrets scanner; lint/format ignore them via the suffix filter).

  • profile – Lint profile: "proto" / "compact" / "strict".

  • include_tests – Same semantics as run_quality_gate().

  • fix – Same semantics as run_quality_gate().

  • sandbox – Same semantics as run_quality_gate().