@techierathore/techieflow 1.0.4 → 1.1.0
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/tasks/_owner-language.md +1 -0
- package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +4 -3
- package/.claude/commands/TechieFlow/tasks/amend-docs.md +1 -1
- package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +1 -1
- package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +1 -1
- package/.claude/commands/TechieFlow/tasks/fix-issues.md +2 -2
- package/.claude/commands/TechieFlow/tasks/generate-html.md +2 -2
- package/.claude/commands/TechieFlow/tasks/handoff-phase.md +2 -2
- package/.claude/commands/TechieFlow/tasks/mockups.md +1 -1
- package/.claude/commands/TechieFlow/tasks/productguide.md +2 -2
- package/.claude/commands/TechieFlow/tasks/render-workflow-docs.md +1 -1
- package/.claude/commands/TechieFlow/tasks/triage-issues.md +2 -2
- package/.opencode/plugin/techieflow.js +179 -7
- package/.tfcore/hooks/block-git.sh +4 -2
- package/.tfcore/hooks/guard-build.sh +5 -1
- package/.tfcore/hooks/guard-db.sh +6 -4
- package/.tfcore/hooks/guard-status-html.sh +86 -2
- package/.tfcore/hooks/guard-verify.sh +8 -3
- package/.tfcore/hooks/sweep-artifacts.sh +46 -0
- package/.tfcore/standards/owner-words.txt +23 -0
- package/.tfcore/tasks/_owner-language.md +1 -0
- package/.tfcore/tasks/_status-update-gate.md +4 -3
- package/.tfcore/tasks/amend-docs.md +1 -1
- package/.tfcore/tasks/day1-brownfield.md +1 -1
- package/.tfcore/tasks/day1-greenfield.md +1 -1
- package/.tfcore/tasks/fix-issues.md +2 -2
- package/.tfcore/tasks/generate-html.md +2 -2
- package/.tfcore/tasks/handoff-phase.md +2 -2
- package/.tfcore/tasks/mockups.md +1 -1
- package/.tfcore/tasks/productguide.md +2 -2
- package/.tfcore/tasks/render-workflow-docs.md +1 -1
- package/.tfcore/tasks/triage-issues.md +2 -2
- package/.tfcore/telemetry/SCHEMA.md +4 -0
- package/.tfcore/telemetry/install-metrics.sh +3 -2
- package/.tfcore/telemetry/tf-metrics.sh +19 -2
- package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +6 -0
- package/.tfcore/templates/v4custom/app-brd-tmpl.md +5 -3
- package/.tfcore/templates/v4custom/app-phase-brd-tmpl.md +94 -0
- package/.tfcore/templates/v4custom/app-phase-uidesign-tmpl.md +60 -0
- package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +4 -2
- package/.tfcore/templates/v4custom/build-subagent-prompt.md +2 -2
- package/.tfcore/templates/v4custom/html-render-shell.md +4 -2
- 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-misses-md.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-assets-browser.mjs +96 -0
- package/.tfcore/utils/tf-assets.sh +97 -12
- package/.tfcore/utils/tf-brd-status.py +14 -5
- package/.tfcore/utils/tf-build-list.py +80 -12
- package/.tfcore/utils/tf-build-list.sh +2 -1
- package/.tfcore/utils/tf-build.sh +88 -15
- package/.tfcore/utils/tf-ci-repro.py +685 -0
- package/.tfcore/utils/tf-ci-repro.sh +24 -0
- package/.tfcore/utils/tf-devguide-list.py +47 -22
- package/.tfcore/utils/tf-doc-check.py +136 -6
- package/.tfcore/utils/tf-doc-tests.sh +79 -0
- package/.tfcore/utils/tf-emit.sh +92 -35
- package/.tfcore/utils/tf-feedback.sh +13 -0
- package/.tfcore/utils/tf-fix-close.sh +79 -29
- package/.tfcore/utils/tf-goal.sh +12 -12
- package/.tfcore/utils/tf-harness.sh +9 -4
- package/.tfcore/utils/tf-lock.sh +29 -0
- package/.tfcore/utils/tf-log-miss.py +34 -8
- package/.tfcore/utils/tf-login.mjs +167 -0
- package/.tfcore/utils/tf-misses-md.py +10 -4
- package/.tfcore/utils/tf-mockup-parity.mjs +427 -63
- package/.tfcore/utils/tf-mockup-parity.sh +14 -2
- package/.tfcore/utils/tf-owner-text.py +233 -0
- package/.tfcore/utils/tf-owner-text.sh +14 -0
- package/.tfcore/utils/tf-phase.sh +15 -1
- package/.tfcore/utils/tf-portable.sh +230 -0
- package/.tfcore/utils/tf-render-html.py +60 -2
- package/.tfcore/utils/tf-selfcheck.sh +39 -6
- package/.tfcore/utils/tf-split-brd.py +11 -1
- package/.tfcore/utils/tf-status-evidence.sh +11 -9
- package/.tfcore/utils/tf-status-facts.py +72 -32
- package/.tfcore/utils/tf-triage.py +62 -16
- package/.tfcore/utils/tf-triage.sh +2 -0
- package/.tfcore/utils/tf-verify-boot.sh +263 -43
- package/.tfcore/utils/tf-verify-emit.sh +20 -0
- package/.tfcore/utils/tf-verify-env.sh +33 -4
- package/.tfcore/utils/tf-verify-list.py +75 -8
- package/.tfcore/utils/tf-verify-screens.mjs +171 -32
- package/.tfcore/utils/tf-verify-screens.sh +8 -3
- package/.tfcore/utils/tf-verify-tests.sh +215 -14
- package/.tfcore/utils/tf-verify-verdict.py +48 -9
- package/.tfcore/utils/tf_defect.py +27 -0
- package/.tfcore/utils/tf_feedback.py +209 -0
- package/.tfcore/utils/tf_roadmap.py +35 -0
- package/README.md +4 -0
- package/docs/TechieFlow-Installation.md +5 -3
- package/package.json +1 -1
|
@@ -10,9 +10,14 @@
|
|
|
10
10
|
# Only when PROJECT-STATUS.md was written in this session (newer than the
|
|
11
11
|
# session pointer .tfcore/.session/<harness>.json):
|
|
12
12
|
# 2. tf-doc-check.sh FAILs on PROJECT-STATUS.md -> fix the file, re-run the check
|
|
13
|
+
# 2c. a BRD or UI design written this session leaves a screen with no row, UI design
|
|
14
|
+
# entry or mockup -> add it, re-run the check (TF-030)
|
|
13
15
|
# 3. docs/<App>-BRD.md older than docs/<App>-Checklist.md
|
|
14
16
|
# -> bash .tfcore/utils/tf-brd-status.sh <App>
|
|
15
17
|
# 4. no runs.jsonl record after the status write -> the run record (_metrics-emit-gate.md)
|
|
18
|
+
# Only on the turn that wrote PROJECT-STATUS.md (a command's closing turn):
|
|
19
|
+
# 5. tf-owner-text.sh FAILs on the closing message or a free-form document it
|
|
20
|
+
# hands the owner -> rewrite it for the owner
|
|
16
21
|
#
|
|
17
22
|
# Wired in .claude/settings.json -> hooks.Stop; OpenCode via
|
|
18
23
|
# .opencode/plugin/techieflow.js session.idle (a nudge: OpenCode has no blocking
|
|
@@ -28,6 +33,7 @@ import datetime
|
|
|
28
33
|
import glob
|
|
29
34
|
import json
|
|
30
35
|
import os
|
|
36
|
+
import re
|
|
31
37
|
import subprocess
|
|
32
38
|
import sys
|
|
33
39
|
|
|
@@ -58,10 +64,18 @@ elif md_mtime > os.path.getmtime(html):
|
|
|
58
64
|
def session_start():
|
|
59
65
|
"""mtime of the harness's session pointer; None when there is none (checks 2-4 skipped)."""
|
|
60
66
|
d = os.path.join(root, ".tfcore", ".session")
|
|
61
|
-
|
|
67
|
+
# The harness in the order tf-harness.sh and tf-emit.sh take it: TF_HARNESS (the OpenCode
|
|
68
|
+
# plugin sets it on every guard it spawns), then Claude Code's own variables, then OPENCODE*.
|
|
69
|
+
# OpenCode first let any OPENCODE_* in the owner's shell (an API key, a config path) send a
|
|
70
|
+
# Claude Code turn to opencode.json; with no such file, checks 2-5 never ran (2026-09-27).
|
|
71
|
+
tf = os.environ.get("TF_HARNESS")
|
|
72
|
+
if tf == "opencode":
|
|
62
73
|
cands = [os.path.join(d, "opencode.json")]
|
|
63
|
-
elif
|
|
74
|
+
elif tf == "claude-code" or any(os.environ.get(k) for k in (
|
|
75
|
+
"CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT", "CLAUDE_CODE_SESSION_ID", "CLAUDE_PROJECT_DIR")):
|
|
64
76
|
cands = [os.path.join(d, "claude-code.json")]
|
|
77
|
+
elif any(k.startswith("OPENCODE") for k in os.environ):
|
|
78
|
+
cands = [os.path.join(d, "opencode.json")]
|
|
65
79
|
else:
|
|
66
80
|
cands = [os.path.join(d, "claude-code.json"), os.path.join(d, "opencode.json")]
|
|
67
81
|
times = [os.path.getmtime(p) for p in cands if os.path.isfile(p)]
|
|
@@ -104,6 +118,31 @@ if written_this_session:
|
|
|
104
118
|
except Exception:
|
|
105
119
|
pass
|
|
106
120
|
|
|
121
|
+
# 2c. a screen is not finished until it has its row, its UI design entry and its mockup. An
|
|
122
|
+
# amendment added a Price providers screen inside a requirement with none of the three, the
|
|
123
|
+
# checker's one finding was on the checklist and passed as old, and the page was built with no
|
|
124
|
+
# design (TfLens TF-030, 2026-09-11). On a turn that wrote a BRD or a UI design, those findings
|
|
125
|
+
# block; a BRD's other findings stay with *amend-docs, as before.
|
|
126
|
+
try:
|
|
127
|
+
chk = os.path.join(root, ".tfcore", "utils", "tf-doc-check.sh")
|
|
128
|
+
chain = re.compile(r'names the screen "|has no "### Screen:" entry|is in the UIDesign but not in the BRD'
|
|
129
|
+
r'|has no mockup link \(docs/mockups')
|
|
130
|
+
apps = set()
|
|
131
|
+
for f in glob.glob(os.path.join(root, "docs", "*.md")):
|
|
132
|
+
m = re.match(r"^(.+?)-(?:P\d+-)?(?:BRD|UIDesign)\.md$", os.path.basename(f))
|
|
133
|
+
if m and os.path.getmtime(f) >= start:
|
|
134
|
+
apps.add(m.group(1))
|
|
135
|
+
for app in sorted(apps) if os.path.isfile(chk) else ():
|
|
136
|
+
out = subprocess.run(["bash", chk, "--quiet", "--app", app], cwd=root,
|
|
137
|
+
capture_output=True, text=True, timeout=180).stdout
|
|
138
|
+
fails = [l for l in out.splitlines() if l.startswith("FAIL") and chain.search(l)]
|
|
139
|
+
if fails:
|
|
140
|
+
problems.append(f"a screen in {app}'s documents has no row, UI design entry or mockup ({len(fails)} line(s)). "
|
|
141
|
+
f"Add what is missing, then run: bash .tfcore/utils/tf-doc-check.sh --app {app}")
|
|
142
|
+
problems.extend(" " + l for l in fails[:5])
|
|
143
|
+
except Exception:
|
|
144
|
+
pass
|
|
145
|
+
|
|
107
146
|
# 3. the BRD's Development status table is not older than the checklist
|
|
108
147
|
try:
|
|
109
148
|
for cl in glob.glob(os.path.join(root, "docs", "*-Checklist.md")):
|
|
@@ -136,6 +175,51 @@ if written_this_session:
|
|
|
136
175
|
except Exception:
|
|
137
176
|
pass
|
|
138
177
|
|
|
178
|
+
# 5. what the owner reads when a command ends: the closing message, and every free-form
|
|
179
|
+
# document it hands over that this turn wrote, through tf-owner-text.sh. TfLens's hand-off of
|
|
180
|
+
# 2026-09-11 reached the owner in jargon, named upstream problems with no word on what they
|
|
181
|
+
# touch, called two fixed ones open, and gave no prompt (MISS-TechieFlow-20260911-03). Only on
|
|
182
|
+
# the turn that closed a command -- PROJECT-STATUS written after this turn's prompt -- so an
|
|
183
|
+
# ordinary conversation is never held to a hand-off's shape. Claude Code hands the hook the
|
|
184
|
+
# message; the OpenCode plugin passes the same two fields.
|
|
185
|
+
try:
|
|
186
|
+
message = data.get("last_assistant_message") or ""
|
|
187
|
+
turn = data.get("turn_started")
|
|
188
|
+
if turn is None and data.get("transcript_path") and os.path.isfile(data["transcript_path"]):
|
|
189
|
+
with open(data["transcript_path"], encoding="utf-8", errors="replace") as f:
|
|
190
|
+
for line in f:
|
|
191
|
+
try:
|
|
192
|
+
r = json.loads(line)
|
|
193
|
+
except Exception:
|
|
194
|
+
continue
|
|
195
|
+
c = (r.get("message") or {}).get("content")
|
|
196
|
+
prompt = r.get("type") == "user" and not r.get("isMeta") and (
|
|
197
|
+
isinstance(c, str) or (isinstance(c, list) and any(isinstance(x, dict) and x.get("type") == "text" for x in c)))
|
|
198
|
+
if prompt and r.get("timestamp"):
|
|
199
|
+
turn = r["timestamp"]
|
|
200
|
+
if isinstance(turn, str):
|
|
201
|
+
turn = datetime.datetime.strptime(turn[:19], "%Y-%m-%dT%H:%M:%S").replace(
|
|
202
|
+
tzinfo=datetime.timezone.utc).timestamp()
|
|
203
|
+
elif isinstance(turn, (int, float)) and turn > 1e12:
|
|
204
|
+
turn = turn / 1000.0
|
|
205
|
+
chk = os.path.join(root, ".tfcore", "utils", "tf-owner-text.sh")
|
|
206
|
+
if message.strip() and turn and md_mtime >= turn - 2 and os.path.isfile(chk):
|
|
207
|
+
handed = []
|
|
208
|
+
for m in set(re.findall(r"(?<![\w/.-])((?:docs/)?[\w.-]+\.md)\b", message)):
|
|
209
|
+
p = os.path.join(root, m if m.startswith("docs/") else os.path.join("docs", m))
|
|
210
|
+
if os.path.isfile(p) and os.path.getmtime(p) >= turn - 2:
|
|
211
|
+
handed.append(os.path.relpath(p, root))
|
|
212
|
+
res = subprocess.run(["bash", chk, "--root", root, "--final", "--stdin"] + sorted(handed), cwd=root,
|
|
213
|
+
input=message, capture_output=True, text=True, timeout=60)
|
|
214
|
+
fails = [l for l in res.stdout.splitlines() if l.startswith("FAIL")]
|
|
215
|
+
if fails:
|
|
216
|
+
problems.append(f"what you are handing the owner is not written for the owner ({len(fails)} line(s)). "
|
|
217
|
+
"Fix each, then write the closing message again — plain words, every upstream problem "
|
|
218
|
+
"with what it affects and its prompt, and the next prompt in a code block:")
|
|
219
|
+
problems.extend(" " + l for l in fails[:8])
|
|
220
|
+
except Exception:
|
|
221
|
+
pass
|
|
222
|
+
|
|
139
223
|
if not problems:
|
|
140
224
|
sys.exit(0)
|
|
141
225
|
|
|
@@ -99,14 +99,19 @@ if ledger is not None:
|
|
|
99
99
|
sys.exit(0) # a same-day ledger from a framework version before Sitting 4c: date unlocks
|
|
100
100
|
else:
|
|
101
101
|
rows = ledger.get("rows") or {}
|
|
102
|
-
|
|
102
|
+
# a ledger kept row by row holds older runs' rows too (Lekhak TF-007): only a row graded
|
|
103
|
+
# today unlocks
|
|
104
|
+
dates = ledger.get("row_dates")
|
|
105
|
+
not_listed = sorted(i for i in introduced if rows.get(i) != "PASS"
|
|
106
|
+
or (isinstance(dates, dict) and str(dates.get(i, "")) != today))
|
|
103
107
|
if not introduced:
|
|
104
108
|
# a Verified cell without a readable row id (a malformed line): refuse, nothing can vouch for it
|
|
105
109
|
not_listed = ["(row id not readable)"]
|
|
106
110
|
if not not_listed:
|
|
107
111
|
sys.exit(0)
|
|
108
|
-
problem = ("lists these rows as " + ", ".join(
|
|
109
|
-
|
|
112
|
+
problem = ("lists these rows as " + ", ".join(
|
|
113
|
+
f"{i}: {rows.get(i, 'absent')}" + (f" (graded {dates.get(i)})" if isinstance(dates, dict) and dates.get(i) and dates.get(i) != today else "")
|
|
114
|
+
for i in not_listed) + ", not PASS today")
|
|
110
115
|
|
|
111
116
|
print(
|
|
112
117
|
"BLOCKED by TechieFlow policy: this write INTRODUCES a `Verified` status, "
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
# test-results/ test-results-*/ scripts-*/ playwright-report/
|
|
27
27
|
# The project's own tracked scripts/ is never touched (needs the hyphen).
|
|
28
28
|
# tests/verify/ (promoted, tracked specs) is never touched (outside the roots).
|
|
29
|
+
# A file under the roots that a test names by path (tests/.artifacts/<folder>/<name>) is kept at
|
|
30
|
+
# any age, with everything below it (Lekhak TF-004); the summary says how many were kept.
|
|
29
31
|
#
|
|
30
32
|
# RETENTION: default 7 days. Override per project with `artifactRetentionDays: N`
|
|
31
33
|
# in .tfcore/core-config.yaml, or per run with TF_ARTIFACT_RETENTION_DAYS=N.
|
|
@@ -98,6 +100,42 @@ DAYS = retention_days()
|
|
|
98
100
|
CUTOFF = time.time() - DAYS * 86400
|
|
99
101
|
|
|
100
102
|
SWEEP_ROOTS = [os.path.join("tests", ".artifacts"), ".verify"]
|
|
103
|
+
|
|
104
|
+
# ---- what a test names is not litter (Lekhak TF-004) ---------------------------
|
|
105
|
+
# A spec that reads tests/.artifacts/harness/hindi-src lost it on the eighth day, and two rows failed
|
|
106
|
+
# "the crawl produced no result" on a working product. Any path under the sweep roots that a file of
|
|
107
|
+
# the project's tests (or its Playwright config) names is kept, with everything below it, whatever its
|
|
108
|
+
# age. The right home for a fixture is still tests/verify/fixtures/, outside the sweep.
|
|
109
|
+
# Only a NAMED thing inside a folder counts (tests/.artifacts/<folder>/<name>): a folder a test writes
|
|
110
|
+
# into is named with a variable after it (`shots/${name}.png`), which the pattern stops at, and the
|
|
111
|
+
# framework's own output folders are never kept. The Playwright config is not read: it names outputs.
|
|
112
|
+
KEEP_RE = re.compile(r"(?:tests[/\\]+\.artifacts|\.verify)[/\\]+[^'\"`\s)\]},;*?<>|${}+]+")
|
|
113
|
+
OUTPUTS = ("tests/.artifacts/verify/", "tests/.artifacts/build/", "tests/.artifacts/test-results",
|
|
114
|
+
"tests/.artifacts/playwright", "tests/.artifacts/regression", "tests/.artifacts/doc-check/")
|
|
115
|
+
TEXT_EXT = (".ts", ".js", ".mjs", ".cjs", ".cs", ".py", ".json", ".sh", ".cmd", ".ps1", ".yaml", ".yml", ".txt", ".md")
|
|
116
|
+
keep = set()
|
|
117
|
+
def scan_refs(fp):
|
|
118
|
+
try:
|
|
119
|
+
if os.path.getsize(fp) > 2 * 1024 * 1024:
|
|
120
|
+
return
|
|
121
|
+
with open(fp, encoding="utf-8", errors="replace") as fh:
|
|
122
|
+
for m in KEEP_RE.finditer(fh.read()):
|
|
123
|
+
k = re.sub(r"/+", "/", m.group(0).replace("\\", "/")).rstrip("/.")
|
|
124
|
+
if k.count("/") >= (2 if k.startswith(".verify/") else 3) and not (k + "/").startswith(OUTPUTS):
|
|
125
|
+
keep.add(k)
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
tests_dir = os.path.join(root, "tests")
|
|
129
|
+
if os.path.isdir(tests_dir) and not os.path.islink(tests_dir):
|
|
130
|
+
for dp, dns, fns in os.walk(tests_dir, followlinks=False):
|
|
131
|
+
dns[:] = [d for d in dns if d not in (".artifacts", "node_modules", "bin", "obj", ".git")]
|
|
132
|
+
for f in fns:
|
|
133
|
+
if f.endswith(TEXT_EXT):
|
|
134
|
+
scan_refs(os.path.join(dp, f))
|
|
135
|
+
kept_n = 0
|
|
136
|
+
def kept(fp):
|
|
137
|
+
rel = os.path.relpath(fp, root).replace(os.sep, "/")
|
|
138
|
+
return any(rel == k or rel.startswith(k + "/") for k in keep)
|
|
101
139
|
LEGACY_ROOT_GLOBS = ["test-results", "test-results-*", "scripts-*", "playwright-report"]
|
|
102
140
|
|
|
103
141
|
removed_files = 0
|
|
@@ -162,6 +200,9 @@ if DAYS > 0:
|
|
|
162
200
|
except Exception:
|
|
163
201
|
continue
|
|
164
202
|
if st.st_mtime < CUTOFF:
|
|
203
|
+
if keep and kept(fp):
|
|
204
|
+
kept_n += 1
|
|
205
|
+
continue
|
|
165
206
|
rm_file(fp)
|
|
166
207
|
# remove dirs emptied by this sweep (never the root itself)
|
|
167
208
|
if dp != top:
|
|
@@ -193,6 +234,11 @@ if removed_files or removed_dirs or legacy:
|
|
|
193
234
|
f"tests/.artifacts/ and .verify/"]
|
|
194
235
|
if legacy:
|
|
195
236
|
parts.append("banned repo-root legacy dirs removed: " + ", ".join(sorted(legacy)))
|
|
237
|
+
if kept_n:
|
|
238
|
+
parts.append(f"kept {kept_n} old file(s) a test names by path (move them to tests/verify/fixtures/)")
|
|
196
239
|
print("; ".join(parts))
|
|
240
|
+
elif kept_n:
|
|
241
|
+
print(f"TechieFlow sweep-artifacts: kept {kept_n} file(s) older than {DAYS}d because a test names them by path; "
|
|
242
|
+
"move them to tests/verify/fixtures/")
|
|
197
243
|
PY
|
|
198
244
|
exit 0
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Words the owner should never have to look up, and what to say instead.
|
|
2
|
+
# Read by .tfcore/utils/tf-owner-text.py. One per line: word | say instead
|
|
3
|
+
# A line is added when the owner has had to ask what a word meant, or when the framework's own
|
|
4
|
+
# rules name it (_owner-language.md). Adding a word is cheap; a word the owner knows is a false alarm,
|
|
5
|
+
# so do not add ordinary English or the owner's own vocabulary (parity, checklist, verify, phase).
|
|
6
|
+
oracle | the framework's own script
|
|
7
|
+
denominator | the number you divide by
|
|
8
|
+
numerator | the number being divided
|
|
9
|
+
idempotent | safe to run twice
|
|
10
|
+
invariant | a rule that always holds
|
|
11
|
+
orthogonal | unrelated
|
|
12
|
+
heuristic | a rule of thumb
|
|
13
|
+
canonical | the standard one
|
|
14
|
+
provenance | where the number came from
|
|
15
|
+
cardinality | how many
|
|
16
|
+
semantics | meaning
|
|
17
|
+
deterministic | gives the same answer every time
|
|
18
|
+
tuple | list
|
|
19
|
+
enum | fixed list of values
|
|
20
|
+
rollup | the combined report
|
|
21
|
+
taint | marked unreliable
|
|
22
|
+
short-circuit | stops early
|
|
23
|
+
payload | the data sent
|
|
@@ -9,6 +9,7 @@ Everything the owner reads — a terminal reply, a document, a status line, the
|
|
|
9
9
|
- **No coined terms, no shorthand.** "the framework's own script", not "the oracle". "the number you divide by", not "the denominator". If the owner would have to look a word up, it is the wrong word.
|
|
10
10
|
- **Writing a glossary means the words are wrong.** Change the words. `tf-doc-check.sh` refuses a glossary section.
|
|
11
11
|
- First sentence carries the answer. Name the thing, not its id. Short sentences. Never dramatise.
|
|
12
|
+
- **A command's closing message is checked before the turn can end** (`tf-owner-text.sh`, run by the Stop hook on it and on any free-form document it hands over): no word from `.tfcore/standards/owner-words.txt`; each open upstream problem in a table row saying what it affects and whether it blocks or breaks anything, with the prompt that fixes it; one recorded as fixed upstream called fixed; each command still to run given as the line to paste; and the next prompt in a code block.
|
|
12
13
|
|
|
13
14
|
## 2. A question for the owner goes in a file, not in the conversation
|
|
14
15
|
|
|
@@ -7,8 +7,9 @@ Steps, in this order:
|
|
|
7
7
|
1. `bash .tfcore/utils/tf-status-facts.sh {App} "{command}"` prints today's date, the phase line, the last verified build, the Open requirements section, the Verification log with its new row, the library feedback lines, and the next command in both harness forms. Copy them as printed.
|
|
8
8
|
2. Write `PROJECT-STATUS.md` with the Write tool in the shape of `.tfcore/templates/v4custom/app-project-status-tmpl.md`: replace every section in place, add none. You write two parts yourself: "Where I am" (at most 80 words, state not story) and "Known blockers". A refused write means the content is mis-shaped: trim it, do not reword it.
|
|
9
9
|
3. `bash .tfcore/utils/tf-doc-check.sh PROJECT-STATUS.md <every document this command wrote>`; day-1 and `*amend-docs` run `--app {App}` instead, because they own every document. A `FAIL` line means the phase is not closed: fix the document and re-run. A line printed as `OLD` was already there when the command started (the phase marker records them); it does not block and is repaired through `*amend-docs`. Never edit a schema to make a document pass. Documents this command did not write are reported with `--warn`, not fixed here: an older project is repaired through `*amend-docs` when it is next worked on (Schemas §7.1, decision 8).
|
|
10
|
-
4. `bash .tfcore/utils/tf-
|
|
11
|
-
5. `bash .tfcore/utils/tf-
|
|
12
|
-
6.
|
|
10
|
+
4. When this command changed a file under `docs/`, `bash .tfcore/utils/tf-doc-tests.sh <every document it changed>`. A unit test may read a document and guard its text; when one does, this runs the unit tests. `NONE` or `PASS` goes on. `FAIL` means the phase is not closed: put the guarded text back, or, when the change is meant, name the test under "Known blockers" for `*triage-and-fix` to update, since a document command never edits code. `NOT-RUN` is reported as it is printed (Lekhak TF-016).
|
|
11
|
+
5. `bash .tfcore/utils/tf-render-html.sh PROJECT-STATUS.md`, and the same for every human document this command wrote. The checklist, the miss log and feedback files are never rendered.
|
|
12
|
+
6. `bash .tfcore/utils/tf-brd-status.sh {App}` rewrites the BRD's Development status table from the checklist and re-renders the BRD.
|
|
13
|
+
7. Append the run record: `.tfcore/tasks/_metrics-emit-gate.md`.
|
|
13
14
|
|
|
14
15
|
Evidence for every line is the checklist's Requirements Status table and the files on disk, never git. Check a framework file by reading its literal path under `.tfcore/`; search tools skip that folder.
|
|
@@ -22,6 +22,6 @@ First: `bash .tfcore/utils/tf-phase.sh start amend-docs {App}` prints the start
|
|
|
22
22
|
8. If the amendment changed code-level flows, say that `*devguide {App} --update` is due after the next build.
|
|
23
23
|
9. Close what this amendment fixed: `bash .tfcore/utils/tf-emit.sh --open-misses {App} --artifact-class doc` lists the open misses whose deficient artifact is a document. Close the ones this amendment resolves in one call: `bash .tfcore/utils/tf-fix-close.sh {App} --misses <id>,<id> --fix-cmd amend-docs --verdict Verified`. Where the mapping from a miss to this amendment is unclear, ask, do not guess. Without this a document miss can never close: `*fix-issues` and `*build-phase` close off checklist rows the verifier touched, which a BRD edit never is.
|
|
24
24
|
10. Decide whether this amendment is a miss: should the original documents plainly have said this (a behaviour that was always required, or two sections that contradicted each other)? Then run `*log-miss {App} "<one sentence>"`. A change of mind or a feature decided today is scope arriving late, not a miss.
|
|
25
|
-
11. `bash .tfcore/utils/tf-doc-check.sh --app {App}`; fix every FAIL.
|
|
25
|
+
11. `bash .tfcore/utils/tf-doc-check.sh --app {App}`; fix every FAIL. Then `bash .tfcore/utils/tf-doc-tests.sh <every document this amendment changed>`: a unit test that reads one of them runs, and a `FAIL` means the amendment removed or changed text a test guards. Put it back, or, when the change is meant, the phase stays open and the report names the test for `*triage-and-fix`; never edit the test here (Lekhak TF-016).
|
|
26
26
|
12. Run the status gate (`.tfcore/tasks/_status-update-gate.md`) with `"cmd":"amend-docs"`.
|
|
27
27
|
13. Report: items added (ids), modified, removed, the highest id now, what changed in the Architecture, the checklist rows touched, and the next command from the gate.
|
|
@@ -19,7 +19,7 @@ First, before anything else: `bash .tfcore/utils/tf-phase.sh start day1-brownfie
|
|
|
19
19
|
4. BRD, first half: `docs/{App}-BRD.md` from `app-brd-tmpl.md`: the header (App, Kind, Size, Stack answer set, Status, Date), Summary, Scope, Users and roles, and the Screens and flow table, one row per routed page found in the code. A dialog is a row under its parent screen with `on /route` in the Route column.
|
|
20
20
|
5. Mockups: run `.tfcore/tasks/mockups.md` for `{App}`. It links the mockups already in `docs/mockups/` to their screens and produces one for every screen that has none, from the code and the running application. Every screen ends with a mockup.
|
|
21
21
|
6. BRD, second half: the Requirements ledger, one `BRD-N` per thing the verifier will test, each naming its screen, linking its mockup and carrying one acceptance line "When <actor> <does what> on <screen>, then <observable result>". Harvested documents come first: every "the system shall" or "the user can" line becomes an item, attributed, and every table, screen inventory and workflow they hold carries in, never compressed to a one-liner. Then the code: what it already does becomes items too. Mark inferred items `<!-- inferred — please verify -->`. The Non-functional table takes a `perf-budget:` only where the owner stated a number, and the logging requirement from the answer set, recorded as met when the scan found it wired. The Development status table has one row per screen with Verified and Open counts from the strongest evidence. If the count would pass the size cap, propose a phase split instead of merging items.
|
|
22
|
-
Large: steps 4 to 6 run once per phase of the Phases table, phase 1 in `docs/{App}-BRD.md` and `docs/{App}-UIDesign.md`, phase n in `docs/{App}-Pn-BRD.md` and `docs/{App}-Pn-UIDesign.md`, each header carrying `Phase | n of m` and its own Size, ids running on across phases.
|
|
22
|
+
Large: steps 4 to 6 run once per phase of the Phases table, phase 1 in `docs/{App}-BRD.md` and `docs/{App}-UIDesign.md`, phase n in `docs/{App}-Pn-BRD.md` (from `app-phase-brd-tmpl.md`: this phase's screens and requirements only, linking back to phase 1) and `docs/{App}-Pn-UIDesign.md` (from `app-phase-uidesign-tmpl.md`, the same way), each header carrying `Phase | n of m` and its own Size, ids running on across phases.
|
|
23
23
|
7. Checklist: run `.tfcore/tasks/split-brd.md` for `{App}` (Large: with `--all-phases`, one checklist per phase). If a phased plan is among the harvested documents, split-brd migrates its statuses; the plan then moves to `docs/OldDocs/`.
|
|
24
24
|
8. Coding Standards: `docs/{App}-Coding-Standards.md` from `app-coding-standards-tmpl.md`. "Standards applied" names `.tfcore/standards/coding-standards-core.md` and the stack file, plus the choices the stack file leaves open. Where the stack file leaves a naming convention open, it follows the drift scan of step 3: the dominant style if one reaches 80 percent, else the stack file's default; a drafting instruction overrides. "Project rules" holds only rules true of this project alone.
|
|
25
25
|
9. `bash .tfcore/utils/tf-day1-files.sh {App} --prefix {obj|none}` writes `.editorconfig`, `AGENTS.md` and `CLAUDE.md`.
|
|
@@ -21,7 +21,7 @@ Then, in this order:
|
|
|
21
21
|
8. BRD, first half: `docs/{App}-BRD.md` from `app-brd-tmpl.md`: the header (App, Kind, Size, Stack answer set, Status, Date), Summary, Scope, Users and roles, and the Screens and flow table. A dialog is a row under its parent screen with `on /route` in the Route column.
|
|
22
22
|
9. Mockups: run `.tfcore/tasks/mockups.md` for `{App}`. It reads the Screens and flow table and produces `docs/{App}-UIDesign.md` and one `docs/mockups/<screen>.html` per screen. An app with no screens records "skipped — no UI".
|
|
23
23
|
10. BRD, second half: the Requirements ledger, one `BRD-N` per thing the verifier will test, each naming its screen, linking its mockup and carrying one acceptance line "When <actor> <does what> on <screen>, then <observable result>"; the Non-functional table (a `perf-budget:` only where the owner gave a number; the logging requirement from the answer set); the Development status table, one row per screen, all Planned. The concept is the primary source: every feature, actor and constraint in it becomes an item. Mark inferred items `<!-- inferred — please verify -->`. Harvested requirements carry in as items, attributed, none dropped. If the count would pass the size cap, propose a phase split instead of merging items.
|
|
24
|
-
Large: steps 8 to 10 run once per phase of the Phases table, phase 1 in `docs/{App}-BRD.md` and `docs/{App}-UIDesign.md`, phase n in `docs/{App}-Pn-BRD.md` and `docs/{App}-Pn-UIDesign.md`, each header carrying `Phase | n of m` and its own Size, ids running on across phases. Every screen gets its mockup now.
|
|
24
|
+
Large: steps 8 to 10 run once per phase of the Phases table, phase 1 in `docs/{App}-BRD.md` and `docs/{App}-UIDesign.md`, phase n in `docs/{App}-Pn-BRD.md` (from `app-phase-brd-tmpl.md`: this phase's screens and requirements only, linking back to phase 1) and `docs/{App}-Pn-UIDesign.md` (from `app-phase-uidesign-tmpl.md`, the same way), each header carrying `Phase | n of m` and its own Size, ids running on across phases. Every screen gets its mockup now.
|
|
25
25
|
11. `bash .tfcore/utils/tf-doc-check.sh --app {App}`; fix every FAIL.
|
|
26
26
|
12. Run the status gate (`.tfcore/tasks/_status-update-gate.md`); the run record carries `"cmd":"day1-greenfield","mode":"stage1"`.
|
|
27
27
|
13. Stop and say: "Stage 1 is complete: BRD, Architecture and mockups are written and rendered. Review them in the HTML files, or edit the markdown. When you approve, run `*day1-greenfield {App} --stage2`. To change a screen, edit the concept and re-run stage 1, or run `*mockups {App} --update`."
|
|
@@ -8,8 +8,8 @@ First: `bash .tfcore/utils/tf-phase.sh start fix-issues {App}` prints the start
|
|
|
8
8
|
|
|
9
9
|
1. Triage first, as `.tfcore/tasks/triage-issues.md` steps 1 to 4: read the evidence, boot (`tf-verify-boot.sh start`), reproduce each issue on its screen with the screens script, map it to its row, and log it with `tf-triage.sh demote` or `new`. Then `bash .tfcore/utils/tf-triage.sh {App} close --started <the step-0 time> --cmd fix-issues` writes the escaped gate records and the misses with the discovery cost. A defect nobody logged has no miss to close, so this step is never skipped.
|
|
10
10
|
2. `bash .tfcore/utils/tf-build-list.sh {App} --prompts` now prints FIX mode with the demoted rows, their clusters and one builder prompt each. Spawn every cluster in one turn, exactly as `.tfcore/tasks/build-phase.md` step 2 says: `[trblazeui]` through `/trblazeui`, `[techierag]` through `/techierag`, the rest through the builder sub-agent. A library gap goes into the library's feedback file and the row becomes `Blocked`; never a workaround.
|
|
11
|
-
3. `bash .tfcore/utils/tf-build.sh` until PASS, then smoke every fixed screen with `tf-verify-screens.sh --screen …` (`.tfcore/tasks/_smoke-test-policy.md`); a screen that still fails re-enters step 2 for its rows.
|
|
12
|
-
4. Execute `.tfcore/tasks/verify-phase.md` inline, scoped to the touched rows
|
|
11
|
+
3. `bash .tfcore/utils/tf-build.sh` until PASS, then smoke every fixed screen with `tf-verify-screens.sh --screen …` (`.tfcore/tasks/_smoke-test-policy.md`); a screen that still fails re-enters step 2 for its rows. When the evidence was a failed CI run, `bash .tfcore/utils/tf-ci-repro.sh` must also print PASS; a local build with warm caches does not prove a CI fix. The report says the fix passed that reproduction and that the owner's next CI run is the final check, never that CI is fixed.
|
|
12
|
+
4. Execute `.tfcore/tasks/verify-phase.md` inline, scoped to the touched rows, with its own step 0; rows of another phase's checklist are a second verify with `--phase N`. Only that run writes `Verified`.
|
|
13
13
|
5. `bash .tfcore/utils/tf-verify-boot.sh stop`, then `bash .tfcore/utils/tf-fix-close.sh {App} --started <the step-0 time> --subagents <what was fanned out> --build <pass|fail>`: the fix-issues run record first, then one miss-fix per touched row with the verifier's verdict from the ledger. A row the verifier left below `Verified` stays open with that verdict on its miss-fix.
|
|
14
14
|
6. Run the status gate (`.tfcore/tasks/_status-update-gate.md`); the run record is the one step 5 wrote. When this run is the last step of a goal, write the sentinel (`.tfcore/tasks/_yolo-mode.md`).
|
|
15
15
|
7. Report: evidence read, issues by kind, rows fixed and now `Verified`, rows still open and why, library gaps logged, and the next command from the gate.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# generate-html
|
|
2
2
|
|
|
3
|
-
Convert one or more **human-readable** markdown files to self-contained HTML using the shared shell. Use this when you need to render documents that `*render-workflow-docs` does not cover (the UsageGuide, the DevGuide,
|
|
3
|
+
Convert one or more **human-readable** markdown files to self-contained HTML using the shared shell. Use this when you need to render documents that `*render-workflow-docs` does not cover (the UsageGuide, the DevGuide, ad-hoc design notes, legacy/archived docs). Note: suffixed doc variants like `-v2` are banned by the collision policy (day1-brownfield §1.6) — superseded docs live unmodified in `docs/OldDocs/`.
|
|
4
4
|
|
|
5
5
|
**NEVER render an agent working document to HTML.** Two are banned by name and `tf-render-html.sh` refuses both with exit 2: the **requirements checklist** `*-Checklist.md` (and its Requirements Status table), and the **miss log** `*-Misses.md`. Agents read them in markdown; an HTML mirror burns tokens and drifts from the source. If asked to render either, decline and explain it is an agent document; if a stale `*-Checklist.html` or `*-Misses.html` exists from before this rule, delete it. The test for any other file: **if the only reader is an agent, it stays markdown.**
|
|
6
6
|
|
|
@@ -92,7 +92,7 @@ Per shell §9 checklist, for each rendered file confirm:
|
|
|
92
92
|
- No external resource references except the two CDN scripts.
|
|
93
93
|
|
|
94
94
|
**Mermaid validity pass (do this for EVERY diagram — it is the #1 reported bug).** Apply the shell §5.5 self-check to each mermaid block before emitting it:
|
|
95
|
-
- Each node/edge/subgraph label that contains anything other than letters, digits, and spaces (i.e. has `(` `)` `/` `&` `:` `,` `[` `]` `{` `}` `#` `@` `<` `>` `|` `"` etc.) MUST be wrapped in double quotes, with the quotes INSIDE the shape brackets (`API["
|
|
95
|
+
- Each node/edge/subgraph label that contains anything other than letters, digits, and spaces (i.e. has `(` `)` `/` `&` `:` `,` `[` `]` `{` `}` `#` `@` `<` `>` `|` `"` etc.) MUST be wrapped in double quotes, with the quotes INSIDE the shape brackets (`API["Orders API (v2)"]`, `DB[("Main database")]`).
|
|
96
96
|
- No node id is a reserved word (lowercase `end`).
|
|
97
97
|
- If you find a bare label in the source MD, **fix it in the emitted HTML** (quote it) rather than copying through a diagram you know will throw "Syntax error" in the browser. Note any such fix in the output summary so the author can also fix the MD source.
|
|
98
98
|
|
|
@@ -84,11 +84,11 @@ Invoke `render-workflow-docs {AppName}` as a subtask. Specifically:
|
|
|
84
84
|
- `docs/{AppName}-Architecture.html` — if implementation revealed "as-built" deltas from "Target", update Architecture.md first, then re-render. Set status to "Current (post-implementation)" or "Current + planned target" as appropriate.
|
|
85
85
|
- `PROJECT-STATUS.html` — always re-render (just updated in §2)
|
|
86
86
|
|
|
87
|
-
Then re-render the **Usage Guide** (updated in §1) and the **DevGuide** (refreshed in §3a) via `.tfcore/tasks/generate-html.md`: `docs/{AppName}-UsageGuide.md` (+
|
|
87
|
+
Then re-render the **Usage Guide** (updated in §1) and the **DevGuide** (refreshed in §3a) via `.tfcore/tasks/generate-html.md`: `docs/{AppName}-UsageGuide.md` (+ `docs/{AppName}-DevGuide.md`) → sibling `.html`. **Do NOT render the checklist to HTML** — it is an AI-agent working document kept in markdown only; its Status table stays accurate in markdown.
|
|
88
88
|
|
|
89
89
|
### 3a. Refresh the Developer Guide
|
|
90
90
|
|
|
91
|
-
Run `.tfcore/tasks/devguide.md` for `{AppName}` (`*devguide {AppName}`) so the screen-by-screen code map reflects the final as-built code — this is the doc a human developer uses to chase bugs and verify the AI-generated code from page → control → service → data-access → stored proc. If a DevGuide already exists, an incremental `--update` is enough (remap only changed screens). It produces `docs/{AppName}-DevGuide.md
|
|
91
|
+
Run `.tfcore/tasks/devguide.md` for `{AppName}` (`*devguide {AppName}`) so the screen-by-screen code map reflects the final as-built code — this is the doc a human developer uses to chase bugs and verify the AI-generated code from page → control → service → data-access → stored proc. If a DevGuide already exists, an incremental `--update` is enough (remap only changed screens). It produces `docs/{AppName}-DevGuide.md`, one file at any size, and its `.html`; §3 above already lists them in the re-render set.
|
|
92
92
|
|
|
93
93
|
### 4. Consolidate the per-library feedback files
|
|
94
94
|
|
package/.tfcore/tasks/mockups.md
CHANGED
|
@@ -9,7 +9,7 @@ First: `bash .tfcore/utils/tf-phase.sh start mockups {App}` prints the start tim
|
|
|
9
9
|
1. `bash .tfcore/utils/tf-mockups-locate.sh` moves any mockup found elsewhere in the repository into `docs/mockups/` and lists what is there.
|
|
10
10
|
2. Read the component reference of the UI library named in the Architecture's Stack decisions (for TrBlazeUI, `.trblazeui/TrBlazeUI-AI-Reference.md`; build once if it is absent). Design only with controls that exist. A control the library lacks is logged as an entry in `docs/{App}-<Library>-Feedback.md`, never designed in silently.
|
|
11
11
|
3. Read the BRD's Screens and flow table: every row is a screen, with its route, roles and fields; a dialog row belongs to its parent screen. A screen that already has a mockup in `docs/mockups/` keeps it and is linked. An app with no screens records "skipped — no UI" in the UIDesign and stops.
|
|
12
|
-
4. For each screen without a mockup (fan out one sub-agent per group of screens when there are many): decide the layout regions, map every region to a control, write `docs/mockups/<screen-slug>.html` with realistic placeholder data in the library's visual language, and fill the screen's section in `docs/{App}-UIDesign.md` from `.tfcore/templates/v4custom/app-uidesign-tmpl.md` (mockup link, roles, regions-to-controls table, fields table, the dialogs it opens, the empty, loading and error states). One shell, one theme, one spacing system across the set.
|
|
12
|
+
4. For each screen without a mockup (fan out one sub-agent per group of screens when there are many): decide the layout regions, map every region to a control, write `docs/mockups/<screen-slug>.html` with realistic placeholder data in the library's visual language, and fill the screen's section in `docs/{App}-UIDesign.md` from `.tfcore/templates/v4custom/app-uidesign-tmpl.md` — for phase n of a Large project, `docs/{App}-Pn-UIDesign.md` from `app-phase-uidesign-tmpl.md`, which carries that phase's screens only (mockup link, roles, regions-to-controls table, fields table, the dialogs it opens, the empty, loading and error states). One shell, one theme, one spacing system across the set.
|
|
13
13
|
5. Make the set click through. The entry screen is the sign-in page (or onboarding, or the home page when there is neither). Every link and every navigation button goes to the mockup it names, written as `<a href="screen.html">` or a form action, never as a script (`onclick="location.href=…"` is refused: it dies in any viewer without scripts): sign in opens the home screen, logout returns to sign in, an "Add" button opens the add screen, a row opens its detail, every menu item leads to its screen. Every screen has a way out. A button that does not navigate shows a message with `onclick`. A dialog opens on its parent page. Sign-in needs no credentials: the button alone navigates.
|
|
14
14
|
6. Anchor every element the build must match with `data-testid`, using the names from the regions-to-controls table: the layout regions, every badge and status indicator, every icon button, every card, table and chart, and every value cell whose width matters. The verifier compares only anchored elements; a mockup without anchors is ungradeable.
|
|
15
15
|
7. `bash .tfcore/utils/tf-doc-check.sh --app {App}`: it refuses a broken link, an unreachable or dead-end screen, an inert button and an unanchored mockup. Fix every FAIL.
|
|
@@ -26,7 +26,7 @@ The Product Guide documents the real running app. If the repo is doc-only / not
|
|
|
26
26
|
### 2. Gather the screen inventory + screenshots (reuse, don't reinvent)
|
|
27
27
|
|
|
28
28
|
The DevGuide already did the hard discovery work — reuse it:
|
|
29
|
-
- Read the **DevGuide** (`docs/{AppName}-DevGuide.md
|
|
29
|
+
- Read the **DevGuide** (`docs/{AppName}-DevGuide.md`) for the role→menu map, the screen list, the real post-login landing per role (LANDING-TRUTH), and each screen's purpose.
|
|
30
30
|
- Use the **screenshots** the DevGuide's OBSERVE pass captured under `docs/screenshots/{AppName}/` (`{role}-{screen-slug}.png`, + `-mobile` where present). These are the images the guide embeds.
|
|
31
31
|
- Read the **BRD** §9 feature catalog (feature purpose/benefit, personas) and the **UsageGuide** (user flows + known limitations) for the plain-language "what it's for" and "tips".
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ The DevGuide already did the hard discovery work — reuse it:
|
|
|
34
34
|
|
|
35
35
|
### 3. Decide structure — single doc vs per-role split
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
**Split per role** when `roles ≥ 3` OR `screens > 12` OR any role has `> 8` screens; else single. Honor `--single`/`--split`. A split guide lives in `docs/productguides/` (index + per-role files co-located, so relative links resolve); a single guide is `docs/{AppName}-ProductGuide.md`. On a structure switch (single↔split), move/delete the stale-location copy so only one canonical copy survives.
|
|
38
38
|
|
|
39
39
|
### 4. Write the guide — END-USER voice, screenshot per screen (FAN OUT per role)
|
|
40
40
|
|
|
@@ -6,7 +6,7 @@ Render BRD.html, Architecture.html, and PROJECT-STATUS.html from their markdown
|
|
|
6
6
|
|
|
7
7
|
After day-1 (or any phase that changes BRD/Architecture/PROJECT-STATUS), regenerate the human-readable HTML mirrors. Self-contained single-file HTML with Mermaid rendering, copy buttons on every code block, mermaid toolbar (zoom/fit/1:1/fullscreen/popout), inline TOC, and sidebar TOC for long docs.
|
|
8
8
|
|
|
9
|
-
**If the user has ad-hoc HUMAN-readable docs to render (
|
|
9
|
+
**If the user has ad-hoc HUMAN-readable docs to render (design notes, archived docs, anything else), use `*generate-html @path/to/file.md` instead — this task only handles the three canonical files.** Note: the **checklist is an AI-agent working document and is NEVER rendered to HTML** — do not render `*-Checklist.md` here or via `generate-html`. Nor are the miss log and feedback files.
|
|
10
10
|
|
|
11
11
|
## Inputs
|
|
12
12
|
|
|
@@ -6,9 +6,9 @@ First: `bash .tfcore/utils/tf-phase.sh start triage-issues {App}` prints the sta
|
|
|
6
6
|
|
|
7
7
|
## Steps
|
|
8
8
|
|
|
9
|
-
1. Read every screenshot and the notes; from a written list take one issue per symptom in the reporter's words. Build the issue list: screen, role, symptom, evidence path, a first guess of kind (`layout`, `render-empty`, `data-logic`, `rag`).
|
|
9
|
+
1. Read every screenshot and the notes; from a written list take one issue per symptom in the reporter's words. Build the issue list: screen, role, symptom, evidence path, a first guess of kind (`layout`, `render-empty`, `data-logic`, `rag`). A failed CI run (its id, link or log) is evidence too: its issue is the failing step and its first error.
|
|
10
10
|
2. `bash .tfcore/utils/tf-verify-boot.sh start` boots the application (dependent services first, from their own configuration). Sign in as a UsageGuide test user (`.tfcore/tasks/_smoke-test-policy.md`). NONE means the issues are logged from the evidence alone and the report says the app was not booted.
|
|
11
|
-
3. Reproduce each issue on its screen; `bash .tfcore/utils/tf-verify-screens.sh --screen <name>=<route> … --base <url>` gives the render and visual evidence and a screenshot. Settle the kind from what you see and map the issue to its row in the checklist (the acceptance line's screen; the DevGuide's screen list). Print the triage table: issue, row, kind, action.
|
|
11
|
+
3. Reproduce each issue on its screen; `bash .tfcore/utils/tf-verify-screens.sh --screen <name>=<route> … --base <url>` gives the render and visual evidence and a screenshot. Settle the kind from what you see and map the issue to its row in the checklist (the acceptance line's screen; the DevGuide's screen list). Print the triage table: issue, row, kind, action. A CI failure is reproduced with `bash .tfcore/utils/tf-ci-repro.sh [<workflow>] [--job <id>]`, never by running the workflow's commands here: this machine's package caches and build output hide what a fresh runner hits (Lekhak TF-019: a runtime pack only the local NuGet cache held). Its FAIL line is the reproduction; a PASS means it could not be reproduced here, logged with `note`.
|
|
12
12
|
4. Log each issue with `bash .tfcore/utils/tf-triage.sh {App} …`: `demote <REQ> "<symptom>" --kind … --evidence <path>` for a defect inside a row's scope, whatever its status; `new "<title>" "<When … on <screen>, then …>" --section "<screen>"` for a defect no row covers (a Not Started row with `BRD-pending`; new scope rather than a defect is still logged, and the report names `*amend-docs`); `note <REQ> "<what was tried>"` when it could not be reproduced. `--source production` for a live report. Whose gap it was is recorded as `--sort` (the four questions of `.tfcore/tasks/log-miss.md` step 1); without it a demoted row is `weak-check` (its line existed and the check let it through) and a new row is `spec`.
|
|
13
13
|
5. With `verify`: execute `.tfcore/tasks/verify-phase.md` inline, scoped to the demoted rows' siblings on the same screens and any scope the owner named. Without it, the verifier does not run and nothing is re-checked informally.
|
|
14
14
|
6. `bash .tfcore/utils/tf-verify-boot.sh stop`, then `bash .tfcore/utils/tf-triage.sh {App} close --started <the step-0 time>`: one escaped gate record per row (no check caught it), one miss per row in the reporter's words with the discovery cost, the run record, and a warning plus a miss if any code file changed during the run.
|
|
@@ -659,6 +659,10 @@ The stream is for scripts. The owner reads `docs/<App>-Misses.md`: one row per `
|
|
|
659
659
|
|
|
660
660
|
`tf-emit.sh` rebuilds the file and its HTML through `tf-misses-md.py` after **every** write to the misses stream, whichever door wrote it (log-miss, triage, fix-close, a hand-written record, an amend), so the file is never older than the record. It is **derived**: nothing reads it, nobody edits it, and a wrong row is corrected by a new record. A project whose stream predates the script gets the file by running `bash .tfcore/utils/tf-misses-md.sh` once. The check for FR-31 is in `tests/bugs/run.sh`: the row count equals the record count.
|
|
661
661
|
|
|
662
|
+
### 5.5.11 `kind: "miss-void"` — withdrawing a miss that should never have been written (added 2026-09-22, TrBlazeUI TF-001)
|
|
663
|
+
|
|
664
|
+
§2.7's `run-void`, for this stream. Fields: `miss_id` (the miss withdrawn) and `reason`. Written only by `bash .tfcore/utils/tf-emit.sh --void-miss <miss_id> "<reason>"`, which refuses a miss not on the stream, a second void and an empty reason. Readers: the miss leaves every figure with its fixes and amends (`misses_voided_n`, `misses_voided`; an orphan is `miss_voids_orphaned_n`), `--open-miss` and `--open-misses` skip it, and `docs/<App>-Misses.md` lists it under *Withdrawn*. Proved by `tests/regression/run.sh tb_001`.
|
|
665
|
+
|
|
662
666
|
## 6. Provenance — three separations, one rule applied three times
|
|
663
667
|
|
|
664
668
|
**Data from different provenances never merges.**
|
|
@@ -90,13 +90,14 @@ detect_type() {
|
|
|
90
90
|
# docs: a markdown/spec repo — no source at all
|
|
91
91
|
[[ -z "$projects" ]] && { echo docs; return; }
|
|
92
92
|
|
|
93
|
+
# ($projects is never empty here, so xargs always has input: no GNU-only -r needed.)
|
|
93
94
|
# Packable = genuinely published. IsPackable is usually <IsPackable>false</IsPackable>
|
|
94
95
|
# on test projects, so only an explicit `true` counts.
|
|
95
|
-
packable="$(printf '%s\n' "$projects" | xargs
|
|
96
|
+
packable="$(printf '%s\n' "$projects" | xargs grep -lE \
|
|
96
97
|
'<(PackageId>|GeneratePackageOnBuild>[[:space:]]*true|IsPackable>[[:space:]]*true)' 2>/dev/null)"
|
|
97
98
|
|
|
98
99
|
# A shipped executable/hostable head: console/desktop (Exe), MAUI, or ASP.NET/Blazor host.
|
|
99
|
-
heads="$(printf '%s\n' "$projects" | xargs
|
|
100
|
+
heads="$(printf '%s\n' "$projects" | xargs grep -lE \
|
|
100
101
|
'<OutputType>[[:space:]]*Exe|<UseMaui>[[:space:]]*true|Sdk="Microsoft\.NET\.Sdk\.(Web|BlazorWebAssembly)"' 2>/dev/null)"
|
|
101
102
|
|
|
102
103
|
# THE DECIDING TEST: is a packaged project the product, or merely a helper?
|
|
@@ -375,6 +375,11 @@ def analyse_misses(misses):
|
|
|
375
375
|
|
|
376
376
|
Raw counts and the miss-class distribution ARE poolable: a miss counts as a miss
|
|
377
377
|
whoever missed it; only its attribution is confidence-bounded."""
|
|
378
|
+
# a miss withdrawn by a miss-void leaves every figure, with its fixes and amends (§5.5.11)
|
|
379
|
+
voids = {m.get("miss_id"): m for m in misses if m.get("kind") == "miss-void"}
|
|
380
|
+
ids = {m.get("miss_id") for m in misses if m.get("kind") == "miss"}
|
|
381
|
+
voided = ["%s — %s" % (k, v.get("reason") or "no reason recorded") for k, v in voids.items() if k in ids]
|
|
382
|
+
misses = [m for m in misses if m.get("miss_id") not in voids or m.get("kind") == "review"]
|
|
378
383
|
opened = [m for m in misses if m.get("kind") == "miss" and not m.get("backfilled")]
|
|
379
384
|
fixes = [m for m in misses if m.get("kind") == "miss-fix" and not m.get("backfilled")]
|
|
380
385
|
amends = [m for m in misses if m.get("kind") == "miss-amend"]
|
|
@@ -566,6 +571,9 @@ def analyse_misses(misses):
|
|
|
566
571
|
"resolved_misses": len(resolved),
|
|
567
572
|
"amendments_applied": amended,
|
|
568
573
|
"orphan_amends": orphan_amends,
|
|
574
|
+
"misses_voided_n": len(voided),
|
|
575
|
+
"misses_voided": voided,
|
|
576
|
+
"miss_voids_orphaned_n": len(voids) - len(voided),
|
|
569
577
|
# Denominator is records that CARRY the field — why_missed is optional (§5.5.6)
|
|
570
578
|
# and a missing value means "not assessed", never a zero for some category —
|
|
571
579
|
# AND records that COULD have carried it: one written before 2026-08-28 had no
|
|
@@ -1266,8 +1274,8 @@ def analyse(repos):
|
|
|
1266
1274
|
"project_type": project_type(repo)[0],
|
|
1267
1275
|
"gates": len(g), "gates_backfilled": sum(1 for x in g if x.get("backfilled")),
|
|
1268
1276
|
"runs": len(r), "sessions": len(s), "commits": len(c),
|
|
1269
|
-
"misses": sum(1 for x in
|
|
1270
|
-
if
|
|
1277
|
+
"misses": sum(1 for x in repo_misses if x.get("kind") == "miss" and x.get("miss_id") not in
|
|
1278
|
+
{v.get("miss_id") for v in repo_misses if v.get("kind") == "miss-void"}),
|
|
1271
1279
|
# A repo reclassified after records were written (most often a
|
|
1272
1280
|
# greenfield project born `docs` and upgraded once it grew a head)
|
|
1273
1281
|
# keeps the OLD value on every existing record — append-only means
|
|
@@ -1795,6 +1803,15 @@ def print_misses(m, W):
|
|
|
1795
1803
|
print(" tokens to apply the corrections : %s (n=%d)"
|
|
1796
1804
|
% (m["review_tokens_correct_mean"] if m["review_tokens_correct_mean"] is not None
|
|
1797
1805
|
else "insufficient data", m["review_tokens_correct_n"]))
|
|
1806
|
+
if m.get("misses_voided_n"):
|
|
1807
|
+
print(" withdrawn : %d miss(es) named by a miss-void, in no figure above:" % m["misses_voided_n"])
|
|
1808
|
+
for line in m["misses_voided"][:6]:
|
|
1809
|
+
print(" " + line)
|
|
1810
|
+
if m["misses_voided_n"] > 6:
|
|
1811
|
+
print(" ... and %d more" % (m["misses_voided_n"] - 6))
|
|
1812
|
+
if m.get("miss_voids_orphaned_n"):
|
|
1813
|
+
print(" ⚠ %d miss-void record(s) name no miss on this stream — nothing was excluded for them"
|
|
1814
|
+
% m["miss_voids_orphaned_n"])
|
|
1798
1815
|
if m["orphan_amends"]:
|
|
1799
1816
|
print(" ⚠ %d miss-amend record(s) name no known miss, or a field outside the"
|
|
1800
1817
|
% m["orphan_amends"])
|
|
@@ -58,3 +58,9 @@ If `/trblazeui` or `/techierag` is missing: run `dotnet build` (the NuGet target
|
|
|
58
58
|
Under Claude Code, if `/flow-master *render-workflow-docs <App>` returns "Unknown command", use `/TechieFlow:agents:flow-master *render-workflow-docs <App>` instead. (Under OpenCode `/flow-master` is the registered agent name — there is no fuller form to fall back to.)
|
|
59
59
|
|
|
60
60
|
After the agent is loaded, every TechieFlow-native agent (analyst, architect, flow-master, verifier) accepts `*command args` style invocations. trblazeui and techierag are free-form personas — normally `flow-master *build-phase <App>` calls them as sub-agents, but you can also drive them directly with prompts like `Implement REQ-UI-* from docs/<App>-Checklist.md to match the mockups in docs/mockups/.`
|
|
61
|
+
|
|
62
|
+
How to talk to me:
|
|
63
|
+
1. Rate your confidence. Tag any claim you are not sure of: [Certain] with evidence, [Likely] a strong inference, [Guessing] filling a gap. If most of a reply is guessing, say so in the first line.
|
|
64
|
+
2. Uncomfortable answer first. If there is something I will not want to hear, it is the first sentence, not the third paragraph.
|
|
65
|
+
3. No warm-up. No "great question", no "there are several ways to look at this", no restating my question. Start with the most useful sentence.
|
|
66
|
+
4. Disagree with structure: "I disagree because [reason]. I would do [alternative]. The risk in your approach is [downside]." Then do what I decide.
|
|
@@ -25,9 +25,11 @@ rule: mockup-links
|
|
|
25
25
|
Size and requirement cap: Small up to 10 screens, one role, 50 requirements; Medium up to 20 screens,
|
|
26
26
|
100 requirements; Large is split into phases, each phase its own BRD. Every routed page is a screen;
|
|
27
27
|
dialogs, tabs and panels are regions of their page and are listed under it.
|
|
28
|
-
Large project: one BRD per phase, phase 1 in docs/{App}-BRD.md, phase n in
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
Large project: one BRD per phase, phase 1 in docs/{App}-BRD.md from this template, phase n in
|
|
29
|
+
docs/{App}-Pn-BRD.md from app-phase-brd-tmpl.md, which carries only that phase's screens and
|
|
30
|
+
requirements; this file keeps the whole application's scope, users, non-functionals, constraints
|
|
31
|
+
and risks. The header gains a row "| Phase | n of m |", Size is the phase's own size (Small or
|
|
32
|
+
Medium), and BRD-N runs on from the previous phase's last id. The phase plan is docs/{App}-Phases.md.
|
|
31
33
|
Mermaid: quote every label (A["Order (v2)"]); never use `end` as a node id. -->
|
|
32
34
|
|
|
33
35
|
# {App} — Business Requirements
|