fix(kaizen): scope still_exists to repo paths; test age nudge; tidy --today
- Add REPO_DIRS constant; still_exists now only checks tokens that start with a known repo top-level dir, ignoring plugin names (caddy-dns/gandi), make command fragments (tf-init/plan), and role-relative paths. - Add test_still_exists_ignores_non_repo_tokens (was failing before fix). - Add test_nudge_line_overdue_on_age to close coverage gap on age threshold. - Add load_signals docstring. - Replace manual --today date parsing with datetime.date.fromisoformat type converter so malformed dates give a clean argparse error. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b185ac4765
commit
fd1e83a378
2 changed files with 20 additions and 8 deletions
|
|
@ -21,6 +21,10 @@ NUDGE_MIN_OPEN = 8
|
|||
NUDGE_MAX_AGE_DAYS = 21
|
||||
NUDGE_MIN_RECURRENCE = 3
|
||||
|
||||
# Top-level repo dirs — only tokens under these are treated as repo-root paths
|
||||
# for still_exists (avoids false negatives on plugin names, make commands, etc.).
|
||||
REPO_DIRS = ("roles/", "scripts/", "docs/", "playbooks/", "inventories/", "tests/", "terraform/", ".claude/")
|
||||
|
||||
TAG_RE = re.compile(r"`\[(friction|gotcha|recurring|unused)\]`")
|
||||
DATE_RE = re.compile(r"(\d{4})-(\d{2})-(\d{2})")
|
||||
ORDINAL_RE = re.compile(r"(\d+)(?:st|nd|rd|th)\s+(?:occurrence|reinforcement|time)", re.I)
|
||||
|
|
@ -106,7 +110,8 @@ def parse_signal(raw, today):
|
|||
first_seen = None
|
||||
age_days = None
|
||||
paths = parse_paths(raw)
|
||||
still_exists = all(os.path.exists(os.path.join(REPO_ROOT, p)) for p in paths) if paths else True
|
||||
repo_paths = [p for p in paths if p.startswith(REPO_DIRS)]
|
||||
still_exists = all(os.path.exists(os.path.join(REPO_ROOT, p)) for p in repo_paths) if repo_paths else True
|
||||
return {
|
||||
"tag": tag_m.group(1) if tag_m else None,
|
||||
"first_seen": first_seen,
|
||||
|
|
@ -119,6 +124,7 @@ def parse_signal(raw, today):
|
|||
|
||||
|
||||
def load_signals(path, today):
|
||||
"""Read a FRICTION.md file and return its Open signals as parsed dicts."""
|
||||
with open(path, encoding="utf-8") as fh:
|
||||
text = fh.read()
|
||||
return [parse_signal(s, today) for s in split_signals(extract_open_section(text))]
|
||||
|
|
@ -137,16 +143,12 @@ def nudge_line(signals):
|
|||
def main():
|
||||
parser = argparse.ArgumentParser(description="Parse FRICTION.md Open signals for /kaizen.")
|
||||
parser.add_argument("--nudge", action="store_true", help="print a one-line overdue summary")
|
||||
parser.add_argument("--today", help="override today's date (YYYY-MM-DD) for testing")
|
||||
parser.add_argument("--today", type=datetime.date.fromisoformat,
|
||||
help="override today's date (YYYY-MM-DD) for testing")
|
||||
parser.add_argument("--file", default=FRICTION, help="path to FRICTION.md")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.today:
|
||||
y, m, d = args.today.split("-")
|
||||
today = datetime.date(int(y), int(m), int(d))
|
||||
else:
|
||||
today = datetime.date.today()
|
||||
|
||||
today = args.today or datetime.date.today()
|
||||
signals = load_signals(args.file, today)
|
||||
if args.nudge:
|
||||
print(nudge_line(signals))
|
||||
|
|
|
|||
|
|
@ -110,6 +110,16 @@ def test_nudge_line_overdue_on_count():
|
|||
assert "OVERDUE" in fs.nudge_line(sigs)
|
||||
|
||||
|
||||
def test_still_exists_ignores_non_repo_tokens():
|
||||
sig = fs.parse_signal("`[gotcha]` **x** (2026-06-01): `caddy-dns/gandi` and `make tf-init/plan`", TODAY)
|
||||
assert sig["still_exists"] is True
|
||||
|
||||
|
||||
def test_nudge_line_overdue_on_age():
|
||||
sigs = [{"age_days": 21, "recurrence_count": 1}]
|
||||
assert "OVERDUE" in fs.nudge_line(sigs)
|
||||
|
||||
|
||||
def test_load_signals_reads_real_friction_file():
|
||||
path = os.path.join(os.path.dirname(__file__), "..", "docs", "FRICTION.md")
|
||||
sigs = fs.load_signals(path, TODAY)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue