@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.
Files changed (95) hide show
  1. package/.claude/commands/TechieFlow/agents/analyst.md +5 -8
  2. package/.claude/commands/TechieFlow/agents/architect.md +1 -10
  3. package/.claude/commands/TechieFlow/agents/flow-master.md +1 -0
  4. package/.claude/commands/TechieFlow/agents/verifier.md +1 -0
  5. package/.claude/commands/TechieFlow/tasks/_owner-language.md +26 -0
  6. package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +1 -1
  7. package/.claude/commands/TechieFlow/tasks/amend-docs.md +6 -5
  8. package/.claude/commands/TechieFlow/tasks/build-phase.md +1 -1
  9. package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +4 -0
  10. package/.claude/commands/TechieFlow/tasks/create-doc.md +4 -0
  11. package/.claude/commands/TechieFlow/tasks/create-project-brief.md +22 -0
  12. package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +2 -2
  13. package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +2 -2
  14. package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +4 -0
  15. package/.claude/commands/TechieFlow/tasks/generate-html.md +5 -1
  16. package/.claude/commands/TechieFlow/tasks/handoff-phase.md +18 -16
  17. package/.claude/commands/TechieFlow/tasks/metrics-report.md +2 -0
  18. package/.claude/commands/TechieFlow/tasks/verify-phase.md +1 -1
  19. package/.opencode/plugin/techieflow.js +2 -2
  20. package/.tfcore/agents/analyst.md +5 -8
  21. package/.tfcore/agents/architect.md +1 -10
  22. package/.tfcore/agents/flow-master.md +1 -0
  23. package/.tfcore/agents/verifier.md +1 -0
  24. package/.tfcore/hooks/guard-metrics.sh +13 -1
  25. package/.tfcore/hooks/guard-verify-deps.sh +124 -0
  26. package/.tfcore/routing.yaml +61 -0
  27. package/.tfcore/tasks/_owner-language.md +26 -0
  28. package/.tfcore/tasks/_smoke-test-policy.md +1 -1
  29. package/.tfcore/tasks/amend-docs.md +6 -5
  30. package/.tfcore/tasks/build-phase.md +1 -1
  31. package/.tfcore/tasks/create-deep-research-prompt.md +4 -0
  32. package/.tfcore/tasks/create-doc.md +4 -0
  33. package/.tfcore/tasks/create-project-brief.md +22 -0
  34. package/.tfcore/tasks/day1-brownfield.md +2 -2
  35. package/.tfcore/tasks/day1-greenfield.md +2 -2
  36. package/.tfcore/tasks/facilitate-brainstorming-session.md +4 -0
  37. package/.tfcore/tasks/generate-html.md +5 -1
  38. package/.tfcore/tasks/handoff-phase.md +18 -16
  39. package/.tfcore/tasks/metrics-report.md +2 -0
  40. package/.tfcore/tasks/verify-phase.md +1 -1
  41. package/.tfcore/telemetry/SCHEMA.md +57 -1
  42. package/.tfcore/telemetry/tf-metrics.sh +416 -19
  43. package/.tfcore/templates/v4custom/app-brief-tmpl.md +65 -0
  44. package/.tfcore/templates/v4custom/app-decision-request-tmpl.md +85 -0
  45. package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +86 -31
  46. package/.tfcore/templates/v4custom/html-render-shell.md +1 -1
  47. package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
  48. package/.tfcore/utils/__pycache__/tf-checklist-edit.cpython-312.pyc +0 -0
  49. package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
  50. package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
  51. package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
  52. package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
  53. package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
  54. package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
  55. package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
  56. package/.tfcore/utils/__pycache__/tf-perf-grade.cpython-312.pyc +0 -0
  57. package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
  58. package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
  59. package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
  60. package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
  61. package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
  62. package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
  63. package/.tfcore/utils/tf-build-list.py +23 -3
  64. package/.tfcore/utils/tf-build-list.sh +3 -2
  65. package/.tfcore/utils/tf-doc-check.py +106 -4
  66. package/.tfcore/utils/tf-emit.sh +353 -16
  67. package/.tfcore/utils/tf-fix-close.sh +34 -2
  68. package/.tfcore/utils/tf-gitignore-audit.sh +50 -3
  69. package/.tfcore/utils/tf-goal.sh +93 -6
  70. package/.tfcore/utils/tf-misses-md.py +14 -17
  71. package/.tfcore/utils/tf-misses-md.sh +5 -4
  72. package/.tfcore/utils/tf-model-pick.sh +475 -0
  73. package/.tfcore/utils/tf-phase.sh +18 -0
  74. package/.tfcore/utils/tf-render-html.py +20 -0
  75. package/.tfcore/utils/tf-routing-bind.sh +87 -1
  76. package/.tfcore/utils/tf-routing.sh +7 -0
  77. package/.tfcore/utils/tf-selfcheck.sh +323 -0
  78. package/.tfcore/utils/tf-split-brd.py +6 -1
  79. package/.tfcore/utils/tf-status-facts.py +61 -12
  80. package/.tfcore/utils/tf-verify-screens.mjs +85 -13
  81. package/.tfcore/utils/tf-verify-screens.sh +12 -3
  82. package/.tfcore/utils/tf-verify-tests.sh +60 -17
  83. package/.tfcore/utils/tf-verify-verdict.py +11 -2
  84. package/README.md +15 -5
  85. package/opencode.jsonc +24 -0
  86. package/package.json +1 -1
  87. package/scripts/install.mjs +4 -0
  88. package/.tfcore/templates/architecture-tmpl.yaml +0 -650
  89. package/.tfcore/templates/brownfield-architecture-tmpl.yaml +0 -476
  90. package/.tfcore/templates/competitor-analysis-tmpl.yaml +0 -306
  91. package/.tfcore/templates/front-end-architecture-tmpl.yaml +0 -218
  92. package/.tfcore/templates/fullstack-architecture-tmpl.yaml +0 -823
  93. package/.tfcore/templates/market-research-tmpl.yaml +0 -252
  94. package/.tfcore/templates/project-brief-tmpl.yaml +0 -221
  95. 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 # a Blazor Hybrid Windows head
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). Saves a screenshot per screen and width under tests/.artifacts/verify/screens/ and
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
- case "${1:-}" in -h|--help|"") sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//'; exit "${1:+0}${1:-3}" ;; esac
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 every test carrying its id passed, FAIL when one
10
- # failed (the reason and the screenshot, when the reporter kept one, are recorded), and absent
11
- # when no test carries its id. Writes tests/.artifacts/verify/tests.json for the verdict script.
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, browser, unit = {}, {"ran": False, "passed": 0, "failed": 0, "tests": 0}, {"ran": False, "line": os.environ.get("TF_UNITLINE", "")}
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, ok, name, source, reason="", shot=""):
60
- r = reqs.setdefault(rid.upper(), {"result": "PASS", "source": source, "tests": [], "reason": "", "screenshot": ""})
61
- r["tests"].append(name)
62
- if not ok:
63
- r["result"] = "FAIL"
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
- ok = status in ("passed", "expected") or t.get("status") == "expected"
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" if ok else "failed"] += 1
112
+ browser[{"pass": "passed", "fail": "failed", "skip": "skipped"}[outcome]] += 1
84
113
  err = ""
85
- if last.get("error"):
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, ok, title.strip(), "browser", err, shot)
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 or m.group(1) == "Skipped":
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, m.group(1) == "Passed", name, "unit", "unit test failed: " + name[:120])
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"); f = len(reqs) - p
117
- print(f"rows with a test: {len(reqs)} — {p} PASS, {f} FAIL" + (f" (browser {browser['passed']}/{browser['tests']} tests passed)" if browser["ran"] else ""))
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; the screen checks passed or did not run) ·
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"no test named {rid} ran" + (f"; screen {r['screen']} renders and looks right" if driven else "")
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
- | Library feedback, one file per library | That library's team. |
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 never does.
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. Dollar figures exist only for OpenCode runs; Claude Code reports tokens and no rate card is ever applied.
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
- Drift is observed, never enforced: a run on the wrong model is recorded as such and nothing blocks. Full guide: [`docs/TechieFlow-Routing-Guide.md`](docs/TechieFlow-Routing-Guide.md).
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",
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": {
@@ -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
  },