feat: add pt hygiene --json portfolio hygiene state command#126
Merged
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Per-repo scan loop now isolates exceptions; broken repos surface as
{error: {class, message}} entries while sibling repos still scan.
- Stale PROGRESS.md detection reuses _hygiene_non_progress_dirty_files
so a repo whose only dirty file is PROGRESS.md no longer trips the
finding (PROGRESS.md is the documented always-dirty file).
- _hygiene_branches_ahead rewritten to use rev-list per upstream-tracking
branch; the previous %(ahead-behind:origin/HEAD) atom failed when
origin/HEAD wasn't set (fresh clones, test fixtures).
- Added behavioral tests for: per-repo isolation, stale PROGRESS.md
positive/negative cases, branch-ahead-of-remote with a real bare
upstream, and open_pr_drift via mocked gh CLI (populated, gh missing,
gh non-zero). Also asserts age_days key in schema completeness check.
Test count: 9 -> 16.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…schema uniform (#6149)
Error result entries previously emitted findings: {} alongside an error
key. That split shape broke the schema contract: a naive consumer doing
for name, finding in repo["findings"].items() would silently read the
empty dict as "checked and found nothing" rather than "couldn't check."
Option A: drop findings from error entries entirely. Consumers MUST now
branch on "error" before accessing "findings"; absence makes wrong code
fail loud (KeyError) rather than reading misleading semantics.
Documented the disjoint shapes in USAGE.md and added a dedicated test
asserting the contract (error entries: findings absent; clean entries:
findings present with all six keys).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
pt hygiene [--json] [--project NAME] [--quiet]— a read-only portfolio scanner that detects hygiene drift across every git repo underPROJECTS_BASE_DIR.PROGRESS.md), local-only branches, branches ahead of remote, stashes, open bot-authored PRs older than 24h (graceful degradation ifghunavailable), stalePROGRESS.md(older than 7 days while non-PROGRESS dirty files exist).pt.hygiene.v1. Exit 0 clean / 6 (EXIT_HYGIENE_FINDING) on any finding — for cron and Phase C's Stop hook to branch on.PT_NO_BANNER,PT_SUPPRESS_MIGRATION_WARNING,PT_SKIP_DOPPLER.Phase context
Phase A of the Locked Project Hygiene Workflow (umbrella card #6118). Foundation for B (
branch-on-first-edithook), C (Stop-event four-condition gate), and E (portfolio rollout).Per-repo result schema contract
Two disjoint shapes:
{project, path, clean: bool, findings: {dirty_tree, local_only_branches, branches_ahead_of_remote, stashes, open_pr_drift, stale_progress_md}}— every finding key is always present.{project, path, clean: false, error: {class, message}}—findingsis omitted.Consumers MUST check
errorbefore iteratingfindings. Documented explicitly inUSAGE.md.Notable robustness wins discovered during review
try/except Exception, surfacing one broken repo as anerrorentry and continuing — one bad repo does not crash the whole portfolio scan.branches_aheadrewrite: the original implementation used%(ahead-behind:origin/HEAD)which silently broke on fresh clones with noorigin/HEADset. Now iteratesfor-each-ref refs/headsand resolves each branch's upstream individually viagit rev-list --count, robust against missingorigin/HEAD, detached HEAD, and per-branch failures.Test plan
pytest tests/test_hygiene_cli.py -v)PermissionErrorinjection — siblings still scan, exit code still 6)dirty_treeandstale_progress_md(shared helper_hygiene_non_progress_dirty_files)findingsand clean entries carry all 6 finding keysghgraceful degradation tested (missing binary, non-zero exit)pt hygiene --jsonon this machine produces a validpt.hygiene.v1payload covering all repos under~/projects/.pt hygiene(no--json) produces readable per-repo summaries; broken repos clearly tagged asscan error: ….Code review history
9e6eb40: FAIL — 2 HIGH (no per-repo isolation, PROGRESS.md exclusion inconsistency in stale check), 1 MEDIUM (test gaps), 1 LOW.4c98935: FAIL — 1 MEDIUM (error-result schema split). Original 4 findings cleanly resolved, but error entries hadfindings: {}while success entries had full sub-keys, breaking schema-conformant consumers.752980a: PASS — error entries omitfindingsentirely; consumers branch onerrorfirst; contract documented in USAGE.md.Related