@techierathore/techieflow 1.0.1 → 1.0.3

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 (178) hide show
  1. package/.claude/commands/TechieFlow/agents/analyst.md +0 -5
  2. package/.claude/commands/TechieFlow/agents/architect.md +0 -6
  3. package/.claude/commands/TechieFlow/agents/flow-master.md +41 -215
  4. package/.claude/commands/TechieFlow/agents/verifier.md +23 -64
  5. package/.claude/commands/TechieFlow/tasks/_metrics-emit-gate.md +24 -117
  6. package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +20 -84
  7. package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +10 -74
  8. package/.claude/commands/TechieFlow/tasks/_yolo-mode.md +20 -58
  9. package/.claude/commands/TechieFlow/tasks/amend-docs.md +20 -143
  10. package/.claude/commands/TechieFlow/tasks/build-phase.md +14 -224
  11. package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +31 -505
  12. package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +28 -213
  13. package/.claude/commands/TechieFlow/tasks/deploy-checklist.md +19 -0
  14. package/.claude/commands/TechieFlow/tasks/devguide.md +10 -219
  15. package/.claude/commands/TechieFlow/tasks/fix-issues.md +10 -129
  16. package/.claude/commands/TechieFlow/tasks/log-miss.md +19 -158
  17. package/.claude/commands/TechieFlow/tasks/metrics-report.md +1 -1
  18. package/.claude/commands/TechieFlow/tasks/mockups.md +12 -117
  19. package/.claude/commands/TechieFlow/tasks/refresh-status.md +9 -130
  20. package/.claude/commands/TechieFlow/tasks/split-brd.md +10 -126
  21. package/.claude/commands/TechieFlow/tasks/triage-and-fix.md +14 -0
  22. package/.claude/commands/TechieFlow/tasks/triage-issues.md +11 -152
  23. package/.claude/commands/TechieFlow/tasks/verify-phase.md +36 -572
  24. package/.opencode/plugin/techieflow.js +39 -12
  25. package/.tfcore/agents/analyst.md +0 -5
  26. package/.tfcore/agents/architect.md +0 -6
  27. package/.tfcore/agents/flow-master.md +41 -215
  28. package/.tfcore/agents/verifier.md +23 -64
  29. package/.tfcore/core-config.yaml +10 -0
  30. package/.tfcore/hooks/block-git.sh +4 -4
  31. package/.tfcore/hooks/guard-artifacts.sh +2 -2
  32. package/.tfcore/hooks/guard-build.sh +76 -0
  33. package/.tfcore/hooks/guard-db.sh +90 -0
  34. package/.tfcore/hooks/guard-metrics.sh +56 -0
  35. package/.tfcore/hooks/guard-status-html.sh +116 -71
  36. package/.tfcore/hooks/guard-status.sh +43 -16
  37. package/.tfcore/hooks/guard-verify.sh +70 -48
  38. package/.tfcore/hooks/sweep-artifacts.sh +2 -2
  39. package/.tfcore/install-manifest.yaml +0 -18
  40. package/.tfcore/routing.yaml +25 -14
  41. package/.tfcore/standards/coding-standards-core.md +61 -0
  42. package/.tfcore/standards/coding-standards-dotnet.md +93 -0
  43. package/.tfcore/tasks/_metrics-emit-gate.md +24 -117
  44. package/.tfcore/tasks/_smoke-test-policy.md +20 -84
  45. package/.tfcore/tasks/_status-update-gate.md +10 -74
  46. package/.tfcore/tasks/_yolo-mode.md +20 -58
  47. package/.tfcore/tasks/amend-docs.md +20 -143
  48. package/.tfcore/tasks/build-phase.md +14 -224
  49. package/.tfcore/tasks/day1-brownfield.md +31 -505
  50. package/.tfcore/tasks/day1-greenfield.md +28 -213
  51. package/.tfcore/tasks/deploy-checklist.md +19 -0
  52. package/.tfcore/tasks/devguide.md +10 -219
  53. package/.tfcore/tasks/fix-issues.md +10 -129
  54. package/.tfcore/tasks/log-miss.md +19 -158
  55. package/.tfcore/tasks/metrics-report.md +1 -1
  56. package/.tfcore/tasks/mockups.md +12 -117
  57. package/.tfcore/tasks/refresh-status.md +9 -130
  58. package/.tfcore/tasks/split-brd.md +10 -126
  59. package/.tfcore/tasks/triage-and-fix.md +14 -0
  60. package/.tfcore/tasks/triage-issues.md +11 -152
  61. package/.tfcore/tasks/verify-phase.md +36 -572
  62. package/.tfcore/telemetry/SCHEMA.md +43 -7
  63. package/.tfcore/telemetry/tf-metrics.sh +152 -17
  64. package/.tfcore/templates/project-brief-tmpl.yaml +1 -1
  65. package/.tfcore/templates/stack-defaults/dotnet.md +35 -0
  66. package/.tfcore/templates/stack-questions.md +77 -0
  67. package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +3 -4
  68. package/.tfcore/templates/v4custom/app-architecture-tmpl.md +107 -115
  69. package/.tfcore/templates/v4custom/app-brd-tmpl.md +108 -184
  70. package/.tfcore/templates/v4custom/app-checklist-tmpl.md +48 -73
  71. package/.tfcore/templates/v4custom/app-claude-md-tmpl.md +1 -1
  72. package/.tfcore/templates/v4custom/app-coding-standards-tmpl.md +52 -0
  73. package/.tfcore/templates/v4custom/app-deployment-checklist-tmpl.md +86 -0
  74. package/.tfcore/templates/v4custom/app-devguide-tmpl.md +69 -146
  75. package/.tfcore/templates/v4custom/app-phases-tmpl.md +35 -0
  76. package/.tfcore/templates/v4custom/app-productguide-tmpl.md +38 -35
  77. package/.tfcore/templates/v4custom/app-project-status-tmpl.md +63 -73
  78. package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +62 -37
  79. package/.tfcore/templates/v4custom/app-usageguide-tmpl.md +69 -54
  80. package/.tfcore/templates/v4custom/build-invocation-ladder.md +17 -180
  81. package/.tfcore/templates/v4custom/build-subagent-prompt.md +17 -0
  82. package/.tfcore/templates/v4custom/metrics-report-template.md +2 -2
  83. package/.tfcore/user-guide.md +0 -23
  84. package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
  85. package/.tfcore/utils/__pycache__/tf-checklist-edit.cpython-312.pyc +0 -0
  86. package/.tfcore/utils/__pycache__/tf-codex-bind.cpython-312.pyc +0 -0
  87. package/.tfcore/utils/__pycache__/tf-codex-telemetry.cpython-312.pyc +0 -0
  88. package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
  89. package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
  90. package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
  91. package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
  92. package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
  93. package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
  94. package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
  95. package/.tfcore/utils/__pycache__/tf-perf-grade.cpython-312.pyc +0 -0
  96. package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
  97. package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
  98. package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
  99. package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
  100. package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
  101. package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
  102. package/.tfcore/utils/tf-brd-status.py +176 -0
  103. package/.tfcore/utils/tf-brd-status.sh +19 -0
  104. package/.tfcore/utils/tf-build-list.py +191 -0
  105. package/.tfcore/utils/tf-build-list.sh +20 -0
  106. package/.tfcore/utils/tf-build.sh +143 -0
  107. package/.tfcore/utils/tf-cdp-relay.ps1 +64 -0
  108. package/.tfcore/utils/tf-checklist-edit.py +133 -0
  109. package/.tfcore/utils/tf-day1-files.py +208 -0
  110. package/.tfcore/utils/tf-day1-files.sh +20 -0
  111. package/.tfcore/utils/tf-devguide-list.py +249 -0
  112. package/.tfcore/utils/tf-devguide-list.sh +18 -0
  113. package/.tfcore/utils/tf-doc-check.py +1267 -0
  114. package/.tfcore/utils/tf-doc-check.sh +29 -0
  115. package/.tfcore/utils/tf-emit.sh +203 -13
  116. package/.tfcore/utils/tf-fix-close.sh +83 -0
  117. package/.tfcore/utils/tf-goal.sh +193 -62
  118. package/.tfcore/utils/tf-harness.sh +4 -9
  119. package/.tfcore/utils/tf-log-miss.py +197 -0
  120. package/.tfcore/utils/tf-log-miss.sh +14 -0
  121. package/.tfcore/utils/tf-misses-md.py +211 -0
  122. package/.tfcore/utils/tf-misses-md.sh +13 -0
  123. package/.tfcore/utils/tf-mockups-locate.py +66 -0
  124. package/.tfcore/utils/tf-mockups-locate.sh +18 -0
  125. package/.tfcore/utils/tf-opencode-audit.py +23 -7
  126. package/.tfcore/utils/tf-perf-grade.py +97 -0
  127. package/.tfcore/utils/tf-perf-grade.sh +12 -0
  128. package/.tfcore/utils/tf-phase.sh +74 -0
  129. package/.tfcore/utils/tf-routing-bind.sh +10 -5
  130. package/.tfcore/utils/tf-routing.sh +6 -7
  131. package/.tfcore/utils/tf-split-brd.py +304 -0
  132. package/.tfcore/utils/tf-split-brd.sh +22 -0
  133. package/.tfcore/utils/tf-status-evidence.sh +68 -0
  134. package/.tfcore/utils/tf-status-facts.py +309 -0
  135. package/.tfcore/utils/tf-status-facts.sh +20 -0
  136. package/.tfcore/utils/tf-triage.py +243 -0
  137. package/.tfcore/utils/tf-triage.sh +16 -0
  138. package/.tfcore/utils/tf-verify-boot.sh +250 -0
  139. package/.tfcore/utils/tf-verify-emit.sh +110 -0
  140. package/.tfcore/utils/tf-verify-env.sh +88 -0
  141. package/.tfcore/utils/tf-verify-list.py +270 -0
  142. package/.tfcore/utils/tf-verify-list.sh +13 -0
  143. package/.tfcore/utils/tf-verify-screens.mjs +283 -0
  144. package/.tfcore/utils/tf-verify-screens.sh +22 -0
  145. package/.tfcore/utils/tf-verify-tests.sh +123 -0
  146. package/.tfcore/utils/tf-verify-verdict.py +283 -0
  147. package/.tfcore/utils/tf-verify-verdict.sh +15 -0
  148. package/.tfcore/utils/tf-yolo.sh +59 -0
  149. package/README.md +114 -1149
  150. package/docs/TechieFlow-Installation.md +27 -7
  151. package/opencode.jsonc +13 -33
  152. package/package.json +4 -7
  153. package/scripts/install.mjs +73 -46
  154. package/scripts/npm-cleanup.mjs +137 -0
  155. package/scripts/npm-postinstall.mjs +49 -0
  156. package/.claude/commands/TechieFlow/tasks/advanced-elicitation.md +0 -117
  157. package/.claude/commands/TechieFlow/tasks/author-brd.md +0 -79
  158. package/.claude/commands/TechieFlow/tasks/document-project.md +0 -343
  159. package/.claude/commands/TechieFlow/tasks/execute-checklist.md +0 -86
  160. package/.claude/commands/TechieFlow/tasks/index-docs.md +0 -173
  161. package/.claude/commands/TechieFlow/tasks/kb-mode-interaction.md +0 -75
  162. package/.claude/commands/TechieFlow/tasks/shard-doc.md +0 -185
  163. package/.codex/config.toml +0 -46
  164. package/.codex/hooks.json +0 -63
  165. package/.codex/rules/techieflow.rules +0 -8
  166. package/.tfcore/hooks/codex-adapter.py +0 -248
  167. package/.tfcore/tasks/advanced-elicitation.md +0 -117
  168. package/.tfcore/tasks/author-brd.md +0 -79
  169. package/.tfcore/tasks/document-project.md +0 -343
  170. package/.tfcore/tasks/execute-checklist.md +0 -86
  171. package/.tfcore/tasks/index-docs.md +0 -173
  172. package/.tfcore/tasks/kb-mode-interaction.md +0 -75
  173. package/.tfcore/tasks/shard-doc.md +0 -185
  174. package/.tfcore/utils/tf-codex-bind.py +0 -196
  175. package/.tfcore/utils/tf-codex-telemetry.py +0 -60
  176. package/WORKFLOW.html +0 -1986
  177. package/scripts/test-install.mjs +0 -505
  178. package/scripts/validate.mjs +0 -163
@@ -1,190 +1,27 @@
1
1
  # Build invocation ladder
2
2
 
3
- When any task says "run `dotnet build`" / "run `dotnet test`" / "run `dotnet run`", **never accept "command not found" or "workload missing" as a stopping condition**. The right way to invoke `dotnet` depends on the host platform. **Detect the platform FIRST, then use that platform's ladder.**
3
+ **Building, testing and running is a script since Sitting 4b (2026-09-05):** `bash .tfcore/utils/tf-build.sh [build|test|run] [<target>]`. It detects the host (macOS, native Linux, native Windows, WSL, OpenCode Docker), decides whether the target has a MAUI head, tries the invocation rungs in order (`dotnet`, `~/.dotnet/dotnet`, `winrun`, `cmd.exe /c dotnet`, `powershell.exe dotnet`), stops at the first that works, and prints one verdict line: `PASS … via <rung>`, `FAIL … real errors` (the code is wrong: fix it), or `NOT-RUN … tried: …` (the host is wrong: never a project blocker). `bash .tfcore/utils/tf-build.sh probe` prints the host and the rungs without building. The full output of every attempt is under `tests/.artifacts/build/`. Copy the verdict line into the status log row or the Remark.
4
4
 
5
- ## 0. Platform detection — do this ONCE per session, then cache it
5
+ Two facts the script cannot change: a MAUI iOS or Mac Catalyst head builds only on a Mac (prepare the source and the on-Mac commands; never substitute another head as "the" deliverable), and a workload missing on macOS is installed once with `sudo dotnet workload install <id>` by the owner.
6
6
 
7
- Run a single probe to classify the host:
7
+ ## Reaching the running UI after a green build
8
8
 
9
- | Probe result | Platform | Ladder to use |
10
- |---|---|---|
11
- | `uname` → `Darwin` | **macOS** (Claude Code / OpenCode native, or via Terminal) | §A — dotnet in PATH, MAUI/iOS native via Xcode |
12
- | `uname` → `Linux` **and** `/proc/version` contains `microsoft` / `WSL` | **WSL-on-Windows** (the reference machine) | §B — the WSL↔Windows interop ladder |
13
- | `TF_OPENCODE_DOCKER=1` (or `uname` → `Linux`, no `microsoft` in `/proc/version`, and `/usr/local/bin/winrun` exists with `TF_WINDOWS_APP_PATH` set) | **OpenCode Docker on Windows** | §E — .NET in container, Windows head through SSH bridge |
14
- | `uname` → `Linux`, no `microsoft` in `/proc/version`, and no Docker bridge | **native Linux** | §A (Linux notes) — standard .NET only; Windows/Apple heads are unavailable |
15
- | `uname` not found / shell is `cmd`/`PowerShell`; paths look like `C:\…` | **native Windows** (Claude Code / OpenCode on Windows, not WSL) | §C — dotnet in PATH, MAUI native |
16
-
17
- One-liner to detect: `uname -a 2>/dev/null || echo windows` — `Darwin…` = macOS, `…microsoft…`/`…WSL…` = WSL, plain `Linux` = native Linux, `windows` (uname absent) = native Windows.
18
-
19
- > The §B WSL ladder below was the framework's original default because the owner's primary machine is WSL-on-Windows (`WORKFLOW.html §0`). It is **one platform's** ladder, not the universal one. A plain native Linux process has no Windows interop, but OpenCode Docker is a deliberate exception: when `/usr/local/bin/winrun` and `TF_WINDOWS_APP_PATH` are present, use §E and probe the SSH bridge. Never report `cmd.exe` missing inside the container; it is expected not to exist there.
20
-
21
- ---
22
-
23
- ## A. macOS / native Linux — `dotnet` is in `PATH`
24
-
25
- → **Start (and stay) at rung #1: `dotnet build`.** The .NET SDK installer puts `dotnet` on the `PATH`; there is no interop layer.
26
-
27
- - **Non-MAUI projects** (Web, API, library, console, test): `dotnet build` / `dotnet test` / `dotnet run` directly.
28
- - **MAUI on macOS:** builds natively once the workload + toolchains are installed — `sudo dotnet workload install maui`. **`sudo` is REQUIRED on macOS**: the SDK lives in root-owned `/usr/local/share/dotnet`, so any `dotnet workload install/update` (and SDK updates) without it fails with `Inadequate permissions. Run the command with elevated privileges.` — that error is a sudo signal, NEVER a project blocker. iOS / Mac Catalyst targets require **Xcode** (and `xcode-select --install`); Android targets require the **Android SDK + a JDK** (`sudo dotnet workload install maui-android`). With those present, `dotnet build -t:Run -f net9.0-maccatalyst` (or `-f net9.0-ios`) just works — no `cmd.exe`.
29
- - **MAUI on native Linux:** this framework's Docker image installs no MAUI workloads. Windows MAUI Blazor Desktop uses §E; mobile/iOS/Mac Catalyst work belongs on a native Mac. Do not install or report `maui-tizen` as a Docker prerequisite.
30
- - If `dotnet build` fails with `NETSDK1178` / "workload not installed", the fix is `dotnet workload install <id>` — with `sudo` on macOS (root-owned SDK dir; see above) — a one-time setup step the user runs, not a rung change; there is no other rung on this platform. If the user reports `Inadequate permissions` from a workload/SDK command, give them the exact `sudo` form of the same command.
31
-
32
- ---
33
-
34
- ## B. WSL-on-Windows (the reference machine) — WSL ↔ Windows interop ladder
35
-
36
- The owner's machine is set up per `WORKFLOW.html §0` and has BOTH:
37
-
38
- - **WSL-side .NET** at `$HOME/.dotnet/dotnet` (rung #2) — *replace with your own WSL dotnet path if different*. Compiles non-MAUI projects green.
39
- - **Windows-side .NET** with full workloads — including MAUI / iOS / Android — reachable via `cmd.exe /c "dotnet …"` (rung #4) or the `winrun` bridge (rung #3). Compiles MAUI/iOS/Android workloads green.
40
-
41
- Both are PERMANENT setups — you are NEVER reporting "MAUI cannot build on WSL" or "MAUI workloads missing" as a project blocker. Those are environment-config misunderstandings, not project issues. If a build fails because the workload isn't where you tried, you are on the wrong rung — switch rungs.
42
-
43
- ### The WSL ladder — try until one works
9
+ A green build is half of a verify. The verifier, the DevGuide and every smoke drive the running UI to check that data shows and the screen looks right. The driver depends on the head; the build does not change:
44
10
 
45
- | # | Command | Works when |
46
- |---|---------|------------|
47
- | 1 | `dotnet build` | dotnet is in WSL `$PATH` (rare on this machine) |
48
- | 2 | `~/.dotnet/dotnet build` (or `$HOME/.dotnet/dotnet build`) | dotnet is WSL-installed but not in PATH (the user's default — confirmed present) |
49
- | 3 | `winrun "dotnet build"` | user's WSL→Windows helper at `$HOME/bin/winrun` |
50
- | 4 | `cmd.exe /c "dotnet build"` | Windows-side .NET 10 (full workloads — MAUI, iOS, Android, etc.) — works for any `/mnt/c/...` project |
51
- | 5 | `powershell.exe -Command "dotnet build"` | same as #4 with PowerShell |
52
-
53
- Each candidate is a SINGLE bash command — no `for`/`if`/loops — so each matches the project's `.claude/settings.json` allow patterns (`Bash(dotnet *)`, `Bash(cmd.exe *)`, `Bash(powershell.exe *)`, `Bash(winrun *)`).
54
-
55
- ## How to PICK the right rung (§B / WSL only — READ THIS, most failures come from skipping it)
56
-
57
- > The rest of this document (rung selection, symptom escalation, probe order, banned-blocker list) is **§B WSL-specific detail**. On macOS / native Windows / native Linux there is a single rung (`dotnet build`), so skip ahead to §C if you're on native Windows; macOS/Linux is fully covered by §A above.
58
-
59
- The right starting rung depends on **what you are building**:
60
-
61
- ### A. Single .csproj — non-MAUI (Web, API, library, console, test project)
62
- → **Start at rung #2.** If it works, stay there for the session.
63
-
64
- ### B. Single .csproj — MAUI / iOS / Android / .NET MAUI Hybrid
65
- Detect: `<UseMaui>true</UseMaui>`, `<TargetFramework>` contains `-android` / `-ios` / `-maccatalyst` / `-windows10`, or the SDK is `Microsoft.NET.Sdk.Maui`.
66
- → **Skip rungs #1–#2 entirely.** Start at rung #4 (or #3 if you prefer the `winrun` wrapper). MAUI workloads ONLY live on the Windows side per the user's setup.
67
-
68
- ### C. Solution / multi-project build (`.sln` or `.slnx`) — MIXED projects
69
- **Do this BEFORE picking a rung.** Scan the solution file's project entries:
70
-
71
- 1. Read the `.sln` / `.slnx` file (use the `Read` tool — single call).
72
- 2. For each `<Project Path="…" />` (slnx) or `Project("…") = "…", "…\X.csproj"` line (sln), look at the .csproj path. Then quickly grep each `.csproj` for `<UseMaui>` or MAUI SDK references.
73
- 3. **If ANY project in the solution is MAUI/iOS/Android → the SOLUTION-level build needs the Windows side. Start at rung #4.** Do NOT try rung #2 on the slnx — it will fail with `NETSDK1178` or `Microsoft.iOS.Sdk missing` or similar workload errors, and that failure is a wasted attempt, not a project blocker.
74
- 4. **If NO project is MAUI** → start at rung #2.
75
-
76
- ### D. Per-project build to isolate a failure
77
- If a solution-level build fails on the right rung, fall back to building each project individually:
78
- - Non-MAUI projects (Web/API/library) on rung #2 (faster).
79
- - MAUI/iOS/Android projects on rung #4.
80
- - Aggregate the results in your status report. This isolates which projects compile clean and which have real code-level issues.
81
-
82
- ## Symptom-based auto-escalation (catch the wrong-rung-by-error pattern)
83
-
84
- If a build fails with ANY of these errors, you are on the WRONG RUNG — escalate immediately, do NOT log it as a project blocker:
85
-
86
- | Error code / message | Meaning | Action |
11
+ | Head | Runtime driver | Where it runs |
87
12
  |---|---|---|
88
- | `NETSDK1178` ("workload missing") | MAUI/iOS/Android workload not installed at the dotnet you're calling | Switch to rung #4 (`cmd.exe /c "dotnet build ..."`) |
89
- | `Microsoft.iOS.Sdk` / `Microsoft.Android.Sdk` / `Microsoft.MacCatalyst.Sdk` missing | Same as above | Rung #4 |
90
- | `error : Workload ID '...' not recognized` | Workload definition missing at the dotnet you're calling | Rung #4 |
91
- | `error : Microsoft.WindowsAppSDK ... not found` | WinUI / WinAppSDK workload missing on WSL side | Rung #4 |
92
- | `command not found: dotnet` | Wrong rung (not the right thing) | Next rung |
93
- | Real C# compile errors (CS####), missing references in source code | This IS a project issue | Log as Known blocker; stay on the rung |
94
-
95
- **Rule of thumb:** Workload/SDK errors → rung change. Source-code errors → project blocker.
96
-
97
- ## Probe order (optional, single commands)
98
-
99
- If you want to detect availability before building (do this once per session, cache the result):
100
-
101
- 1. `ls $HOME/.dotnet/dotnet 2>/dev/null` — fastest WSL probe
102
- 2. `cmd.exe /c "where dotnet"` — fastest Windows probe (confirms rung #4 is available)
103
- 3. `cmd.exe /c "dotnet workload list"` — see which workloads Windows-side has (one-time check)
104
-
105
- ## Recording the result
106
-
107
- After a successful build (any rung):
108
- - `last_verified_build: PASS`
109
- - `last_verified_date: {today YYYY-MM-DD}`
110
- - Note which rung was used in the PROJECT-STATUS verification-log row or Remarks (e.g. `built via rung #4`).
111
-
112
- After a failed build that's NOT a workload/wrong-rung issue (real CS#### errors):
113
- - `last_verified_build: FAIL`
114
- - `last_verified_date: {today YYYY-MM-DD}`
115
- - Add to "Known blockers" with one-line error summary AND which rung was used.
116
-
117
- After all rungs genuinely failed (probed all 5, none could find dotnet at all):
118
- - `last_verified_build: not-run`
119
- - `last_verified_date: {today YYYY-MM-DD}`
120
- - Add to "Known blockers": "Tried ladder rungs 1-5; no dotnet found in WSL PATH, ~/.dotnet/, winrun, cmd.exe, or powershell.exe. User needs to install .NET SDK or expose it to WSL PATH."
121
-
122
- ## What NEVER goes into Known blockers (banned escape-hatch entries)
123
-
124
- These are NOT blockers — they are signals you used the wrong rung. If you find yourself writing one, you skipped this doc:
125
-
126
- - ❌ "MAUI build cannot run on WSL"
127
- - ❌ "Microsoft.iOS.Sdk workload missing"
128
- - ❌ "Microsoft.Android.Sdk workload missing"
129
- - ❌ "NETSDK1178 — workload not installed"
130
- - ❌ "WSL doesn't have the MAUI workload"
131
- - ❌ "dotnet not in PATH"
132
- - ❌ Anything that boils down to "I tried the wrong rung and it failed"
133
-
134
- The user has the bridge set up. Their projects build green on the right rung. Document what RUNG produced the working build, not what failed on the wrong one.
135
-
136
- ## What NOT to do
137
-
138
- - Do NOT report `last_verified_build: not-run` because rung #1 failed. That is a lazy escape. Try rungs 2-5 first.
139
- - Do NOT wrap the probe in `if [ -x ... ]; then ... else ... fi` — that triggers a permission prompt. Run probes as plain single commands.
140
- - Do NOT log workload-missing / NETSDK1178 / wrong-rung errors as "Known blockers". They are wrong-rung errors. Switch and retry.
141
- - Do NOT claim ".NET is unavailable on this system" unless ALL five rungs failed AND your probe commands also failed. Even then, report which rungs you tried verbatim.
142
- - **Do NOT let a host limitation cancel a cross-OS deliverable.** A MAUI Mac Catalyst / iOS head genuinely cannot be built from Windows/WSL (`-r osx-arm64` only cross-compiles *plain* .NET — a Catalyst `.app` needs Xcode's toolchain, macOS only; `NETSDK1005`/`NETSDK1139`/a maccatalyst TFM guarded by `IsOSPlatform('OSX')` is that fact surfacing, not a failure). That is a **rung-selection fact, never a reason to drop the native head, declare it impossible, or quietly substitute another head** (e.g. a web-head publish) as "the" deliverable. The app's BuildAndRun guide documents the owner-run on-Mac path — prepare the source + the exact on-Mac commands, state plainly "the Catalyst `.app` is built on the Mac per the guide §<n> (one command, owner-run)", and treat any substitute you can build locally as a *complement*, offered alongside, never a silent replacement.
143
-
144
- ---
145
-
146
- ## C. Native Windows (Claude Code / OpenCode running on Windows, not inside WSL)
147
-
148
- → **Use rung #1: `dotnet build`.** You are already on the Windows side, so there is no interop bridge — `cmd.exe`/`winrun`/`/home/...` from §B do NOT apply.
149
-
150
- - **Non-MAUI** projects: `dotnet build` / `dotnet test` / `dotnet run` directly.
151
- - **MAUI**: builds natively here. Windows (`net9.0-windows10…`) and Android heads build directly once `dotnet workload install maui` has been run. **iOS / Mac Catalyst heads require a paired Mac** (Apple's toolchain only runs on macOS) — building those from Windows needs a Mac build host; absence of one is a genuine platform limitation, not a wrong-rung error.
152
- - Paths in commands use the project's actual location (e.g. `C:\src\App\App.sln`). Single commands, no `if/for` wrappers (same permission-prompt reason as §B).
153
- - `NETSDK1178` / "workload not installed" → run `dotnet workload install <id>` once; there is no alternate rung to switch to.
154
-
155
- ## D. Runtime-observe leg — reaching the RUNNING UI after a green build
156
-
157
- Building is only half of "verify". Once a head builds green, the verifier / devguide OBSERVE / smoke gates must **drive the running UI** to apply the data-render (§4a) and visual-truth (§4b) gates. The driver depends on the head — this is the runtime counterpart to the build rungs above, and it does NOT change how anything builds:
158
-
159
- | Head | Build (above) | **Runtime driver** | Where it runs |
160
- |------|---------------|--------------------|---------------|
161
- | Blazor | rung #1/#2 | headless **Playwright** (system Chromium) | WSL |
162
- | MAUI **Windows** | §B rung #4 / §C native | **FlaUI / Appium-Windows** | Windows side |
163
- | MAUI **Android** | §B rung #4 / §A/§C native | **Appium** (`uiautomator2`) → emulator/device | Windows host (SDK + emulator); WSL drives over HTTP |
164
- | MAUI **iOS** | paired Mac (Xcode) | **Appium** (`xcuitest`) → iOS Simulator | LAN Mac; WSL drives over HTTP |
165
- | MAUI **Mac Catalyst** | paired Mac (Xcode) | **Appium** (`mac2`) → the running .app | LAN Mac; WSL drives over HTTP |
166
-
167
- **Appium is the native analogue of Playwright** — same WebDriver protocol, returns a `base64` screenshot (visual-truth) + an element tree with `rect`/text (data-render). So the gates' assertions are identical; only the driver differs.
168
-
169
- - The endpoints live in `core-config.yaml → runtimeVerification.appium` (per app, opt-in). The WSL side needs only the **HTTP url** — no `adb`, emulator, or Xcode inside WSL.
170
- - **Android** runs entirely on the Windows host; the verifier boots the emulator + Appium itself via the registry `launch` command (you may launch it through `winrun`), then drives it over `http://localhost:4723` (Win11 mirrored networking) — booting it yourself, never asking the owner, is the same rule as the build ladder.
171
- - **iOS / Mac Catalyst** depend on the **LAN Mac being up**. `curl http://<mac>:4723/status` first; if unreachable, that head degrades to `⚠ STATIC-ONLY` (a session dependency, like "stack down"), never a faked `Verified`.
172
- - One-time host setup (Android SDK + AVD + Appium on Windows; Xcode + Appium + xcuitest/mac2 on the Mac; the `.wslconfig` mirrored-networking switch) is **`WORKFLOW.html §0b`**.
173
- - **Input discipline (all native heads, esp. MAUI Windows):** bind the driver to the app under test by identity — the **PID you launched → its top-level window handle** (Appium Windows `appium:appTopLevelWindow` / FlaUI `Application.Attach(pid)`) or the app **package/bundle id** on mobile — and interact **element-by-element via `AutomationId`** inside that bound window. NEVER global keyboard/mouse injection (FlaUI `Keyboard.Type`, coordinate clicks, `SendKeys`): it lands in whatever window has focus, not the app. Full rules: `verify-phase.md §3b`.
174
-
175
- ## E. OpenCode Docker on Windows — host bridge
176
-
177
- > **FALLBACK ONLY (since 2026-08-20).** OpenCode's primary deployment is native WSL — the same distro as Claude Code, using the §B ladder like any other WSL process (`docs/OpenCode-Deployment-Guide.md`). This section applies only when the Docker container is deliberately in use (`TF_OPENCODE_DOCKER=1`); nothing here concerns a WSL OpenCode session.
178
-
179
- An OpenCode Linux container is not WSL: `/proc/version` does not expose Windows interop, and `cmd.exe` cannot be installed into it. The framework Dockerfile uses the .NET 10 SDK and deliberately installs no MAUI workloads. Standard .NET projects build and test in the container. For a Windows MAUI Blazor Desktop target, use the image's `winrun` wrapper, which sends the command over SSH to the Windows host and starts it in `TF_WINDOWS_APP_PATH`. Mobile/iOS/Mac Catalyst builds and runtime tests belong on a native Mac.
13
+ | Blazor | headless Playwright (system Chromium) | WSL |
14
+ | MAUI Windows | FlaUI or Appium-Windows | Windows side |
15
+ | MAUI Android | Appium (`uiautomator2`) against the emulator or a device | Windows host runs the SDK and emulator; WSL drives it over HTTP |
16
+ | MAUI iOS | Appium (`xcuitest`) against the simulator | the LAN Mac; WSL drives it over HTTP |
17
+ | MAUI Mac Catalyst | Appium (`mac2`) against the running app | the LAN Mac; WSL drives it over HTTP |
180
18
 
181
- - Probe: `test -x /usr/local/bin/winrun && test -n "$TF_WINDOWS_SSH_USER" && test -n "$TF_WINDOWS_APP_PATH"` followed by `winrun "dotnet --info"`. The first checks configuration; the second proves the SSH bridge actually works. The Docker launcher may mount `/root/.ssh` read-only; `winrun` copies the key to a writable `/tmp` file with mode `0600` and maintains host trust in `/tmp`, so do not attempt to `chmod` the mounted key.
182
- - Build: `winrun "dotnet build -c Release"`.
183
- - Do not report missing `cmd.exe` as a code blocker when the bridge probe succeeds.
184
- - Do not check for or install `maui-tizen` in the container. The image intentionally contains no MAUI workloads.
185
- - If the bridge is absent or SSH fails, report the exact setup failure and mark only the Windows head `STATIC-ONLY`; continue Linux-compatible builds and Appium HTTP verification.
186
- - For private feeds, use the Docker-specific user config mounted at `/root/.nuget/NuGet/NuGet.Config`. A Windows `%AppData%\NuGet\NuGet.Config` may contain DPAPI-encrypted credentials that cannot be decrypted in Linux. Never put the Docker config in the repository.
19
+ Appium is the native analogue of Playwright: the same WebDriver protocol, a screenshot and an element tree with positions and text, so the checks are identical and only the driver differs.
187
20
 
188
- ## Recording the result (all platforms)
21
+ - The endpoints are in `core-config.yaml → runtimeVerification.appium`, per app and opt-in. WSL needs only the HTTP URL.
22
+ - Android runs on the Windows host: the verifier boots the emulator and Appium itself through the registry `launch` command (through `winrun` if needed), then drives `http://localhost:4723`. Booting it yourself, never asking the owner, is the same rule as the build.
23
+ - iOS and Mac Catalyst need the LAN Mac up: `curl http://<mac>:4723/status` first; unreachable means that head is `⚠ STATIC-ONLY`, a session dependency like a stopped database, never a faked `Verified`.
24
+ - One-time host setup (Android SDK, AVD and Appium on Windows; Xcode, Appium, `xcuitest` and `mac2` on the Mac; the `.wslconfig` mirrored-networking switch): `WORKFLOW.html §0b`.
25
+ - Bind the driver to the app under test by identity, the PID you launched and its top-level window or the package or bundle id on mobile, and interact element by element through `AutomationId`. Never global keyboard or mouse injection, which lands in whichever window has focus. Full rules: `verify-phase.md §3b`.
189
26
 
190
- After a successful build, note **which platform + invocation** produced it (e.g. "built on macOS via `dotnet build` (rung #1)", "built on WSL via `cmd.exe /c` (rung #4)") in the PROJECT-STATUS verification-log row or Remarks — so a reader on a different machine knows what to expect. The §B "what NEVER goes into Known blockers" list applies to WSL specifically; on §A/§C the equivalent non-blocker is "workload not yet installed — run `dotnet workload install`", which is a one-time user setup step, not a project defect.
27
+ **Dependent services are yours to start.** When the feature needs more than one process (a database, an API, a web front end, a model endpoint), a service that is down is something you start, not a blocker you hand to the owner. Read each dependent project's configuration for its port and URL, then start each one yourself in dependency order (data stores and endpoints first, then APIs, then the front end). Only when a service on another machine does not answer does that head degrade to `⚠ STATIC-ONLY`, and the report says which host and which probe.
@@ -0,0 +1,17 @@
1
+ <!-- build-subagent-prompt.md — the prompt build-phase gives every cluster sub-agent.
2
+ Filled by tf-build-list.sh --prompts: {App} {Cluster} {Builder} {Section} {Rows} {Mockups} {Checklist}.
3
+ Library agents (trblazeui, techierag) never read .tfcore/ tasks, so the two standing rules
4
+ reach them only through this text. Keep it short; the rows carry the detail. -->
5
+ You are building cluster {Cluster} of {App} ({Section}) as the {Builder}. Implement exactly these rows, nothing else:
6
+
7
+ {Rows}
8
+
9
+ Read first: `docs/{App}-Coding-Standards.md` (and the standard files it names under `.tfcore/standards/`), `docs/{App}-Architecture.md` for module boundaries, and the mockups: {Mockups}. Build a UI row to match its mockup control for control, with the same `data-testid` anchors. Use only controls the UI library's reference lists (`.trblazeui/TrBlazeUI-AI-Reference.md`, `.techierag/TechieRag-AI-Reference.md`); a control or feature the library lacks goes as an entry into `docs/{App}-<Library>-Feedback.md` and the row becomes `Blocked`, never a workaround.
10
+
11
+ Two standing rules:
12
+ 1. Git is manual. Never run `git` or `gh` for any purpose. Record your row ids in the checklist Remarks (`[REQ-…]`), not in commits.
13
+ 2. Smoke it yourself before you return: `bash .tfcore/utils/tf-build.sh` must PASS, then boot the app and open every screen you touched with headless Playwright (or the native driver the ladder names); the data must show and nothing may overlap; use a test user from `docs/{App}-UsageGuide.md`, never an invented one. A green compiler is not a smoke.
14
+
15
+ Unit tests for every FN, RAG and NFR row go under `tests/unit/`. Run-generated files go under `tests/.artifacts/`, never at the repository root.
16
+
17
+ When done, set each row in `{Checklist}` to `Implemented` (smoke passed) or `PARTIAL` / `FAIL` with a one-line Remark, and return: rows implemented, files changed, tests added, library gaps logged, and the exact smoke you ran.
@@ -99,7 +99,7 @@ pooled deliberately.*
99
99
  | Tokens per `Verified` REQ | {n} |
100
100
  | Commit cadence | {n} commits/active day over {n} days |
101
101
 
102
- **Cost in USD is not reported here.** Claude Code transcripts and Codex usage
102
+ **Cost in USD is not reported here.** Claude Code transcripts and usage
103
103
  payloads carry token counts but no per-message dollar cost, and this framework runs
104
104
  on subscriptions where marginal per-token cost is not the real unit. Multiplying
105
105
  tokens by a rate card would be an estimate presented as a measurement, so the row
@@ -192,7 +192,7 @@ than the worst. Read it as a question to investigate, never as a ranking to rout
192
192
  | Unattributable (`none` — no usable token window) | {n} | — |
193
193
 
194
194
  **Dollars.** {Either: "$X per miss — MEASURED, from {n} OpenCode records." Or:
195
- "No measured dollars. Claude Code and Codex carry `cost_usd: null` permanently —
195
+ "No measured dollars. Claude Code carries `cost_usd: null` permanently —
196
196
  no cost source exists on either, and pricing tokens from a rate card here would be
197
197
  an estimate presented as a measurement. Tokens are the honest figure."}
198
198
 
@@ -235,29 +235,6 @@ TechieFlow integrates with OpenCode via a project-level `opencode.jsonc`/`openco
235
235
  }
236
236
  ```
237
237
 
238
- ### Codex (CLI & Web)
239
-
240
- TechieFlow's full build/runtime workflow is supported in local Codex CLI, IDE,
241
- and desktop sessions. The scaffold/update scripts install `.codex/` project
242
- config, custom agents, hooks and rules plus reusable workflows under
243
- `.agents/skills/`. Trust the repository and review `/hooks` after installation
244
- or a hook update.
245
-
246
- Codex loads the root `AGENTS.md`. Invoke `$techieflow-build`,
247
- `$techieflow-verify`, `$techieflow-refresh-status`, or name the skill in plain
248
- language. Literal Claude/OpenCode `*command` and slash names are vocabulary
249
- aliases, not Codex command registrations.
250
-
251
- For an unattended local run:
252
-
253
- ```bash
254
- bash .tfcore/utils/tf-goal.sh --harness codex . "<goal>"
255
- ```
256
-
257
- Codex cloud cannot automatically reach local `winrun`, Appium hosts, NuGet
258
- credentials, or already-running services. It is static-only unless equivalent
259
- infrastructure is explicitly provisioned.
260
-
261
238
  ## Special Agents
262
239
 
263
240
  There are two TechieFlow agents — in the future they'll be consolidated into a single Flow-Master.
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env python3
2
+ """tf-brd-status.py — rewrite the BRD's "Development status" table from the checklist.
3
+
4
+ bash .tfcore/utils/tf-brd-status.sh <App> [--no-render] [--phase N]
5
+
6
+ On a Large project the pair is the phase being worked: --phase N, else appPhase in
7
+ .tfcore/core-config.yaml (phase 1 is docs/<App>-BRD.md and -Checklist.md, phase n is
8
+ docs/<App>-Pn-BRD.md and docs/<App>-Pn-Checklist.md).
9
+
10
+ Reads docs/<App>-Checklist.md: every REQ row of the Requirements Status table, and
11
+ the "## Page: …" section its detail entry sits under, which names the screen.
12
+ Writes one row per screen into the "Development status" section of
13
+ docs/<App>-BRD.md (Screen | Requirements | Verified | Open | Status) with a
14
+ "Snapshot as of <today>" line, inserting the section before "Context diagram"
15
+ (or at the end) when the BRD has none. Touches nothing else in the BRD. Then
16
+ re-renders docs/<App>-BRD.html through tf-render-html.sh unless --no-render.
17
+
18
+ Status per screen: Done (every row terminal), Planned (no row started),
19
+ Partial (some terminal, some open), In progress (started, none terminal).
20
+ Terminal = Verified, Done (pre-existing), N/A. Blocked is counted as open.
21
+
22
+ Python 3 standard library only. Exit 0 written or nothing to do, 2 could not run.
23
+ """
24
+ import datetime
25
+ import os
26
+ import re
27
+ import subprocess
28
+ import sys
29
+
30
+ TERMINAL = {"verified", "done (pre-existing)", "n/a"}
31
+ NOT_STARTED = {"not started"}
32
+
33
+
34
+ def die(msg):
35
+ print(f"tf-brd-status: {msg}", file=sys.stderr)
36
+ sys.exit(2)
37
+
38
+
39
+ def read(path):
40
+ with open(path, encoding="utf-8", errors="replace") as f:
41
+ return f.read()
42
+
43
+
44
+ def norm(raw):
45
+ s = raw.strip().strip("`* ").lower()
46
+ return re.sub(r"\s*\(.*$", "", s).strip()
47
+
48
+
49
+ def screen_name(heading):
50
+ h = heading.strip()
51
+ h = re.sub(r"^(?:\d+[.)]\s*)?(?:page|screen)\s*:\s*", "", h, flags=re.I)
52
+ h = re.sub(r"\s*\(`?/[^)]*`?\)\s*$", "", h) # trailing (`/route`)
53
+ return h.strip("` ").strip() or heading.strip()
54
+
55
+
56
+ def parse_checklist(text):
57
+ rows = []
58
+ for line in text.splitlines():
59
+ if re.match(r"^\s*\|\s*`?REQ-", line):
60
+ cells = [c.strip() for c in line.strip().strip("|").split("|")]
61
+ if len(cells) >= 3:
62
+ rows.append((cells[0].strip("`* "), norm(cells[2])))
63
+ screen_of = {}
64
+ current = "Other"
65
+ for line in text.splitlines():
66
+ m = re.match(r"^##\s+(.+)$", line)
67
+ if m:
68
+ current = screen_name(m.group(1))
69
+ continue
70
+ for a in re.findall(r"<a id=['\"]d-(req-[a-z]+-\d+)['\"]", line, flags=re.I):
71
+ screen_of[a.upper()] = current
72
+ return rows, screen_of
73
+
74
+
75
+ def table(rows, screen_of):
76
+ per = {}
77
+ order = []
78
+ for rid, status in rows:
79
+ s = screen_of.get(rid.upper(), "Unmapped")
80
+ if s not in per:
81
+ per[s] = {"n": 0, "v": 0, "ns": 0}
82
+ order.append(s)
83
+ per[s]["n"] += 1
84
+ per[s]["v"] += 1 if status in TERMINAL else 0
85
+ per[s]["ns"] += 1 if status in NOT_STARTED else 0
86
+ out = ["| Screen | Requirements | Verified | Open | Status |", "|---|---|---|---|---|"]
87
+ for s in order:
88
+ d = per[s]
89
+ open_n = d["n"] - d["v"]
90
+ if open_n == 0:
91
+ st = "Done"
92
+ elif d["ns"] == d["n"]:
93
+ st = "Planned"
94
+ elif d["v"] > 0:
95
+ st = "Partial"
96
+ else:
97
+ st = "In progress"
98
+ out.append(f"| {s} | {d['n']} | {d['v']} | {open_n} | {st} |")
99
+ return "\n".join(out), len(order)
100
+
101
+
102
+ def write_section(brd_text, app, body):
103
+ """Replace the body of the Development status section, or insert the section."""
104
+ sec = re.compile(r"(?ms)^(##\s+(?:\d+[.)]\s*)?Development status[^\n]*\n)(.*?)(?=^## |\Z)")
105
+ m = sec.search(brd_text)
106
+ if m:
107
+ return brd_text[:m.start()] + m.group(1) + "\n" + body + "\n\n" + brd_text[m.end():], "updated"
108
+ heading = "## Development status\n"
109
+ ctx = re.search(r"(?m)^##\s+(?:\d+[.)]\s*)?Context diagram", brd_text)
110
+ if ctx:
111
+ return brd_text[:ctx.start()] + heading + "\n" + body + "\n\n" + brd_text[ctx.start():], "inserted"
112
+ return brd_text.rstrip("\n") + "\n\n" + heading + "\n" + body + "\n", "appended"
113
+
114
+
115
+ def main(argv):
116
+ if len(argv) < 2 or argv[1] in ("-h", "--help"):
117
+ print(__doc__)
118
+ return 0
119
+ app = argv[1]
120
+ render = "--no-render" not in argv
121
+ root = os.getcwd()
122
+ phase = None
123
+ if "--phase" in argv:
124
+ i = argv.index("--phase")
125
+ if i + 1 >= len(argv) or not argv[i + 1].isdigit() or int(argv[i + 1]) < 1:
126
+ die("--phase needs a number from 1 upwards")
127
+ phase = int(argv[i + 1])
128
+ if phase is None:
129
+ cfg = os.path.join(root, ".tfcore", "core-config.yaml")
130
+ m = re.search(r"(?m)^appPhase:\s*(\d+)", read(cfg)) if os.path.isfile(cfg) else None
131
+ phase = int(m.group(1)) if m else 1
132
+ pfx = f"{app}-" if phase <= 1 else f"{app}-P{phase}-"
133
+ cl_rel, brd_rel = f"docs/{pfx}Checklist.md", f"docs/{pfx}BRD.md"
134
+ cl = os.path.join(root, cl_rel)
135
+ brd = os.path.join(root, brd_rel)
136
+ if not os.path.isfile(cl):
137
+ print(f"tf-brd-status: {cl_rel} does not exist yet; nothing to do")
138
+ return 0
139
+ if not os.path.isfile(brd):
140
+ die(f"{brd_rel} does not exist")
141
+ rows, screen_of = parse_checklist(read(cl))
142
+ if not rows:
143
+ die(f"{cl_rel} has no REQ rows in its Requirements Status table")
144
+ tbl, screens = table(rows, screen_of)
145
+ today = datetime.date.today().isoformat()
146
+ body = (
147
+ "Written by the status gate after every build, verify and handoff; not by hand.\n\n"
148
+ f"**Snapshot as of {today}.** Live per-requirement status: `PROJECT-STATUS.md` and the "
149
+ f"Requirements Status table in `{cl_rel}`.\n\n" + tbl
150
+ )
151
+ text = read(brd)
152
+ new_text, how = write_section(text, app, body)
153
+ with open(brd, "w", encoding="utf-8") as f:
154
+ f.write(new_text)
155
+ verified = sum(1 for _, s in rows if s in TERMINAL)
156
+ print(f"tf-brd-status: {brd_rel} Development status {how}: {screens} screens, {verified} of {len(rows)} requirements verified")
157
+ if render:
158
+ script = os.path.join(root, ".tfcore", "utils", "tf-render-html.sh")
159
+ if os.path.isfile(script):
160
+ rc = subprocess.call(["bash", script, os.path.relpath(brd, root)], cwd=root)
161
+ if rc != 0:
162
+ print(f"tf-brd-status: the HTML render exited {rc}; run bash .tfcore/utils/tf-render-html.sh {brd_rel} by hand", file=sys.stderr)
163
+ return 1
164
+ print(f"tf-brd-status: rendered {brd_rel[:-3]}.html")
165
+ else:
166
+ print("tf-brd-status: .tfcore/utils/tf-render-html.sh not found at its literal path; HTML not rendered", file=sys.stderr)
167
+ return 0
168
+
169
+
170
+ if __name__ == "__main__":
171
+ try:
172
+ sys.exit(main(sys.argv))
173
+ except SystemExit:
174
+ raise
175
+ except Exception as e:
176
+ die(str(e))
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # tf-brd-status.sh — rewrite the BRD's "Development status" table from the checklist.
3
+ #
4
+ # bash .tfcore/utils/tf-brd-status.sh MyApp # writes the table, re-renders the BRD HTML
5
+ # bash .tfcore/utils/tf-brd-status.sh MyApp --no-render
6
+ #
7
+ # One row per screen (Screen | Requirements | Verified | Open | Status) with a
8
+ # "Snapshot as of <today>" line, computed from docs/MyApp-Checklist.md. Nothing
9
+ # else in the BRD is touched. Step 5 of _status-update-gate.md; the Stop hook
10
+ # refuses to end a turn while the BRD is older than the checklist.
11
+ #
12
+ # Thin wrapper over tf-brd-status.py. Python 3 standard library only.
13
+ set -uo pipefail
14
+ SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ if ! command -v python3 >/dev/null 2>&1; then
16
+ echo "tf-brd-status: python3 is required (standard library only)." >&2
17
+ exit 2
18
+ fi
19
+ exec python3 "$SELF_DIR/tf-brd-status.py" "$@"