@techierathore/techieflow 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/TechieFlow/agents/analyst.md +5 -13
- package/.claude/commands/TechieFlow/agents/architect.md +1 -16
- package/.claude/commands/TechieFlow/agents/flow-master.md +42 -215
- package/.claude/commands/TechieFlow/agents/verifier.md +24 -64
- package/.claude/commands/TechieFlow/tasks/_metrics-emit-gate.md +24 -117
- package/.claude/commands/TechieFlow/tasks/_owner-language.md +26 -0
- package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +20 -84
- package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +10 -74
- package/.claude/commands/TechieFlow/tasks/_yolo-mode.md +20 -58
- package/.claude/commands/TechieFlow/tasks/amend-docs.md +21 -143
- package/.claude/commands/TechieFlow/tasks/build-phase.md +14 -224
- package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +4 -0
- package/.claude/commands/TechieFlow/tasks/create-doc.md +4 -0
- package/.claude/commands/TechieFlow/tasks/create-project-brief.md +22 -0
- package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +31 -505
- package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +28 -213
- package/.claude/commands/TechieFlow/tasks/deploy-checklist.md +19 -0
- package/.claude/commands/TechieFlow/tasks/devguide.md +10 -219
- package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +4 -0
- package/.claude/commands/TechieFlow/tasks/fix-issues.md +10 -129
- package/.claude/commands/TechieFlow/tasks/generate-html.md +5 -1
- package/.claude/commands/TechieFlow/tasks/handoff-phase.md +18 -16
- package/.claude/commands/TechieFlow/tasks/log-miss.md +19 -158
- package/.claude/commands/TechieFlow/tasks/metrics-report.md +3 -1
- package/.claude/commands/TechieFlow/tasks/mockups.md +12 -117
- package/.claude/commands/TechieFlow/tasks/refresh-status.md +9 -130
- package/.claude/commands/TechieFlow/tasks/split-brd.md +10 -126
- package/.claude/commands/TechieFlow/tasks/triage-and-fix.md +14 -0
- package/.claude/commands/TechieFlow/tasks/triage-issues.md +11 -152
- package/.claude/commands/TechieFlow/tasks/verify-phase.md +36 -572
- package/.opencode/plugin/techieflow.js +40 -13
- package/.tfcore/agents/analyst.md +5 -13
- package/.tfcore/agents/architect.md +1 -16
- package/.tfcore/agents/flow-master.md +42 -215
- package/.tfcore/agents/verifier.md +24 -64
- package/.tfcore/core-config.yaml +10 -0
- package/.tfcore/hooks/block-git.sh +4 -4
- package/.tfcore/hooks/guard-artifacts.sh +2 -2
- package/.tfcore/hooks/guard-build.sh +76 -0
- package/.tfcore/hooks/guard-db.sh +90 -0
- package/.tfcore/hooks/guard-metrics.sh +68 -0
- package/.tfcore/hooks/guard-status-html.sh +116 -71
- package/.tfcore/hooks/guard-status.sh +43 -16
- package/.tfcore/hooks/guard-verify-deps.sh +124 -0
- package/.tfcore/hooks/guard-verify.sh +70 -48
- package/.tfcore/hooks/sweep-artifacts.sh +2 -2
- package/.tfcore/install-manifest.yaml +0 -18
- package/.tfcore/routing.yaml +86 -14
- package/.tfcore/standards/coding-standards-core.md +61 -0
- package/.tfcore/standards/coding-standards-dotnet.md +93 -0
- package/.tfcore/tasks/_metrics-emit-gate.md +24 -117
- package/.tfcore/tasks/_owner-language.md +26 -0
- package/.tfcore/tasks/_smoke-test-policy.md +20 -84
- package/.tfcore/tasks/_status-update-gate.md +10 -74
- package/.tfcore/tasks/_yolo-mode.md +20 -58
- package/.tfcore/tasks/amend-docs.md +21 -143
- package/.tfcore/tasks/build-phase.md +14 -224
- package/.tfcore/tasks/create-deep-research-prompt.md +4 -0
- package/.tfcore/tasks/create-doc.md +4 -0
- package/.tfcore/tasks/create-project-brief.md +22 -0
- package/.tfcore/tasks/day1-brownfield.md +31 -505
- package/.tfcore/tasks/day1-greenfield.md +28 -213
- package/.tfcore/tasks/deploy-checklist.md +19 -0
- package/.tfcore/tasks/devguide.md +10 -219
- package/.tfcore/tasks/facilitate-brainstorming-session.md +4 -0
- package/.tfcore/tasks/fix-issues.md +10 -129
- package/.tfcore/tasks/generate-html.md +5 -1
- package/.tfcore/tasks/handoff-phase.md +18 -16
- package/.tfcore/tasks/log-miss.md +19 -158
- package/.tfcore/tasks/metrics-report.md +3 -1
- package/.tfcore/tasks/mockups.md +12 -117
- package/.tfcore/tasks/refresh-status.md +9 -130
- package/.tfcore/tasks/split-brd.md +10 -126
- package/.tfcore/tasks/triage-and-fix.md +14 -0
- package/.tfcore/tasks/triage-issues.md +11 -152
- package/.tfcore/tasks/verify-phase.md +36 -572
- package/.tfcore/telemetry/SCHEMA.md +100 -8
- package/.tfcore/telemetry/tf-metrics.sh +564 -32
- package/.tfcore/templates/stack-defaults/dotnet.md +35 -0
- package/.tfcore/templates/stack-questions.md +77 -0
- package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +3 -4
- package/.tfcore/templates/v4custom/app-architecture-tmpl.md +107 -115
- package/.tfcore/templates/v4custom/app-brd-tmpl.md +108 -184
- package/.tfcore/templates/v4custom/app-brief-tmpl.md +65 -0
- package/.tfcore/templates/v4custom/app-checklist-tmpl.md +48 -73
- package/.tfcore/templates/v4custom/app-claude-md-tmpl.md +1 -1
- package/.tfcore/templates/v4custom/app-coding-standards-tmpl.md +52 -0
- package/.tfcore/templates/v4custom/app-decision-request-tmpl.md +85 -0
- package/.tfcore/templates/v4custom/app-deployment-checklist-tmpl.md +86 -0
- package/.tfcore/templates/v4custom/app-devguide-tmpl.md +69 -146
- package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +86 -31
- package/.tfcore/templates/v4custom/app-phases-tmpl.md +35 -0
- package/.tfcore/templates/v4custom/app-productguide-tmpl.md +38 -35
- package/.tfcore/templates/v4custom/app-project-status-tmpl.md +63 -73
- package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +62 -37
- package/.tfcore/templates/v4custom/app-usageguide-tmpl.md +69 -54
- package/.tfcore/templates/v4custom/build-invocation-ladder.md +17 -180
- package/.tfcore/templates/v4custom/build-subagent-prompt.md +17 -0
- package/.tfcore/templates/v4custom/html-render-shell.md +1 -1
- package/.tfcore/templates/v4custom/metrics-report-template.md +2 -2
- package/.tfcore/user-guide.md +0 -23
- package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-checklist-edit.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-codex-bind.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-codex-telemetry.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-perf-grade.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
- package/.tfcore/utils/tf-brd-status.py +176 -0
- package/.tfcore/utils/tf-brd-status.sh +19 -0
- package/.tfcore/utils/tf-build-list.py +211 -0
- package/.tfcore/utils/tf-build-list.sh +21 -0
- package/.tfcore/utils/tf-build.sh +143 -0
- package/.tfcore/utils/tf-cdp-relay.ps1 +64 -0
- package/.tfcore/utils/tf-checklist-edit.py +133 -0
- package/.tfcore/utils/tf-day1-files.py +208 -0
- package/.tfcore/utils/tf-day1-files.sh +20 -0
- package/.tfcore/utils/tf-devguide-list.py +249 -0
- package/.tfcore/utils/tf-devguide-list.sh +18 -0
- package/.tfcore/utils/tf-doc-check.py +1369 -0
- package/.tfcore/utils/tf-doc-check.sh +29 -0
- package/.tfcore/utils/tf-emit.sh +545 -18
- package/.tfcore/utils/tf-fix-close.sh +115 -0
- package/.tfcore/utils/tf-gitignore-audit.sh +50 -3
- package/.tfcore/utils/tf-goal.sh +284 -66
- package/.tfcore/utils/tf-harness.sh +4 -9
- package/.tfcore/utils/tf-log-miss.py +197 -0
- package/.tfcore/utils/tf-log-miss.sh +14 -0
- package/.tfcore/utils/tf-misses-md.py +208 -0
- package/.tfcore/utils/tf-misses-md.sh +14 -0
- package/.tfcore/utils/tf-mockups-locate.py +66 -0
- package/.tfcore/utils/tf-mockups-locate.sh +18 -0
- package/.tfcore/utils/tf-model-pick.sh +475 -0
- package/.tfcore/utils/tf-opencode-audit.py +23 -7
- package/.tfcore/utils/tf-perf-grade.py +97 -0
- package/.tfcore/utils/tf-perf-grade.sh +12 -0
- package/.tfcore/utils/tf-phase.sh +92 -0
- package/.tfcore/utils/tf-render-html.py +20 -0
- package/.tfcore/utils/tf-routing-bind.sh +97 -6
- package/.tfcore/utils/tf-routing.sh +13 -7
- package/.tfcore/utils/tf-selfcheck.sh +323 -0
- package/.tfcore/utils/tf-split-brd.py +309 -0
- package/.tfcore/utils/tf-split-brd.sh +22 -0
- package/.tfcore/utils/tf-status-evidence.sh +68 -0
- package/.tfcore/utils/tf-status-facts.py +358 -0
- package/.tfcore/utils/tf-status-facts.sh +20 -0
- package/.tfcore/utils/tf-triage.py +243 -0
- package/.tfcore/utils/tf-triage.sh +16 -0
- package/.tfcore/utils/tf-verify-boot.sh +250 -0
- package/.tfcore/utils/tf-verify-emit.sh +110 -0
- package/.tfcore/utils/tf-verify-env.sh +88 -0
- package/.tfcore/utils/tf-verify-list.py +270 -0
- package/.tfcore/utils/tf-verify-list.sh +13 -0
- package/.tfcore/utils/tf-verify-screens.mjs +355 -0
- package/.tfcore/utils/tf-verify-screens.sh +31 -0
- package/.tfcore/utils/tf-verify-tests.sh +166 -0
- package/.tfcore/utils/tf-verify-verdict.py +292 -0
- package/.tfcore/utils/tf-verify-verdict.sh +15 -0
- package/.tfcore/utils/tf-yolo.sh +59 -0
- package/README.md +120 -1145
- package/docs/TechieFlow-Installation.md +1 -5
- package/opencode.jsonc +37 -33
- package/package.json +1 -5
- package/scripts/install.mjs +56 -46
- package/.claude/commands/TechieFlow/tasks/advanced-elicitation.md +0 -117
- package/.claude/commands/TechieFlow/tasks/author-brd.md +0 -79
- package/.claude/commands/TechieFlow/tasks/document-project.md +0 -343
- package/.claude/commands/TechieFlow/tasks/execute-checklist.md +0 -86
- package/.claude/commands/TechieFlow/tasks/index-docs.md +0 -173
- package/.claude/commands/TechieFlow/tasks/kb-mode-interaction.md +0 -75
- package/.claude/commands/TechieFlow/tasks/shard-doc.md +0 -185
- package/.codex/config.toml +0 -46
- package/.codex/hooks.json +0 -63
- package/.codex/rules/techieflow.rules +0 -8
- package/.tfcore/hooks/codex-adapter.py +0 -248
- package/.tfcore/tasks/advanced-elicitation.md +0 -117
- package/.tfcore/tasks/author-brd.md +0 -79
- package/.tfcore/tasks/document-project.md +0 -343
- package/.tfcore/tasks/execute-checklist.md +0 -86
- package/.tfcore/tasks/index-docs.md +0 -173
- package/.tfcore/tasks/kb-mode-interaction.md +0 -75
- package/.tfcore/tasks/shard-doc.md +0 -185
- package/.tfcore/templates/architecture-tmpl.yaml +0 -650
- package/.tfcore/templates/brownfield-architecture-tmpl.yaml +0 -476
- package/.tfcore/templates/competitor-analysis-tmpl.yaml +0 -306
- package/.tfcore/templates/front-end-architecture-tmpl.yaml +0 -218
- package/.tfcore/templates/fullstack-architecture-tmpl.yaml +0 -823
- package/.tfcore/templates/market-research-tmpl.yaml +0 -252
- package/.tfcore/templates/project-brief-tmpl.yaml +0 -221
- package/.tfcore/utils/techieflow-doc-template.md +0 -325
- package/.tfcore/utils/tf-codex-bind.py +0 -196
- package/.tfcore/utils/tf-codex-telemetry.py +0 -60
- package/WORKFLOW.html +0 -1986
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tf-fix-close.sh — the telemetry a fix owes when it lands (Sitting 4c, 2026-09-06; D-15).
|
|
3
|
+
#
|
|
4
|
+
# bash .tfcore/utils/tf-fix-close.sh <App> [--started <ISO>] [--reqs REQ-UI-009,REQ-FN-014]
|
|
5
|
+
# [--subagents trblazeui,builder] [--build pass|fail|not-run] [--files N]
|
|
6
|
+
# bash .tfcore/utils/tf-fix-close.sh <App> --misses MISS-App-20260901-06,MISS-App-20260830-03 \
|
|
7
|
+
# --fix-cmd amend-docs [--verdict Verified]
|
|
8
|
+
#
|
|
9
|
+
# The second form closes misses DIRECTLY, by id, with no checklist row and no verify ledger.
|
|
10
|
+
# It exists because a miss whose deficient artifact is a document is fixed by *amend-docs
|
|
11
|
+
# editing the BRD -- never by the verifier touching a row -- so the row-driven form above can
|
|
12
|
+
# never reach it, and the miss stayed open forever (TF-016). It writes NO run record: the
|
|
13
|
+
# amending command emits its own through the status gate. `fix_cmd: "amend-docs"` was already
|
|
14
|
+
# legal in SCHEMA §5.5.2; only this door was missing.
|
|
15
|
+
#
|
|
16
|
+
# Emits, in this order, through tf-emit.sh only:
|
|
17
|
+
# 1. the fix-issues run record (cmd fix-issues, mode fix, the rows touched) — first, because the
|
|
18
|
+
# miss-fix records below carry no numbers of their own: the emitter copies this run's token window
|
|
19
|
+
# 2. one miss-fix per row that has an open miss, with verdict_after taken from the verify ledger
|
|
20
|
+
# docs/.last-verify.json written by the verify the fix chained, mapped to a checklist status (PASS →
|
|
21
|
+
# Verified; FAIL → FAIL; any other failing verdict or an absent row → Needs re-verify). A row with no open miss is listed,
|
|
22
|
+
# not invented: a fix on a defect nobody logged is a triage gap, so open it first with tf-triage.sh
|
|
23
|
+
# demote or tf-log-miss.sh, then re-run this.
|
|
24
|
+
# --reqs defaults to every row in the ledger. Telemetry never blocks: refusals are printed, exit 0.
|
|
25
|
+
set -u
|
|
26
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
[[ $# -ge 1 && "$1" != "-h" && "$1" != "--help" ]] || { sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//'; exit 3; }
|
|
28
|
+
APP="$1"; shift
|
|
29
|
+
STARTED=""; REQS=""; SUBS=""; BUILD="pass"; FILES=""; MISSES=""; FIXCMD="fix-issues"; VERDICT=""
|
|
30
|
+
while [[ $# -gt 0 ]]; do
|
|
31
|
+
case "$1" in
|
|
32
|
+
--started) STARTED="${2:-}"; shift 2 ;;
|
|
33
|
+
--reqs) REQS="${2:-}"; shift 2 ;;
|
|
34
|
+
--subagents) SUBS="${2:-}"; shift 2 ;;
|
|
35
|
+
--build) BUILD="${2:-}"; shift 2 ;;
|
|
36
|
+
--files) FILES="${2:-}"; shift 2 ;;
|
|
37
|
+
--misses) MISSES="${2:-}"; shift 2 ;;
|
|
38
|
+
--fix-cmd) FIXCMD="${2:-}"; shift 2 ;;
|
|
39
|
+
--verdict) VERDICT="${2:-}"; shift 2 ;;
|
|
40
|
+
*) echo "tf-fix-close: unknown argument $1" >&2; exit 3 ;;
|
|
41
|
+
esac
|
|
42
|
+
done
|
|
43
|
+
[[ -z "$STARTED" ]] && STARTED="$(bash "$HERE/tf-phase.sh" show 2>/dev/null | sed -n 's/.*"started":"\([^"]*\)".*/\1/p')"
|
|
44
|
+
TF_APP="$APP" TF_STARTED="$STARTED" TF_REQS="$REQS" TF_SUBS="$SUBS" TF_BUILD="$BUILD" TF_FILES="$FILES" \
|
|
45
|
+
TF_MISSES="$MISSES" TF_FIXCMD="$FIXCMD" TF_VERDICT="$VERDICT" TF_EMIT="$HERE/tf-emit.sh" python3 - <<'PY'
|
|
46
|
+
import datetime, json, os, subprocess
|
|
47
|
+
EMIT = os.environ["TF_EMIT"]; app = os.environ["TF_APP"]; started = os.environ["TF_STARTED"]
|
|
48
|
+
now = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
49
|
+
def q(*a): return subprocess.run(["bash", EMIT, *a], capture_output=True, text=True).stdout.strip()
|
|
50
|
+
def emit(stream, rec):
|
|
51
|
+
p = subprocess.run(["bash", EMIT, stream], input=json.dumps(rec), capture_output=True, text=True)
|
|
52
|
+
msg = (p.stdout + p.stderr).strip(); ok = p.returncode == 0 and "refus" not in msg.lower() and "error" not in msg.lower()
|
|
53
|
+
if not ok: print(f" {stream}: not written — {msg.splitlines()[0][:160] if msg else 'no reason printed'}")
|
|
54
|
+
return ok
|
|
55
|
+
# --- close by miss id (the *amend-docs door) -------------------------------------------
|
|
56
|
+
direct = [m.strip() for m in (os.environ.get("TF_MISSES") or "").split(",") if m.strip()]
|
|
57
|
+
if direct:
|
|
58
|
+
fix_cmd = os.environ.get("TF_FIXCMD") or "amend-docs"
|
|
59
|
+
verdict = os.environ.get("TF_VERDICT") or "Verified"
|
|
60
|
+
closed = 0
|
|
61
|
+
for mid in direct:
|
|
62
|
+
fa = q("--next-fix-attempt", mid) or "1"
|
|
63
|
+
rec = {"kind": "miss-fix", "miss_id": mid, "fix_cmd": fix_cmd,
|
|
64
|
+
"fix_attempt": int(fa) if fa.isdigit() else 1,
|
|
65
|
+
"verdict_after": verdict, "reopened": False}
|
|
66
|
+
if started:
|
|
67
|
+
rec["fix_run_id"] = started
|
|
68
|
+
if emit("misses", rec):
|
|
69
|
+
closed += 1
|
|
70
|
+
print("%s: %d of %d miss-fix record(s) written (no run record — the amending command emits its own)"
|
|
71
|
+
% (fix_cmd, closed, len(direct)))
|
|
72
|
+
raise SystemExit(0)
|
|
73
|
+
|
|
74
|
+
ledger = {}
|
|
75
|
+
try:
|
|
76
|
+
ledger = json.load(open(os.path.join("docs", ".last-verify.json"), encoding="utf-8"))
|
|
77
|
+
except Exception:
|
|
78
|
+
pass
|
|
79
|
+
rows = ledger.get("rows") or {}
|
|
80
|
+
reqs = [r.strip().upper() for r in os.environ["TF_REQS"].split(",") if r.strip()] or sorted(rows)
|
|
81
|
+
subs = [s.strip() for s in os.environ["TF_SUBS"].split(",") if s.strip()]
|
|
82
|
+
files = os.environ["TF_FILES"]
|
|
83
|
+
run = {"kind": "run", "app": app, "cmd": "fix-issues", "mode": "fix", "ended": now, "reqs_touched": reqs, "reqs_count": len(reqs),
|
|
84
|
+
"subagents": subs, "files_written": int(files) if files.isdigit() else len(reqs), "build_result": os.environ["TF_BUILD"]}
|
|
85
|
+
if started: run["started"] = started
|
|
86
|
+
# one run record per start: a close called twice must not count the fix twice
|
|
87
|
+
runs_path = os.path.join(q("--where") or "docs/metrics", "runs.jsonl")
|
|
88
|
+
already = False
|
|
89
|
+
if started and os.path.isfile(runs_path):
|
|
90
|
+
for line in open(runs_path, encoding="utf-8", errors="replace"):
|
|
91
|
+
if '"cmd":"fix-issues"' in line and f'"started":"{started}"' in line:
|
|
92
|
+
already = True
|
|
93
|
+
break
|
|
94
|
+
ok = True if already else emit("runs", run)
|
|
95
|
+
if already:
|
|
96
|
+
print(f"fix-issues: run record for start {started} already exists; not written again")
|
|
97
|
+
closed, none = 0, []
|
|
98
|
+
for rid in reqs:
|
|
99
|
+
opened = q("--open-miss", rid)
|
|
100
|
+
if not opened:
|
|
101
|
+
none.append(rid); continue
|
|
102
|
+
mid = opened.split()[0]
|
|
103
|
+
fa = q("--next-fix-attempt", mid) or "1"
|
|
104
|
+
v = rows.get(rid)
|
|
105
|
+
# verdict_after takes a checklist status (SCHEMA §5.5): the ledger's verdict is mapped, never passed raw
|
|
106
|
+
# (MISS-TechieFlow-20260906-25: RENDER-FAIL was refused and twenty miss-fix records never landed)
|
|
107
|
+
after = ("Verified" if v == "PASS" else "FAIL" if v in ("FAIL", "BUILD-FAIL") else "Needs re-verify")
|
|
108
|
+
rec = {"kind": "miss-fix", "miss_id": mid, "req_id": rid, "fix_cmd": "fix-issues", "fix_attempt": int(fa) if fa.isdigit() else 1,
|
|
109
|
+
"verdict_after": after, "reopened": False}
|
|
110
|
+
if started: rec["fix_run_id"] = started
|
|
111
|
+
if emit("misses", rec): closed += 1
|
|
112
|
+
print(f"fix-issues: run record {'already there' if already else 'written' if ok else 'not written'}; {closed} miss-fix record(s) with the verifier's verdict"
|
|
113
|
+
+ (f"; no open miss on {', '.join(none)} (log it first, then re-run)" if none else ""))
|
|
114
|
+
PY
|
|
115
|
+
exit 0
|
|
@@ -74,7 +74,7 @@ DRY = os.environ["TF_DRY"] == "1"
|
|
|
74
74
|
# ones this audit exists to catch. The file system is the fact.
|
|
75
75
|
STACKS = [
|
|
76
76
|
("dotnet", (".csproj", ".fsproj", ".vbproj", ".sln", ".slnx"),
|
|
77
|
-
["bin/", "obj/", "*.user", "TestResults/"]),
|
|
77
|
+
["bin/", "obj/", "*.user", "TestResults/", ".vs/"]),
|
|
78
78
|
("node", ("package.json",), ["dist/", "build/", ".next/", ".nuxt/", ".turbo/"]),
|
|
79
79
|
("python", ("pyproject.toml", "setup.py", "requirements.txt"),
|
|
80
80
|
["__pycache__/", "*.py[cod]", ".venv/", "venv/", ".pytest_cache/", "*.egg-info/"]),
|
|
@@ -83,6 +83,14 @@ STACKS = [
|
|
|
83
83
|
("java", ("pom.xml", "build.gradle", "build.gradle.kts"), ["target/", "build/", ".gradle/"]),
|
|
84
84
|
]
|
|
85
85
|
|
|
86
|
+
# Per-developer IDE state, wanted whatever the stack is. It is not build output, but it is
|
|
87
|
+
# the same harm and this tool is the only thing looking: TfLens tracked three .bin files
|
|
88
|
+
# under .vs/ProjectEvaluation/, one carrying 246 absolute paths out of one developer's
|
|
89
|
+
# machine, while the audit ran on that repo every week and said nothing. `.vscode/` is NOT
|
|
90
|
+
# here -- a team often commits it deliberately, and a rule that fights the owner gets
|
|
91
|
+
# switched off. tests/regression/run.sh tf_014.
|
|
92
|
+
IDE_STATE = [".vs/", ".idea/"]
|
|
93
|
+
|
|
86
94
|
# `package.json` alone does NOT make a repo a Node project, and this exception is
|
|
87
95
|
# load-bearing rather than fussy: verify-phase §1 runs `npm init -y` in EVERY repo
|
|
88
96
|
# to provision Playwright, so a bare package.json sits in essentially every .NET
|
|
@@ -114,14 +122,19 @@ def _is_real_node(root, files):
|
|
|
114
122
|
# `npm init -y` writes exactly one script: a `test` stub that echoes and exits 1.
|
|
115
123
|
return any(k in scripts for k in ("build", "dist", "bundle", "compile"))
|
|
116
124
|
|
|
125
|
+
# A NAME list, never a blanket dot-rule. `not d.startswith(".")` used to prune every
|
|
126
|
+
# dot-directory from the walk -- .vs/, .idea/, .gradle/, .terraform/, .pytest_cache/ --
|
|
127
|
+
# which is close to a complete list of the per-developer state a gitignore audit is FOR.
|
|
128
|
+
# The tool cannot report a folder it never visits, and its silence read exactly like a pass.
|
|
117
129
|
SKIP_DIRS = {".git", "node_modules", ".tfcore", "bin", "obj", "target", "dist",
|
|
118
|
-
"build", ".venv", "venv", "__pycache__", ".next"
|
|
130
|
+
"build", ".venv", "venv", "__pycache__", ".next", ".claude", ".opencode",
|
|
131
|
+
".vscode", ".github", ".husky", ".yarn"}
|
|
119
132
|
|
|
120
133
|
|
|
121
134
|
def detect():
|
|
122
135
|
found = set()
|
|
123
136
|
for root, dirs, files in os.walk(REPO):
|
|
124
|
-
dirs[:] = [d for d in dirs if d not in SKIP_DIRS
|
|
137
|
+
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
|
|
125
138
|
if root.count(os.sep) - REPO.count(os.sep) > 3:
|
|
126
139
|
dirs[:] = []
|
|
127
140
|
for name, markers, _ in STACKS:
|
|
@@ -154,6 +167,24 @@ def covered(rule, lines):
|
|
|
154
167
|
return False
|
|
155
168
|
|
|
156
169
|
|
|
170
|
+
def child_only_rules(rule, lines):
|
|
171
|
+
"""Rules that ignore something INSIDE `rule` while `rule` itself stays open.
|
|
172
|
+
|
|
173
|
+
This shape is more dangerous than no rule at all, because it looks deliberate:
|
|
174
|
+
TfLens carried `/.vs/App.slnx`, which covers the solution-named subfolder and
|
|
175
|
+
leaves `.vs/ProjectEvaluation/` tracked. The next folder the IDE creates is not
|
|
176
|
+
covered either, and nobody re-reads a line that already mentions the directory."""
|
|
177
|
+
core = rule.strip("/*").rstrip("/")
|
|
178
|
+
out = []
|
|
179
|
+
for l in lines:
|
|
180
|
+
if not l or l.startswith("#"):
|
|
181
|
+
continue
|
|
182
|
+
c = l.lstrip("!").strip().replace("**/", "").strip("/")
|
|
183
|
+
if c != core and (c.startswith(core + "/") or c.startswith(core + "\\")):
|
|
184
|
+
out.append(l.strip())
|
|
185
|
+
return out
|
|
186
|
+
|
|
187
|
+
|
|
157
188
|
def tracked_paths():
|
|
158
189
|
"""Read .git/index directly. Returns the set of tracked paths, or None when the
|
|
159
190
|
index cannot be read (a bare repo, a worktree file, a version we do not know) —
|
|
@@ -210,8 +241,18 @@ wanted = []
|
|
|
210
241
|
for name, _, rules in STACKS:
|
|
211
242
|
if name in stacks:
|
|
212
243
|
wanted += [(name, r) for r in rules]
|
|
244
|
+
if stacks:
|
|
245
|
+
wanted += [("ide", r) for r in IDE_STATE if not any(r == w for _n, w in wanted)]
|
|
213
246
|
|
|
214
247
|
missing = [(s, r) for s, r in wanted if not covered(r, lines)]
|
|
248
|
+
# A rule naming a child of a directory that is itself unignored: report it separately,
|
|
249
|
+
# because "MISSING .vs/" reads like nobody thought about it when in fact somebody did
|
|
250
|
+
# and got it half right, which is the harder thing to spot in a review.
|
|
251
|
+
half_done = []
|
|
252
|
+
for _s, r in missing:
|
|
253
|
+
kids = child_only_rules(r, lines)
|
|
254
|
+
if kids:
|
|
255
|
+
half_done.append((r, kids))
|
|
215
256
|
|
|
216
257
|
tracked = tracked_paths()
|
|
217
258
|
offenders = {}
|
|
@@ -244,6 +285,12 @@ else:
|
|
|
244
285
|
print(" ⚠ MISSING build-output rules: %s" % names)
|
|
245
286
|
print(" add them with: bash .tfcore/utils/tf-gitignore-audit.sh . --fix")
|
|
246
287
|
|
|
288
|
+
for r, kids in half_done:
|
|
289
|
+
rc |= 1
|
|
290
|
+
print(" ⚠ %s is not ignored, but %s covers a child of it — the parent is open"
|
|
291
|
+
% (r, ", ".join(kids)))
|
|
292
|
+
print(" a rule on a child looks deliberate and leaves every sibling tracked")
|
|
293
|
+
|
|
247
294
|
if tracked is None:
|
|
248
295
|
print(" tracked-file check skipped (no readable .git/index) — rules alone do not")
|
|
249
296
|
print(" prove build output is untracked; re-run where the index is readable")
|