@techierathore/techieflow 1.0.3 → 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 -8
- package/.claude/commands/TechieFlow/agents/architect.md +1 -10
- package/.claude/commands/TechieFlow/agents/flow-master.md +1 -0
- package/.claude/commands/TechieFlow/agents/verifier.md +1 -0
- package/.claude/commands/TechieFlow/tasks/_owner-language.md +26 -0
- package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +1 -1
- package/.claude/commands/TechieFlow/tasks/amend-docs.md +6 -5
- package/.claude/commands/TechieFlow/tasks/build-phase.md +1 -1
- 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 +2 -2
- package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +2 -2
- package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +4 -0
- 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/metrics-report.md +2 -0
- package/.claude/commands/TechieFlow/tasks/verify-phase.md +1 -1
- package/.opencode/plugin/techieflow.js +2 -2
- package/.tfcore/agents/analyst.md +5 -8
- package/.tfcore/agents/architect.md +1 -10
- package/.tfcore/agents/flow-master.md +1 -0
- package/.tfcore/agents/verifier.md +1 -0
- package/.tfcore/hooks/guard-metrics.sh +13 -1
- package/.tfcore/hooks/guard-verify-deps.sh +124 -0
- package/.tfcore/routing.yaml +61 -0
- package/.tfcore/tasks/_owner-language.md +26 -0
- package/.tfcore/tasks/_smoke-test-policy.md +1 -1
- package/.tfcore/tasks/amend-docs.md +6 -5
- package/.tfcore/tasks/build-phase.md +1 -1
- 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 +2 -2
- package/.tfcore/tasks/day1-greenfield.md +2 -2
- package/.tfcore/tasks/facilitate-brainstorming-session.md +4 -0
- package/.tfcore/tasks/generate-html.md +5 -1
- package/.tfcore/tasks/handoff-phase.md +18 -16
- package/.tfcore/tasks/metrics-report.md +2 -0
- package/.tfcore/tasks/verify-phase.md +1 -1
- package/.tfcore/telemetry/SCHEMA.md +57 -1
- package/.tfcore/telemetry/tf-metrics.sh +416 -19
- package/.tfcore/templates/v4custom/app-brief-tmpl.md +65 -0
- package/.tfcore/templates/v4custom/app-decision-request-tmpl.md +85 -0
- package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +86 -31
- package/.tfcore/templates/v4custom/html-render-shell.md +1 -1
- 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-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-build-list.py +23 -3
- package/.tfcore/utils/tf-build-list.sh +3 -2
- package/.tfcore/utils/tf-doc-check.py +106 -4
- package/.tfcore/utils/tf-emit.sh +353 -16
- package/.tfcore/utils/tf-fix-close.sh +34 -2
- package/.tfcore/utils/tf-gitignore-audit.sh +50 -3
- package/.tfcore/utils/tf-goal.sh +93 -6
- package/.tfcore/utils/tf-misses-md.py +14 -17
- package/.tfcore/utils/tf-misses-md.sh +5 -4
- package/.tfcore/utils/tf-model-pick.sh +475 -0
- package/.tfcore/utils/tf-phase.sh +18 -0
- package/.tfcore/utils/tf-render-html.py +20 -0
- package/.tfcore/utils/tf-routing-bind.sh +87 -1
- package/.tfcore/utils/tf-routing.sh +7 -0
- package/.tfcore/utils/tf-selfcheck.sh +323 -0
- package/.tfcore/utils/tf-split-brd.py +6 -1
- package/.tfcore/utils/tf-status-facts.py +61 -12
- package/.tfcore/utils/tf-verify-screens.mjs +85 -13
- package/.tfcore/utils/tf-verify-screens.sh +12 -3
- package/.tfcore/utils/tf-verify-tests.sh +60 -17
- package/.tfcore/utils/tf-verify-verdict.py +11 -2
- package/README.md +15 -5
- package/opencode.jsonc +24 -0
- package/package.json +1 -1
- package/scripts/install.mjs +4 -0
- 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
|
@@ -4,18 +4,27 @@
|
|
|
4
4
|
# bash .tfcore/utils/tf-verify-screens.sh --list tests/.artifacts/verify/list.json --base http://localhost:5099 \
|
|
5
5
|
# [--login-path /login --user ravi --password '…'] [--cookie 'k=v'] [--storage-state file]
|
|
6
6
|
# bash .tfcore/utils/tf-verify-screens.sh --screen home=/ --screen posts=/posts --base URL # a smoke: changed screens only
|
|
7
|
-
# bash .tfcore/utils/tf-verify-screens.sh --list … --cdp http://172.18.144.1:9223 #
|
|
7
|
+
# bash .tfcore/utils/tf-verify-screens.sh --list … --cdp http://172.18.144.1:9223 # an embedded-browser desktop head
|
|
8
|
+
# … --error-selector '.my-crash-banner' # the banner THIS stack shows when its client side falls over
|
|
9
|
+
# # (repeatable; defaults cover one framework's id and [data-error-ui])
|
|
8
10
|
#
|
|
9
11
|
# For every screen, at 1280 and 390 px: every control the mockup anchors is present and shows
|
|
10
12
|
# something, no header-only table, no blank page, no Blazor or console error (render); nothing
|
|
11
13
|
# overlaps, nothing has zero size or sits off-screen, no sideways scroll, a stylesheet is loaded
|
|
12
|
-
# (visual).
|
|
14
|
+
# (visual). Overlap and off-screen are measured on the rectangle an element paints in, clipped by
|
|
15
|
+
# every scrolling ancestor, and the page is read only after its first render (--render-wait,
|
|
16
|
+
# default 5000 ms). Saves a screenshot per screen and width under tests/.artifacts/verify/screens/ and
|
|
13
17
|
# writes tests/.artifacts/verify/screens.json; that JSON is the evidence a smoke names before a
|
|
14
18
|
# row is written Implemented, and what the verdict script reads. It decides no row's status.
|
|
15
19
|
# Exit 0 every screen OK · 5 a screen failed · 2 the app was unreachable · 4 Playwright missing.
|
|
16
20
|
set -uo pipefail
|
|
17
21
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
-
|
|
22
|
+
# `--help` is a request and exits 0; no argument at all is a mistake and exits 3. (The one-line
|
|
23
|
+
# form this replaces computed "0--help" as its exit code and printed a bash error under the help.)
|
|
24
|
+
case "${1:-}" in
|
|
25
|
+
-h|--help) sed -n '2,19p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
26
|
+
"") sed -n '2,19p' "$0" | sed 's/^# \{0,1\}//'; exit 3 ;;
|
|
27
|
+
esac
|
|
19
28
|
command -v node >/dev/null 2>&1 || { echo "tf-verify-screens: node is required (bash .tfcore/utils/tf-verify-env.sh)" >&2; exit 4; }
|
|
20
29
|
node -e "import('playwright').then(()=>process.exit(0)).catch(()=>process.exit(1))" 2>/dev/null || {
|
|
21
30
|
echo "tf-verify-screens: the 'playwright' package is not resolvable from this folder; run bash .tfcore/utils/tf-verify-env.sh" >&2; exit 4; }
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
#
|
|
7
7
|
# Browser tests: `npx playwright test` over tests/verify/ (the JSON reporter). Unit tests:
|
|
8
8
|
# `tf-build.sh test` with normal console verbosity. A test belongs to a row when its name contains
|
|
9
|
-
# the row's id (REQ-UI-004 …). A row is PASS when
|
|
10
|
-
# failed (the reason and the screenshot, when the reporter kept one, are recorded),
|
|
11
|
-
# when
|
|
9
|
+
# the row's id (REQ-UI-004 …). A row is PASS when a test carrying its id passed and none failed,
|
|
10
|
+
# FAIL when one failed (the reason and the screenshot, when the reporter kept one, are recorded),
|
|
11
|
+
# NOT-TESTED when every test carrying its id was SKIPPED, and absent when no test carries its id.
|
|
12
|
+
# A skipped clause is a third outcome, never a failure: `test.skip(!SEEDED, …)` says the state does
|
|
13
|
+
# not exist in the data, which is not a defect (TF-022). Writes tests/.artifacts/verify/tests.json.
|
|
12
14
|
# Exit 0 ran (whatever the results) · 2 nothing could run.
|
|
13
15
|
set -uo pipefail
|
|
14
16
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -54,17 +56,38 @@ fi
|
|
|
54
56
|
TF_PWJSON="$PWJSON" TF_UNITLOG="$UNITLOG" TF_UNITLINE="$UNITLINE" TF_OUT="$OUT" python3 - <<'PY'
|
|
55
57
|
import json, os, re
|
|
56
58
|
ID = re.compile(r"(REQ-(?:UI|FN|NFR|RAG)-\d{3})", re.I)
|
|
57
|
-
reqs
|
|
59
|
+
reqs = {}
|
|
60
|
+
browser = {"ran": False, "passed": 0, "failed": 0, "skipped": 0, "tests": 0}
|
|
61
|
+
unit = {"ran": False, "line": os.environ.get("TF_UNITLINE", "")}
|
|
58
62
|
|
|
59
|
-
def add(rid,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
def add(rid, outcome, name, source, reason="", shot=""):
|
|
64
|
+
"""Record one test against a row. `outcome` is "pass", "fail" or "skip".
|
|
65
|
+
|
|
66
|
+
A SKIPPED clause is the third outcome and neither of the other two. A conditional
|
|
67
|
+
`test.skip(!SEEDED, "needs the seeded dataset")` says the state does not exist in the
|
|
68
|
+
data yet: that is not a pass, and it is not a defect either. Counting it as a failure
|
|
69
|
+
put FAIL on rows no code could clear and sent build-phase back into FIX mode against
|
|
70
|
+
them (TF-022, TfLens 2026-09-09). A row whose clauses were ALL skipped ends
|
|
71
|
+
NOT-TESTED, which the verdict script reads as not measured — never Verified, never a
|
|
72
|
+
defect. tests/regression/run.sh tf_022."""
|
|
73
|
+
r = reqs.setdefault(rid.upper(), {"result": "NOT-TESTED", "source": source, "tests": [],
|
|
74
|
+
"skipped": [], "passed": 0, "failed": 0,
|
|
75
|
+
"reason": "", "screenshot": ""})
|
|
76
|
+
if outcome == "skip":
|
|
77
|
+
r["skipped"].append(name)
|
|
64
78
|
if not r["reason"]:
|
|
79
|
+
r["reason"] = (reason or "the test was skipped")[:200]
|
|
80
|
+
return
|
|
81
|
+
r["tests"].append(name)
|
|
82
|
+
if outcome == "pass":
|
|
83
|
+
r["passed"] += 1
|
|
84
|
+
else:
|
|
85
|
+
if r["result"] != "FAIL": # the first real failure owns the reason
|
|
65
86
|
r["reason"] = reason[:200]
|
|
87
|
+
r["failed"] += 1
|
|
66
88
|
if shot and not r["screenshot"]:
|
|
67
89
|
r["screenshot"] = shot
|
|
90
|
+
r["result"] = "FAIL" if r["failed"] else "PASS"
|
|
68
91
|
|
|
69
92
|
pw = os.environ.get("TF_PWJSON", "")
|
|
70
93
|
if pw and os.path.isfile(pw):
|
|
@@ -78,11 +101,21 @@ if pw and os.path.isfile(pw):
|
|
|
78
101
|
res = t.get("results", [])
|
|
79
102
|
last = res[-1] if res else {}
|
|
80
103
|
status = last.get("status", t.get("status", "unexpected"))
|
|
81
|
-
|
|
104
|
+
# skipped is read FIRST: it is neither a pass nor a failure (TF-022)
|
|
105
|
+
if status == "skipped" or t.get("status") == "skipped":
|
|
106
|
+
outcome = "skip"
|
|
107
|
+
elif status in ("passed", "expected") or t.get("status") == "expected":
|
|
108
|
+
outcome = "pass"
|
|
109
|
+
else:
|
|
110
|
+
outcome = "fail"
|
|
82
111
|
browser["tests"] += 1
|
|
83
|
-
browser["passed"
|
|
112
|
+
browser[{"pass": "passed", "fail": "failed", "skip": "skipped"}[outcome]] += 1
|
|
84
113
|
err = ""
|
|
85
|
-
if
|
|
114
|
+
if outcome == "skip":
|
|
115
|
+
why = [a.get("description") for a in (t.get("annotations") or [])
|
|
116
|
+
if a.get("type") == "skip" and a.get("description")]
|
|
117
|
+
err = "skipped" + (": " + str(why[0]) if why else "")
|
|
118
|
+
elif last.get("error"):
|
|
86
119
|
err = re.sub(r"\x1b\[[0-9;]*m", "", str(last["error"].get("message", "")).split("\n")[0])
|
|
87
120
|
shot = ""
|
|
88
121
|
for a in last.get("attachments", []):
|
|
@@ -91,7 +124,7 @@ if pw and os.path.isfile(pw):
|
|
|
91
124
|
shot = shot[shot.find("tests/"):] if "tests/" in shot else shot
|
|
92
125
|
break
|
|
93
126
|
for rid in set(ID.findall(title)):
|
|
94
|
-
add(rid,
|
|
127
|
+
add(rid, outcome, title.strip(), "browser", err, shot)
|
|
95
128
|
for s in suite.get("suites", []):
|
|
96
129
|
walk(s, path + [s.get("title", "")] if s.get("title") and not s["title"].endswith(".ts") and not s["title"].endswith(".js") and not s["title"].endswith(".mjs") else path)
|
|
97
130
|
for s in data.get("suites", []):
|
|
@@ -104,20 +137,30 @@ if ul and os.path.isfile(ul):
|
|
|
104
137
|
unit["ran"] = True
|
|
105
138
|
for line in open(ul, encoding="utf-8", errors="replace"):
|
|
106
139
|
m = re.match(r"\s*(Passed|Failed|Skipped)\s+(\S.*?)\s*(\[[^\]]*\])?\s*$", line)
|
|
107
|
-
if not m
|
|
140
|
+
if not m:
|
|
108
141
|
continue
|
|
142
|
+
# a skipped unit test is recorded as skipped, not dropped: the row then reads
|
|
143
|
+
# NOT-TESTED with the evidence, rather than looking as if no test exists (TF-022)
|
|
144
|
+
outcome = {"Passed": "pass", "Failed": "fail", "Skipped": "skip"}[m.group(1)]
|
|
109
145
|
name = m.group(2).strip()
|
|
110
146
|
for rid in set(ID.findall(name)):
|
|
111
|
-
add(rid,
|
|
147
|
+
add(rid, outcome, name, "unit",
|
|
148
|
+
("unit test failed: " if outcome == "fail" else "unit test skipped: ") + name[:120])
|
|
112
149
|
unit["passed"] = sum(1 for r in reqs.values() if r["source"] == "unit" and r["result"] == "PASS")
|
|
113
150
|
unit["failed"] = sum(1 for r in reqs.values() if r["source"] == "unit" and r["result"] == "FAIL")
|
|
151
|
+
unit["not_tested"] = sum(1 for r in reqs.values() if r["source"] == "unit" and r["result"] == "NOT-TESTED")
|
|
114
152
|
|
|
115
153
|
json.dump({"reqs": reqs, "browser": browser, "unit": unit}, open(os.environ["TF_OUT"], "w"), indent=1)
|
|
116
|
-
p = sum(1 for r in reqs.values() if r["result"] == "PASS")
|
|
117
|
-
|
|
154
|
+
p = sum(1 for r in reqs.values() if r["result"] == "PASS")
|
|
155
|
+
f = sum(1 for r in reqs.values() if r["result"] == "FAIL")
|
|
156
|
+
ns = sum(1 for r in reqs.values() if r["result"] == "NOT-TESTED")
|
|
157
|
+
print(f"rows with a test: {len(reqs)} — {p} PASS, {f} FAIL, {ns} NOT-TESTED (every clause skipped)"
|
|
158
|
+
+ (f" (browser {browser['passed']}/{browser['tests']} tests passed, {browser['skipped']} skipped)" if browser["ran"] else ""))
|
|
118
159
|
for rid, r in sorted(reqs.items()):
|
|
119
160
|
if r["result"] == "FAIL":
|
|
120
161
|
print(f" FAIL {rid} — {r['reason']}" + (f" — {r['screenshot']}" if r["screenshot"] else ""))
|
|
162
|
+
elif r["result"] == "NOT-TESTED":
|
|
163
|
+
print(f" NOT-TESTED {rid} — {len(r['skipped'])} test(s) skipped, none ran — {r['reason']}")
|
|
121
164
|
print(f"JSON: {os.environ['TF_OUT']}")
|
|
122
165
|
PY
|
|
123
166
|
[[ $ran_any -eq 1 ]] && exit 0 || exit 2
|
|
@@ -19,7 +19,8 @@ For each row, the seven checks in order, and the first that fails is the verdict
|
|
|
19
19
|
(standards has no script yet and is never listed as run)
|
|
20
20
|
|
|
21
21
|
Verdicts: PASS · FAIL (acceptance) · BUILD-FAIL · RENDER-FAIL · ASSET-FAIL · VISUAL-FAIL · MOCKUP-FAIL ·
|
|
22
|
-
PERF-FAIL · NOT-TESTED (no test carried the id
|
|
22
|
+
PERF-FAIL · NOT-TESTED (no test carried the id, or every test carrying it was skipped; the screen
|
|
23
|
+
checks passed or did not run) ·
|
|
23
24
|
NOT-OBSERVABLE (an NFR row with no budget and no unit test) · NOT-DRIVEN (its screen was never driven:
|
|
24
25
|
the head could not be booted, or has no driver). A row is Verified only on PASS. NOT-* rows keep their
|
|
25
26
|
status and get a Remark saying so; nothing is ever written as a static-only pass.
|
|
@@ -114,6 +115,13 @@ def main(argv):
|
|
|
114
115
|
scr = scr_by_name.get(r.get("screen") or "")
|
|
115
116
|
driven = bool(scr) and scr.get("render") != "UNREACHABLE"
|
|
116
117
|
t = tests.get(rid)
|
|
118
|
+
# A row whose every clause was SKIPPED is NOT-TESTED: the acceptance gate did not run on
|
|
119
|
+
# it. It is neither a pass nor a defect, so it grades exactly like a row with no test at
|
|
120
|
+
# all — never Verified, no gate record (TF-022). `t` is kept for the note.
|
|
121
|
+
skipped_only = bool(t) and t.get("result") == "NOT-TESTED"
|
|
122
|
+
if skipped_only:
|
|
123
|
+
notes.append(f"acceptance not measured ({len(t.get('skipped') or [])} test(s) skipped, none ran)")
|
|
124
|
+
t = None
|
|
117
125
|
# every check on its own: (name, ran, failed, verdict, class, detail, evidence); the first failure wins
|
|
118
126
|
checks = []
|
|
119
127
|
|
|
@@ -188,7 +196,8 @@ def main(argv):
|
|
|
188
196
|
else f"screen {r['screen']} was not driven in this run")
|
|
189
197
|
elif not t:
|
|
190
198
|
verdict = "NOT-TESTED"
|
|
191
|
-
detail = f"
|
|
199
|
+
detail = (f"every test named {rid} was skipped" if skipped_only else f"no test named {rid} ran") \
|
|
200
|
+
+ (f"; screen {r['screen']} renders and looks right" if driven else "")
|
|
192
201
|
elif r.get("screen") and not driven and not booted:
|
|
193
202
|
verdict = "NOT-DRIVEN"
|
|
194
203
|
detail = f"test passed but {boot.get('head', 'the app')} was not driven: {boot.get('reason')}"
|
package/README.md
CHANGED
|
@@ -126,10 +126,11 @@ Replace `{App}` with your application name. Claude Code takes the long persona p
|
|
|
126
126
|
|
|
127
127
|
## 5. What it produces
|
|
128
128
|
|
|
129
|
-
Every per-project document is named after the application: `docs/<App>-BRD.md`, `-Architecture.md`, `-Checklist.md`, `-UIDesign.md`, `-Coding-Standards.md`, `-UsageGuide.md`, `-DevGuide.md`, `-ProductGuide.md`, `-Deployment-Checklist.md`, plus `PROJECT-STATUS.md` and `CLAUDE.md` at the root, mockups under `docs/mockups/`, and screenshots under `docs/screenshots/<App>/`.
|
|
129
|
+
Every per-project document is named after the application: `docs/<App>-Brief.md`, `-BRD.md`, `-Architecture.md`, `-Checklist.md`, `-UIDesign.md`, `-Coding-Standards.md`, `-UsageGuide.md`, `-DevGuide.md`, `-ProductGuide.md`, `-Deployment-Checklist.md`, `-Misses.md`, `-Decision-Request.md`, `-<Upstream>-Feedback.md`, plus `PROJECT-STATUS.md` and `CLAUDE.md` at the root, mockups under `docs/mockups/`, and screenshots under `docs/screenshots/<App>/`.
|
|
130
130
|
|
|
131
131
|
| Document | Who reads it |
|
|
132
132
|
|---|---|
|
|
133
|
+
| Brief | You, before anything is built. One page: what it is, who for, must do, out of scope. Day-1 reads it. |
|
|
133
134
|
| BRD, Architecture, UIDesign and the mockups | You, at the day-1 review. This is the cheap moment to redirect. |
|
|
134
135
|
| Checklist | Agents only. One table, one row per requirement, the single source of truth. Never rendered to HTML. |
|
|
135
136
|
| Coding Standards | Every implementing agent, and the verifier's standards check. |
|
|
@@ -138,9 +139,11 @@ Every per-project document is named after the application: `docs/<App>-BRD.md`,
|
|
|
138
139
|
| DevGuide | A developer tracing a bug: every screen to its code, with the line to break on. |
|
|
139
140
|
| ProductGuide | The end user. |
|
|
140
141
|
| Deployment Checklist | Whoever puts it on the host, after UAT. |
|
|
141
|
-
|
|
|
142
|
+
| Misses | You and the agents. Rebuilt from the record whenever something is logged as missed. |
|
|
143
|
+
| Decision Request | You. Every choice an agent cannot make: options, what each costs, a recommendation, and a block to paste back. It is never read back by an agent. |
|
|
144
|
+
| Feedback, one file per upstream | That team — each library, and the framework itself. Every entry says in its first word whether it blocks your work. Reporting is never refused; a non-blocking entry never stops a run. |
|
|
142
145
|
|
|
143
|
-
Each document has a required shape and a size budget for the application's size (Small, Medium or Large), enforced by `bash .tfcore/utils/tf-doc-check.sh --app {App}`. Add `--warn` to get a report instead of a refusal, which is what you want on a project that predates the schemas. Human documents render to HTML by script; the checklist
|
|
146
|
+
Each document has a required shape and a size budget for the application's size (Small, Medium or Large), enforced by `bash .tfcore/utils/tf-doc-check.sh --app {App}`. Add `--warn` to get a report instead of a refusal, which is what you want on a project that predates the schemas. Human documents render to HTML by script; an agent document never does — the checklist and the miss list are refused by name.
|
|
144
147
|
|
|
145
148
|
---
|
|
146
149
|
|
|
@@ -152,7 +155,7 @@ The shipped configuration allows every file operation and all of Bash with no pr
|
|
|
152
155
|
|
|
153
156
|
Five append-only streams per project under `docs/metrics/`, read with `*metrics {App}`: command runs, verification verdicts, misses, chat sessions, and your own commits. They answer five questions — the first-pass rate, which check caught the failure, the escape rate, what misses cost to fix, and the effort per phase. Definitions, real figures and the sentence to say about each are in [`docs/TechieFlow-Telemetry-Explained.md`](docs/TechieFlow-Telemetry-Explained.md); the field contract is `.tfcore/telemetry/SCHEMA.md`.
|
|
154
157
|
|
|
155
|
-
Standing rules: provenance never merges, only identifiers and counts are recorded, telemetry has no veto, and an agent never writes what it cannot know.
|
|
158
|
+
Standing rules: provenance never merges, only identifiers and counts are recorded, telemetry has no veto, and an agent never writes what it cannot know. **Tokens are what is recorded; money is worked out when the report runs.** A record stores what the provider itself charged — only a metered provider charges per token — and never a computed price. The report separately prints a **list price**, the published rate applied to the tokens actually measured, which is the one figure that compares a Claude phase with an OpenCode one. A price is never called a bill, and a monthly plan's allowance is counted apart from both.
|
|
156
159
|
|
|
157
160
|
## 8. Running phases on cheaper models
|
|
158
161
|
|
|
@@ -164,7 +167,14 @@ bash .tfcore/utils/tf-routing.sh on
|
|
|
164
167
|
bash .tfcore/utils/tf-routing.sh set-tier verify-phase economy
|
|
165
168
|
```
|
|
166
169
|
|
|
167
|
-
|
|
170
|
+
Each tier also names the models to try **instead**, while its own is out of quota. When a usage limit is hit, the supervisor parks that model until its stated reset, moves to the next model in the tier's chain, re-binds so the sub-agents move with it, and carries on; only when every model in the chain is limited does it wait. `--no-fallback` restores the old wait-for-reset behaviour.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
bash .tfcore/utils/tf-model-pick.sh status # what each tier would run right now
|
|
174
|
+
bash .tfcore/utils/tf-model-pick.sh chain standard opencode # the order it falls back in
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Drift is observed, never enforced: a run on the wrong model is recorded as such and nothing blocks. The step-by-step setup, and what a cost figure means on each kind of plan, are in the full guide: [`docs/TechieFlow-Routing-Guide.md`](docs/TechieFlow-Routing-Guide.md).
|
|
168
178
|
|
|
169
179
|
---
|
|
170
180
|
|
package/opencode.jsonc
CHANGED
|
@@ -897,6 +897,30 @@
|
|
|
897
897
|
"template": "{file:./.tfcore/tasks/create-doc.md}",
|
|
898
898
|
"description": "Create a document from a YAML-driven TechieFlow template (interactive, section by section)."
|
|
899
899
|
},
|
|
900
|
+
"techieflow:tasks:verify": {
|
|
901
|
+
"template": "{file:./.tfcore/tasks/verify-phase.md}",
|
|
902
|
+
"description": "The verifier's *verify. Boot the app, run the seven checks on every row in scope, write the verdicts."
|
|
903
|
+
},
|
|
904
|
+
"techieflow:tasks:metrics": {
|
|
905
|
+
"template": "{file:./.tfcore/tasks/metrics-report.md}",
|
|
906
|
+
"description": "The flow-master's *metrics. Read the telemetry streams and write the metrics report."
|
|
907
|
+
},
|
|
908
|
+
"techieflow:tasks:brainstorm": {
|
|
909
|
+
"template": "{file:./.tfcore/tasks/facilitate-brainstorming-session.md}",
|
|
910
|
+
"description": "The analyst's *brainstorm. Run a structured brainstorming session."
|
|
911
|
+
},
|
|
912
|
+
"techieflow:tasks:research-prompt": {
|
|
913
|
+
"template": "{file:./.tfcore/tasks/create-deep-research-prompt.md}",
|
|
914
|
+
"description": "The analyst's *research-prompt. Generate a well-structured research prompt."
|
|
915
|
+
},
|
|
916
|
+
"techieflow:tasks:research": {
|
|
917
|
+
"template": "{file:./.tfcore/tasks/create-deep-research-prompt.md}",
|
|
918
|
+
"description": "The architect's *research. Generate a well-structured research prompt."
|
|
919
|
+
},
|
|
920
|
+
"techieflow:tasks:create-project-brief": {
|
|
921
|
+
"template": "{file:./.tfcore/tasks/create-project-brief.md}",
|
|
922
|
+
"description": "Write the one-page project brief that day-1 takes as input: product, users, must-do, out of scope. The analyst's *create-project-brief."
|
|
923
|
+
},
|
|
900
924
|
"techieflow:tasks:create-deep-research-prompt": {
|
|
901
925
|
"template": "{file:./.tfcore/tasks/create-deep-research-prompt.md}",
|
|
902
926
|
"description": "Generate well-structured research prompts."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techierathore/techieflow",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "TechieFlow: a spec-driven development framework for AI coding agents, for Claude Code and OpenCode. One command installs it into any project.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
package/scripts/install.mjs
CHANGED
|
@@ -369,6 +369,10 @@ const canonicalSettings = String.raw`{
|
|
|
369
369
|
{
|
|
370
370
|
"type": "command",
|
|
371
371
|
"command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-build.sh\""
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
"type": "command",
|
|
375
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify-deps.sh\""
|
|
372
376
|
}
|
|
373
377
|
]
|
|
374
378
|
},
|