@techierathore/techieflow 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/TechieFlow/agents/analyst.md +5 -8
- package/.claude/commands/TechieFlow/agents/architect.md +1 -10
- package/.claude/commands/TechieFlow/agents/flow-master.md +1 -0
- package/.claude/commands/TechieFlow/agents/verifier.md +1 -0
- package/.claude/commands/TechieFlow/tasks/_owner-language.md +27 -0
- package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +1 -1
- package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +4 -3
- package/.claude/commands/TechieFlow/tasks/amend-docs.md +6 -5
- package/.claude/commands/TechieFlow/tasks/build-phase.md +1 -1
- package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +4 -0
- package/.claude/commands/TechieFlow/tasks/create-doc.md +4 -0
- package/.claude/commands/TechieFlow/tasks/create-project-brief.md +22 -0
- package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +3 -3
- package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +3 -3
- package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +4 -0
- package/.claude/commands/TechieFlow/tasks/fix-issues.md +2 -2
- package/.claude/commands/TechieFlow/tasks/generate-html.md +7 -3
- package/.claude/commands/TechieFlow/tasks/handoff-phase.md +20 -18
- package/.claude/commands/TechieFlow/tasks/metrics-report.md +2 -0
- package/.claude/commands/TechieFlow/tasks/mockups.md +1 -1
- package/.claude/commands/TechieFlow/tasks/productguide.md +2 -2
- package/.claude/commands/TechieFlow/tasks/render-workflow-docs.md +1 -1
- package/.claude/commands/TechieFlow/tasks/triage-issues.md +2 -2
- package/.claude/commands/TechieFlow/tasks/verify-phase.md +1 -1
- package/.opencode/plugin/techieflow.js +181 -9
- package/.tfcore/agents/analyst.md +5 -8
- package/.tfcore/agents/architect.md +1 -10
- package/.tfcore/agents/flow-master.md +1 -0
- package/.tfcore/agents/verifier.md +1 -0
- package/.tfcore/hooks/block-git.sh +4 -2
- package/.tfcore/hooks/guard-build.sh +5 -1
- package/.tfcore/hooks/guard-db.sh +6 -4
- package/.tfcore/hooks/guard-metrics.sh +13 -1
- package/.tfcore/hooks/guard-status-html.sh +86 -2
- package/.tfcore/hooks/guard-verify-deps.sh +124 -0
- package/.tfcore/hooks/guard-verify.sh +8 -3
- package/.tfcore/hooks/sweep-artifacts.sh +46 -0
- package/.tfcore/routing.yaml +61 -0
- package/.tfcore/standards/owner-words.txt +23 -0
- package/.tfcore/tasks/_owner-language.md +27 -0
- package/.tfcore/tasks/_smoke-test-policy.md +1 -1
- package/.tfcore/tasks/_status-update-gate.md +4 -3
- package/.tfcore/tasks/amend-docs.md +6 -5
- package/.tfcore/tasks/build-phase.md +1 -1
- package/.tfcore/tasks/create-deep-research-prompt.md +4 -0
- package/.tfcore/tasks/create-doc.md +4 -0
- package/.tfcore/tasks/create-project-brief.md +22 -0
- package/.tfcore/tasks/day1-brownfield.md +3 -3
- package/.tfcore/tasks/day1-greenfield.md +3 -3
- package/.tfcore/tasks/facilitate-brainstorming-session.md +4 -0
- package/.tfcore/tasks/fix-issues.md +2 -2
- package/.tfcore/tasks/generate-html.md +7 -3
- package/.tfcore/tasks/handoff-phase.md +20 -18
- package/.tfcore/tasks/metrics-report.md +2 -0
- package/.tfcore/tasks/mockups.md +1 -1
- package/.tfcore/tasks/productguide.md +2 -2
- package/.tfcore/tasks/render-workflow-docs.md +1 -1
- package/.tfcore/tasks/triage-issues.md +2 -2
- package/.tfcore/tasks/verify-phase.md +1 -1
- package/.tfcore/telemetry/SCHEMA.md +61 -1
- package/.tfcore/telemetry/install-metrics.sh +3 -2
- package/.tfcore/telemetry/tf-metrics.sh +435 -21
- package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +6 -0
- package/.tfcore/templates/v4custom/app-brd-tmpl.md +5 -3
- package/.tfcore/templates/v4custom/app-brief-tmpl.md +65 -0
- package/.tfcore/templates/v4custom/app-decision-request-tmpl.md +85 -0
- package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +86 -31
- package/.tfcore/templates/v4custom/app-phase-brd-tmpl.md +94 -0
- package/.tfcore/templates/v4custom/app-phase-uidesign-tmpl.md +60 -0
- package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +4 -2
- package/.tfcore/templates/v4custom/build-subagent-prompt.md +2 -2
- package/.tfcore/templates/v4custom/html-render-shell.md +4 -2
- package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-checklist-edit.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-perf-grade.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
- package/.tfcore/utils/tf-assets-browser.mjs +96 -0
- package/.tfcore/utils/tf-assets.sh +97 -12
- package/.tfcore/utils/tf-brd-status.py +14 -5
- package/.tfcore/utils/tf-build-list.py +99 -11
- package/.tfcore/utils/tf-build-list.sh +5 -3
- package/.tfcore/utils/tf-build.sh +88 -15
- package/.tfcore/utils/tf-ci-repro.py +685 -0
- package/.tfcore/utils/tf-ci-repro.sh +24 -0
- package/.tfcore/utils/tf-devguide-list.py +47 -22
- package/.tfcore/utils/tf-doc-check.py +242 -10
- package/.tfcore/utils/tf-doc-tests.sh +79 -0
- package/.tfcore/utils/tf-emit.sh +413 -19
- package/.tfcore/utils/tf-feedback.sh +13 -0
- package/.tfcore/utils/tf-fix-close.sh +113 -31
- package/.tfcore/utils/tf-gitignore-audit.sh +50 -3
- package/.tfcore/utils/tf-goal.sh +105 -18
- package/.tfcore/utils/tf-harness.sh +9 -4
- package/.tfcore/utils/tf-lock.sh +29 -0
- package/.tfcore/utils/tf-log-miss.py +34 -8
- package/.tfcore/utils/tf-login.mjs +167 -0
- package/.tfcore/utils/tf-misses-md.py +24 -21
- package/.tfcore/utils/tf-misses-md.sh +5 -4
- package/.tfcore/utils/tf-mockup-parity.mjs +427 -63
- package/.tfcore/utils/tf-mockup-parity.sh +14 -2
- package/.tfcore/utils/tf-model-pick.sh +475 -0
- package/.tfcore/utils/tf-owner-text.py +233 -0
- package/.tfcore/utils/tf-owner-text.sh +14 -0
- package/.tfcore/utils/tf-phase.sh +33 -1
- package/.tfcore/utils/tf-portable.sh +230 -0
- package/.tfcore/utils/tf-render-html.py +80 -2
- package/.tfcore/utils/tf-routing-bind.sh +87 -1
- package/.tfcore/utils/tf-routing.sh +7 -0
- package/.tfcore/utils/tf-selfcheck.sh +356 -0
- package/.tfcore/utils/tf-split-brd.py +17 -2
- package/.tfcore/utils/tf-status-evidence.sh +11 -9
- package/.tfcore/utils/tf-status-facts.py +133 -44
- package/.tfcore/utils/tf-triage.py +62 -16
- package/.tfcore/utils/tf-triage.sh +2 -0
- package/.tfcore/utils/tf-verify-boot.sh +263 -43
- package/.tfcore/utils/tf-verify-emit.sh +20 -0
- package/.tfcore/utils/tf-verify-env.sh +33 -4
- package/.tfcore/utils/tf-verify-list.py +75 -8
- package/.tfcore/utils/tf-verify-screens.mjs +254 -43
- package/.tfcore/utils/tf-verify-screens.sh +18 -4
- package/.tfcore/utils/tf-verify-tests.sh +272 -28
- package/.tfcore/utils/tf-verify-verdict.py +59 -11
- package/.tfcore/utils/tf_defect.py +27 -0
- package/.tfcore/utils/tf_feedback.py +209 -0
- package/.tfcore/utils/tf_roadmap.py +35 -0
- package/README.md +19 -5
- package/docs/TechieFlow-Installation.md +5 -3
- package/opencode.jsonc +24 -0
- package/package.json +1 -1
- package/scripts/install.mjs +4 -0
- package/.tfcore/templates/architecture-tmpl.yaml +0 -650
- package/.tfcore/templates/brownfield-architecture-tmpl.yaml +0 -476
- package/.tfcore/templates/competitor-analysis-tmpl.yaml +0 -306
- package/.tfcore/templates/front-end-architecture-tmpl.yaml +0 -218
- package/.tfcore/templates/fullstack-architecture-tmpl.yaml +0 -823
- package/.tfcore/templates/market-research-tmpl.yaml +0 -252
- package/.tfcore/templates/project-brief-tmpl.yaml +0 -221
- package/.tfcore/utils/techieflow-doc-template.md +0 -325
|
@@ -23,26 +23,28 @@ The Usage Guide was created at day-1 (`day1-*` §7.4) from `.tfcore/templates/v4
|
|
|
23
23
|
|
|
24
24
|
Below, "populate" means refresh these sections of the existing doc. Hard rules:
|
|
25
25
|
|
|
26
|
-
- **No narrative prose anywhere.** No "this app
|
|
26
|
+
- **No narrative prose anywhere.** No "this app is built this way because…", no caveats, no explanations. The deployment-steps section must look like a runbook — one command per line, numbered in execution order.
|
|
27
27
|
- **Skip any step that doesn't apply.** If the repo has no SQL migrations, omit the database-setup step entirely — do not leave "N/A" placeholders or "// not applicable" comments.
|
|
28
28
|
- **No optional steps in the numbered list.** The numbered deployment-steps section is the minimum sufficient set of commands to go from `git clone` to a running app. Anything optional goes in "Known limitations" or a separate doc.
|
|
29
29
|
|
|
30
30
|
Detection sources for each section:
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
**Every command below comes from the project, never from this file.** The restore, build, run and test commands are the ones the project's Architecture stack table and `docs/{App}-Coding-Standards.md` name, and `bash .tfcore/utils/tf-build.sh --print` prints the build command this repository actually uses. A toolchain assumed here would be wrong on the next project.
|
|
33
|
+
|
|
34
|
+
- **Prerequisites**: the runtime version from wherever this stack pins it (the Architecture stack table names the file). Then one line for each external runtime the build, run or test commands depend on — a package runtime only if the tests need one, a database server only if migrations exist, a platform workload only if a project targets that platform. One line each. No prose.
|
|
33
35
|
- **Deployment steps**: detect in this order and emit one numbered step per command:
|
|
34
36
|
1. `git clone <repo>` (always)
|
|
35
|
-
2.
|
|
36
|
-
3.
|
|
37
|
-
4.
|
|
38
|
-
5. Backend run — find the
|
|
39
|
-
6. Frontend run — find the
|
|
40
|
-
7. "Open http://localhost
|
|
41
|
-
-
|
|
42
|
-
- If frontend and backend are
|
|
43
|
-
- **Test**:
|
|
37
|
+
2. Restore dependencies — the stack's restore command (always)
|
|
38
|
+
3. Database migrations — scan the migration folders the Architecture names, plus the conventional ones (`database/`, `db/migrations/`, `migrations/`). List the migration files in the order they run, one per line; if the stack has a migration runner, prefer one step invoking it over listing every file.
|
|
39
|
+
4. Build — the stack's build command
|
|
40
|
+
5. Backend run — find the service or API entry point. One line, with the port it listens on.
|
|
41
|
+
6. Frontend run — find the UI entry point. One line, with its port.
|
|
42
|
+
7. "Open http://localhost:<frontend port> in a browser."
|
|
43
|
+
- A packaged desktop or mobile head replaces steps 5 and 6 with that platform's build-and-run pair.
|
|
44
|
+
- If frontend and backend are one process, collapse steps 5 and 6 into one.
|
|
45
|
+
- **Test**: the stack's test command. Add the browser-test command ONLY if such tests are checked in.
|
|
44
46
|
- **Smoke checklist**: one checkbox per top-level BRD capability (5–10 max). Each line is a user action, not a technical step ("Log in as admin and load the dashboard" — NOT "verify JWT validation").
|
|
45
|
-
- **Known limitations**: pull every `Blocked` REQ row from the checklist Status table + every entry from the per-library feedback files (`docs/{AppName}-
|
|
47
|
+
- **Known limitations**: pull every `Blocked` REQ row from the checklist Status table + every entry from the per-library feedback files (`docs/{AppName}-{Library}-Feedback.md`, one per library the project uses). One line each. Cross-reference each to its issue id.
|
|
46
48
|
|
|
47
49
|
**If you find yourself writing more than one line per numbered step, you're doing it wrong** — the user has explicitly called out that the deployment steps got polluted with "unnecessary details" in past runs. Strip aggressively.
|
|
48
50
|
|
|
@@ -50,7 +52,7 @@ Detection sources for each section:
|
|
|
50
52
|
|
|
51
53
|
- `current_phase: Handoff` (the user manually sets `Released` after UAT passes — §5)
|
|
52
54
|
- `last_updated`: today
|
|
53
|
-
- `last_verified_build: PASS` (you just verified — confirm with `
|
|
55
|
+
- `last_verified_build: PASS` (you just verified — confirm with `bash .tfcore/utils/tf-build.sh` if uncertain)
|
|
54
56
|
- `last_verified_date`: today
|
|
55
57
|
- "Where I am": one paragraph — "All REQ-* verified; awaiting UAT per usage doc."
|
|
56
58
|
- "Next command to run": `Manual UAT per docs/{AppName}-UsageGuide.md smoke checklist.`
|
|
@@ -82,15 +84,15 @@ Invoke `render-workflow-docs {AppName}` as a subtask. Specifically:
|
|
|
82
84
|
- `docs/{AppName}-Architecture.html` — if implementation revealed "as-built" deltas from "Target", update Architecture.md first, then re-render. Set status to "Current (post-implementation)" or "Current + planned target" as appropriate.
|
|
83
85
|
- `PROJECT-STATUS.html` — always re-render (just updated in §2)
|
|
84
86
|
|
|
85
|
-
Then re-render the **Usage Guide** (updated in §1) and the **DevGuide** (refreshed in §3a) via `.tfcore/tasks/generate-html.md`: `docs/{AppName}-UsageGuide.md` (+
|
|
87
|
+
Then re-render the **Usage Guide** (updated in §1) and the **DevGuide** (refreshed in §3a) via `.tfcore/tasks/generate-html.md`: `docs/{AppName}-UsageGuide.md` (+ `docs/{AppName}-DevGuide.md`) → sibling `.html`. **Do NOT render the checklist to HTML** — it is an AI-agent working document kept in markdown only; its Status table stays accurate in markdown.
|
|
86
88
|
|
|
87
89
|
### 3a. Refresh the Developer Guide
|
|
88
90
|
|
|
89
|
-
Run `.tfcore/tasks/devguide.md` for `{AppName}` (`*devguide {AppName}`) so the screen-by-screen code map reflects the final as-built code — this is the doc a human developer uses to chase bugs and verify the AI-generated code from page → control → service → data-access → stored proc. If a DevGuide already exists, an incremental `--update` is enough (remap only changed screens). It produces `docs/{AppName}-DevGuide.md
|
|
91
|
+
Run `.tfcore/tasks/devguide.md` for `{AppName}` (`*devguide {AppName}`) so the screen-by-screen code map reflects the final as-built code — this is the doc a human developer uses to chase bugs and verify the AI-generated code from page → control → service → data-access → stored proc. If a DevGuide already exists, an incremental `--update` is enough (remap only changed screens). It produces `docs/{AppName}-DevGuide.md`, one file at any size, and its `.html`; §3 above already lists them in the re-render set.
|
|
90
92
|
|
|
91
93
|
### 4. Consolidate the per-library feedback files
|
|
92
94
|
|
|
93
|
-
For EACH
|
|
95
|
+
For EACH `docs/{AppName}-{Upstream}-Feedback.md` that exists — one per library the project uses, plus the framework's own — they are separate files because each goes to a different team:
|
|
94
96
|
- Deduplicate entries (same expected/actual pair = duplicate; merge them).
|
|
95
97
|
- Sort by severity: blocker → major → minor → nice-to-have.
|
|
96
98
|
- Ensure each entry has all the schema fields (Severity, Repro, Expected, Actual, Encountered in, Workaround, Suggested fix). Fill in missing ones from context if possible.
|
|
@@ -108,11 +110,11 @@ If no feedback file exists (no library issues encountered), skip this step.
|
|
|
108
110
|
|
|
109
111
|
Deliver:
|
|
110
112
|
- "✔ {AppName} is ready for UAT." OR
|
|
111
|
-
- "⚠ {AppName} has {N} `Blocked` (library-gap) items — see docs/{AppName}-
|
|
113
|
+
- "⚠ {AppName} has {N} `Blocked` (library-gap) items — see the `docs/{AppName}-{Upstream}-Feedback.md` files before UAT."
|
|
112
114
|
|
|
113
115
|
Then explicitly tell the user:
|
|
114
116
|
1. Open `docs/{AppName}-UsageGuide.md` and run the smoke checklist.
|
|
115
|
-
2. Hand each
|
|
117
|
+
2. Hand each feedback file to the team named in its file name (or file its entries as issues against that repository). Those teams use this same framework, so the file drops straight into their flow.
|
|
116
118
|
3. After UAT passes, manually update `PROJECT-STATUS.md`: `current_phase: Released`.
|
|
117
119
|
4. For end-user / external documentation, run `*productguide {AppName}` (flow-master) — it builds a screenshot-illustrated, task-oriented user manual (MD + HTML) from the DevGuide's screen map + captured screenshots. On-demand; re-run with `--update` as the UI evolves.
|
|
118
120
|
|
|
@@ -108,6 +108,8 @@ Written: docs/metrics/METRICS.md + .html
|
|
|
108
108
|
{if backfilled records present: "Backfilled data is reported in a separate column and cannot support a published first-pass rate."}
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
Then one `runs.jsonl` record carrying `"cmd":"metrics-report"`, written as `.tfcore/tasks/_metrics-emit-gate.md` shows; `bash .tfcore/utils/tf-phase.sh start metrics-report {App}` is the first action of this task, so the start time is measured.
|
|
112
|
+
|
|
111
113
|
Then stop. Do not run the status gate — this task reports on history, it does not advance the project, and PROJECT-STATUS is not where metrics go.
|
|
112
114
|
|
|
113
115
|
## Hard rules
|
package/.tfcore/tasks/mockups.md
CHANGED
|
@@ -9,7 +9,7 @@ First: `bash .tfcore/utils/tf-phase.sh start mockups {App}` prints the start tim
|
|
|
9
9
|
1. `bash .tfcore/utils/tf-mockups-locate.sh` moves any mockup found elsewhere in the repository into `docs/mockups/` and lists what is there.
|
|
10
10
|
2. Read the component reference of the UI library named in the Architecture's Stack decisions (for TrBlazeUI, `.trblazeui/TrBlazeUI-AI-Reference.md`; build once if it is absent). Design only with controls that exist. A control the library lacks is logged as an entry in `docs/{App}-<Library>-Feedback.md`, never designed in silently.
|
|
11
11
|
3. Read the BRD's Screens and flow table: every row is a screen, with its route, roles and fields; a dialog row belongs to its parent screen. A screen that already has a mockup in `docs/mockups/` keeps it and is linked. An app with no screens records "skipped — no UI" in the UIDesign and stops.
|
|
12
|
-
4. For each screen without a mockup (fan out one sub-agent per group of screens when there are many): decide the layout regions, map every region to a control, write `docs/mockups/<screen-slug>.html` with realistic placeholder data in the library's visual language, and fill the screen's section in `docs/{App}-UIDesign.md` from `.tfcore/templates/v4custom/app-uidesign-tmpl.md` (mockup link, roles, regions-to-controls table, fields table, the dialogs it opens, the empty, loading and error states). One shell, one theme, one spacing system across the set.
|
|
12
|
+
4. For each screen without a mockup (fan out one sub-agent per group of screens when there are many): decide the layout regions, map every region to a control, write `docs/mockups/<screen-slug>.html` with realistic placeholder data in the library's visual language, and fill the screen's section in `docs/{App}-UIDesign.md` from `.tfcore/templates/v4custom/app-uidesign-tmpl.md` — for phase n of a Large project, `docs/{App}-Pn-UIDesign.md` from `app-phase-uidesign-tmpl.md`, which carries that phase's screens only (mockup link, roles, regions-to-controls table, fields table, the dialogs it opens, the empty, loading and error states). One shell, one theme, one spacing system across the set.
|
|
13
13
|
5. Make the set click through. The entry screen is the sign-in page (or onboarding, or the home page when there is neither). Every link and every navigation button goes to the mockup it names, written as `<a href="screen.html">` or a form action, never as a script (`onclick="location.href=…"` is refused: it dies in any viewer without scripts): sign in opens the home screen, logout returns to sign in, an "Add" button opens the add screen, a row opens its detail, every menu item leads to its screen. Every screen has a way out. A button that does not navigate shows a message with `onclick`. A dialog opens on its parent page. Sign-in needs no credentials: the button alone navigates.
|
|
14
14
|
6. Anchor every element the build must match with `data-testid`, using the names from the regions-to-controls table: the layout regions, every badge and status indicator, every icon button, every card, table and chart, and every value cell whose width matters. The verifier compares only anchored elements; a mockup without anchors is ungradeable.
|
|
15
15
|
7. `bash .tfcore/utils/tf-doc-check.sh --app {App}`: it refuses a broken link, an unreachable or dead-end screen, an inert button and an unanchored mockup. Fix every FAIL.
|
|
@@ -26,7 +26,7 @@ The Product Guide documents the real running app. If the repo is doc-only / not
|
|
|
26
26
|
### 2. Gather the screen inventory + screenshots (reuse, don't reinvent)
|
|
27
27
|
|
|
28
28
|
The DevGuide already did the hard discovery work — reuse it:
|
|
29
|
-
- Read the **DevGuide** (`docs/{AppName}-DevGuide.md
|
|
29
|
+
- Read the **DevGuide** (`docs/{AppName}-DevGuide.md`) for the role→menu map, the screen list, the real post-login landing per role (LANDING-TRUTH), and each screen's purpose.
|
|
30
30
|
- Use the **screenshots** the DevGuide's OBSERVE pass captured under `docs/screenshots/{AppName}/` (`{role}-{screen-slug}.png`, + `-mobile` where present). These are the images the guide embeds.
|
|
31
31
|
- Read the **BRD** §9 feature catalog (feature purpose/benefit, personas) and the **UsageGuide** (user flows + known limitations) for the plain-language "what it's for" and "tips".
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ The DevGuide already did the hard discovery work — reuse it:
|
|
|
34
34
|
|
|
35
35
|
### 3. Decide structure — single doc vs per-role split
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
**Split per role** when `roles ≥ 3` OR `screens > 12` OR any role has `> 8` screens; else single. Honor `--single`/`--split`. A split guide lives in `docs/productguides/` (index + per-role files co-located, so relative links resolve); a single guide is `docs/{AppName}-ProductGuide.md`. On a structure switch (single↔split), move/delete the stale-location copy so only one canonical copy survives.
|
|
38
38
|
|
|
39
39
|
### 4. Write the guide — END-USER voice, screenshot per screen (FAN OUT per role)
|
|
40
40
|
|
|
@@ -6,7 +6,7 @@ Render BRD.html, Architecture.html, and PROJECT-STATUS.html from their markdown
|
|
|
6
6
|
|
|
7
7
|
After day-1 (or any phase that changes BRD/Architecture/PROJECT-STATUS), regenerate the human-readable HTML mirrors. Self-contained single-file HTML with Mermaid rendering, copy buttons on every code block, mermaid toolbar (zoom/fit/1:1/fullscreen/popout), inline TOC, and sidebar TOC for long docs.
|
|
8
8
|
|
|
9
|
-
**If the user has ad-hoc HUMAN-readable docs to render (
|
|
9
|
+
**If the user has ad-hoc HUMAN-readable docs to render (design notes, archived docs, anything else), use `*generate-html @path/to/file.md` instead — this task only handles the three canonical files.** Note: the **checklist is an AI-agent working document and is NEVER rendered to HTML** — do not render `*-Checklist.md` here or via `generate-html`. Nor are the miss log and feedback files.
|
|
10
10
|
|
|
11
11
|
## Inputs
|
|
12
12
|
|
|
@@ -6,9 +6,9 @@ First: `bash .tfcore/utils/tf-phase.sh start triage-issues {App}` prints the sta
|
|
|
6
6
|
|
|
7
7
|
## Steps
|
|
8
8
|
|
|
9
|
-
1. Read every screenshot and the notes; from a written list take one issue per symptom in the reporter's words. Build the issue list: screen, role, symptom, evidence path, a first guess of kind (`layout`, `render-empty`, `data-logic`, `rag`).
|
|
9
|
+
1. Read every screenshot and the notes; from a written list take one issue per symptom in the reporter's words. Build the issue list: screen, role, symptom, evidence path, a first guess of kind (`layout`, `render-empty`, `data-logic`, `rag`). A failed CI run (its id, link or log) is evidence too: its issue is the failing step and its first error.
|
|
10
10
|
2. `bash .tfcore/utils/tf-verify-boot.sh start` boots the application (dependent services first, from their own configuration). Sign in as a UsageGuide test user (`.tfcore/tasks/_smoke-test-policy.md`). NONE means the issues are logged from the evidence alone and the report says the app was not booted.
|
|
11
|
-
3. Reproduce each issue on its screen; `bash .tfcore/utils/tf-verify-screens.sh --screen <name>=<route> … --base <url>` gives the render and visual evidence and a screenshot. Settle the kind from what you see and map the issue to its row in the checklist (the acceptance line's screen; the DevGuide's screen list). Print the triage table: issue, row, kind, action.
|
|
11
|
+
3. Reproduce each issue on its screen; `bash .tfcore/utils/tf-verify-screens.sh --screen <name>=<route> … --base <url>` gives the render and visual evidence and a screenshot. Settle the kind from what you see and map the issue to its row in the checklist (the acceptance line's screen; the DevGuide's screen list). Print the triage table: issue, row, kind, action. A CI failure is reproduced with `bash .tfcore/utils/tf-ci-repro.sh [<workflow>] [--job <id>]`, never by running the workflow's commands here: this machine's package caches and build output hide what a fresh runner hits (Lekhak TF-019: a runtime pack only the local NuGet cache held). Its FAIL line is the reproduction; a PASS means it could not be reproduced here, logged with `note`.
|
|
12
12
|
4. Log each issue with `bash .tfcore/utils/tf-triage.sh {App} …`: `demote <REQ> "<symptom>" --kind … --evidence <path>` for a defect inside a row's scope, whatever its status; `new "<title>" "<When … on <screen>, then …>" --section "<screen>"` for a defect no row covers (a Not Started row with `BRD-pending`; new scope rather than a defect is still logged, and the report names `*amend-docs`); `note <REQ> "<what was tried>"` when it could not be reproduced. `--source production` for a live report. Whose gap it was is recorded as `--sort` (the four questions of `.tfcore/tasks/log-miss.md` step 1); without it a demoted row is `weak-check` (its line existed and the check let it through) and a new row is `spec`.
|
|
13
13
|
5. With `verify`: execute `.tfcore/tasks/verify-phase.md` inline, scoped to the demoted rows' siblings on the same screens and any scope the owner named. Without it, the verifier does not run and nothing is re-checked informally.
|
|
14
14
|
6. `bash .tfcore/utils/tf-verify-boot.sh stop`, then `bash .tfcore/utils/tf-triage.sh {App} close --started <the step-0 time>`: one escaped gate record per row (no check caught it), one miss per row in the reporter's words with the discovery cost, the run record, and a warning plus a miss if any code file changed during the run.
|
|
@@ -28,7 +28,7 @@ The phase's checklist, UIDesign and `docs/mockups/` (`appPhase` in `core-config.
|
|
|
28
28
|
|
|
29
29
|
1. `bash .tfcore/utils/tf-verify-list.sh {App} {scope}` prints the rows to grade with their acceptance line, screen, route, mockup and perf budget, the screens to drive, and the test users; it writes `tests/.artifacts/verify/list.json`. Every row in scope is graded whatever its status; only N/A is skipped, so a `Verified` row is re-confirmed and an old `Done` claim is tested. NOTHING means run the status gate and stop. A row without an acceptance line cannot be graded: fix the checklist first. A dialog is listed under its parent screen and checked there; never give a dialog a route so it can be tested.
|
|
30
30
|
2. `bash .tfcore/utils/tf-verify-env.sh` prints READY, or the one command the owner must run once.
|
|
31
|
-
3. `bash .tfcore/utils/tf-verify-boot.sh start` boots the head it finds (`--head` or `--project` when there are several) and prints BOOTED with a URL, or a CDP URL for
|
|
31
|
+
3. `bash .tfcore/utils/tf-verify-boot.sh start` boots the head it finds (`--head` or `--project` when there are several) and prints BOOTED with a URL, or a CDP URL for an embedded-browser desktop head, which the browser tools attach to. A dependent service (database, API) is yours to start first, from its own configuration, in order. NONE `kind=build-error`: every row fails the build check; go to step 6. NONE `kind=host` or `kind=no-driver` (an Android, iOS or Mac head): go to step 6 as well; those rows are written "not verified" with the reason. Never a static-only pass, never "the owner should start it".
|
|
32
32
|
4. Tests. Every UI and functional row has one test whose title starts with its id, written from the acceptance line, black box, under `tests/verify/`; an NFR row without a screen has a unit test carrying its id in the test project, or is not observable. Reuse a test that exists. Fan out one test-writer sub-agent per screen group in one turn, each with its rows, their acceptance lines, the URL and the test user; a test writer touches no source. Sign in as a UsageGuide test user (`.tfcore/tasks/_smoke-test-policy.md`).
|
|
33
33
|
5. Run the checks, with `<url>` from step 3 and, on a signed-in app, the session cookie from the test login for the tools that take `--cookie`:
|
|
34
34
|
- `bash .tfcore/utils/tf-verify-tests.sh --base <url>`
|
|
@@ -127,6 +127,34 @@ So `tf-emit.sh` detects it and injects it. **Never write `harness` into an emit
|
|
|
127
127
|
|
|
128
128
|
Provenance rule applied once more: **never pool `cost_usd` across harness** — Claude records are `null`, and a sum over mixed records silently under-reports. Tokens may be compared across harness; dollars may not.
|
|
129
129
|
|
|
130
|
+
### 2.5b How the model was paid for (added 2026-09-10; injected by `tf-emit.sh`, never emitted by an agent)
|
|
131
|
+
|
|
132
|
+
`cost_usd` alone answers *how much* and never *of what*, and on one machine it is three different quantities at once. The owner found this on 2026-09-10 (MISS-TechieFlow-20260910-02): a `runs.jsonl` here holds OpenCode records on a metered API key where the dollars are real money, OpenCode records on an OAuth subscription where OpenCode records **0.0** because the plan was already paid for, OpenCode records on a model running on this machine where there is no bill at all, and Claude Code records that are permanently `null`. Summing them produces a number that is neither the money spent nor the effort used, and the two zeroes are indistinguishable from a free run.
|
|
133
|
+
|
|
134
|
+
| Field | Type | Meaning / source |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `billing_mode` | string | `subscription` \| `metered` \| `local` \| `mixed` \| `unknown`. Resolved per observed model by `bash .tfcore/utils/tf-model-pick.sh billing <model> <harness>`: OpenCode's own `auth.json` says `oauth` (a plan) or `api` (a key), a provider that runs on this machine is `local`, a provider with no credential is `unknown` and is never assumed to be billable, and `routing.yaml` `billing:` overrides any of it by name. Claude Code is **declared** in `billing.claude` (default `subscription`), because nothing in a transcript says which plan paid for it. `mixed` when one window ran models of more than one mode — which is exactly what a fallback from a subscription to a metered key looks like. |
|
|
137
|
+
| `cost_source` | string | `opencode-db` where a per-message cost was read, `none` where there is no cost source at all. **Never a rate card.** |
|
|
138
|
+
|
|
139
|
+
`billing_mode` has five values, and the `plan` one is the reason the field is not a boolean:
|
|
140
|
+
|
|
141
|
+
| Value | What the provider's cost number is |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `subscription` | Nothing, or a recorded `0.0`. A flat fee bought the quota before the run started. |
|
|
144
|
+
| `plan` | **Allowance consumed against a per-model limit, not an invoice.** OpenCode Go is $10/month with a $15–$60 monthly allowance *per model* and 5-hour / weekly / monthly thresholds (<https://opencode.ai/docs/go>), so its dollars are a meter — and they are precisely the numbers that produce the limit `routing.yaml` `fallbacks:` exists for. Detected from the provider name, because an API key alone cannot tell a plan from a per-token bill. |
|
|
145
|
+
| `metered` | Money actually billed, per token. |
|
|
146
|
+
| `local` | No bill at all. |
|
|
147
|
+
| `mixed` / `unknown` | Several modes in one window; or nothing here could tell. `unknown` is never quietly treated as billable. |
|
|
148
|
+
|
|
149
|
+
**Money is never computed into a record.** Tokens are what the framework measures and stores — per model, main thread and sub-agents apart (§2.5, §2.6) — and turning them into a price is a *reporting* job, done by `tf-metrics.sh` (and by any other reader, TfLens included) from the fields already on the stream. That is §8's existing rule, *derived metrics are computed, never stored*, applied to money; it is why a rate card can change, or a reader disagree with this one, without a single record becoming wrong, and why every run ever recorded is re-priced the moment the rate card is corrected. The shared rate lookup is `bash .tfcore/utils/tf-model-pick.sh rate <model>`, which reads the models.dev catalog and lets a project's own `.tfcore/rate-card.json` win.
|
|
150
|
+
|
|
151
|
+
Two simplifications in that price, stated rather than hidden: the **base** rate is used, so a provider's long-context surcharge is not applied (nothing records per-message context length, and guessing it would be worse than the small understatement); and a model the rate card does not carry produces no number rather than a zero.
|
|
152
|
+
| `fallback_from` | string | **`runs`/`gates` only.** The tier's own model, named here when the run started while that model was on cooldown for a usage limit. It is what separates *"routing drifted"* from *"the tier model was out of quota and the chain moved on"* — both of which set `routed:false`, and only one of which is a problem. |
|
|
153
|
+
|
|
154
|
+
**The reporting rule, enforced in `tf-metrics.sh` and not merely stated here:** the report carries **three** money lines and never merges them — *list price*, computed over every record that can be priced and labelled a price rather than a bill; *plan allowance used*, per model, over `billing_mode:"plan"` records, because that is the meter a per-model limit is read against; and *money billed*, over `billing_mode:"metered"` records only. Each prints what it excluded. A window whose models were paid for in different ways contributes real money that cannot be separated from the part a subscription had already covered, so it gets its own line rather than being folded into any of the three. Per-model spend is attributed only where the window ran a single model; splitting one window's dollars across several models by token share is arithmetic, not measurement — the same line §5.5.3 draws between `sole` and `shared`. A record written before 2026-09-10 carries no `billing_mode`; it is admitted on its old terms and counted separately, so this change does not silently rewrite the past.
|
|
155
|
+
|
|
156
|
+
Tokens remain the unit that works everywhere, and they are what every figure is built from. `cost_usd` is whatever the provider itself reported, and `billing_mode` is what stops a reader mistaking a meter, a flat fee and an invoice for one another.
|
|
157
|
+
|
|
130
158
|
### 2.6 Per-phase effort fields (added 2026-08-31; injected by `tf-emit.sh`, never emitted by an agent)
|
|
131
159
|
|
|
132
160
|
Added so the question *"what did each phase cost — in time, in tokens, on which model, with how much fan-out?"* is answerable from the streams rather than reconstructed by hand. §2 and §2.5 already carried `cmd`, `started`/`ended`/`duration_s`, the four token counters, `model`, `harness`, `tier`/`tier_model`/`routed` and `attempt`. Three things were missing, and each was missing for the same reason: it was either **self-reported** or **not represented at all**.
|
|
@@ -147,6 +175,34 @@ Added so the question *"what did each phase cost — in time, in tokens, on whic
|
|
|
147
175
|
|
|
148
176
|
**These do not create per-feature timing.** The unit stays **the run**; §0's non-goal is unchanged. `cmd` is the phase, and a phase figure is an aggregate over runs of that `cmd` — never a per-ticket cycle time.
|
|
149
177
|
|
|
178
|
+
### 2.7 `kind: "run-void"` — taking a wrong run out of the figures without deleting it (added 2026-09-09)
|
|
179
|
+
|
|
180
|
+
The stream is append-only, so a run record that turns out to be **wrong** can be neither edited nor deleted — and nothing inside such a record says it is wrong, so every figure built over it is quietly polluted. §5.5.7 solved the same problem on the misses stream by adding a record kind rather than an edit. This is that answer for runs.
|
|
181
|
+
|
|
182
|
+
A `run-void` names one run record and says why it should not be counted:
|
|
183
|
+
|
|
184
|
+
| Field | Type | Meaning |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| `kind` | `"run-void"` | the record kind |
|
|
187
|
+
| `app` | string | filled by the emitter from the run it names |
|
|
188
|
+
| `cmd` | string | the named run's `cmd` |
|
|
189
|
+
| `started` | ISO-8601 | the named run's `started` — `cmd` + `started` is the pair the readers already key on |
|
|
190
|
+
| `reason` | string | free text, required: why the record is wrong, in a sentence a reader can check |
|
|
191
|
+
|
|
192
|
+
**Written only by `bash .tfcore/utils/tf-emit.sh --void-run <cmd> <started> "<reason>"`**, which refuses one that names no run on the stream (a void about nothing is a claim about nothing) and refuses a second void on the same run. Like an amend, it exits 0 either way: telemetry has no veto (§10).
|
|
193
|
+
|
|
194
|
+
**What the readers must do, and `tf-metrics.sh` does:** the named record leaves **every** figure — it is never clamped, halved or guessed at — and the count of what left travels with the figures, as `runs_voided_n` with the reasons in `runs_voided`. A total offered without its exclusions is just a different wrong number (the §5.5.8 rule, one stream over). A void naming a run that is not on this stream is an **orphan**: counted as `run_voids_orphaned_n` and reported, never silently dropped, exactly as an orphaned `miss-amend` is.
|
|
195
|
+
|
|
196
|
+
**What it is not.** It is not a delete and not an edit: both records stay, in order, and a reader can always see what was corrected and why. It cannot be used to remove a run whose figures are merely unflattering — the reason is on the record and a reader can check it, which is the whole protection. It is not a way to fix a number: there is no "corrected duration" field, because a figure nobody measured is not recoverable by asserting one.
|
|
197
|
+
|
|
198
|
+
### 2.7b A run may not start before the last one ended (added 2026-09-10)
|
|
199
|
+
|
|
200
|
+
§2.7 gave a way to **correct** a start time nobody measured, and nothing refused one — so the same mistake was made twice more on the day the correction shipped, each record beginning before the record before it ended, each quietly adding its overlap to every total that crossed it (MISS-TechieFlow-20260910-04, sorted `ignored`: it was written down and not followed). A correction mechanism with no refusal in front of it is a fix that has to be remembered, which is the definition of a rule that rots.
|
|
201
|
+
|
|
202
|
+
`tf-emit.sh` now **refuses** a live `run` record for an app whose `started` precedes the latest `ended` of that app's live runs. The refusal names the record it collides with and the timestamp to use instead; nothing is appended. **Voided and backfilled records are excluded from the comparison**, which is what keeps the §2.7 repair path working: void the wrong record, then the corrected one is accepted.
|
|
203
|
+
|
|
204
|
+
The one real overlap is two machines appending to the same `merge=union` stream (§6). That is declared, with `bash .tfcore/utils/tf-emit.sh runs --allow-overlap`, and never assumed — the mistake this refuses is easy to make and impossible to see afterwards, so the door is a flag rather than a fallback. FR-72; `bash tests/regression/run.sh tf_overlap`.
|
|
205
|
+
|
|
150
206
|
## 3. `docs/metrics/gates.jsonl` — one record per REQ verdict per verify run
|
|
151
207
|
|
|
152
208
|
**This is the primary stream.** If only one stream survives, it is this one.
|
|
@@ -298,7 +354,7 @@ Written by `.tfcore/hooks/metrics-session.sh`, wired to the **`SessionEnd`** hoo
|
|
|
298
354
|
| `cache_creation_tokens` | int | Σ `usage.cache_creation_input_tokens`. |
|
|
299
355
|
| `cost_usd` | number \| null | **Almost always `null`.** See the limitation below. |
|
|
300
356
|
|
|
301
|
-
**Known limitation — cost is not in the transcript.** Claude Code `2.x` transcripts carry token counts but no per-message dollar cost, and this framework runs on a Claude Max subscription where marginal per-token cost is not the real unit anyway. `cost_usd` is therefore emitted as `null` and *never* computed from a rate card — that would be an estimate presented as a measurement. Consequently **"cost per verified REQ" is reported in tokens, not dollars**, unless a real cost source appears. (OpenCode's `opencode stats` *does* report real cost — that is the one thing it can measure which Claude Code cannot. Wiring it in is the obvious way this field stops being `null`.)
|
|
357
|
+
**Known limitation — cost is not in the transcript.** Claude Code `2.x` transcripts carry token counts but no per-message dollar cost, and this framework runs on a Claude Max subscription where marginal per-token cost is not the real unit anyway. `cost_usd` is therefore emitted as `null` and *never* computed from a rate card — that would be an estimate presented as a measurement. (Since 2026-09-10 the report does price a run from a rate card, at read time and never into a record; §2.5b is what keeps the two apart.) Consequently **"cost per verified REQ" is reported in tokens, not dollars**, unless a real cost source appears. (OpenCode's `opencode stats` *does* report real cost — that is the one thing it can measure which Claude Code cannot. Wiring it in is the obvious way this field stops being `null`.) **A caveat added 2026-09-10:** OpenCode's cost is real only where the provider is billed per token. On a provider signed in with OAuth it records `0.0`, which is true of the marginal cost and false of the money, so a session record's cost carries the same warning as a run record's — with the difference that `sessions.jsonl` has no `billing_mode` field, because it is not joinable to `runs.jsonl` on anything but time (§4, below) and would have no report to serve. Read session dollars per provider or not at all.
|
|
302
358
|
|
|
303
359
|
**Known limitation — session ≠ run.** One session may span several commands, or one command several sessions. `sessions.jsonl` is **not** joinable to `runs.jsonl` on anything but time. Do not attribute a session's tokens to a single run.
|
|
304
360
|
|
|
@@ -603,6 +659,10 @@ The stream is for scripts. The owner reads `docs/<App>-Misses.md`: one row per `
|
|
|
603
659
|
|
|
604
660
|
`tf-emit.sh` rebuilds the file and its HTML through `tf-misses-md.py` after **every** write to the misses stream, whichever door wrote it (log-miss, triage, fix-close, a hand-written record, an amend), so the file is never older than the record. It is **derived**: nothing reads it, nobody edits it, and a wrong row is corrected by a new record. A project whose stream predates the script gets the file by running `bash .tfcore/utils/tf-misses-md.sh` once. The check for FR-31 is in `tests/bugs/run.sh`: the row count equals the record count.
|
|
605
661
|
|
|
662
|
+
### 5.5.11 `kind: "miss-void"` — withdrawing a miss that should never have been written (added 2026-09-22, TrBlazeUI TF-001)
|
|
663
|
+
|
|
664
|
+
§2.7's `run-void`, for this stream. Fields: `miss_id` (the miss withdrawn) and `reason`. Written only by `bash .tfcore/utils/tf-emit.sh --void-miss <miss_id> "<reason>"`, which refuses a miss not on the stream, a second void and an empty reason. Readers: the miss leaves every figure with its fixes and amends (`misses_voided_n`, `misses_voided`; an orphan is `miss_voids_orphaned_n`), `--open-miss` and `--open-misses` skip it, and `docs/<App>-Misses.md` lists it under *Withdrawn*. Proved by `tests/regression/run.sh tb_001`.
|
|
665
|
+
|
|
606
666
|
## 6. Provenance — three separations, one rule applied three times
|
|
607
667
|
|
|
608
668
|
**Data from different provenances never merges.**
|
|
@@ -90,13 +90,14 @@ detect_type() {
|
|
|
90
90
|
# docs: a markdown/spec repo — no source at all
|
|
91
91
|
[[ -z "$projects" ]] && { echo docs; return; }
|
|
92
92
|
|
|
93
|
+
# ($projects is never empty here, so xargs always has input: no GNU-only -r needed.)
|
|
93
94
|
# Packable = genuinely published. IsPackable is usually <IsPackable>false</IsPackable>
|
|
94
95
|
# on test projects, so only an explicit `true` counts.
|
|
95
|
-
packable="$(printf '%s\n' "$projects" | xargs
|
|
96
|
+
packable="$(printf '%s\n' "$projects" | xargs grep -lE \
|
|
96
97
|
'<(PackageId>|GeneratePackageOnBuild>[[:space:]]*true|IsPackable>[[:space:]]*true)' 2>/dev/null)"
|
|
97
98
|
|
|
98
99
|
# A shipped executable/hostable head: console/desktop (Exe), MAUI, or ASP.NET/Blazor host.
|
|
99
|
-
heads="$(printf '%s\n' "$projects" | xargs
|
|
100
|
+
heads="$(printf '%s\n' "$projects" | xargs grep -lE \
|
|
100
101
|
'<OutputType>[[:space:]]*Exe|<UseMaui>[[:space:]]*true|Sdk="Microsoft\.NET\.Sdk\.(Web|BlazorWebAssembly)"' 2>/dev/null)"
|
|
101
102
|
|
|
102
103
|
# THE DECIDING TEST: is a packaged project the product, or merely a helper?
|