@techierathore/techieflow 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/TechieFlow/agents/analyst.md +5 -13
- package/.claude/commands/TechieFlow/agents/architect.md +1 -16
- package/.claude/commands/TechieFlow/agents/flow-master.md +42 -215
- package/.claude/commands/TechieFlow/agents/verifier.md +24 -64
- package/.claude/commands/TechieFlow/tasks/_metrics-emit-gate.md +24 -117
- package/.claude/commands/TechieFlow/tasks/_owner-language.md +26 -0
- package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +20 -84
- package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +10 -74
- package/.claude/commands/TechieFlow/tasks/_yolo-mode.md +20 -58
- package/.claude/commands/TechieFlow/tasks/amend-docs.md +21 -143
- package/.claude/commands/TechieFlow/tasks/build-phase.md +14 -224
- package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +4 -0
- package/.claude/commands/TechieFlow/tasks/create-doc.md +4 -0
- package/.claude/commands/TechieFlow/tasks/create-project-brief.md +22 -0
- package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +31 -505
- package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +28 -213
- package/.claude/commands/TechieFlow/tasks/deploy-checklist.md +19 -0
- package/.claude/commands/TechieFlow/tasks/devguide.md +10 -219
- package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +4 -0
- package/.claude/commands/TechieFlow/tasks/fix-issues.md +10 -129
- package/.claude/commands/TechieFlow/tasks/generate-html.md +5 -1
- package/.claude/commands/TechieFlow/tasks/handoff-phase.md +18 -16
- package/.claude/commands/TechieFlow/tasks/log-miss.md +19 -158
- package/.claude/commands/TechieFlow/tasks/metrics-report.md +3 -1
- package/.claude/commands/TechieFlow/tasks/mockups.md +12 -117
- package/.claude/commands/TechieFlow/tasks/refresh-status.md +9 -130
- package/.claude/commands/TechieFlow/tasks/split-brd.md +10 -126
- package/.claude/commands/TechieFlow/tasks/triage-and-fix.md +14 -0
- package/.claude/commands/TechieFlow/tasks/triage-issues.md +11 -152
- package/.claude/commands/TechieFlow/tasks/verify-phase.md +36 -572
- package/.opencode/plugin/techieflow.js +40 -13
- package/.tfcore/agents/analyst.md +5 -13
- package/.tfcore/agents/architect.md +1 -16
- package/.tfcore/agents/flow-master.md +42 -215
- package/.tfcore/agents/verifier.md +24 -64
- package/.tfcore/core-config.yaml +10 -0
- package/.tfcore/hooks/block-git.sh +4 -4
- package/.tfcore/hooks/guard-artifacts.sh +2 -2
- package/.tfcore/hooks/guard-build.sh +76 -0
- package/.tfcore/hooks/guard-db.sh +90 -0
- package/.tfcore/hooks/guard-metrics.sh +68 -0
- package/.tfcore/hooks/guard-status-html.sh +116 -71
- package/.tfcore/hooks/guard-status.sh +43 -16
- package/.tfcore/hooks/guard-verify-deps.sh +124 -0
- package/.tfcore/hooks/guard-verify.sh +70 -48
- package/.tfcore/hooks/sweep-artifacts.sh +2 -2
- package/.tfcore/install-manifest.yaml +0 -18
- package/.tfcore/routing.yaml +86 -14
- package/.tfcore/standards/coding-standards-core.md +61 -0
- package/.tfcore/standards/coding-standards-dotnet.md +93 -0
- package/.tfcore/tasks/_metrics-emit-gate.md +24 -117
- package/.tfcore/tasks/_owner-language.md +26 -0
- package/.tfcore/tasks/_smoke-test-policy.md +20 -84
- package/.tfcore/tasks/_status-update-gate.md +10 -74
- package/.tfcore/tasks/_yolo-mode.md +20 -58
- package/.tfcore/tasks/amend-docs.md +21 -143
- package/.tfcore/tasks/build-phase.md +14 -224
- package/.tfcore/tasks/create-deep-research-prompt.md +4 -0
- package/.tfcore/tasks/create-doc.md +4 -0
- package/.tfcore/tasks/create-project-brief.md +22 -0
- package/.tfcore/tasks/day1-brownfield.md +31 -505
- package/.tfcore/tasks/day1-greenfield.md +28 -213
- package/.tfcore/tasks/deploy-checklist.md +19 -0
- package/.tfcore/tasks/devguide.md +10 -219
- package/.tfcore/tasks/facilitate-brainstorming-session.md +4 -0
- package/.tfcore/tasks/fix-issues.md +10 -129
- package/.tfcore/tasks/generate-html.md +5 -1
- package/.tfcore/tasks/handoff-phase.md +18 -16
- package/.tfcore/tasks/log-miss.md +19 -158
- package/.tfcore/tasks/metrics-report.md +3 -1
- package/.tfcore/tasks/mockups.md +12 -117
- package/.tfcore/tasks/refresh-status.md +9 -130
- package/.tfcore/tasks/split-brd.md +10 -126
- package/.tfcore/tasks/triage-and-fix.md +14 -0
- package/.tfcore/tasks/triage-issues.md +11 -152
- package/.tfcore/tasks/verify-phase.md +36 -572
- package/.tfcore/telemetry/SCHEMA.md +100 -8
- package/.tfcore/telemetry/tf-metrics.sh +564 -32
- package/.tfcore/templates/stack-defaults/dotnet.md +35 -0
- package/.tfcore/templates/stack-questions.md +77 -0
- package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +3 -4
- package/.tfcore/templates/v4custom/app-architecture-tmpl.md +107 -115
- package/.tfcore/templates/v4custom/app-brd-tmpl.md +108 -184
- package/.tfcore/templates/v4custom/app-brief-tmpl.md +65 -0
- package/.tfcore/templates/v4custom/app-checklist-tmpl.md +48 -73
- package/.tfcore/templates/v4custom/app-claude-md-tmpl.md +1 -1
- package/.tfcore/templates/v4custom/app-coding-standards-tmpl.md +52 -0
- package/.tfcore/templates/v4custom/app-decision-request-tmpl.md +85 -0
- package/.tfcore/templates/v4custom/app-deployment-checklist-tmpl.md +86 -0
- package/.tfcore/templates/v4custom/app-devguide-tmpl.md +69 -146
- package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +86 -31
- package/.tfcore/templates/v4custom/app-phases-tmpl.md +35 -0
- package/.tfcore/templates/v4custom/app-productguide-tmpl.md +38 -35
- package/.tfcore/templates/v4custom/app-project-status-tmpl.md +63 -73
- package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +62 -37
- package/.tfcore/templates/v4custom/app-usageguide-tmpl.md +69 -54
- package/.tfcore/templates/v4custom/build-invocation-ladder.md +17 -180
- package/.tfcore/templates/v4custom/build-subagent-prompt.md +17 -0
- package/.tfcore/templates/v4custom/html-render-shell.md +1 -1
- package/.tfcore/templates/v4custom/metrics-report-template.md +2 -2
- package/.tfcore/user-guide.md +0 -23
- package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-checklist-edit.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-codex-bind.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-codex-telemetry.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-perf-grade.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
- package/.tfcore/utils/tf-brd-status.py +176 -0
- package/.tfcore/utils/tf-brd-status.sh +19 -0
- package/.tfcore/utils/tf-build-list.py +211 -0
- package/.tfcore/utils/tf-build-list.sh +21 -0
- package/.tfcore/utils/tf-build.sh +143 -0
- package/.tfcore/utils/tf-cdp-relay.ps1 +64 -0
- package/.tfcore/utils/tf-checklist-edit.py +133 -0
- package/.tfcore/utils/tf-day1-files.py +208 -0
- package/.tfcore/utils/tf-day1-files.sh +20 -0
- package/.tfcore/utils/tf-devguide-list.py +249 -0
- package/.tfcore/utils/tf-devguide-list.sh +18 -0
- package/.tfcore/utils/tf-doc-check.py +1369 -0
- package/.tfcore/utils/tf-doc-check.sh +29 -0
- package/.tfcore/utils/tf-emit.sh +545 -18
- package/.tfcore/utils/tf-fix-close.sh +115 -0
- package/.tfcore/utils/tf-gitignore-audit.sh +50 -3
- package/.tfcore/utils/tf-goal.sh +284 -66
- package/.tfcore/utils/tf-harness.sh +4 -9
- package/.tfcore/utils/tf-log-miss.py +197 -0
- package/.tfcore/utils/tf-log-miss.sh +14 -0
- package/.tfcore/utils/tf-misses-md.py +208 -0
- package/.tfcore/utils/tf-misses-md.sh +14 -0
- package/.tfcore/utils/tf-mockups-locate.py +66 -0
- package/.tfcore/utils/tf-mockups-locate.sh +18 -0
- package/.tfcore/utils/tf-model-pick.sh +475 -0
- package/.tfcore/utils/tf-opencode-audit.py +23 -7
- package/.tfcore/utils/tf-perf-grade.py +97 -0
- package/.tfcore/utils/tf-perf-grade.sh +12 -0
- package/.tfcore/utils/tf-phase.sh +92 -0
- package/.tfcore/utils/tf-render-html.py +20 -0
- package/.tfcore/utils/tf-routing-bind.sh +97 -6
- package/.tfcore/utils/tf-routing.sh +13 -7
- package/.tfcore/utils/tf-selfcheck.sh +323 -0
- package/.tfcore/utils/tf-split-brd.py +309 -0
- package/.tfcore/utils/tf-split-brd.sh +22 -0
- package/.tfcore/utils/tf-status-evidence.sh +68 -0
- package/.tfcore/utils/tf-status-facts.py +358 -0
- package/.tfcore/utils/tf-status-facts.sh +20 -0
- package/.tfcore/utils/tf-triage.py +243 -0
- package/.tfcore/utils/tf-triage.sh +16 -0
- package/.tfcore/utils/tf-verify-boot.sh +250 -0
- package/.tfcore/utils/tf-verify-emit.sh +110 -0
- package/.tfcore/utils/tf-verify-env.sh +88 -0
- package/.tfcore/utils/tf-verify-list.py +270 -0
- package/.tfcore/utils/tf-verify-list.sh +13 -0
- package/.tfcore/utils/tf-verify-screens.mjs +355 -0
- package/.tfcore/utils/tf-verify-screens.sh +31 -0
- package/.tfcore/utils/tf-verify-tests.sh +166 -0
- package/.tfcore/utils/tf-verify-verdict.py +292 -0
- package/.tfcore/utils/tf-verify-verdict.sh +15 -0
- package/.tfcore/utils/tf-yolo.sh +59 -0
- package/README.md +120 -1145
- package/docs/TechieFlow-Installation.md +1 -5
- package/opencode.jsonc +37 -33
- package/package.json +1 -5
- package/scripts/install.mjs +56 -46
- package/.claude/commands/TechieFlow/tasks/advanced-elicitation.md +0 -117
- package/.claude/commands/TechieFlow/tasks/author-brd.md +0 -79
- package/.claude/commands/TechieFlow/tasks/document-project.md +0 -343
- package/.claude/commands/TechieFlow/tasks/execute-checklist.md +0 -86
- package/.claude/commands/TechieFlow/tasks/index-docs.md +0 -173
- package/.claude/commands/TechieFlow/tasks/kb-mode-interaction.md +0 -75
- package/.claude/commands/TechieFlow/tasks/shard-doc.md +0 -185
- package/.codex/config.toml +0 -46
- package/.codex/hooks.json +0 -63
- package/.codex/rules/techieflow.rules +0 -8
- package/.tfcore/hooks/codex-adapter.py +0 -248
- package/.tfcore/tasks/advanced-elicitation.md +0 -117
- package/.tfcore/tasks/author-brd.md +0 -79
- package/.tfcore/tasks/document-project.md +0 -343
- package/.tfcore/tasks/execute-checklist.md +0 -86
- package/.tfcore/tasks/index-docs.md +0 -173
- package/.tfcore/tasks/kb-mode-interaction.md +0 -75
- package/.tfcore/tasks/shard-doc.md +0 -185
- package/.tfcore/templates/architecture-tmpl.yaml +0 -650
- package/.tfcore/templates/brownfield-architecture-tmpl.yaml +0 -476
- package/.tfcore/templates/competitor-analysis-tmpl.yaml +0 -306
- package/.tfcore/templates/front-end-architecture-tmpl.yaml +0 -218
- package/.tfcore/templates/fullstack-architecture-tmpl.yaml +0 -823
- package/.tfcore/templates/market-research-tmpl.yaml +0 -252
- package/.tfcore/templates/project-brief-tmpl.yaml +0 -221
- package/.tfcore/utils/techieflow-doc-template.md +0 -325
- package/.tfcore/utils/tf-codex-bind.py +0 -196
- package/.tfcore/utils/tf-codex-telemetry.py +0 -60
- package/WORKFLOW.html +0 -1986
|
@@ -1,190 +1,27 @@
|
|
|
1
1
|
# Build invocation ladder
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
+
## Reaching the running UI after a green build
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
|
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
|
-
|
|
|
89
|
-
|
|
|
90
|
-
| `
|
|
91
|
-
|
|
|
92
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
@@ -23,7 +23,7 @@ Each rendered HTML file is:
|
|
|
23
23
|
- A **single self-contained `.html` file**, no external CSS, no external JS except the two `<script>` tags below (mermaid + svg-pan-zoom from jsdelivr CDN).
|
|
24
24
|
- Sibling to the source MD (same filename, `.html` extension).
|
|
25
25
|
- **Light + dark theme with a persisted toggle** (§2 + §3 + §7). Default is chosen by **time of day** on first open — light between 07:00 and 19:00 local, softened dark otherwise — then the user's explicit toggle choice (saved in `localStorage` as `tf-theme`) wins on every later visit. Light is a **warm off-white** (never harsh bright white); dark is a softened near-black. Monospace for code/pre, sans for body.
|
|
26
|
-
- **Only human-readable docs are ever rendered to HTML** (BRD, Architecture, UsageGuide, DevGuide, PROJECT-STATUS).
|
|
26
|
+
- **Only human-readable docs are ever rendered to HTML** (BRD, Architecture, UsageGuide, DevGuide, ProductGuide, PROJECT-STATUS, a Decision Request, a feedback file). **An agent working document is never rendered to HTML** — it is read in markdown, and an HTML mirror wastes tokens and drifts from the source. Two are banned by name, and `tf-render-html.py` refuses both with exit 2: the **requirements checklist** `{App}-Checklist.md`, and the **miss log** `{App}-Misses.md`. If you are about to emit a `*-Checklist.html` or a `*-Misses.html`, STOP — that is banned. The test for anything else: if the only reader is an agent, it stays markdown.
|
|
27
27
|
- Working anchor links from any auto-generated or hand-written TOC to its target heading.
|
|
28
28
|
- Copy buttons on every **code** `<pre>` — but NOT on `pre.mermaid` (a button injected there corrupts the diagram source mermaid reads → "Syntax error"; mermaid blocks get copy/export from their own diagram toolbar).
|
|
29
29
|
- Mermaid toolbar on every rendered diagram.
|
|
@@ -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
|
|
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
|
|
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
|
|
package/.tfcore/user-guide.md
CHANGED
|
@@ -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.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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" "$@"
|