@techierathore/techieflow 1.0.3 → 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.
Files changed (150) 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 +27 -0
  6. package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +1 -1
  7. package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +4 -3
  8. package/.claude/commands/TechieFlow/tasks/amend-docs.md +6 -5
  9. package/.claude/commands/TechieFlow/tasks/build-phase.md +1 -1
  10. package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +4 -0
  11. package/.claude/commands/TechieFlow/tasks/create-doc.md +4 -0
  12. package/.claude/commands/TechieFlow/tasks/create-project-brief.md +22 -0
  13. package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +3 -3
  14. package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +3 -3
  15. package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +4 -0
  16. package/.claude/commands/TechieFlow/tasks/fix-issues.md +2 -2
  17. package/.claude/commands/TechieFlow/tasks/generate-html.md +7 -3
  18. package/.claude/commands/TechieFlow/tasks/handoff-phase.md +20 -18
  19. package/.claude/commands/TechieFlow/tasks/metrics-report.md +2 -0
  20. package/.claude/commands/TechieFlow/tasks/mockups.md +1 -1
  21. package/.claude/commands/TechieFlow/tasks/productguide.md +2 -2
  22. package/.claude/commands/TechieFlow/tasks/render-workflow-docs.md +1 -1
  23. package/.claude/commands/TechieFlow/tasks/triage-issues.md +2 -2
  24. package/.claude/commands/TechieFlow/tasks/verify-phase.md +1 -1
  25. package/.opencode/plugin/techieflow.js +181 -9
  26. package/.tfcore/agents/analyst.md +5 -8
  27. package/.tfcore/agents/architect.md +1 -10
  28. package/.tfcore/agents/flow-master.md +1 -0
  29. package/.tfcore/agents/verifier.md +1 -0
  30. package/.tfcore/hooks/block-git.sh +4 -2
  31. package/.tfcore/hooks/guard-build.sh +5 -1
  32. package/.tfcore/hooks/guard-db.sh +6 -4
  33. package/.tfcore/hooks/guard-metrics.sh +13 -1
  34. package/.tfcore/hooks/guard-status-html.sh +86 -2
  35. package/.tfcore/hooks/guard-verify-deps.sh +124 -0
  36. package/.tfcore/hooks/guard-verify.sh +8 -3
  37. package/.tfcore/hooks/sweep-artifacts.sh +46 -0
  38. package/.tfcore/routing.yaml +61 -0
  39. package/.tfcore/standards/owner-words.txt +23 -0
  40. package/.tfcore/tasks/_owner-language.md +27 -0
  41. package/.tfcore/tasks/_smoke-test-policy.md +1 -1
  42. package/.tfcore/tasks/_status-update-gate.md +4 -3
  43. package/.tfcore/tasks/amend-docs.md +6 -5
  44. package/.tfcore/tasks/build-phase.md +1 -1
  45. package/.tfcore/tasks/create-deep-research-prompt.md +4 -0
  46. package/.tfcore/tasks/create-doc.md +4 -0
  47. package/.tfcore/tasks/create-project-brief.md +22 -0
  48. package/.tfcore/tasks/day1-brownfield.md +3 -3
  49. package/.tfcore/tasks/day1-greenfield.md +3 -3
  50. package/.tfcore/tasks/facilitate-brainstorming-session.md +4 -0
  51. package/.tfcore/tasks/fix-issues.md +2 -2
  52. package/.tfcore/tasks/generate-html.md +7 -3
  53. package/.tfcore/tasks/handoff-phase.md +20 -18
  54. package/.tfcore/tasks/metrics-report.md +2 -0
  55. package/.tfcore/tasks/mockups.md +1 -1
  56. package/.tfcore/tasks/productguide.md +2 -2
  57. package/.tfcore/tasks/render-workflow-docs.md +1 -1
  58. package/.tfcore/tasks/triage-issues.md +2 -2
  59. package/.tfcore/tasks/verify-phase.md +1 -1
  60. package/.tfcore/telemetry/SCHEMA.md +61 -1
  61. package/.tfcore/telemetry/install-metrics.sh +3 -2
  62. package/.tfcore/telemetry/tf-metrics.sh +435 -21
  63. package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +6 -0
  64. package/.tfcore/templates/v4custom/app-brd-tmpl.md +5 -3
  65. package/.tfcore/templates/v4custom/app-brief-tmpl.md +65 -0
  66. package/.tfcore/templates/v4custom/app-decision-request-tmpl.md +85 -0
  67. package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +86 -31
  68. package/.tfcore/templates/v4custom/app-phase-brd-tmpl.md +94 -0
  69. package/.tfcore/templates/v4custom/app-phase-uidesign-tmpl.md +60 -0
  70. package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +4 -2
  71. package/.tfcore/templates/v4custom/build-subagent-prompt.md +2 -2
  72. package/.tfcore/templates/v4custom/html-render-shell.md +4 -2
  73. package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
  74. package/.tfcore/utils/__pycache__/tf-checklist-edit.cpython-312.pyc +0 -0
  75. package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
  76. package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
  77. package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
  78. package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
  79. package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
  80. package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
  81. package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
  82. package/.tfcore/utils/__pycache__/tf-perf-grade.cpython-312.pyc +0 -0
  83. package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
  84. package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
  85. package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
  86. package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
  87. package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
  88. package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
  89. package/.tfcore/utils/tf-assets-browser.mjs +96 -0
  90. package/.tfcore/utils/tf-assets.sh +97 -12
  91. package/.tfcore/utils/tf-brd-status.py +14 -5
  92. package/.tfcore/utils/tf-build-list.py +99 -11
  93. package/.tfcore/utils/tf-build-list.sh +5 -3
  94. package/.tfcore/utils/tf-build.sh +88 -15
  95. package/.tfcore/utils/tf-ci-repro.py +685 -0
  96. package/.tfcore/utils/tf-ci-repro.sh +24 -0
  97. package/.tfcore/utils/tf-devguide-list.py +47 -22
  98. package/.tfcore/utils/tf-doc-check.py +242 -10
  99. package/.tfcore/utils/tf-doc-tests.sh +79 -0
  100. package/.tfcore/utils/tf-emit.sh +413 -19
  101. package/.tfcore/utils/tf-feedback.sh +13 -0
  102. package/.tfcore/utils/tf-fix-close.sh +113 -31
  103. package/.tfcore/utils/tf-gitignore-audit.sh +50 -3
  104. package/.tfcore/utils/tf-goal.sh +105 -18
  105. package/.tfcore/utils/tf-harness.sh +9 -4
  106. package/.tfcore/utils/tf-lock.sh +29 -0
  107. package/.tfcore/utils/tf-log-miss.py +34 -8
  108. package/.tfcore/utils/tf-login.mjs +167 -0
  109. package/.tfcore/utils/tf-misses-md.py +24 -21
  110. package/.tfcore/utils/tf-misses-md.sh +5 -4
  111. package/.tfcore/utils/tf-mockup-parity.mjs +427 -63
  112. package/.tfcore/utils/tf-mockup-parity.sh +14 -2
  113. package/.tfcore/utils/tf-model-pick.sh +475 -0
  114. package/.tfcore/utils/tf-owner-text.py +233 -0
  115. package/.tfcore/utils/tf-owner-text.sh +14 -0
  116. package/.tfcore/utils/tf-phase.sh +33 -1
  117. package/.tfcore/utils/tf-portable.sh +230 -0
  118. package/.tfcore/utils/tf-render-html.py +80 -2
  119. package/.tfcore/utils/tf-routing-bind.sh +87 -1
  120. package/.tfcore/utils/tf-routing.sh +7 -0
  121. package/.tfcore/utils/tf-selfcheck.sh +356 -0
  122. package/.tfcore/utils/tf-split-brd.py +17 -2
  123. package/.tfcore/utils/tf-status-evidence.sh +11 -9
  124. package/.tfcore/utils/tf-status-facts.py +133 -44
  125. package/.tfcore/utils/tf-triage.py +62 -16
  126. package/.tfcore/utils/tf-triage.sh +2 -0
  127. package/.tfcore/utils/tf-verify-boot.sh +263 -43
  128. package/.tfcore/utils/tf-verify-emit.sh +20 -0
  129. package/.tfcore/utils/tf-verify-env.sh +33 -4
  130. package/.tfcore/utils/tf-verify-list.py +75 -8
  131. package/.tfcore/utils/tf-verify-screens.mjs +254 -43
  132. package/.tfcore/utils/tf-verify-screens.sh +18 -4
  133. package/.tfcore/utils/tf-verify-tests.sh +272 -28
  134. package/.tfcore/utils/tf-verify-verdict.py +59 -11
  135. package/.tfcore/utils/tf_defect.py +27 -0
  136. package/.tfcore/utils/tf_feedback.py +209 -0
  137. package/.tfcore/utils/tf_roadmap.py +35 -0
  138. package/README.md +19 -5
  139. package/docs/TechieFlow-Installation.md +5 -3
  140. package/opencode.jsonc +24 -0
  141. package/package.json +1 -1
  142. package/scripts/install.mjs +4 -0
  143. package/.tfcore/templates/architecture-tmpl.yaml +0 -650
  144. package/.tfcore/templates/brownfield-architecture-tmpl.yaml +0 -476
  145. package/.tfcore/templates/competitor-analysis-tmpl.yaml +0 -306
  146. package/.tfcore/templates/front-end-architecture-tmpl.yaml +0 -218
  147. package/.tfcore/templates/fullstack-architecture-tmpl.yaml +0 -823
  148. package/.tfcore/templates/market-research-tmpl.yaml +0 -252
  149. package/.tfcore/templates/project-brief-tmpl.yaml +0 -221
  150. package/.tfcore/utils/techieflow-doc-template.md +0 -325
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env python3
2
+ """tf_feedback.py — the one reader of a project's upstream feedback files (2026-09-11).
3
+
4
+ bash .tfcore/utils/tf-feedback.sh <App> every entry and its state
5
+ bash .tfcore/utils/tf-feedback.sh <App> --waiting fixed upstream, not yet re-checked here
6
+ bash .tfcore/utils/tf-feedback.sh <App> --close <ID> "<what you ran and what it showed>"
7
+
8
+ An entry is one of three things, and every script that reports on these files reads them here:
9
+
10
+ open nobody upstream has answered it
11
+ fixed the upstream team's reply ("## Resolution status (…, <date>)") lists it, or its own body
12
+ says "fixed upstream" — the fix exists, and this project has not yet re-checked it
13
+ closed this project re-checked it and marked it closed (✅, "Closed <date>", "will not fix")
14
+
15
+ Why one reader. Until 2026-09-11 three scripts read these files three ways, and the one that writes
16
+ PROJECT-STATUS counted every `###` heading as an entry and accepted only a closing format no file
17
+ used: TfLens's status read "TechieFlow: 62 open of 62" for a file of 27 entries with 16 fixed, and
18
+ the agent reported two fixed problems to the owner as open (MISS-TechieFlow-20260911-04).
19
+
20
+ --close writes one line under the entry's heading and nothing else. Python 3 standard library only.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import datetime
25
+ import glob
26
+ import os
27
+ import re
28
+ import sys
29
+
30
+ # a heading is one line: [ \t], never \s, which runs past the line break and makes the next line
31
+ # of a bare "## TF-013" its title — its closing line included (TF-028)
32
+ ENTRY_HEAD = re.compile(r"(?m)^(#{2,3})[ \t]+`?([A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*-\d+)\b[` \t—–-]*(.*)$")
33
+ REPLY_HEAD = re.compile(r"(?mi)^([ \t]*>[ \t]*)?#{2,3}[ \t]+[^\n]*\b(?:resolution status|resolved|repl(?:y|ies) from)\b[^\n]*")
34
+ ANY_ID = re.compile(r"(?<![\w-])([A-Z][A-Z0-9]*(?:-[A-Z]+)*-\d{2,})(?!\d)")
35
+ FIXED_WORDS = re.compile(r"(?i)\b(fixed|resolved|corrected|closes|closed|wired)\b")
36
+ EVERYTHING = re.compile(r"(?i)\beverything else is (?:now )?fixed\b|\ball (?:the )?others? (?:are )?(?:now )?fixed\b")
37
+ # a project's own mark that it re-checked the fix, that it will never be fixed, or that the entry
38
+ # was folded into another
39
+ CLOSED = re.compile(r"(?i)(✅|\bclosed (?:on )?\d{4}-\d{2}-\d{2}|is \*\*closed\*\*|\bwill not fix\b|\bwont-fix\b)")
40
+ MERGED = re.compile(r"(?i)^\s*merged into\b")
41
+ FIXED_IN_BODY = re.compile(r"(?i)\bfixed upstream\b")
42
+ BLOCKS = re.compile(r"(?im)^\s*[-*]\s*\*\*Blocks:?\*\*:?\s*(yes|no)\b")
43
+
44
+
45
+ def entries(path):
46
+ """-> [{id, title, state, since, blocks, start, end}] in file order."""
47
+ text = open(path, encoding="utf-8", errors="replace").read()
48
+ heads = list(ENTRY_HEAD.finditer(text))
49
+ out = {}
50
+ for i, m in enumerate(heads):
51
+ end = heads[i + 1].start() if i + 1 < len(heads) else len(text)
52
+ nxt = re.search(r"(?m)^#{1,%d}\s" % len(m.group(1)), text[m.end():end])
53
+ body = text[m.end(): m.end() + nxt.start()] if nxt else text[m.end():end]
54
+ head = body[:2500]
55
+ state = ("closed" if CLOSED.search(head) or MERGED.search(m.group(3))
56
+ else "fixed" if FIXED_IN_BODY.search(head) else "open")
57
+ b = BLOCKS.search(body)
58
+ out.setdefault(m.group(2), {"id": m.group(2), "title": m.group(3).strip(" `"), "state": state,
59
+ "since": "", "blocks": b.group(1).lower() if b else "",
60
+ "start": m.start(), "end": m.end() + len(body)})
61
+ # the upstream team's replies, in the shapes they have actually been written in
62
+ for rid, date in replied_fixed(text, list(out)):
63
+ e = out.get(rid)
64
+ if e and e["state"] == "open":
65
+ e["state"] = "fixed"
66
+ if e and not e["since"] and date:
67
+ e["since"] = date
68
+ return list(out.values())
69
+
70
+
71
+ def reply_blocks(text):
72
+ """-> [(date, block text)]. A reply is a heading naming a resolution or a reply —
73
+ "## Resolution status (TechieFlow team, 2026-09-09)", "## Replies from …", or a quoted
74
+ "> ## ✅ RESOLVED LIBRARY-SIDE 2026-08-31" — and runs to the next heading of its level,
75
+ or, when quoted, to the end of the quote."""
76
+ out = []
77
+ for m in REPLY_HEAD.finditer(text):
78
+ rest = text[m.end():]
79
+ if m.group(1): # quoted: the block is the run of '>' lines after the heading's own line
80
+ rest = rest[1:] if rest.startswith("\n") else rest
81
+ q = re.match(r"(?:[ \t]*>[^\n]*\n?)*", rest)
82
+ body = re.sub(r"(?m)^[ \t]*>[ \t]?", "", q.group(0))
83
+ else:
84
+ nxt = re.search(r"(?m)^##\s", rest)
85
+ body = rest[: nxt.start()] if nxt else rest
86
+ d = re.search(r"\d{4}-\d{2}-\d{2}", m.group(0))
87
+ out.append((d.group(0) if d else "", body))
88
+ return out
89
+
90
+
91
+ def replied_fixed(text, known):
92
+ """-> [(id, date)] for every entry a reply says is fixed. Inside a reply, an item — a table
93
+ row, a bullet, or a paragraph — fixes the ids it names when it is a table row or says fixed,
94
+ resolved, corrected or closes; "everything else is fixed" covers every entry up to the highest
95
+ number the reply mentions (entries filed after it stay open)."""
96
+ out = []
97
+ for date, body in reply_blocks(text):
98
+ items = re.split(r"\n\s*\n|\n(?=\s*[-*]\s)|\n(?=\s*\|)", body)
99
+ mentioned = []
100
+ for it in items:
101
+ ids = ANY_ID.findall(it)
102
+ mentioned += ids
103
+ if not ids:
104
+ continue
105
+ if it.lstrip().startswith("|"):
106
+ first = ANY_ID.findall(it.split("|")[1] if it.count("|") > 1 else "")
107
+ out += [(i, date) for i in first]
108
+ elif FIXED_WORDS.search(it):
109
+ out += [(i, date) for i in ids]
110
+ for it in items:
111
+ if EVERYTHING.search(it) and mentioned:
112
+ top = {}
113
+ for i in mentioned:
114
+ p, n = i.rsplit("-", 1)
115
+ top[p] = max(top.get(p, 0), int(n))
116
+ for k in known:
117
+ p, n = k.rsplit("-", 1)
118
+ if p in top and int(n) <= top[p]:
119
+ out.append((k, date))
120
+ return out
121
+
122
+
123
+ def files(root, app=None):
124
+ pat = f"{app}-*-Feedback.md" if app else "*-Feedback.md"
125
+ return sorted(glob.glob(os.path.join(root, "docs", pat)))
126
+
127
+
128
+ def upstream_of(path, app=None):
129
+ base = os.path.basename(path)
130
+ m = re.match(rf"^{re.escape(app)}-(.+)-Feedback\.md$", base) if app else re.match(r"^[^-]+-(.+)-Feedback\.md$", base)
131
+ return m.group(1) if m else base
132
+
133
+
134
+ def state_map(root):
135
+ """Every entry id across the project's feedback files -> (state, "<file>, <date>")."""
136
+ out = {}
137
+ for f in files(root):
138
+ rel = os.path.relpath(f, root).replace(os.sep, "/")
139
+ for e in entries(f):
140
+ out[e["id"]] = (e["state"], f"{rel}" + (f", Resolution status {e['since']}" if e["since"] else ""))
141
+ return out
142
+
143
+
144
+ def summary_line(path, app):
145
+ es = entries(path)
146
+ n = {s: [e["id"] for e in es if e["state"] == s] for s in ("open", "fixed", "closed")}
147
+ rel = "docs/" + os.path.basename(path)
148
+ parts = [f"{len(n['open'])} open"]
149
+ if n["fixed"]:
150
+ ids = ", ".join(n["fixed"][:3]) + (" …" if len(n["fixed"]) > 3 else "")
151
+ parts.append(f"{len(n['fixed'])} fixed upstream, not yet re-checked ({ids})")
152
+ parts.append(f"{len(n['closed'])} closed")
153
+ return f"- {upstream_of(path, app)}: " + " · ".join(parts) + f" — {rel}"
154
+
155
+
156
+ def close(path, rid, evidence):
157
+ text = open(path, encoding="utf-8").read()
158
+ e = next((x for x in entries(path) if x["id"] == rid), None)
159
+ if not e:
160
+ return f"tf-feedback: no entry {rid} in {os.path.basename(path)}", 2
161
+ if e["state"] == "closed":
162
+ return f"tf-feedback: {rid} is already closed; nothing written", 0
163
+ if not evidence.strip():
164
+ return "tf-feedback: say what you ran and what it showed; a close with no evidence is refused", 2
165
+ line_end = text.index("\n", e["start"]) + 1
166
+ today = datetime.date.today().isoformat()
167
+ mark = f"\n> ✅ **Closed {today}** — re-checked here: {evidence.strip()}\n"
168
+ open(path, "w", encoding="utf-8").write(text[:line_end] + mark + text[line_end:])
169
+ return f"tf-feedback: {rid} closed in {os.path.basename(path)}", 0
170
+
171
+
172
+ def main(argv):
173
+ if not argv or argv[0] in ("-h", "--help"):
174
+ print(__doc__)
175
+ return 0 if argv else 2
176
+ app, rest = argv[0], argv[1:]
177
+ root = os.getcwd()
178
+ fs = files(root, app)
179
+ if not fs:
180
+ print(f"tf-feedback: no docs/{app}-*-Feedback.md here")
181
+ return 0
182
+ if rest[:1] == ["--close"]:
183
+ if len(rest) < 3:
184
+ print('usage: tf-feedback.sh <App> --close <ID> "<what you ran and what it showed>"')
185
+ return 2
186
+ for f in fs:
187
+ if any(e["id"] == rest[1] for e in entries(f)):
188
+ msg, rc = close(f, rest[1], rest[2])
189
+ print(msg)
190
+ return rc
191
+ print(f"tf-feedback: no entry {rest[1]} in any docs/{app}-*-Feedback.md")
192
+ return 2
193
+ waiting = rest[:1] == ["--waiting"]
194
+ for f in fs:
195
+ es = entries(f)
196
+ if waiting:
197
+ for e in es:
198
+ if e["state"] == "fixed":
199
+ print(f"{e['id']} fixed upstream{' ' + e['since'] if e['since'] else ''}, waiting to be "
200
+ f"re-checked here — {os.path.basename(f)}")
201
+ continue
202
+ print(summary_line(f, app))
203
+ for e in es:
204
+ print(f" {e['id']:<12} {e['state']:<7} {e['since'] or '':<10} {e['title'][:80]}")
205
+ return 0
206
+
207
+
208
+ if __name__ == "__main__":
209
+ sys.exit(main(sys.argv[1:]))
@@ -0,0 +1,35 @@
1
+ """tf_roadmap.py — which checklist rows are roadmap items, out of this phase's scope.
2
+
3
+ A row is a roadmap item when its acceptance line in the checklist's detail section carries
4
+ "Roadmap — not in this phase's scope" (any dash, any case). Such a row is not work for this
5
+ phase: it stays off the build list and out of the "not built yet" count, and it never makes
6
+ *build-phase the next command. Only the owner moves it, through *amend-docs.
7
+
8
+ AppManager's REQ-NFR-008 carried the marker at In Progress; tf-build-list.py put it on the
9
+ working list with a builder prompt, a builder built it against the owner's decision, and
10
+ tf-status-facts.py named *build-phase as the next command for it forever (TF-017).
11
+ tests/regression/run.sh am_017. Imported by tf-build-list.py and tf-status-facts.py.
12
+ """
13
+ import re
14
+
15
+ MARK = re.compile(r"roadmap\s*[—–-]+\s*not in this phase['’]?s scope", re.I)
16
+ ACCEPTANCE = re.compile(r"\*Acceptance:?\*|^\s*[-*]\s*Acceptance:", re.I)
17
+ OWNER = re.compile(r"<a id=['\"]d-(req-[a-z]+-\d+)['\"]|\*\*`?(REQ-[A-Z]+-\d+)`?\*\*", re.I)
18
+
19
+
20
+ def ids(text):
21
+ """The set of REQ ids whose acceptance line carries the roadmap marker.
22
+
23
+ The owner of an acceptance line is the nearest REQ anchor or bold REQ id above it, in the
24
+ detail section; status-table rows (lines starting with `|`) are never read."""
25
+ out, owner = set(), None
26
+ for line in text.splitlines():
27
+ if line.lstrip().startswith("|"):
28
+ continue
29
+ if re.match(r"^#{1,2}\s", line):
30
+ owner = None
31
+ for m in OWNER.finditer(line):
32
+ owner = (m.group(1) or m.group(2)).upper()
33
+ if owner and ACCEPTANCE.search(line) and MARK.search(line):
34
+ out.add(owner)
35
+ return out
package/README.md CHANGED
@@ -27,6 +27,8 @@ Three things make it different from prompting an agent by hand:
27
27
  - **Rules that were ignored became hooks.** Eight guards refuse an action outright and print why: any git write, a malformed status file, `Verified` without a verify run, a hand edit of a telemetry file, a database write outside build and fix, run material at the repository root, a backgrounded build in unattended mode, and ending a turn with the status gate incomplete.
28
28
  - **The work is measured.** Five append-only streams per project record what each command cost, which check caught which failure, and what each miss cost to fix.
29
29
 
30
+ The framework has an opinion about how agents talk to people. Every project's `AGENTS.md` (written at day-1) tells the agent to tag each claim it is unsure of as Certain, Likely or Guessing; to say the thing you will not want to hear first; to skip warm-up and start with the most useful sentence; and to disagree in a fixed shape ("I disagree because … I would do … The risk in your approach is …") and then do what you decide.
31
+
30
32
  The framework is technology-neutral. It asks a set of stack questions at day-1 and records the answers in the project's Architecture document; an answer set can fill them in (the owner's .NET set ships as `docs/TechieFlow-Stack-Defaults-DotNet.md`).
31
33
 
32
34
  ---
@@ -35,6 +37,8 @@ The framework is technology-neutral. It asks a set of stack questions at day-1 a
35
37
 
36
38
  Prerequisites are one-time per machine and live in [`docs/TechieFlow-Setup.md`](docs/TechieFlow-Setup.md): the toolchain, headless Chromium, and — for mobile heads — the Appium device hosts. **`python3` is required**; the guard hooks fail open without it.
37
39
 
40
+ It runs on Linux, WSL and a stock Mac. On a Mac the scripts use Apple's own bash 3.2 and BSD tools, so Homebrew, a newer bash and GNU coreutils are not needed; `python3` comes with the Command Line Tools (`xcode-select --install`).
41
+
38
42
  Copy the framework in; never `npm install` it. All three scripts locate the framework from their own directory, so invoke whichever machine's copy you are on.
39
43
 
40
44
  ```bash
@@ -126,10 +130,11 @@ Replace `{App}` with your application name. Claude Code takes the long persona p
126
130
 
127
131
  ## 5. What it produces
128
132
 
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>/`.
133
+ 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
134
 
131
135
  | Document | Who reads it |
132
136
  |---|---|
137
+ | Brief | You, before anything is built. One page: what it is, who for, must do, out of scope. Day-1 reads it. |
133
138
  | BRD, Architecture, UIDesign and the mockups | You, at the day-1 review. This is the cheap moment to redirect. |
134
139
  | Checklist | Agents only. One table, one row per requirement, the single source of truth. Never rendered to HTML. |
135
140
  | Coding Standards | Every implementing agent, and the verifier's standards check. |
@@ -138,9 +143,11 @@ Every per-project document is named after the application: `docs/<App>-BRD.md`,
138
143
  | DevGuide | A developer tracing a bug: every screen to its code, with the line to break on. |
139
144
  | ProductGuide | The end user. |
140
145
  | Deployment Checklist | Whoever puts it on the host, after UAT. |
141
- | Library feedback, one file per library | That library's team. |
146
+ | Misses | You and the agents. Rebuilt from the record whenever something is logged as missed. |
147
+ | 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. |
148
+ | 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
149
 
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.
150
+ 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
151
 
145
152
  ---
146
153
 
@@ -152,7 +159,7 @@ The shipped configuration allows every file operation and all of Bash with no pr
152
159
 
153
160
  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
161
 
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.
162
+ 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
163
 
157
164
  ## 8. Running phases on cheaper models
158
165
 
@@ -164,7 +171,14 @@ bash .tfcore/utils/tf-routing.sh on
164
171
  bash .tfcore/utils/tf-routing.sh set-tier verify-phase economy
165
172
  ```
166
173
 
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).
174
+ 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.
175
+
176
+ ```bash
177
+ bash .tfcore/utils/tf-model-pick.sh status # what each tier would run right now
178
+ bash .tfcore/utils/tf-model-pick.sh chain standard opencode # the order it falls back in
179
+ ```
180
+
181
+ 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
182
 
169
183
  ---
170
184
 
@@ -16,14 +16,16 @@ You need four things on the machine.
16
16
  | Tool | Why | Check |
17
17
  |---|---|---|
18
18
  | Node.js 20 or newer | Runs the installer. Nothing is added to your project. | `node --version` |
19
- | bash | The framework's guard hooks and helper scripts run under bash. macOS and Linux have it. On Windows, work inside WSL or Git Bash. | `bash --version` |
19
+ | bash | The framework's guard hooks and helper scripts run under bash. macOS and Linux have it; the bash 3.2 that comes with macOS is enough. On Windows, work inside WSL or Git Bash. | `bash --version` |
20
20
  | Python 3 | Powers the HTML renderer, the telemetry writer and the guard hooks. | `python3 --version` |
21
- | Claude Code or OpenCode | The harness that runs the agents. Either one. Both work from the same install. | `claude --version` or `opencode --version` |
21
+ | Claude Code or OpenCode | The harness that runs the agents. Either one. Both work from the same install. The framework targets OpenCode 2; its plugin still loads on OpenCode 1.x. | `claude --version` or `opencode --version` |
22
22
 
23
23
  For a .NET project you also need the .NET SDK. Run `dotnet --version` to check.
24
24
 
25
25
  You do not need rsync, git on the command line, or a clone of this repository.
26
26
 
27
+ **On a Mac.** A stock Mac is enough. The scripts run on Apple's own bash 3.2 and the `sed`, `date`, `stat` and `xargs` that come with macOS. You do not need Homebrew, a newer bash or GNU coreutils for them. The one thing to add is Python 3: it comes with Apple's Command Line Tools, so run `xcode-select --install` once if `python3 --version` asks for them. perl and rsync already ship with macOS. Your project's own tools, such as the .NET SDK, are installed as usual.
28
+
27
29
  ---
28
30
 
29
31
  ## 1. What gets installed
@@ -312,7 +314,7 @@ It installs the framework if it is not there yet, then removes the leftover pack
312
314
  |---|---|
313
315
  | The framework is under `node_modules/@techierathore/techieflow/` and nowhere else | You ran `npm install`. See section 9. Run `npx @techierathore/techieflow@latest install`. |
314
316
  | `bash was not found` | On Windows, run the command inside WSL or Git Bash. |
315
- | `python3 was not found` | Install Python 3 and run the command again. On macOS: `brew install python3`. On Ubuntu or WSL: `sudo apt-get install -y python3`. |
317
+ | `python3 was not found` | Install Python 3 and run the command again. On macOS: `xcode-select --install` (Apple's Command Line Tools include python3), or `brew install python3`. On Ubuntu or WSL: `sudo apt-get install -y python3`. |
316
318
  | `Refusing to install into the framework itself` | You ran the command inside a clone of this repository. Pass `--target=<your project>`. |
317
319
  | `does not look installed` on update | The folder has no `.tfcore/`. Run `install` first. |
318
320
  | Claude Code does not show the `/TechieFlow:agents:analyst` command | Restart Claude Code in the project folder. Check that `.claude/commands/TechieFlow/agents/analyst.md` exists. |
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.1.0",
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
  },