From b77c2ce3046c74b96f2ff69d189c7d5e501fe1b8 Mon Sep 17 00:00:00 2001 From: S Ravi Kumar Date: Sat, 5 Sep 2026 01:18:27 +0530 Subject: [PATCH 1/3] Session 3 Complete of Reset --- .../TechieFlow/tasks/_status-update-gate.md | 1 + .../TechieFlow/tasks/day1-brownfield.md | 239 ++---- .../TechieFlow/tasks/day1-greenfield.md | 64 +- .claude/settings.json | 4 + .opencode/plugin/techieflow.js | 2 +- .tfcore/core-config.yaml | 5 + .tfcore/hooks/guard-status.sh | 33 +- .tfcore/standards/coding-standards-core.md | 61 ++ .tfcore/standards/coding-standards-dotnet.md | 93 +++ .tfcore/tasks/_status-update-gate.md | 1 + .tfcore/tasks/day1-brownfield.md | 208 +---- .tfcore/tasks/day1-greenfield.md | 27 +- .tfcore/templates/stack-defaults/dotnet.md | 35 + .tfcore/templates/stack-questions.md | 77 ++ .../v4custom/app-architecture-tmpl.md | 220 +++-- .tfcore/templates/v4custom/app-brd-tmpl.md | 285 +++---- .../templates/v4custom/app-checklist-tmpl.md | 118 +-- .../v4custom/app-coding-standards-tmpl.md | 52 ++ .../templates/v4custom/app-devguide-tmpl.md | 211 ++--- .../v4custom/app-productguide-tmpl.md | 73 +- .../v4custom/app-project-status-tmpl.md | 136 ++- .../templates/v4custom/app-uidesign-tmpl.md | 96 ++- .../templates/v4custom/app-usageguide-tmpl.md | 122 +-- .../__pycache__/tf-doc-check.cpython-312.pyc | Bin 0 -> 57216 bytes .tfcore/utils/tf-doc-check.py | 784 ++++++++++++++++++ .tfcore/utils/tf-doc-check.sh | 29 + docs/TechieFlow-Document-Schemas.html | 630 ++++++++++++++ docs/TechieFlow-Document-Schemas.md | 333 ++++++++ docs/TechieFlow-Reset-Plan-2026-09-04.html | 6 +- docs/TechieFlow-Reset-Plan-2026-09-04.md | 5 +- docs/metrics/commits.jsonl | 1 + docs/metrics/misses.jsonl | 3 + docs/metrics/runs.jsonl | 1 + docs/metrics/sessions.jsonl | 2 + scaffold-brownfield.sh | 4 + scaffold-greenfield.sh | 4 + .../doc-check/fx-bad/PROJECT-STATUS.md | 50 ++ .../fx-bad/docs/MyDiary-Architecture.md | 66 ++ .../doc-check/fx-bad/docs/MyDiary-BRD.md | 58 ++ .../fx-bad/docs/MyDiary-Checklist.md | 40 + .../fx-bad/docs/MyDiary-Coding-Standards.md | 29 + .../doc-check/fx-bad/docs/MyDiary-DevGuide.md | 39 + .../fx-bad/docs/MyDiary-ProductGuide.md | 28 + .../doc-check/fx-bad/docs/MyDiary-UIDesign.md | 52 ++ .../fx-bad/docs/MyDiary-UsageGuide.md | 44 + .../fx-bad/docs/mockups/entries.html | 1 + .../doc-check/fx-bad/docs/mockups/login.html | 1 + .../docs/screenshots/MyDiary/entries.png | 1 + .../fx-bad/docs/screenshots/MyDiary/login.png | 1 + .../doc-check/fx-good/PROJECT-STATUS.md | 54 ++ .../fx-good/docs/MyDiary-Architecture.md | 62 ++ .../doc-check/fx-good/docs/MyDiary-BRD.md | 62 ++ .../fx-good/docs/MyDiary-Checklist.md | 36 + .../fx-good/docs/MyDiary-Coding-Standards.md | 29 + .../fx-good/docs/MyDiary-DevGuide.md | 42 + .../fx-good/docs/MyDiary-ProductGuide.md | 28 + .../fx-good/docs/MyDiary-UIDesign.md | 52 ++ .../fx-good/docs/MyDiary-UsageGuide.md | 44 + .../fx-good/docs/mockups/entries.html | 1 + .../doc-check/fx-good/docs/mockups/login.html | 1 + .../docs/screenshots/MyDiary/entries.png | 1 + .../docs/screenshots/MyDiary/login.png | 1 + tests/doc-check/make-fixtures.py | 483 +++++++++++ tests/doc-check/run.sh | 19 + update-framework.sh | 5 + 65 files changed, 4183 insertions(+), 1112 deletions(-) create mode 100644 .tfcore/standards/coding-standards-core.md create mode 100644 .tfcore/standards/coding-standards-dotnet.md create mode 100644 .tfcore/templates/stack-defaults/dotnet.md create mode 100644 .tfcore/templates/stack-questions.md create mode 100644 .tfcore/templates/v4custom/app-coding-standards-tmpl.md create mode 100644 .tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc create mode 100644 .tfcore/utils/tf-doc-check.py create mode 100644 .tfcore/utils/tf-doc-check.sh create mode 100644 docs/TechieFlow-Document-Schemas.html create mode 100644 docs/TechieFlow-Document-Schemas.md create mode 100644 tests/.artifacts/doc-check/fx-bad/PROJECT-STATUS.md create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Architecture.md create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/MyDiary-BRD.md create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Checklist.md create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Coding-Standards.md create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/MyDiary-DevGuide.md create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/MyDiary-ProductGuide.md create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/MyDiary-UIDesign.md create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/MyDiary-UsageGuide.md create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/mockups/entries.html create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/mockups/login.html create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/screenshots/MyDiary/entries.png create mode 100644 tests/.artifacts/doc-check/fx-bad/docs/screenshots/MyDiary/login.png create mode 100644 tests/.artifacts/doc-check/fx-good/PROJECT-STATUS.md create mode 100644 tests/.artifacts/doc-check/fx-good/docs/MyDiary-Architecture.md create mode 100644 tests/.artifacts/doc-check/fx-good/docs/MyDiary-BRD.md create mode 100644 tests/.artifacts/doc-check/fx-good/docs/MyDiary-Checklist.md create mode 100644 tests/.artifacts/doc-check/fx-good/docs/MyDiary-Coding-Standards.md create mode 100644 tests/.artifacts/doc-check/fx-good/docs/MyDiary-DevGuide.md create mode 100644 tests/.artifacts/doc-check/fx-good/docs/MyDiary-ProductGuide.md create mode 100644 tests/.artifacts/doc-check/fx-good/docs/MyDiary-UIDesign.md create mode 100644 tests/.artifacts/doc-check/fx-good/docs/MyDiary-UsageGuide.md create mode 100644 tests/.artifacts/doc-check/fx-good/docs/mockups/entries.html create mode 100644 tests/.artifacts/doc-check/fx-good/docs/mockups/login.html create mode 100644 tests/.artifacts/doc-check/fx-good/docs/screenshots/MyDiary/entries.png create mode 100644 tests/.artifacts/doc-check/fx-good/docs/screenshots/MyDiary/login.png create mode 100644 tests/doc-check/make-fixtures.py create mode 100644 tests/doc-check/run.sh diff --git a/.claude/commands/TechieFlow/tasks/_status-update-gate.md b/.claude/commands/TechieFlow/tasks/_status-update-gate.md index 0095fb6..83975b2 100644 --- a/.claude/commands/TechieFlow/tasks/_status-update-gate.md +++ b/.claude/commands/TechieFlow/tasks/_status-update-gate.md @@ -65,6 +65,7 @@ Before you record a framework file as missing — in a verdict, a checklist Rema **"Next command = `*build-phase` for the remaining REQs" is NOT a way to end a build pass early (build-phase §2b, owner rule 2026-08-21).** If you are writing this gate at the end of a `*build-phase` run and the reason some REQs are still `Planned` is that *this pass did not get to them*, you are not at the gate — go back and build them (fan out more sub-agents). The build-leads bullet above describes a state found on re-entry (a crashed session, an owner-gated or `Blocked` remainder, FIX mode after verifier failures), never a pass that chose to stop. Under YOLO / goal mode (`_yolo-mode.md`) there is nobody to "run it again". 6. A new **Verification log** row whose "Status table" column links to the checklist that holds the per-REQ detail (`docs/{AppName}-Checklist.md#requirements-status`) — NOT a dated `docs/qa/*.md` file (those no longer exist). 7. Library-feedback counts + standards-compliance lines refreshed if the phase touched them. +7b. **Document check (added 2026-09-04, reset Session 3):** run `bash .tfcore/utils/tf-doc-check.sh --app {AppName}` on the documents this command wrote (PROJECT-STATUS.md is always included). Every template carries a schema block (required sections in order, word budgets by app size, row rules); the script prints one line per problem. A `FAIL` line means the phase is NOT closed: fix the document and re-run until only `WARN` or `OK` remain. Never edit the schema to make a document pass. 8. **HTML refresh (MANDATORY — every time, same turn you edit the `.md`):** re-render `PROJECT-STATUS.html` from the markdown you just wrote — **one command, `bash .tfcore/utils/tf-render-html.sh PROJECT-STATUS.md`** (added 2026-08-27; never hand-author the HTML, never bash-heredoc it — see `.tfcore/tasks/generate-html.md`). This is not optional cleanup and not a "later" step — the owner reads the `.html`, so a markdown-only update is an **incomplete** update that fails this gate. If you edited `PROJECT-STATUS.md` you re-render `PROJECT-STATUS.html` in the same turn, full stop. **The harness enforces this MECHANICALLY** (added 2026-08-25, same treatment as the git ban and the status shape): the `.tfcore/hooks/guard-status-html.sh` **Stop** hook refuses to end your turn while `PROJECT-STATUS.html` is older than `PROJECT-STATUS.md`, or missing (Claude Code and Codex block the stop; OpenCode, which has no blocking Stop hook, sends the same message back into the session as a follow-up prompt when it idles). A blocked stop means *the render is genuinely outstanding* — re-render it, do not look for a way around the hook. **Do NOT render the checklists to HTML** — they are AI-agent working documents kept in markdown only (the per-REQ Requirements Status table is the agent's source of truth, not a human HTML page). 9. **BRD §4 Development status rollup** (keeps the human BRD snapshot tracking reality). If `docs/{AppName}-BRD.md` has a `## … Development status` section, refresh it from the checklists: - One row per feature (each §"Feature catalog" `### F-…` entry). Roll its owned REQs up to a feature-level status: feature → its `Requirements: BRD-…` line → the `REQ-*` those BRDs split into → the per-REQ Status in the checklist tables. diff --git a/.claude/commands/TechieFlow/tasks/day1-brownfield.md b/.claude/commands/TechieFlow/tasks/day1-brownfield.md index baf22be..3d9f666 100644 --- a/.claude/commands/TechieFlow/tasks/day1-brownfield.md +++ b/.claude/commands/TechieFlow/tasks/day1-brownfield.md @@ -8,7 +8,7 @@ Replace the multi-step paste-and-substitute prompt with a single command: `*day1 ## elicit -elicit=false — this task runs autonomously end-to-end. It asks AT MOST TWO questions (app name if missing, then optional source-doc hints), then drafts every artifact (including the full BRD) in bulk and presents them for ONE-shot review at the end. NO per-section confirmation. NO per-requirement confirmation. The user reviews the written docs and edits the files directly, or replies with bulk changes. +elicit=false — this task runs autonomously end-to-end. It asks AT MOST THREE questions (app name if missing, optional source-doc hints, then the size confirmation in §1), then drafts every artifact (including the full BRD) in bulk and presents them for ONE-shot review at the end. NO per-section confirmation. NO per-requirement confirmation. The user reviews the written docs and edits the files directly, or replies with bulk changes. This is a deliberate departure from TechieFlow's standard `author-brd` per-item elicitation — the user has explicitly opted into a low-friction flow for a one-person team. @@ -43,6 +43,7 @@ This is a deliberate departure from TechieFlow's standard `author-brd` per-item - docs/{AppName}-Coding-Standards.md - docs/{AppName}-Architecture.md ``` +- **Size and kind (reset Session 3, 2026-09-04):** count the routed pages in the code (every `@page` route or equivalent counts, sign-in included; dialogs and tabs are regions of a page) and the roles, then confirm once: "Size: Small (up to 10 screens, one role, 50 requirements), Medium (up to 20 screens, 100 requirements) or Large (split into phases)? I count {N} screens and {N} roles, so I propose {X}." Kind is `app`, or `library` for a component or service library. Write `appSize:` and `appKind:` into `.tfcore/core-config.yaml` in the same write and carry both into every document header. The size sets the document budgets and the requirement cap that `bash .tfcore/utils/tf-doc-check.sh` enforces at the status gate. ### 1.5. Discovery hints — harvest existing docs before inferring anything @@ -84,21 +85,20 @@ Net effect: `docs/` always contains exactly one current version of each doc unde - Status field: "Current" (this is brownfield). - **Source-doc harvesting (do this FIRST, before any inference):** - If `SourceDocs[]` from §1.5 contains anything that reads as architecture / design / system / data-flow material, harvest from it directly: copy structural prose verbatim (with attribution), pull diagram intent, map their components to your §4 module table. Do not re-invent what the user already wrote. - - At the end of the doc, add a `## Sources harvested` section listing each source file that contributed content. + - Attribute harvested content inline; there is no "Sources harvested" section (the run record carries the list). - **Apply `CustomInstructions`** from §1.5 throughout — if the user said "stack is .NET 8", use .NET 8 in §1; if they said "ignore `legacy/`", skip that folder in the scan. -- Populate remaining sections by SCANNING the codebase (only for what the source docs didn't cover): - - **§1 Tech stack:** read `.csproj`/`.sln` files, package references, target frameworks. Note TrBlazeUI / TechieRag presence (look in package references AND in `.claude/` for deployed agent files). - - **§2 Component map:** scan `src/` (or `source/`) for projects; build a Mermaid `flowchart TB` showing project-level dependencies inferred from `` and `using` directives. - - **§3 Data flow:** if you can identify a primary request path (e.g. controller → service → repo), diagram it as a `sequenceDiagram`. Otherwise leave the template placeholder and add a note "data flow unknown from static analysis — populate after first feature pass." - - **§4 Module responsibilities:** one row per project under `src/`. Responsibility = one-line summary derived from top-of-namespace XML doc or README mentions. - - **§5 Cross-cutting:** detect logging library (Serilog/ILogger), auth scheme (JWT/cookies/Identity), telemetry (OTel/AppInsights) by package references. - - **§6 Deployment:** if `.github/workflows/`, `Dockerfile`, `azure-pipelines.yml` exist, derive the path. Otherwise mark "no CI/CD detected; manual deploy." - - **§7 ADRs:** seed with `ADR-001 — current stack as-is (reverse-doc baseline).` and any obvious decisions visible in README. - - **§8 Target architecture:** leave blank unless the BRD (§3 below) calls out a structural change. - - **§9 Open questions:** include "field-prefix drift" detection (see below) and any TODOs / FIXMEs that look architectural. -- **Depth mandate (Architecture is a HUMAN document too):** apply the same information-preservation rule as the BRD (§3) — source-doc architecture content carries forward, never gets summarized into a stub. Each module in §4 with non-trivial behavior gets a short prose paragraph (not just a table row), and any significant runtime flow beyond the primary path (background jobs, ingestion pipelines, auth handshakes, external-API round-trips) gets its own `sequenceDiagram` or `flowchart` in the relevant section. A reader skimming only the diagrams should grasp how the system hangs together. -- **Field-prefix drift detection:** scan `src/`, `source/`, or any `.cs` files for instance-field declarations and note the dominant style (`obj`-prefixed vs bare PascalCase vs `_underscore`/mixed) — §4 uses this to pick the project's field convention. If no style reaches ~80% dominance, add to §9 Open questions: "Standards drift detected — mixed instance-field naming (N obj / M bare / K underscore). §4 picked {chosen}; remediation happens incrementally during implementation." -- **Table of Contents:** the template at `.tfcore/templates/v4custom/app-architecture-tmpl.md` includes a `## Table of Contents` section. After populating the rest of the doc, regenerate that section to match the actual H2 headings you wrote (drop entries for sections that ended up empty, add entries for any new sections). Use the slug rule from `.tfcore/templates/v4custom/html-render-shell.md §1` — same slug for the link in the TOC and the `id` the renderer will assign. Broken TOC links are a known recurring bug; don't be the next instance. +- Populate the template's sections, in its order, by SCANNING the codebase (only for what the source docs didn't cover; reset Session 3, 2026-09-04 — `tf-doc-check.sh` refuses any other shape): + - **Stack decisions:** one row per stack question, answered from the project files (`.csproj`/`.sln`, package references, target frameworks, configuration files) and the Stack answer set where the code agrees with it; cite the source per row. + - **Solution structure:** one row per project with its kind (web app, class library, test project, migrations project) and purpose. + - **Component map:** a `flowchart TB` of project-level dependencies from `` and `using` directives, then the "How a request travels" numbered list in words for the primary path (controller → service → data access). No sequence diagram; per-screen detail goes to the DevGuide (§7.6). + - **Data model:** a mermaid `erDiagram` and an entity table from the migrations project, entity classes or schema scripts. + - **Cross-cutting:** logging library, auth scheme, configuration mechanism, error handling — from package references and startup code. + - **Decisions log:** a first row `current stack as-is (reverse-doc baseline)`, Status `decided`; any structural change the BRD (§3) calls out is a row with Status `planned` naming its BRD item. There is no Target architecture section. + - **Module responsibilities** (required for Medium and Large): one row per project, from top-of-namespace docs or README. + - **Open questions:** the field-prefix drift finding (below) and any TODOs / FIXMEs that look architectural. No Deployment section: hosting is decided after UAT. +- Source-doc architecture content carries forward into these sections, attributed, never summarised away. +- **Field-prefix drift detection:** scan `src/`, `source/`, or any `.cs` files for instance-field declarations and note the dominant style (`obj`-prefixed vs bare PascalCase vs `_underscore`/mixed) — §4 uses this to pick the project's field convention. If no style reaches ~80% dominance, add to Open questions: "Standards drift detected — mixed instance-field naming (N obj / M bare / K underscore). §4 picked {chosen}; remediation happens incrementally during implementation." +- Mermaid: quote every label; never use `end` as a node id. No Table of Contents (the renderer builds it). - Write the populated doc to `docs/{AppName}-Architecture.md`. ### 3. Draft the FULL BRD in one pass → `docs/{AppName}-BRD.md` @@ -115,22 +115,10 @@ This is the friction-removal step. **Do NOT run `author-brd`. Do NOT prompt the - **INFORMATION-PRESERVATION RULE (hard requirement when SourceDocs exist):** the new BRD must be a SUPERSET of the requirements content in the harvested source docs — never a summary of it. Concretely: - Every table, matrix, screen inventory, route list, license/feature matrix, navigation-menu tree, persona-detail block, and per-feature workflow in a source BRD/spec **carries forward** into the new doc (updated where stale, attributed where copied) — it does NOT get compressed into a one-liner. - **Length sanity check before writing:** if the source docs' requirements content totals X lines and your draft (excluding boilerplate) is under ~60% of X, you compressed — go back and restore the detail. A 1,000-line source BRD should never produce a 250-line replacement. - - One-line statements are allowed ONLY in the §10 BRD ledger. Everything else is full prose, tables, and diagrams — this is a HUMAN document read as rendered HTML; the coding agents get their compact view later from `*split-brd` / the Checklist. -- **§4 Development status (brownfield: the reader's first question — "what's built, what's pending?"):** fill the §4 table with ONE row per §9 feature-catalog F-code. Derive each row's Status / % / Phase / Notes from the **strongest evidence available**, in priority order: (1) a migrated dev/phase plan (§3.5) — carry its phase + completion verbatim; (2) the code scan from §2 — a feature whose screens/handlers actually compile and exist is `Done`, partially-present is `Partial`, absent is `Planned`; (3) source-doc status notes. Set the "Snapshot as of" date to today. This is a feature-level SUMMARY only — do NOT restate per-REQ status (that's PROJECT-STATUS + the checklists). Keep it consistent with §3.5's migrated statuses and with PROJECT-STATUS. -- **§9 Feature catalog (the heart of the doc):** one `### F-{CODE}: {Name}` subsection per feature/capability area found in the source docs and the codebase. Per feature: personas + phase, 1-2 paragraphs of what/why, a screens & routes table, a numbered workflow (inputs → outputs), and the owning BRD-N IDs. If a source doc already has a feature catalog, preserve its feature codes and per-feature detail. Depth scales with the app (8–25 features is normal) — there is NO cap. Every F-code MUST also appear as a row in the §4 Development status table. -- For §10 Functional requirements ledger: walk the feature catalog and emit `BRD-1`, `BRD-2`, … as one-line ` can ` or `system shall ` statements, each tagged with its catalog feature `(F-CODE)`. Number monotonically. **One BRD per discrete capability — the count scales with the app (20–60 is normal for a real product); NEVER merge capabilities to keep the count low.** If a BRD came directly from a source doc, suffix the line with ``. -- For §11 Non-functional: cover performance, security, accessibility, scalability, reliability based on visible NFR signals (auth scheme, target framework, any `aria-` attrs in Razor). Where concrete targets exist (latency, uptime, concurrency), present them as a target table, not buried in prose. **ALWAYS include the standing Observability NFR: Serilog file-based logging in every executable head** — if the §5 cross-cutting scan found Serilog (or an equivalent structured file-logging stack) already wired, record it as met/`Done (pre-existing)`; if the app logs only to console or not at all, add the NFR as `Planned` so it becomes a `REQ-NFR-*` row and gets built (recipe: coding-standards §Logging). -- **Mermaid mandate:** §6, §7, §8 (context, journey, component) are the MINIMUM — build them from §2's component map (copy verbatim if identical). Additionally, every feature-catalog entry with a multi-step or multi-actor flow gets its own diagram (`flowchart` or `sequenceDiagram`). Target: a reader skimming only the diagrams should grasp how the app works. Simple CRUD features may skip the diagram. **Every diagram MUST follow the authoring rules in `.tfcore/templates/v4custom/html-render-shell.md §5.5` — quote every node/edge/subgraph label and never use `end` as a node id; unquoted special characters in flowchart labels are the #1 cause of broken diagrams in the rendered HTML.** -- Append a footer: - ``` - --- - Last updated: {YYYY-MM-DD} - Highest BRD ID: BRD-{N} - Sources harvested: {comma-separated list of SourceDocs paths, or "none — drafted from reverse-doc"} - Custom instructions applied: {one-line summary of CustomInstructions, or "none"} - Drafted from reverse-doc — review and edit. New BRDs may be added (append-only); do not renumber. - ``` -- **Table of Contents:** the template includes a `## Table of Contents` section. Regenerate it after populating the doc so it matches the actual H2 headings, and list each `### F-…` feature-catalog entry as an H3 sub-entry under "Feature catalog". Use the slug rule from `.tfcore/templates/v4custom/html-render-shell.md §1`. + - Source requirements become BRD items; the Requirements ledger is where one-line statements live, and each item still carries its screen, mockup link and acceptance line. +- **Fill the template's sections in its order** (reset Session 3, 2026-09-04; `tf-doc-check.sh` refuses any other shape). Header: App, Kind, Size, Stack answer set, Status, Date. **Summary** at most 200 words. **Scope**. **Users and roles**. **Screens and flow** — one table row per routed page found in the code (screen, route, role, mockup link where a mockup exists, fields); a dialog is a row under its parent screen with `on /route` in the Route column; then the primary journey as a numbered list. **Requirements** — one `**BRD-N**` item per thing the verifier will test, each naming its screen, linking its mockup, and carrying one acceptance line "When on , then "; ids append-only, never renumbered; a source-doc item is attributed inline. **Non-functional requirements** table: the `perf-budget:` measure only where the owner stated a number; the logging requirement from the Stack answer set always, recorded as met when the §2 scan found it wired. **Development status** — one row per screen with Verified / Open counts from the strongest evidence (a migrated plan first, then the code scan); the status gate maintains it afterwards. Context diagram, Constraints and assumptions and Risks are required for Medium and Large only. No Feature catalog, no Table of Contents, no footer. +- **The requirement count stays within the size cap** (Small 50, Medium 100). If the code holds more, propose a phase split — each phase its own BRD, checklist and build — rather than merging requirements or growing the document. +- Mermaid: quote every label; never use `end` as a node id. - Write the populated doc to `docs/{AppName}-BRD.md`. ### 3.5. Migrate an existing development/phase plan → split requirement docs (CONDITIONAL) @@ -149,167 +137,60 @@ This is the friction-removal step. **Do NOT run `author-brd`. Do NOT prompt the - Partial items (e.g. "50% Scaffolded") → Status `In Progress` or `PARTIAL`, carry the plan's `%` and remark verbatim. - Not-yet-started items → Status `Not Started`, `0%`. - Add a header note to both docs: `> Migrated from {plan-file} on {YYYY-MM-DD}. Phase structure, completion %, and status remarks carried over verbatim — verify before building.` -- **Keep the BRD §4 Development status table consistent with this migration:** the feature-level rows in BRD §4 must agree with the per-REQ statuses you just wrote (a feature whose REQs are all `Done (pre-existing)` → `Done` in §4; mixed → `Partial`; none started → `Planned`). The checklist is the live per-REQ truth; BRD §4 is the human feature-level snapshot of the same reality. +- **Keep the BRD Development status table consistent with this migration:** its per-screen rows must agree with the per-REQ statuses you just wrote (all `Done (pre-existing)` → `Done`; mixed → `Partial`; none started → `Planned`). The checklist is the live per-REQ truth; the BRD table is the human snapshot of the same reality. - Do NOT modify the dev-plan file's content. After migration it is superseded — move it to `docs/OldDocs/` per §1.6 and say so in the §8 summary. - If the `*split-brd` artifact (`docs/{AppName}-Checklist.md`) already exists (re-run scenario), apply §1.6: archive the old one to `docs/OldDocs/`, write fresh at the canonical name. No questions. ### 4. Create the Coding Standards → `docs/{AppName}-Coding-Standards.md` -Write the file with the exact content below, substituting `{AppName}` in the title only. - -**ONE per-project decision first — the instance-field prefix.** The shared rules (no underscores, `a` params, `v` locals, PascalCase everywhere) are fixed; the instance-field convention is decided per project (existing samples: AppManager = `obj` prefix, AstroLyfe = bare PascalCase no-prefix): -- If the existing code has a clear dominant style (≥80% of instance fields `obj`-prefixed OR ≥80% bare PascalCase), adopt that style. -- If `{Hints}` / CustomInstructions specify one, that wins. -- Otherwise default to `obj`. -Record the decision in the table below (swap the Instance-fields row to `PascalCase, no prefix — e.g. private readonly ILogger Logger;` if no-prefix won), in the §"Enforcement" greps (the missing-obj-prefix grep only applies to obj-style projects), and in CLAUDE.md (§7). - -```markdown -# {AppName} Coding Standards - -**Last Updated:** {today YYYY-MM-DD} -**Status:** Authoritative for all code under `src/`/`source/` and `tests/`. Conformance enforced via repo-root `.editorconfig` + verifier grep checks in §"Enforcement". - -## Database Naming Conventions - -### Tables and Columns -- PascalCase: `CustomerOrder` NOT `customer_order` -- Singular: `CustomerOrder` NOT `CustomerOrders` -- **NEVER use underscores** in any DB object name -- FK columns: `{TableName}Id` (e.g., `CustomerId`) -- PK: `{TableName}Id` (e.g., `UserId`) - -### Stored Procedures & Functions -- PascalCase verb prefix: `GetCustomerOrders`, `InsertOrder`, `CalculateTotal` -- Action prefixes: Get / Insert / Update / Delete / Calculate - -### Indexes & Constraints -- Index: `IX{Table}{Column}` · PK: `Pk{Table}` · FK: `Fk{Table}{Ref}` · Unique: `Uc{Table}{Column}` - -## C# Conventions - -### Classes & Interfaces -- PascalCase for classes; `I` prefix for interfaces; descriptive names. -- Async methods end with `Async`. - -### Fields, Parameters, Locals - -**NEVER use underscores** anywhere in any identifier. - -| Kind | Convention | Example | -|------|-----------|---------| -| **Instance fields** | `obj` prefix + PascalCase tail (no underscores) | `private readonly ILogger objLogger;`
`private readonly HttpClient objHttpClient;`
`private string objCachedPublicKey;` | -| **Static / `const` fields** | PascalCase, no prefix | `private const string CachePrefix = "…";` | -| **Method parameters** | `a` prefix + PascalCase | `LoginAsync(string aEmail, string aPassword)` | -| **Local variables** | `v` prefix + PascalCase | `var vResponse = await …` | -| **Booleans** | same prefix + `Is`/`Has`/`Can` | `IsAuthenticated`, `vIsValid`, `aHasAccess` | -| **Properties** | PascalCase, no prefix | `public string ConnectionString { get; set; }` | -| **Constants** | PascalCase, no underscores | `MaxRetryCount` NOT `MAX_RETRY_COUNT` | -| **Test methods** | Short PascalCase, no underscores — full scenario in XML `` | `LoginRejectsBadPassword` not `Login_BadPassword_ReturnsUnauthorized` | - -**Rejected forms:** `_underscore` field prefixes, snake_case anywhere, Hungarian prefixes (`strName`), underscores in test method names. +Load `.tfcore/templates/v4custom/app-coding-standards-tmpl.md` (reset Session 3, 2026-09-04: the standards themselves now live in the framework and are not copied per project). -### Controller-action parameters -The `a`-prefix applies uniformly to `[FromRoute]`/`[FromQuery]`/`[FromBody]`. Parameter name flows through to OpenAPI. Body DTO **property** names stay PascalCase no prefix; only the parameter symbol holding the deserialized DTO gets the `a` prefix. +- The standards are `.tfcore/standards/coding-standards-core.md` (every project) plus `.tfcore/standards/coding-standards-.md` for the Stack answer set named in the Architecture (`dotnet` for the .NET set). List both under "Standards applied". +- **One per-project choice for .NET — the instance-field prefix.** Use the drift scan from §2: if ≥80% of instance fields are `obj`-prefixed or ≥80% bare PascalCase, adopt that style; `{Hints}` / CustomInstructions override; otherwise default to `obj`. Record it in the "Standards applied" choices table and in CLAUDE.md (§7). +- "Project rules" holds only rules that are true of this project alone (a mixed MAUI build invocation is the kind of thing that belongs here). Empty is a valid answer. +- "Enforcement" names the `.editorconfig` (§5), the analyzers in use, and any project-specific grep beyond the stack file's. +- Run `bash .tfcore/utils/tf-doc-check.sh docs/{AppName}-Coding-Standards.md`; fix any FAIL. -### Environment Variables -**PascalCase, no separators.** `{AppName}BaseUrl` NOT `APPNAME_BASE_URL` and NOT `AppName__BaseUrl`. Use a custom configuration provider mapping PascalCase env vars → `:`-nested config paths. Read via `IConfiguration["Section:Key"]` only — never `Environment.GetEnvironmentVariable(...)`. - -### Project & solution naming — the primary head carries the PRODUCT name -- The product's **primary executable head** project is named exactly `{AppName}` — `src/{AppName}/{AppName}.csproj`. A single-head product's one head IS `{AppName}`. -- **`{AppName}.App` is BANNED** (owner rule 2026-07-10): "App" says nothing — the product name already names the app. Never scaffold it; if the codebase has one, log a rename REQ (dir + `.csproj` + sln entry + namespaces) in the checklist instead of propagating the name. -- Secondary heads of a multi-head product take a **descriptive** dotted suffix: `{AppName}.Api`, `{AppName}.Desktop`, `{AppName}.Cli`. Satellites keep their conventional names: `{AppName}.Core` (engine), `{AppName}UI` (RCL), `{AppName}.Core.Tests` / `{AppName}.Tests`. - -### File Structure -```csharp -using System; - -namespace {AppName}.Services.Example; - -public class DatabaseService -{ - private readonly ILogger objLogger; - private readonly IConfiguration objConfiguration; - - public DatabaseService(ILogger aLogger, IConfiguration aConfiguration) - { - objLogger = aLogger; - objConfiguration = aConfiguration; - } +### 5. Create `.editorconfig` at the repo root - public string ConnectionString { get; set; } +Copy `.tfcore/templates/v4custom/app-editorconfig-tmpl.editorconfig` verbatim to `.editorconfig` at the repo root. No substitution — the rules are identical across projects. - public async Task GetDataAsync(string aQueryName) - { - var vConnString = objConfiguration.GetConnectionString("Default"); - var vResult = await ExecuteQueryAsync(vConnString, aQueryName); - return vResult; - } -} -``` +### 5b. Close the `.gitignore` on the stack this repo is written in (MANDATORY) -### Best Practices -- One class per file. File name matches class. -- File-scoped namespaces. Nullable reference types enabled. -- Methods small (<20 lines). Single responsibility. -- Max 3 nesting levels. Early returns for validation. -- ConfigureAwait(false) in libraries. -- StringBuilder for loop concatenation. Dispose IDisposable. Cache expensive ops. - -### XML Documentation (MANDATORY on public members) -``, ``, ``, ``, `` — all required. - -### Testing -- Short PascalCase test name, no underscores. Full scenario in XML ``. -- Arrange-Act-Assert. One assertion per test where practical. - -### Security -- Never hardcode credentials. Parameterized queries. Validate inputs. Log security events. - -### Logging — Serilog file sink (MANDATORY, every .NET app type) -- **Every executable head gets Serilog with a rolling FILE sink — web (Blazor Server/WASM host), API, MAUI, WinForms/WPF desktop, console/CLI, background service. No exceptions, and never wait for the owner to ask.** -- Wire at startup, before anything else can fail: `Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Console().WriteTo.File("logs/{appname}-.log", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 14).CreateLogger();` then plug into DI (`builder.Services.AddSerilog()` / `builder.Host.UseSerilog()` for hosts; `builder.Logging.AddSerilog()` in `MauiProgram.CreateMauiApp`). Read overrides from `appsettings.json` (`Serilog` section) where the host has one. For MAUI/desktop, root the path in a writable per-app location (`FileSystem.AppDataDirectory` / `Environment.SpecialFolder.LocalApplicationData`), not the install dir. -- Log unhandled exceptions at the head boundary: `try/catch` + `Log.Fatal` around startup, `AppDomain.CurrentDomain.UnhandledException` / `TaskScheduler.UnobservedTaskException` handlers, and `Log.CloseAndFlush()` on exit. -- **Class libraries never reference Serilog** — they log through `ILogger` / `Microsoft.Extensions.Logging.Abstractions` only; the head's Serilog config picks those up automatically. -- App code logs through injected `ILogger` (structured message templates, e.g. `logger.LogInformation("Imported {Count} rows", n)`), not static `Log.*`, outside the startup boundary. -- The `logs/` output folder is gitignored (the owner adds it — agents never run git). -- Brownfield: an app already on a working structured file-logging stack (e.g. NLog-to-file) is compliant — record the stack in this section; new heads added to it still use Serilog. - -### MAUI UI testability — stable AutomationId (MAUI apps only) -- Every interactive or data-bound control the verifier must reach (buttons, entries, pickers, list/collection views, key labels/values) carries a stable, unique **`AutomationId`** — the native analogue of a stable DOM id for Playwright. Without it Appium selectors drift and the runtime gates (`verify-phase §4a/§4b`) can't reliably find controls on the Android/iOS/Mac Catalyst heads. -- Name them by intent, not layout: `AutomationId="LoginSubmitButton"`, `AutomationId="ClientsGrid"`, `AutomationId="TotalBalanceValue"` — never positional (`Button2`). -- Set it on the control whose data the gate asserts (the grid/list itself, the value label), so "rows present AND non-empty" / "value not blank" maps to one addressable element. -- (Blazor screens use the equivalent `data-testid`/stable element ids for Playwright — same principle.) - -## Enforcement - -### .editorconfig (machine-checkable) -- File-scoped namespaces (`warning`) -- Async-method `Async` suffix (`warning`) -- `var` for locals (`warning`) -- Nullable reference types enabled -- No `_` prefix on private fields (`warning` via custom naming rule) - -### Verifier grep checks ```bash -# Forbidden underscore-prefix fields -grep -rE "private(\s+readonly)?\s+\w+\s+_[a-z]" src/ source/ 2>/dev/null - -# Forbidden test-method underscores -grep -rE "public\s+(async\s+)?Task\s+\w+_\w+\s*\(" tests/ - -# Field missing obj prefix -grep -rE "private(\s+readonly)?\s+\w+\s+(?!obj)[A-Z]\w+\s*[;=]" src/ source/ 2>/dev/null | grep -v "static\|const" -``` - -### Severity -- **Error**: file-scoped namespace, underscore field prefix -- **Warning**: nullable, async suffix -- **Info**: consider fixing +bash .tfcore/utils/tf-gitignore-audit.sh . --fix ``` -### 5. Create `.editorconfig` at the repo root - -Copy `.tfcore/templates/v4custom/app-editorconfig-tmpl.editorconfig` verbatim to `.editorconfig` at the repo root. No substitution — the rules are identical across projects. +**Run it here, in this step, and read the output.** The scaffold wrote a `.gitignore` +covering **TechieFlow's** artifacts — `.tfcore/`, `.claude/`, `node_modules/`, +`tests/.artifacts/`, `playwright-report/`, `logs/` — every section framework-managed +and labelled as such. It says **nothing** about the stack the project is written in. +On brownfield that stack is already on disk and you have just spent §2–§4 reading it, +so you are the step that knows the answer — and the file the scaffold left is +complete-looking enough to be read as finished. Existing repos are the likelier +offenders here, not the safer ones: the build output may already be committed. + +That is exactly how it went wrong once (TfLens TF-007, 2026-08-29): a repository whose +`core-config.yaml` and four `.csproj` files said .NET throughout carried an ignore file +with **no `bin/`, no `obj/`, and no rule of any kind for .NET**. The first build produced +output and one commit — named, with some irony, *"Updated git ignore"* — swept **1,041** +build-output files into the index; four later commits reached **1,962**. Those files carry +the static-web-assets manifest, whose content roots are **machine-absolute** (`/mnt/c/…` +after a WSL build, `C:\…` after a Windows one), so committing them ships one machine's +paths to another — a plausible route to precisely the asset 404 that TF-007 is about. + +**The agent that ran day-1 generated that file and did not read it. That agent was +responsible**, and the audit exists to make the mistake harder rather than to move the +blame — a generator's omission is not a defence for the agent operating the generator. + +Two outputs, and the second one is the one people miss: + +- **Missing rules** — `--fix` appends them under their own labelled header. Existing + owner content is never rewritten. +- **Build output that is ALREADY TRACKED** — reported, never fixed here. **A tracked file + is never ignored, whatever the ignore file says**, so adding the rule does nothing on + its own. The audit prints the exact `git rm -r --cached ` lines; **put them in + your §8 summary for the owner to run.** Agents never run git, in any mode. ### 6. Create `PROJECT-STATUS.md` at the repo root diff --git a/.claude/commands/TechieFlow/tasks/day1-greenfield.md b/.claude/commands/TechieFlow/tasks/day1-greenfield.md index 1f371d0..9c3ee84 100644 --- a/.claude/commands/TechieFlow/tasks/day1-greenfield.md +++ b/.claude/commands/TechieFlow/tasks/day1-greenfield.md @@ -8,7 +8,7 @@ Replace the multi-step paste-and-substitute prompt with a single command: `*day1 ## elicit -elicit=false (after at most a 3-question kickoff). The task asks ONLY for `{AppName}` (if missing), the concept (any length), and optional custom instructions / source-doc hints — then drafts every artifact (including the full BRD) in one pass and presents them for one-shot review. NO per-section confirmation. NO per-requirement confirmation. +elicit=false (after at most a 4-question kickoff). The task asks ONLY for `{AppName}` (if missing), the concept (any length), optional custom instructions / source-doc hints, and the app size (§1) — then drafts every artifact (including the full BRD) in one pass and presents them for one-shot review. NO per-section confirmation. NO per-requirement confirmation. ## Inputs @@ -28,6 +28,7 @@ elicit=false (after at most a 3-question kickoff). The task asks ONLY for `{AppN - Parse `{Hints}` the same way day1-brownfield §1.5 does: paths/globs → `SourceDocs[]` (Read each), instructions → `CustomInstructions` text blob, `none`/empty → both empty. - **Collision policy:** apply day1-brownfield §1.6 verbatim — every deliverable written fresh at its canonical name; any pre-existing version moves to `docs/OldDocs/` (created if missing, date-suffixed on collision); superseded source docs move there after harvesting; NEVER ask merge-vs-new; NEVER write `-v2`-style variants. - Update `.tfcore/core-config.yaml` with the `customTechnicalDocuments` paths AND the `devLoadAlwaysFiles` list exactly as in day1-brownfield §1. +- **Size and kind (reset Session 3, 2026-09-04):** from the concept, count the routed pages (every page with its own route counts, sign-in included; dialogs and tabs are regions of a page) and the roles, then ask once: "Size: Small (up to 10 screens, one role, 50 requirements), Medium (up to 20 screens, 100 requirements) or Large (split into phases)? I count {N} screens and {N} roles, so I propose {X}." Kind is `app` unless the concept is a library. Write `appSize:` and `appKind:` into `.tfcore/core-config.yaml` and carry both into every document header. The size sets the document budgets and the requirement cap that `bash .tfcore/utils/tf-doc-check.sh` enforces at the status gate. ### 2. Propose target architecture → `docs/{AppName}-Architecture.md` @@ -44,10 +45,9 @@ elicit=false (after at most a 3-question kickoff). The task asks ONLY for `{AppN - DB: SQLite for dev, configurable per env - Vector store: SqliteVec for dev (only if RAG/AI is implied by the concept) - Auth: cookie auth for MVP, JWT for API tier if API is in scope -- Populate Mermaid diagrams (component map, primary user journey, deployment) from the concept and any source docs. The richer the concept, the richer the diagrams — a 5-bullet feature list should produce a 5-component diagram, not a 2-box generic placeholder. -- **Depth mandate (Architecture is a HUMAN document):** if `SourceDocs[]` exist, apply the information-preservation rule — their architecture content carries forward, never gets summarized into a stub. Each non-trivial module in §4 gets a short prose paragraph (not just a table row), and any significant runtime flow beyond the primary path (background jobs, ingestion pipelines, auth handshakes, external-API round-trips) gets its own `sequenceDiagram` or `flowchart`. A reader skimming only the diagrams should grasp how the system hangs together. -- Seed §7 ADRs with: `ADR-001 — {chosen UI host}`, `ADR-002 — {chosen DB}`, `ADR-003 — {chosen vector store, if RAG}` — each with a one-line reason that cites the concept/CustomInstructions when relevant. -- **Table of Contents:** the template at `.tfcore/templates/v4custom/app-architecture-tmpl.md` ships with a `## Table of Contents` section. After drafting, regenerate that section to match the actual H2 headings you wrote. Use the slug rule from `.tfcore/templates/v4custom/html-render-shell.md §1` so the links work in both MD and the rendered HTML. +- **Fill the template's sections in its order** (reset Session 3, 2026-09-04; `tf-doc-check.sh` refuses any other shape): **Stack decisions** — one row per stack question, answered from the Stack answer set (`.tfcore/templates/stack-defaults/.md`) or the owner, citing the source; **Solution structure** — one row per project with its kind; **Component map** — one diagram plus the "How a request travels" numbered list in words (no sequence diagram; per-screen detail belongs to the DevGuide later); **Data model** — a mermaid `erDiagram` and an entity table; **Cross-cutting** — identity, configuration, logging, errors, one short paragraph each; **Decisions log** — a row each for the UI host, the database, and every package, with Why and Status. Module responsibilities is required for Medium and Large. No Deployment section (decided after UAT) and no Table of Contents (the renderer builds it). +- If `SourceDocs[]` exist, their architecture content carries forward into these sections, attributed, never summarised away. +- Mermaid: quote every label; never use `end` as a node id. - Write to `docs/{AppName}-Architecture.md`. Do NOT prompt the user mid-draft. ### 3. Draft the FULL BRD in one pass → `docs/{AppName}-BRD.md` @@ -62,13 +62,10 @@ elicit=false (after at most a 3-question kickoff). The task asks ONLY for `{AppN 4. **`CustomInstructions`** — apply throughout (scope limits, stack overrides, NFR additions). 5. Reasonable inference for sections still empty. Mark inferred items with `` HTML comments so the user can scan and confirm. - **INFORMATION-PRESERVATION RULE (when SourceDocs exist):** the BRD must be a SUPERSET of the requirements content in the harvested docs — never a summary. Tables, matrices, screen lists, persona detail, and per-feature workflows carry forward (updated, attributed), not compressed into one-liners. Length sanity check: a draft under ~60% of the source docs' requirements content means you compressed — go back and restore the detail. One-line statements are allowed ONLY in the §10 ledger; this is a HUMAN document read as rendered HTML (the coding agents get their compact view later from `*split-brd` / the Checklist). -- **§4 Development status (greenfield: this is the build ROADMAP):** fill the §4 table with one row per §9 feature-catalog F-code. Nothing is built yet, so every row is `Planned`, `0%`, with its target Phase (and a one-line Notes scope). Set the "Snapshot as of" date to today. As build phases complete later, the live status lives in PROJECT-STATUS + the checklists; this table stays the human roadmap view. -- **§9 Feature catalog (the heart of the doc):** one `### F-{CODE}: {Name}` subsection per feature/capability area implied by the concept and source docs. Per feature: personas + phase, 1-2 paragraphs of what/why, a screens & routes table (proposed, for greenfield), a numbered workflow (inputs → outputs), and the owning BRD-N IDs. Depth scales with the concept — a rich concept should yield 8–25 features; there is NO cap. Every F-code MUST also appear as a row in the §4 Development status table. -- For §10 Functional requirements ledger: walk the feature catalog and emit `BRD-1`, `BRD-2`, … as one-line ` can ` statements, each tagged `(F-CODE)`. **One BRD per discrete capability — the count scales with the concept; NEVER merge capabilities to keep the count low.** Suffix BRDs pulled directly from a source doc with ``. -- For §11 Non-functional: cover performance, security, accessibility, auth model — derived from the stack you chose in §2 and from any NFR signals in the concept or CustomInstructions. Present concrete targets (latency, uptime, concurrency) as a target table. **ALWAYS include the standing Observability NFR: Serilog file-based logging (rolling file sink under `logs/`) in EVERY executable head — web, API, MAUI, desktop, console, background service — no exceptions and no owner prompt needed** (the wiring recipe lives in the coding-standards §Logging block; `*split-brd` turns this BRD into a `REQ-NFR-*` row so the build phase implements it like any other requirement). -- **Mermaid mandate:** the three canonical diagrams (context, user journey, component sketch — copied from the architecture, adapted to BRD framing) are the MINIMUM. Every feature-catalog entry with a multi-step or multi-actor flow gets its own diagram. Simple CRUD features may skip it. **Every diagram MUST follow the authoring rules in `.tfcore/templates/v4custom/html-render-shell.md §5.5` — quote every node/edge/subgraph label and never use `end` as a node id; unquoted special characters in flowchart labels are the #1 cause of broken diagrams in the rendered HTML.** -- Append footer with `Highest BRD ID: BRD-{N}`, a `Sources harvested:` line, a `Custom instructions applied:` line, and the note: "First-pass draft from concept — review and edit. New BRDs may be added (append-only); do not renumber existing IDs." -- **Table of Contents:** the BRD template includes a `## Table of Contents` section. Regenerate it to match the actual H2 headings, and list each `### F-…` feature-catalog entry as an H3 sub-entry under "Feature catalog". Use the slug rule from `.tfcore/templates/v4custom/html-render-shell.md §1`. +- **Fill the template's sections in its order** (reset Session 3, 2026-09-04; `tf-doc-check.sh` refuses any other shape). Header: App, Kind, Size, Stack answer set, Status, Date. **Summary** at most 200 words. **Scope** in and out. **Users and roles** table. **Screens and flow** — one table row per routed page (screen, route, role, mockup link, fields); a dialog is a row under its parent screen with `on /route` in the Route column; then the primary journey as a numbered list. **Requirements** — one `**BRD-N**` item per thing the verifier will test, each naming its screen, linking its mockup, and carrying one acceptance line in the form "When on , then "; ids append-only, never renumbered. **Non-functional requirements** table: the `perf-budget:` measure only where the owner gave a number; the logging requirement from the Stack answer set always. **Development status** — one row per screen, all `Planned` for greenfield; the status gate maintains it afterwards. Context diagram, Constraints and assumptions and Risks are required for Medium and Large only. No Feature catalog, no Table of Contents, no footer. +- **The requirement count stays within the size cap** (Small 50, Medium 100). If the concept needs more, propose a phase split — each phase its own BRD, checklist and build — rather than merging requirements or growing the document. Never merge capabilities to fit. +- If `SourceDocs[]` exist, their requirements carry forward as BRD items (attributed inline), never summarised away. +- Mermaid: quote every label; never use `end` as a node id. ### 3.5. Migrate an existing development/phase plan → split requirement docs (CONDITIONAL) @@ -84,12 +81,49 @@ The mockups are part of the day-1 review (§8) — the owner approves them along ### 4. Create the Coding Standards → `docs/{AppName}-Coding-Standards.md` -Use the exact template content embedded in `day1-brownfield.md` §4 (the canonical block), including its one per-project decision: the instance-field prefix. Greenfield has no existing code to detect from, so default to `obj` unless `CustomInstructions` pick no-prefix. Record the decision in the standards file and CLAUDE.md per §4's instructions. +Load `.tfcore/templates/v4custom/app-coding-standards-tmpl.md` (reset Session 3, 2026-09-04: the standards live in the framework, `.tfcore/standards/coding-standards-core.md` plus `.tfcore/standards/coding-standards-.md` for the Stack answer set, and are not copied per project). Fill "Standards applied" with those two files and the per-project choices the stack file leaves open (for .NET: the instance-field prefix, default `obj` unless `CustomInstructions` pick no-prefix; record it in CLAUDE.md as before). Leave "Project rules" empty unless the concept demands a rule true of this project alone. Fill "Enforcement" from §5. Run `bash .tfcore/utils/tf-doc-check.sh docs/{AppName}-Coding-Standards.md`; fix any FAIL. ### 5. Create `.editorconfig` at repo root Copy `.tfcore/templates/v4custom/app-editorconfig-tmpl.editorconfig` verbatim. No substitution. +### 5b. Close the `.gitignore` on the stack you just chose (MANDATORY) + +```bash +bash .tfcore/utils/tf-gitignore-audit.sh . --fix +``` + +**Run it here, in this step, and read the output.** The scaffold wrote a `.gitignore` +covering **TechieFlow's** artifacts — `.tfcore/`, `.claude/`, `node_modules/`, +`tests/.artifacts/`, `playwright-report/`, `logs/` — every section framework-managed +and labelled as such. It says **nothing** about the stack, because at scaffold time +nobody had chosen one. **You just did.** You picked the stack, wrote it into +`core-config.yaml` and generated the solution; you are the only step that knows the +answer, and the file the scaffold left is complete-looking enough to be read as +finished. + +That is exactly how it went wrong once (TfLens TF-007, 2026-08-29): a repository whose +`core-config.yaml` and four `.csproj` files said .NET throughout carried an ignore file +with **no `bin/`, no `obj/`, and no rule of any kind for .NET**. The first build produced +output and one commit — named, with some irony, *"Updated git ignore"* — swept **1,041** +build-output files into the index; four later commits reached **1,962**. Those files carry +the static-web-assets manifest, whose content roots are **machine-absolute** (`/mnt/c/…` +after a WSL build, `C:\…` after a Windows one), so committing them ships one machine's +paths to another — a plausible route to precisely the asset 404 that TF-007 is about. + +**The agent that ran day-1 generated that file and did not read it. That agent was +responsible**, and the audit exists to make the mistake harder rather than to move the +blame — a generator's omission is not a defence for the agent operating the generator. + +Two outputs, and the second one is the one people miss: + +- **Missing rules** — `--fix` appends them under their own labelled header. Existing + owner content is never rewritten. +- **Build output that is ALREADY TRACKED** — reported, never fixed here. **A tracked file + is never ignored, whatever the ignore file says**, so adding the rule does nothing on + its own. The audit prints the exact `git rm -r --cached ` lines; **put them in + your §8 summary for the owner to run.** Agents never run git, in any mode. + ### 6. Create `PROJECT-STATUS.md` Load `.tfcore/templates/v4custom/app-project-status-tmpl.md` and substitute: @@ -171,7 +205,9 @@ Do NOT auto-advance past day-1 (no split/build without the user). Rendering HTML - [ ] core-config.yaml has customTechnicalDocuments for this app - [ ] `docs/{AppName}-Architecture.md` (status: Target) with Mermaid -- [ ] `docs/{AppName}-BRD.md` with a §4 Development status table (one row per F-code, all `Planned` for greenfield) + a populated §9 Feature catalog (one `### F-…` per feature) + §10 BRD-N ledger + Mermaid diagrams (canonical three + per-feature where non-trivial), every diagram passing the §5.5 authoring self-check (quoted labels, no `end` ids) +- [ ] `docs/{AppName}-BRD.md` in template shape: header with Size and Kind, Screens and flow table, BRD-N ledger with one "When …, then …" acceptance line per item, Development status table (one row per screen, all `Planned`) +- [ ] `appSize` and `appKind` written to `.tfcore/core-config.yaml` +- [ ] `bash .tfcore/utils/tf-doc-check.sh --app {AppName}` prints no FAIL - [ ] If SourceDocs were harvested: BRD is a SUPERSET of their requirements content (no tables/detail dropped) - [ ] `docs/{AppName}-UIDesign.md` + `docs/mockups/*.html` produced (§3.6, TrBlazeUI-replicable, one per key screen) — or "skipped — no UI" recorded for an API-only app - [ ] `docs/{AppName}-Coding-Standards.md` diff --git a/.claude/settings.json b/.claude/settings.json index f067d91..786242e 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -61,6 +61,10 @@ { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-artifacts.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" } ] }, diff --git a/.opencode/plugin/techieflow.js b/.opencode/plugin/techieflow.js index 873efd5..f2402e3 100644 --- a/.opencode/plugin/techieflow.js +++ b/.opencode/plugin/techieflow.js @@ -234,7 +234,7 @@ export const TechieFlowPlugin = async ({ directory, client }) => { let scripts = [] if (input.tool === "bash") { payload = { tool_name: "Bash", tool_input: { command: String(args.command || "") } } - scripts = ["block-git.sh", "guard-artifacts.sh"] + scripts = ["block-git.sh", "guard-artifacts.sh", "guard-status.sh"] } else if (input.tool === "edit") { payload = { tool_name: "Edit", diff --git a/.tfcore/core-config.yaml b/.tfcore/core-config.yaml index 20e1545..822f44a 100644 --- a/.tfcore/core-config.yaml +++ b/.tfcore/core-config.yaml @@ -18,6 +18,11 @@ architecture: architectureSharded: false architectureShardedLocation: docs/architecture customTechnicalDocuments: null +# appSize / appKind: set by the day-1 tasks (reset Session 3, 2026-09-04). Size is Small (up to 10 +# screens, one role, 50 requirements), Medium (up to 20 screens, 100 requirements) or Large (split +# into phases). Kind is app or library. tf-doc-check.sh reads them when a document header lacks them. +appSize: null +appKind: null # devLoadAlwaysFiles: the day-1 tasks rewrite this list with the project's concrete paths # (docs/{AppName}-Coding-Standards.md + docs/{AppName}-Architecture.md). Empty until then — # the stock paths (docs/architecture/coding-standards.md etc.) never exist in this flow. diff --git a/.tfcore/hooks/guard-status.sh b/.tfcore/hooks/guard-status.sh index 1f7559f..4c4a62d 100755 --- a/.tfcore/hooks/guard-status.sh +++ b/.tfcore/hooks/guard-status.sh @@ -8,7 +8,8 @@ # this hook makes the shape MECHANICAL, the same way block-git.sh made the git # ban mechanical. # -# Wired in .claude/settings.json → hooks.PreToolUse (matcher "Write|Edit|MultiEdit"). +# Wired in .claude/settings.json → hooks.PreToolUse (matcher "Write|Edit|MultiEdit" +# AND matcher "Bash"); OpenCode via .opencode/plugin/techieflow.js. # Exit 2 + stderr = block the call and feed the message back to the agent. # # What it blocks (only for files named PROJECT-STATUS.md): @@ -17,6 +18,11 @@ # - Headings that name a command run (*verify / *build-phase / *fix-issues). # - A paragraph stuffed into `current_phase:` (must be ONE short line). # - A full-file Write longer than 120 lines (template shape is ~60). +# - (Bash) any shell command that WRITES the file — redirection, tee, cp, mv, +# sed -i, a Python/Node one-liner — because a shell write is invisible to the +# Write/Edit checks above (reset Session 3, 2026-09-04). Reading it is fine. +# Content rules (section word limits, the two command blocks, the five-row log) +# are checked by .tfcore/utils/tf-doc-check.sh at the status gate. # Fails OPEN (exit 0) if python3 or parseable JSON is unavailable. INPUT="$(cat)" @@ -32,6 +38,31 @@ except Exception: sys.exit(0) ti = data.get("tool_input") or {} + +# --- Bash branch: refuse shell writes to PROJECT-STATUS.md ------------------- +cmd = ti.get("command") +if isinstance(cmd, str): + if re.search(r"PROJECT-STATUS\.md", cmd, re.I) and re.search( + r"(>>?\s*[\"']?[^\s|;&]*PROJECT-STATUS\.md)" # > / >> redirection + r"|(\btee\b[^|;&]*PROJECT-STATUS\.md)" # tee + r"|(\b(cp|mv|install)\b[^|;&]*PROJECT-STATUS\.md)" # copy / move onto it + r"|(\b(sed|perl)\b[^|;&]*\s-[a-zA-Z]*i[a-zA-Z]*\b[^|;&]*PROJECT-STATUS\.md)" # in-place edit + r"|(\b(python3?|node)\b[^|;&]*PROJECT-STATUS\.md)" # a one-liner that opens it + r"|(\b(truncate|dd)\b[^|;&]*PROJECT-STATUS\.md)", + cmd, re.I, + ): + print( + "BLOCKED by TechieFlow policy: PROJECT-STATUS.md is written ONLY through " + "the harness Write/Edit tool, never through the shell (redirection, tee, " + "cp, mv, sed -i, a script). The shell path bypasses the shape guard, which " + "is how status files got spoiled. Write the file with the Write tool in the " + "template shape (.tfcore/templates/v4custom/app-project-status-tmpl.md), " + "then run: bash .tfcore/utils/tf-doc-check.sh PROJECT-STATUS.md", + file=sys.stderr, + ) + sys.exit(2) + sys.exit(0) + path = (ti.get("file_path") or "").replace("\\", "/") if path.rsplit("/", 1)[-1].lower() != "project-status.md": sys.exit(0) diff --git a/.tfcore/standards/coding-standards-core.md b/.tfcore/standards/coding-standards-core.md new file mode 100644 index 0000000..29f5fd1 --- /dev/null +++ b/.tfcore/standards/coding-standards-core.md @@ -0,0 +1,61 @@ +# TechieFlow — Coding Standards, core + + + +## 1. Names + +- One naming style per kind of thing, declared in the stack file, used everywhere. Never mix styles in one project. +- Names say what a thing is or does. No abbreviations a newcomer would have to ask about. +- Files are named after the one type they hold. + +## 2. Layout + +- Source under `src/`, tests under `tests/`. No other folders at the repository root unless the stack file allows one. +- Log files go under the build output folder, never at the repository root. +- The primary executable project carries the product name. A project named `.App` is not allowed. + +## 3. Dependencies and configuration + +- No package is added without a row in the Architecture Decisions log saying why. +- One configuration mechanism per project. A second one is a defect. +- A missing feature in an internal library (TrBlazeUI, TechieRag, TechieFlow) is recorded in that library's feedback file and the feature is held. No workaround is written. + +## 4. Code shape + +- One type per file. Small methods. Early returns for validation. At most three levels of nesting. +- Every public member has a documentation comment in the language's own form. +- No commented-out code. Comments say why, not what. + +## 5. Tests + +- A test project exists from day one. +- Test names are short; the scenario lives in the test's documentation comment. +- Arrange, act, assert. One behaviour per test. + +## 6. Security + +- No credentials in code or in committed configuration. Secrets come from the mechanism in Stack Q2. +- Database access uses parameters, never string-built queries. +- Every input from outside the process is validated at the boundary. Security events are logged. + +## 7. Logging + +- Every executable head writes a rolling log file, wired at startup before anything else can fail, and logs unhandled exceptions at its boundary. +- Libraries log through the logging abstraction only; they never reference a logging implementation. + +## 8. Testability + +- Every interactive or data-bound control the verifier must reach carries a stable test id, named by intent (`LoginSubmit`, `EntriesGrid`), never by position. + +## 9. Enforcement + +- The machine-checkable subset lives in the repository's editor configuration and analyzer settings, as the stack file specifies. +- The verifier's standards check runs the greps listed in the stack file's Enforcement section and records findings in the checklist Remarks and PROJECT-STATUS. diff --git a/.tfcore/standards/coding-standards-dotnet.md b/.tfcore/standards/coding-standards-dotnet.md new file mode 100644 index 0000000..e7302b4 --- /dev/null +++ b/.tfcore/standards/coding-standards-dotnet.md @@ -0,0 +1,93 @@ +# TechieFlow — Coding Standards, .NET + + + +## 1. Names + +**Never use underscores** in any identifier, database object or environment variable name. + +| Kind | Convention | Example | +|---|---|---| +| Classes, records, structs | PascalCase, descriptive | `SqlQueryBuilder`, not `SqlQB` | +| Interfaces | `I` prefix | `IQueryExecutor` | +| Methods | PascalCase verb phrase; async methods end in `Async` | `GetConnectionAsync()` | +| Properties | PascalCase, no prefix | `ConnectionString` | +| Instance fields | **per-project choice**: `obj` prefix (`objLogger`) or bare PascalCase (`Logger`). Default `obj`. Recorded in the project's Coding Standards | `private readonly ILogger objLogger;` | +| Static and const fields | PascalCase, no prefix | `MaxRetryCount`, not `MAX_RETRY_COUNT` | +| Method parameters | `a` prefix + PascalCase | `LoginAsync(string aEmail)` | +| Local variables | `v` prefix + PascalCase | `var vResponse = …` | +| Booleans | same prefix, question form | `IsValid`, `vHasRows`, `aCanEdit` | +| Test methods | short PascalCase; scenario in the XML `` | `LoginRejectsBadPassword` | +| Environment variables | PascalCase, no separators; read through `IConfiguration` via a provider that maps them to `Section:Key` | `AppManagerBaseUrl` | + +Rejected: `_field` prefixes, snake_case, Hungarian type prefixes (`strName`), `Method_State_Result` test names, `UPPER_SNAKE` or `Section__Key` environment variables. + +Controller actions: the `a` prefix applies to `[FromRoute]`, `[FromQuery]` and `[FromBody]` parameters and flows into the OpenAPI schema. DTO **property** names stay PascalCase without prefix. + +## 2. Database + +- PascalCase, singular table names: `CustomerOrder`. PascalCase columns: `FirstName`. +- Primary key `Id`; foreign key `Id`. +- Stored procedures and functions: verb prefix, PascalCase: `GetCustomerOrders`, `InsertOrder`. +- Index `IX
` · primary key `Pk
` · foreign key `Fk
` · unique `Uc
`. +- Data access is Dapper. Migrations live in a dedicated `Db` project using DbUp, run at application startup or by the pipeline. Never a `database` folder of loose scripts at the root. + +## 3. Projects and files + +- `src/` and `tests/` at the root. The primary head is `src//.csproj`; `.App` is banned. Secondary heads take a descriptive suffix: `.Api`, `.Desktop`, `.Cli`. Satellites: `.Core`, `UI` (Razor class library), `.Tests`. +- One class per file; file-scoped namespaces; nullable reference types enabled. +- File order: usings, namespace, type; inside the type: fields, constructor, properties, methods. +- Configuration: `appsettings.json` layered by `appsettings.{Environment}.json`; development secrets in `dotnet user-secrets`, with a `secrets.example.json` listing every key. No second mechanism. + +## 4. Code + +- Methods under about 20 lines; single responsibility; at most three levels of nesting; early returns. +- `async`/`await` for all I/O; `ConfigureAwait(false)` in libraries; no `async void` outside event handlers. +- LINQ method syntax for simple queries; avoid multiple enumeration. +- `StringBuilder` in loops; dispose `IDisposable`; cache expensive work. +- XML documentation on every public member: ``, ``, ``, ``; `` where the flow is not obvious. + +## 5. Tests + +- xUnit. A test project from day one. +- Short PascalCase names, scenario in ``; arrange, act, assert; one assertion per test where practical. + +## 6. Logging + +- Serilog with a rolling file sink in every executable head (web, API, MAUI, desktop, console, background service), wired before anything else can fail: `Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Console().WriteTo.File("/logs/-.log", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 14).CreateLogger();` then `builder.Services.AddSerilog()` or `builder.Host.UseSerilog()`; MAUI: `builder.Logging.AddSerilog()` in `MauiProgram`, path under `FileSystem.AppDataDirectory`. +- `Log.Fatal` around startup, handlers for `AppDomain.CurrentDomain.UnhandledException` and `TaskScheduler.UnobservedTaskException`, `Log.CloseAndFlush()` on exit. +- Class libraries reference only `Microsoft.Extensions.Logging.Abstractions` and log through `ILogger` with structured templates. +- Brownfield: an existing structured file-logging stack is compliant; new heads still use Serilog. + +## 7. Testability + +- Blazor: a stable `data-testid` (or element id) on every control the verifier must reach. +- MAUI: a stable, unique `AutomationId` on every interactive or data-bound control, named by intent (`LoginSubmitButton`, `ClientsGrid`), set on the element whose data the gate asserts. + +## 8. Security + +- No credentials in code. Parameterised queries. Validate inputs. Log security events. + +## 9. Enforcement + +`.editorconfig` at the repository root (created at day-1) enforces: file-scoped namespaces (warning), `Async` suffix (warning), `var` for locals (warning), nullable enabled, no `_` prefix on private fields (custom naming rule, warning). `StyleCop.Analyzers` is optional and off by default. + +The verifier's standards check runs: + +```bash +# underscore-prefixed fields +grep -rE "private(\s+readonly)?\s+\w+\s+_[a-z]" src/ 2>/dev/null +# underscores in test method names +grep -rE "public\s+(async\s+)?Task\s+\w+_\w+\s*\(" tests/ 2>/dev/null +# fields missing the obj prefix (obj-style projects only) +grep -rPE "private(\s+readonly)?\s+\w+\s+(?!obj)[A-Z]\w+\s*[;=]" src/ 2>/dev/null | grep -v "static\|const" +``` + +Severity: error for file-scoped namespace and underscore field prefix; warning for nullable and the `Async` suffix; the rest informational. diff --git a/.tfcore/tasks/_status-update-gate.md b/.tfcore/tasks/_status-update-gate.md index 0095fb6..83975b2 100644 --- a/.tfcore/tasks/_status-update-gate.md +++ b/.tfcore/tasks/_status-update-gate.md @@ -65,6 +65,7 @@ Before you record a framework file as missing — in a verdict, a checklist Rema **"Next command = `*build-phase` for the remaining REQs" is NOT a way to end a build pass early (build-phase §2b, owner rule 2026-08-21).** If you are writing this gate at the end of a `*build-phase` run and the reason some REQs are still `Planned` is that *this pass did not get to them*, you are not at the gate — go back and build them (fan out more sub-agents). The build-leads bullet above describes a state found on re-entry (a crashed session, an owner-gated or `Blocked` remainder, FIX mode after verifier failures), never a pass that chose to stop. Under YOLO / goal mode (`_yolo-mode.md`) there is nobody to "run it again". 6. A new **Verification log** row whose "Status table" column links to the checklist that holds the per-REQ detail (`docs/{AppName}-Checklist.md#requirements-status`) — NOT a dated `docs/qa/*.md` file (those no longer exist). 7. Library-feedback counts + standards-compliance lines refreshed if the phase touched them. +7b. **Document check (added 2026-09-04, reset Session 3):** run `bash .tfcore/utils/tf-doc-check.sh --app {AppName}` on the documents this command wrote (PROJECT-STATUS.md is always included). Every template carries a schema block (required sections in order, word budgets by app size, row rules); the script prints one line per problem. A `FAIL` line means the phase is NOT closed: fix the document and re-run until only `WARN` or `OK` remain. Never edit the schema to make a document pass. 8. **HTML refresh (MANDATORY — every time, same turn you edit the `.md`):** re-render `PROJECT-STATUS.html` from the markdown you just wrote — **one command, `bash .tfcore/utils/tf-render-html.sh PROJECT-STATUS.md`** (added 2026-08-27; never hand-author the HTML, never bash-heredoc it — see `.tfcore/tasks/generate-html.md`). This is not optional cleanup and not a "later" step — the owner reads the `.html`, so a markdown-only update is an **incomplete** update that fails this gate. If you edited `PROJECT-STATUS.md` you re-render `PROJECT-STATUS.html` in the same turn, full stop. **The harness enforces this MECHANICALLY** (added 2026-08-25, same treatment as the git ban and the status shape): the `.tfcore/hooks/guard-status-html.sh` **Stop** hook refuses to end your turn while `PROJECT-STATUS.html` is older than `PROJECT-STATUS.md`, or missing (Claude Code and Codex block the stop; OpenCode, which has no blocking Stop hook, sends the same message back into the session as a follow-up prompt when it idles). A blocked stop means *the render is genuinely outstanding* — re-render it, do not look for a way around the hook. **Do NOT render the checklists to HTML** — they are AI-agent working documents kept in markdown only (the per-REQ Requirements Status table is the agent's source of truth, not a human HTML page). 9. **BRD §4 Development status rollup** (keeps the human BRD snapshot tracking reality). If `docs/{AppName}-BRD.md` has a `## … Development status` section, refresh it from the checklists: - One row per feature (each §"Feature catalog" `### F-…` entry). Roll its owned REQs up to a feature-level status: feature → its `Requirements: BRD-…` line → the `REQ-*` those BRDs split into → the per-REQ Status in the checklist tables. diff --git a/.tfcore/tasks/day1-brownfield.md b/.tfcore/tasks/day1-brownfield.md index df18b28..3d9f666 100644 --- a/.tfcore/tasks/day1-brownfield.md +++ b/.tfcore/tasks/day1-brownfield.md @@ -8,7 +8,7 @@ Replace the multi-step paste-and-substitute prompt with a single command: `*day1 ## elicit -elicit=false — this task runs autonomously end-to-end. It asks AT MOST TWO questions (app name if missing, then optional source-doc hints), then drafts every artifact (including the full BRD) in bulk and presents them for ONE-shot review at the end. NO per-section confirmation. NO per-requirement confirmation. The user reviews the written docs and edits the files directly, or replies with bulk changes. +elicit=false — this task runs autonomously end-to-end. It asks AT MOST THREE questions (app name if missing, optional source-doc hints, then the size confirmation in §1), then drafts every artifact (including the full BRD) in bulk and presents them for ONE-shot review at the end. NO per-section confirmation. NO per-requirement confirmation. The user reviews the written docs and edits the files directly, or replies with bulk changes. This is a deliberate departure from TechieFlow's standard `author-brd` per-item elicitation — the user has explicitly opted into a low-friction flow for a one-person team. @@ -43,6 +43,7 @@ This is a deliberate departure from TechieFlow's standard `author-brd` per-item - docs/{AppName}-Coding-Standards.md - docs/{AppName}-Architecture.md ``` +- **Size and kind (reset Session 3, 2026-09-04):** count the routed pages in the code (every `@page` route or equivalent counts, sign-in included; dialogs and tabs are regions of a page) and the roles, then confirm once: "Size: Small (up to 10 screens, one role, 50 requirements), Medium (up to 20 screens, 100 requirements) or Large (split into phases)? I count {N} screens and {N} roles, so I propose {X}." Kind is `app`, or `library` for a component or service library. Write `appSize:` and `appKind:` into `.tfcore/core-config.yaml` in the same write and carry both into every document header. The size sets the document budgets and the requirement cap that `bash .tfcore/utils/tf-doc-check.sh` enforces at the status gate. ### 1.5. Discovery hints — harvest existing docs before inferring anything @@ -84,21 +85,20 @@ Net effect: `docs/` always contains exactly one current version of each doc unde - Status field: "Current" (this is brownfield). - **Source-doc harvesting (do this FIRST, before any inference):** - If `SourceDocs[]` from §1.5 contains anything that reads as architecture / design / system / data-flow material, harvest from it directly: copy structural prose verbatim (with attribution), pull diagram intent, map their components to your §4 module table. Do not re-invent what the user already wrote. - - At the end of the doc, add a `## Sources harvested` section listing each source file that contributed content. + - Attribute harvested content inline; there is no "Sources harvested" section (the run record carries the list). - **Apply `CustomInstructions`** from §1.5 throughout — if the user said "stack is .NET 8", use .NET 8 in §1; if they said "ignore `legacy/`", skip that folder in the scan. -- Populate remaining sections by SCANNING the codebase (only for what the source docs didn't cover): - - **§1 Tech stack:** read `.csproj`/`.sln` files, package references, target frameworks. Note TrBlazeUI / TechieRag presence (look in package references AND in `.claude/` for deployed agent files). - - **§2 Component map:** scan `src/` (or `source/`) for projects; build a Mermaid `flowchart TB` showing project-level dependencies inferred from `` and `using` directives. - - **§3 Data flow:** if you can identify a primary request path (e.g. controller → service → repo), diagram it as a `sequenceDiagram`. Otherwise leave the template placeholder and add a note "data flow unknown from static analysis — populate after first feature pass." - - **§4 Module responsibilities:** one row per project under `src/`. Responsibility = one-line summary derived from top-of-namespace XML doc or README mentions. - - **§5 Cross-cutting:** detect logging library (Serilog/ILogger), auth scheme (JWT/cookies/Identity), telemetry (OTel/AppInsights) by package references. - - **§6 Deployment:** if `.github/workflows/`, `Dockerfile`, `azure-pipelines.yml` exist, derive the path. Otherwise mark "no CI/CD detected; manual deploy." - - **§7 ADRs:** seed with `ADR-001 — current stack as-is (reverse-doc baseline).` and any obvious decisions visible in README. - - **§8 Target architecture:** leave blank unless the BRD (§3 below) calls out a structural change. - - **§9 Open questions:** include "field-prefix drift" detection (see below) and any TODOs / FIXMEs that look architectural. -- **Depth mandate (Architecture is a HUMAN document too):** apply the same information-preservation rule as the BRD (§3) — source-doc architecture content carries forward, never gets summarized into a stub. Each module in §4 with non-trivial behavior gets a short prose paragraph (not just a table row), and any significant runtime flow beyond the primary path (background jobs, ingestion pipelines, auth handshakes, external-API round-trips) gets its own `sequenceDiagram` or `flowchart` in the relevant section. A reader skimming only the diagrams should grasp how the system hangs together. -- **Field-prefix drift detection:** scan `src/`, `source/`, or any `.cs` files for instance-field declarations and note the dominant style (`obj`-prefixed vs bare PascalCase vs `_underscore`/mixed) — §4 uses this to pick the project's field convention. If no style reaches ~80% dominance, add to §9 Open questions: "Standards drift detected — mixed instance-field naming (N obj / M bare / K underscore). §4 picked {chosen}; remediation happens incrementally during implementation." -- **Table of Contents:** the template at `.tfcore/templates/v4custom/app-architecture-tmpl.md` includes a `## Table of Contents` section. After populating the rest of the doc, regenerate that section to match the actual H2 headings you wrote (drop entries for sections that ended up empty, add entries for any new sections). Use the slug rule from `.tfcore/templates/v4custom/html-render-shell.md §1` — same slug for the link in the TOC and the `id` the renderer will assign. Broken TOC links are a known recurring bug; don't be the next instance. +- Populate the template's sections, in its order, by SCANNING the codebase (only for what the source docs didn't cover; reset Session 3, 2026-09-04 — `tf-doc-check.sh` refuses any other shape): + - **Stack decisions:** one row per stack question, answered from the project files (`.csproj`/`.sln`, package references, target frameworks, configuration files) and the Stack answer set where the code agrees with it; cite the source per row. + - **Solution structure:** one row per project with its kind (web app, class library, test project, migrations project) and purpose. + - **Component map:** a `flowchart TB` of project-level dependencies from `` and `using` directives, then the "How a request travels" numbered list in words for the primary path (controller → service → data access). No sequence diagram; per-screen detail goes to the DevGuide (§7.6). + - **Data model:** a mermaid `erDiagram` and an entity table from the migrations project, entity classes or schema scripts. + - **Cross-cutting:** logging library, auth scheme, configuration mechanism, error handling — from package references and startup code. + - **Decisions log:** a first row `current stack as-is (reverse-doc baseline)`, Status `decided`; any structural change the BRD (§3) calls out is a row with Status `planned` naming its BRD item. There is no Target architecture section. + - **Module responsibilities** (required for Medium and Large): one row per project, from top-of-namespace docs or README. + - **Open questions:** the field-prefix drift finding (below) and any TODOs / FIXMEs that look architectural. No Deployment section: hosting is decided after UAT. +- Source-doc architecture content carries forward into these sections, attributed, never summarised away. +- **Field-prefix drift detection:** scan `src/`, `source/`, or any `.cs` files for instance-field declarations and note the dominant style (`obj`-prefixed vs bare PascalCase vs `_underscore`/mixed) — §4 uses this to pick the project's field convention. If no style reaches ~80% dominance, add to Open questions: "Standards drift detected — mixed instance-field naming (N obj / M bare / K underscore). §4 picked {chosen}; remediation happens incrementally during implementation." +- Mermaid: quote every label; never use `end` as a node id. No Table of Contents (the renderer builds it). - Write the populated doc to `docs/{AppName}-Architecture.md`. ### 3. Draft the FULL BRD in one pass → `docs/{AppName}-BRD.md` @@ -115,22 +115,10 @@ This is the friction-removal step. **Do NOT run `author-brd`. Do NOT prompt the - **INFORMATION-PRESERVATION RULE (hard requirement when SourceDocs exist):** the new BRD must be a SUPERSET of the requirements content in the harvested source docs — never a summary of it. Concretely: - Every table, matrix, screen inventory, route list, license/feature matrix, navigation-menu tree, persona-detail block, and per-feature workflow in a source BRD/spec **carries forward** into the new doc (updated where stale, attributed where copied) — it does NOT get compressed into a one-liner. - **Length sanity check before writing:** if the source docs' requirements content totals X lines and your draft (excluding boilerplate) is under ~60% of X, you compressed — go back and restore the detail. A 1,000-line source BRD should never produce a 250-line replacement. - - One-line statements are allowed ONLY in the §10 BRD ledger. Everything else is full prose, tables, and diagrams — this is a HUMAN document read as rendered HTML; the coding agents get their compact view later from `*split-brd` / the Checklist. -- **§4 Development status (brownfield: the reader's first question — "what's built, what's pending?"):** fill the §4 table with ONE row per §9 feature-catalog F-code. Derive each row's Status / % / Phase / Notes from the **strongest evidence available**, in priority order: (1) a migrated dev/phase plan (§3.5) — carry its phase + completion verbatim; (2) the code scan from §2 — a feature whose screens/handlers actually compile and exist is `Done`, partially-present is `Partial`, absent is `Planned`; (3) source-doc status notes. Set the "Snapshot as of" date to today. This is a feature-level SUMMARY only — do NOT restate per-REQ status (that's PROJECT-STATUS + the checklists). Keep it consistent with §3.5's migrated statuses and with PROJECT-STATUS. -- **§9 Feature catalog (the heart of the doc):** one `### F-{CODE}: {Name}` subsection per feature/capability area found in the source docs and the codebase. Per feature: personas + phase, 1-2 paragraphs of what/why, a screens & routes table, a numbered workflow (inputs → outputs), and the owning BRD-N IDs. If a source doc already has a feature catalog, preserve its feature codes and per-feature detail. Depth scales with the app (8–25 features is normal) — there is NO cap. Every F-code MUST also appear as a row in the §4 Development status table. -- For §10 Functional requirements ledger: walk the feature catalog and emit `BRD-1`, `BRD-2`, … as one-line ` can ` or `system shall ` statements, each tagged with its catalog feature `(F-CODE)`. Number monotonically. **One BRD per discrete capability — the count scales with the app (20–60 is normal for a real product); NEVER merge capabilities to keep the count low.** If a BRD came directly from a source doc, suffix the line with ``. -- For §11 Non-functional: cover performance, security, accessibility, scalability, reliability based on visible NFR signals (auth scheme, target framework, any `aria-` attrs in Razor). Where concrete targets exist (latency, uptime, concurrency), present them as a target table, not buried in prose. **ALWAYS include the standing Observability NFR: Serilog file-based logging in every executable head** — if the §5 cross-cutting scan found Serilog (or an equivalent structured file-logging stack) already wired, record it as met/`Done (pre-existing)`; if the app logs only to console or not at all, add the NFR as `Planned` so it becomes a `REQ-NFR-*` row and gets built (recipe: coding-standards §Logging). -- **Mermaid mandate:** §6, §7, §8 (context, journey, component) are the MINIMUM — build them from §2's component map (copy verbatim if identical). Additionally, every feature-catalog entry with a multi-step or multi-actor flow gets its own diagram (`flowchart` or `sequenceDiagram`). Target: a reader skimming only the diagrams should grasp how the app works. Simple CRUD features may skip the diagram. **Every diagram MUST follow the authoring rules in `.tfcore/templates/v4custom/html-render-shell.md §5.5` — quote every node/edge/subgraph label and never use `end` as a node id; unquoted special characters in flowchart labels are the #1 cause of broken diagrams in the rendered HTML.** -- Append a footer: - ``` - --- - Last updated: {YYYY-MM-DD} - Highest BRD ID: BRD-{N} - Sources harvested: {comma-separated list of SourceDocs paths, or "none — drafted from reverse-doc"} - Custom instructions applied: {one-line summary of CustomInstructions, or "none"} - Drafted from reverse-doc — review and edit. New BRDs may be added (append-only); do not renumber. - ``` -- **Table of Contents:** the template includes a `## Table of Contents` section. Regenerate it after populating the doc so it matches the actual H2 headings, and list each `### F-…` feature-catalog entry as an H3 sub-entry under "Feature catalog". Use the slug rule from `.tfcore/templates/v4custom/html-render-shell.md §1`. + - Source requirements become BRD items; the Requirements ledger is where one-line statements live, and each item still carries its screen, mockup link and acceptance line. +- **Fill the template's sections in its order** (reset Session 3, 2026-09-04; `tf-doc-check.sh` refuses any other shape). Header: App, Kind, Size, Stack answer set, Status, Date. **Summary** at most 200 words. **Scope**. **Users and roles**. **Screens and flow** — one table row per routed page found in the code (screen, route, role, mockup link where a mockup exists, fields); a dialog is a row under its parent screen with `on /route` in the Route column; then the primary journey as a numbered list. **Requirements** — one `**BRD-N**` item per thing the verifier will test, each naming its screen, linking its mockup, and carrying one acceptance line "When on , then "; ids append-only, never renumbered; a source-doc item is attributed inline. **Non-functional requirements** table: the `perf-budget:` measure only where the owner stated a number; the logging requirement from the Stack answer set always, recorded as met when the §2 scan found it wired. **Development status** — one row per screen with Verified / Open counts from the strongest evidence (a migrated plan first, then the code scan); the status gate maintains it afterwards. Context diagram, Constraints and assumptions and Risks are required for Medium and Large only. No Feature catalog, no Table of Contents, no footer. +- **The requirement count stays within the size cap** (Small 50, Medium 100). If the code holds more, propose a phase split — each phase its own BRD, checklist and build — rather than merging requirements or growing the document. +- Mermaid: quote every label; never use `end` as a node id. - Write the populated doc to `docs/{AppName}-BRD.md`. ### 3.5. Migrate an existing development/phase plan → split requirement docs (CONDITIONAL) @@ -149,163 +137,19 @@ This is the friction-removal step. **Do NOT run `author-brd`. Do NOT prompt the - Partial items (e.g. "50% Scaffolded") → Status `In Progress` or `PARTIAL`, carry the plan's `%` and remark verbatim. - Not-yet-started items → Status `Not Started`, `0%`. - Add a header note to both docs: `> Migrated from {plan-file} on {YYYY-MM-DD}. Phase structure, completion %, and status remarks carried over verbatim — verify before building.` -- **Keep the BRD §4 Development status table consistent with this migration:** the feature-level rows in BRD §4 must agree with the per-REQ statuses you just wrote (a feature whose REQs are all `Done (pre-existing)` → `Done` in §4; mixed → `Partial`; none started → `Planned`). The checklist is the live per-REQ truth; BRD §4 is the human feature-level snapshot of the same reality. +- **Keep the BRD Development status table consistent with this migration:** its per-screen rows must agree with the per-REQ statuses you just wrote (all `Done (pre-existing)` → `Done`; mixed → `Partial`; none started → `Planned`). The checklist is the live per-REQ truth; the BRD table is the human snapshot of the same reality. - Do NOT modify the dev-plan file's content. After migration it is superseded — move it to `docs/OldDocs/` per §1.6 and say so in the §8 summary. - If the `*split-brd` artifact (`docs/{AppName}-Checklist.md`) already exists (re-run scenario), apply §1.6: archive the old one to `docs/OldDocs/`, write fresh at the canonical name. No questions. ### 4. Create the Coding Standards → `docs/{AppName}-Coding-Standards.md` -Write the file with the exact content below, substituting `{AppName}` in the title only. - -**ONE per-project decision first — the instance-field prefix.** The shared rules (no underscores, `a` params, `v` locals, PascalCase everywhere) are fixed; the instance-field convention is decided per project (existing samples: AppManager = `obj` prefix, AstroLyfe = bare PascalCase no-prefix): -- If the existing code has a clear dominant style (≥80% of instance fields `obj`-prefixed OR ≥80% bare PascalCase), adopt that style. -- If `{Hints}` / CustomInstructions specify one, that wins. -- Otherwise default to `obj`. -Record the decision in the table below (swap the Instance-fields row to `PascalCase, no prefix — e.g. private readonly ILogger Logger;` if no-prefix won), in the §"Enforcement" greps (the missing-obj-prefix grep only applies to obj-style projects), and in CLAUDE.md (§7). - -```markdown -# {AppName} Coding Standards - -**Last Updated:** {today YYYY-MM-DD} -**Status:** Authoritative for all code under `src/`/`source/` and `tests/`. Conformance enforced via repo-root `.editorconfig` + verifier grep checks in §"Enforcement". - -## Database Naming Conventions - -### Tables and Columns -- PascalCase: `CustomerOrder` NOT `customer_order` -- Singular: `CustomerOrder` NOT `CustomerOrders` -- **NEVER use underscores** in any DB object name -- FK columns: `{TableName}Id` (e.g., `CustomerId`) -- PK: `{TableName}Id` (e.g., `UserId`) - -### Stored Procedures & Functions -- PascalCase verb prefix: `GetCustomerOrders`, `InsertOrder`, `CalculateTotal` -- Action prefixes: Get / Insert / Update / Delete / Calculate - -### Indexes & Constraints -- Index: `IX{Table}{Column}` · PK: `Pk{Table}` · FK: `Fk{Table}{Ref}` · Unique: `Uc{Table}{Column}` - -## C# Conventions - -### Classes & Interfaces -- PascalCase for classes; `I` prefix for interfaces; descriptive names. -- Async methods end with `Async`. - -### Fields, Parameters, Locals - -**NEVER use underscores** anywhere in any identifier. - -| Kind | Convention | Example | -|------|-----------|---------| -| **Instance fields** | `obj` prefix + PascalCase tail (no underscores) | `private readonly ILogger objLogger;`
`private readonly HttpClient objHttpClient;`
`private string objCachedPublicKey;` | -| **Static / `const` fields** | PascalCase, no prefix | `private const string CachePrefix = "…";` | -| **Method parameters** | `a` prefix + PascalCase | `LoginAsync(string aEmail, string aPassword)` | -| **Local variables** | `v` prefix + PascalCase | `var vResponse = await …` | -| **Booleans** | same prefix + `Is`/`Has`/`Can` | `IsAuthenticated`, `vIsValid`, `aHasAccess` | -| **Properties** | PascalCase, no prefix | `public string ConnectionString { get; set; }` | -| **Constants** | PascalCase, no underscores | `MaxRetryCount` NOT `MAX_RETRY_COUNT` | -| **Test methods** | Short PascalCase, no underscores — full scenario in XML `` | `LoginRejectsBadPassword` not `Login_BadPassword_ReturnsUnauthorized` | - -**Rejected forms:** `_underscore` field prefixes, snake_case anywhere, Hungarian prefixes (`strName`), underscores in test method names. - -### Controller-action parameters -The `a`-prefix applies uniformly to `[FromRoute]`/`[FromQuery]`/`[FromBody]`. Parameter name flows through to OpenAPI. Body DTO **property** names stay PascalCase no prefix; only the parameter symbol holding the deserialized DTO gets the `a` prefix. +Load `.tfcore/templates/v4custom/app-coding-standards-tmpl.md` (reset Session 3, 2026-09-04: the standards themselves now live in the framework and are not copied per project). -### Environment Variables -**PascalCase, no separators.** `{AppName}BaseUrl` NOT `APPNAME_BASE_URL` and NOT `AppName__BaseUrl`. Use a custom configuration provider mapping PascalCase env vars → `:`-nested config paths. Read via `IConfiguration["Section:Key"]` only — never `Environment.GetEnvironmentVariable(...)`. - -### Project & solution naming — the primary head carries the PRODUCT name -- The product's **primary executable head** project is named exactly `{AppName}` — `src/{AppName}/{AppName}.csproj`. A single-head product's one head IS `{AppName}`. -- **`{AppName}.App` is BANNED** (owner rule 2026-07-10): "App" says nothing — the product name already names the app. Never scaffold it; if the codebase has one, log a rename REQ (dir + `.csproj` + sln entry + namespaces) in the checklist instead of propagating the name. -- Secondary heads of a multi-head product take a **descriptive** dotted suffix: `{AppName}.Api`, `{AppName}.Desktop`, `{AppName}.Cli`. Satellites keep their conventional names: `{AppName}.Core` (engine), `{AppName}UI` (RCL), `{AppName}.Core.Tests` / `{AppName}.Tests`. - -### File Structure -```csharp -using System; - -namespace {AppName}.Services.Example; - -public class DatabaseService -{ - private readonly ILogger objLogger; - private readonly IConfiguration objConfiguration; - - public DatabaseService(ILogger aLogger, IConfiguration aConfiguration) - { - objLogger = aLogger; - objConfiguration = aConfiguration; - } - - public string ConnectionString { get; set; } - - public async Task GetDataAsync(string aQueryName) - { - var vConnString = objConfiguration.GetConnectionString("Default"); - var vResult = await ExecuteQueryAsync(vConnString, aQueryName); - return vResult; - } -} -``` - -### Best Practices -- One class per file. File name matches class. -- File-scoped namespaces. Nullable reference types enabled. -- Methods small (<20 lines). Single responsibility. -- Max 3 nesting levels. Early returns for validation. -- ConfigureAwait(false) in libraries. -- StringBuilder for loop concatenation. Dispose IDisposable. Cache expensive ops. - -### XML Documentation (MANDATORY on public members) -``, ``, ``, ``, `` — all required. - -### Testing -- Short PascalCase test name, no underscores. Full scenario in XML ``. -- Arrange-Act-Assert. One assertion per test where practical. - -### Security -- Never hardcode credentials. Parameterized queries. Validate inputs. Log security events. - -### Logging — Serilog file sink (MANDATORY, every .NET app type) -- **Every executable head gets Serilog with a rolling FILE sink — web (Blazor Server/WASM host), API, MAUI, WinForms/WPF desktop, console/CLI, background service. No exceptions, and never wait for the owner to ask.** -- Wire at startup, before anything else can fail: `Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Console().WriteTo.File("logs/{appname}-.log", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 14).CreateLogger();` then plug into DI (`builder.Services.AddSerilog()` / `builder.Host.UseSerilog()` for hosts; `builder.Logging.AddSerilog()` in `MauiProgram.CreateMauiApp`). Read overrides from `appsettings.json` (`Serilog` section) where the host has one. For MAUI/desktop, root the path in a writable per-app location (`FileSystem.AppDataDirectory` / `Environment.SpecialFolder.LocalApplicationData`), not the install dir. -- Log unhandled exceptions at the head boundary: `try/catch` + `Log.Fatal` around startup, `AppDomain.CurrentDomain.UnhandledException` / `TaskScheduler.UnobservedTaskException` handlers, and `Log.CloseAndFlush()` on exit. -- **Class libraries never reference Serilog** — they log through `ILogger` / `Microsoft.Extensions.Logging.Abstractions` only; the head's Serilog config picks those up automatically. -- App code logs through injected `ILogger` (structured message templates, e.g. `logger.LogInformation("Imported {Count} rows", n)`), not static `Log.*`, outside the startup boundary. -- The `logs/` output folder is gitignored (the owner adds it — agents never run git). -- Brownfield: an app already on a working structured file-logging stack (e.g. NLog-to-file) is compliant — record the stack in this section; new heads added to it still use Serilog. - -### MAUI UI testability — stable AutomationId (MAUI apps only) -- Every interactive or data-bound control the verifier must reach (buttons, entries, pickers, list/collection views, key labels/values) carries a stable, unique **`AutomationId`** — the native analogue of a stable DOM id for Playwright. Without it Appium selectors drift and the runtime gates (`verify-phase §4a/§4b`) can't reliably find controls on the Android/iOS/Mac Catalyst heads. -- Name them by intent, not layout: `AutomationId="LoginSubmitButton"`, `AutomationId="ClientsGrid"`, `AutomationId="TotalBalanceValue"` — never positional (`Button2`). -- Set it on the control whose data the gate asserts (the grid/list itself, the value label), so "rows present AND non-empty" / "value not blank" maps to one addressable element. -- (Blazor screens use the equivalent `data-testid`/stable element ids for Playwright — same principle.) - -## Enforcement - -### .editorconfig (machine-checkable) -- File-scoped namespaces (`warning`) -- Async-method `Async` suffix (`warning`) -- `var` for locals (`warning`) -- Nullable reference types enabled -- No `_` prefix on private fields (`warning` via custom naming rule) - -### Verifier grep checks -```bash -# Forbidden underscore-prefix fields -grep -rE "private(\s+readonly)?\s+\w+\s+_[a-z]" src/ source/ 2>/dev/null - -# Forbidden test-method underscores -grep -rE "public\s+(async\s+)?Task\s+\w+_\w+\s*\(" tests/ - -# Field missing obj prefix -grep -rE "private(\s+readonly)?\s+\w+\s+(?!obj)[A-Z]\w+\s*[;=]" src/ source/ 2>/dev/null | grep -v "static\|const" -``` - -### Severity -- **Error**: file-scoped namespace, underscore field prefix -- **Warning**: nullable, async suffix -- **Info**: consider fixing -``` +- The standards are `.tfcore/standards/coding-standards-core.md` (every project) plus `.tfcore/standards/coding-standards-.md` for the Stack answer set named in the Architecture (`dotnet` for the .NET set). List both under "Standards applied". +- **One per-project choice for .NET — the instance-field prefix.** Use the drift scan from §2: if ≥80% of instance fields are `obj`-prefixed or ≥80% bare PascalCase, adopt that style; `{Hints}` / CustomInstructions override; otherwise default to `obj`. Record it in the "Standards applied" choices table and in CLAUDE.md (§7). +- "Project rules" holds only rules that are true of this project alone (a mixed MAUI build invocation is the kind of thing that belongs here). Empty is a valid answer. +- "Enforcement" names the `.editorconfig` (§5), the analyzers in use, and any project-specific grep beyond the stack file's. +- Run `bash .tfcore/utils/tf-doc-check.sh docs/{AppName}-Coding-Standards.md`; fix any FAIL. ### 5. Create `.editorconfig` at the repo root diff --git a/.tfcore/tasks/day1-greenfield.md b/.tfcore/tasks/day1-greenfield.md index 7eec623..9c3ee84 100644 --- a/.tfcore/tasks/day1-greenfield.md +++ b/.tfcore/tasks/day1-greenfield.md @@ -8,7 +8,7 @@ Replace the multi-step paste-and-substitute prompt with a single command: `*day1 ## elicit -elicit=false (after at most a 3-question kickoff). The task asks ONLY for `{AppName}` (if missing), the concept (any length), and optional custom instructions / source-doc hints — then drafts every artifact (including the full BRD) in one pass and presents them for one-shot review. NO per-section confirmation. NO per-requirement confirmation. +elicit=false (after at most a 4-question kickoff). The task asks ONLY for `{AppName}` (if missing), the concept (any length), optional custom instructions / source-doc hints, and the app size (§1) — then drafts every artifact (including the full BRD) in one pass and presents them for one-shot review. NO per-section confirmation. NO per-requirement confirmation. ## Inputs @@ -28,6 +28,7 @@ elicit=false (after at most a 3-question kickoff). The task asks ONLY for `{AppN - Parse `{Hints}` the same way day1-brownfield §1.5 does: paths/globs → `SourceDocs[]` (Read each), instructions → `CustomInstructions` text blob, `none`/empty → both empty. - **Collision policy:** apply day1-brownfield §1.6 verbatim — every deliverable written fresh at its canonical name; any pre-existing version moves to `docs/OldDocs/` (created if missing, date-suffixed on collision); superseded source docs move there after harvesting; NEVER ask merge-vs-new; NEVER write `-v2`-style variants. - Update `.tfcore/core-config.yaml` with the `customTechnicalDocuments` paths AND the `devLoadAlwaysFiles` list exactly as in day1-brownfield §1. +- **Size and kind (reset Session 3, 2026-09-04):** from the concept, count the routed pages (every page with its own route counts, sign-in included; dialogs and tabs are regions of a page) and the roles, then ask once: "Size: Small (up to 10 screens, one role, 50 requirements), Medium (up to 20 screens, 100 requirements) or Large (split into phases)? I count {N} screens and {N} roles, so I propose {X}." Kind is `app` unless the concept is a library. Write `appSize:` and `appKind:` into `.tfcore/core-config.yaml` and carry both into every document header. The size sets the document budgets and the requirement cap that `bash .tfcore/utils/tf-doc-check.sh` enforces at the status gate. ### 2. Propose target architecture → `docs/{AppName}-Architecture.md` @@ -44,10 +45,9 @@ elicit=false (after at most a 3-question kickoff). The task asks ONLY for `{AppN - DB: SQLite for dev, configurable per env - Vector store: SqliteVec for dev (only if RAG/AI is implied by the concept) - Auth: cookie auth for MVP, JWT for API tier if API is in scope -- Populate Mermaid diagrams (component map, primary user journey, deployment) from the concept and any source docs. The richer the concept, the richer the diagrams — a 5-bullet feature list should produce a 5-component diagram, not a 2-box generic placeholder. -- **Depth mandate (Architecture is a HUMAN document):** if `SourceDocs[]` exist, apply the information-preservation rule — their architecture content carries forward, never gets summarized into a stub. Each non-trivial module in §4 gets a short prose paragraph (not just a table row), and any significant runtime flow beyond the primary path (background jobs, ingestion pipelines, auth handshakes, external-API round-trips) gets its own `sequenceDiagram` or `flowchart`. A reader skimming only the diagrams should grasp how the system hangs together. -- Seed §7 ADRs with: `ADR-001 — {chosen UI host}`, `ADR-002 — {chosen DB}`, `ADR-003 — {chosen vector store, if RAG}` — each with a one-line reason that cites the concept/CustomInstructions when relevant. -- **Table of Contents:** the template at `.tfcore/templates/v4custom/app-architecture-tmpl.md` ships with a `## Table of Contents` section. After drafting, regenerate that section to match the actual H2 headings you wrote. Use the slug rule from `.tfcore/templates/v4custom/html-render-shell.md §1` so the links work in both MD and the rendered HTML. +- **Fill the template's sections in its order** (reset Session 3, 2026-09-04; `tf-doc-check.sh` refuses any other shape): **Stack decisions** — one row per stack question, answered from the Stack answer set (`.tfcore/templates/stack-defaults/.md`) or the owner, citing the source; **Solution structure** — one row per project with its kind; **Component map** — one diagram plus the "How a request travels" numbered list in words (no sequence diagram; per-screen detail belongs to the DevGuide later); **Data model** — a mermaid `erDiagram` and an entity table; **Cross-cutting** — identity, configuration, logging, errors, one short paragraph each; **Decisions log** — a row each for the UI host, the database, and every package, with Why and Status. Module responsibilities is required for Medium and Large. No Deployment section (decided after UAT) and no Table of Contents (the renderer builds it). +- If `SourceDocs[]` exist, their architecture content carries forward into these sections, attributed, never summarised away. +- Mermaid: quote every label; never use `end` as a node id. - Write to `docs/{AppName}-Architecture.md`. Do NOT prompt the user mid-draft. ### 3. Draft the FULL BRD in one pass → `docs/{AppName}-BRD.md` @@ -62,13 +62,10 @@ elicit=false (after at most a 3-question kickoff). The task asks ONLY for `{AppN 4. **`CustomInstructions`** — apply throughout (scope limits, stack overrides, NFR additions). 5. Reasonable inference for sections still empty. Mark inferred items with `` HTML comments so the user can scan and confirm. - **INFORMATION-PRESERVATION RULE (when SourceDocs exist):** the BRD must be a SUPERSET of the requirements content in the harvested docs — never a summary. Tables, matrices, screen lists, persona detail, and per-feature workflows carry forward (updated, attributed), not compressed into one-liners. Length sanity check: a draft under ~60% of the source docs' requirements content means you compressed — go back and restore the detail. One-line statements are allowed ONLY in the §10 ledger; this is a HUMAN document read as rendered HTML (the coding agents get their compact view later from `*split-brd` / the Checklist). -- **§4 Development status (greenfield: this is the build ROADMAP):** fill the §4 table with one row per §9 feature-catalog F-code. Nothing is built yet, so every row is `Planned`, `0%`, with its target Phase (and a one-line Notes scope). Set the "Snapshot as of" date to today. As build phases complete later, the live status lives in PROJECT-STATUS + the checklists; this table stays the human roadmap view. -- **§9 Feature catalog (the heart of the doc):** one `### F-{CODE}: {Name}` subsection per feature/capability area implied by the concept and source docs. Per feature: personas + phase, 1-2 paragraphs of what/why, a screens & routes table (proposed, for greenfield), a numbered workflow (inputs → outputs), and the owning BRD-N IDs. Depth scales with the concept — a rich concept should yield 8–25 features; there is NO cap. Every F-code MUST also appear as a row in the §4 Development status table. -- For §10 Functional requirements ledger: walk the feature catalog and emit `BRD-1`, `BRD-2`, … as one-line ` can ` statements, each tagged `(F-CODE)`. **One BRD per discrete capability — the count scales with the concept; NEVER merge capabilities to keep the count low.** Suffix BRDs pulled directly from a source doc with ``. -- For §11 Non-functional: cover performance, security, accessibility, auth model — derived from the stack you chose in §2 and from any NFR signals in the concept or CustomInstructions. Present concrete targets (latency, uptime, concurrency) as a target table. **ALWAYS include the standing Observability NFR: Serilog file-based logging (rolling file sink under `logs/`) in EVERY executable head — web, API, MAUI, desktop, console, background service — no exceptions and no owner prompt needed** (the wiring recipe lives in the coding-standards §Logging block; `*split-brd` turns this BRD into a `REQ-NFR-*` row so the build phase implements it like any other requirement). -- **Mermaid mandate:** the three canonical diagrams (context, user journey, component sketch — copied from the architecture, adapted to BRD framing) are the MINIMUM. Every feature-catalog entry with a multi-step or multi-actor flow gets its own diagram. Simple CRUD features may skip it. **Every diagram MUST follow the authoring rules in `.tfcore/templates/v4custom/html-render-shell.md §5.5` — quote every node/edge/subgraph label and never use `end` as a node id; unquoted special characters in flowchart labels are the #1 cause of broken diagrams in the rendered HTML.** -- Append footer with `Highest BRD ID: BRD-{N}`, a `Sources harvested:` line, a `Custom instructions applied:` line, and the note: "First-pass draft from concept — review and edit. New BRDs may be added (append-only); do not renumber existing IDs." -- **Table of Contents:** the BRD template includes a `## Table of Contents` section. Regenerate it to match the actual H2 headings, and list each `### F-…` feature-catalog entry as an H3 sub-entry under "Feature catalog". Use the slug rule from `.tfcore/templates/v4custom/html-render-shell.md §1`. +- **Fill the template's sections in its order** (reset Session 3, 2026-09-04; `tf-doc-check.sh` refuses any other shape). Header: App, Kind, Size, Stack answer set, Status, Date. **Summary** at most 200 words. **Scope** in and out. **Users and roles** table. **Screens and flow** — one table row per routed page (screen, route, role, mockup link, fields); a dialog is a row under its parent screen with `on /route` in the Route column; then the primary journey as a numbered list. **Requirements** — one `**BRD-N**` item per thing the verifier will test, each naming its screen, linking its mockup, and carrying one acceptance line in the form "When on , then "; ids append-only, never renumbered. **Non-functional requirements** table: the `perf-budget:` measure only where the owner gave a number; the logging requirement from the Stack answer set always. **Development status** — one row per screen, all `Planned` for greenfield; the status gate maintains it afterwards. Context diagram, Constraints and assumptions and Risks are required for Medium and Large only. No Feature catalog, no Table of Contents, no footer. +- **The requirement count stays within the size cap** (Small 50, Medium 100). If the concept needs more, propose a phase split — each phase its own BRD, checklist and build — rather than merging requirements or growing the document. Never merge capabilities to fit. +- If `SourceDocs[]` exist, their requirements carry forward as BRD items (attributed inline), never summarised away. +- Mermaid: quote every label; never use `end` as a node id. ### 3.5. Migrate an existing development/phase plan → split requirement docs (CONDITIONAL) @@ -84,7 +81,7 @@ The mockups are part of the day-1 review (§8) — the owner approves them along ### 4. Create the Coding Standards → `docs/{AppName}-Coding-Standards.md` -Use the exact template content embedded in `day1-brownfield.md` §4 (the canonical block), including its one per-project decision: the instance-field prefix. Greenfield has no existing code to detect from, so default to `obj` unless `CustomInstructions` pick no-prefix. Record the decision in the standards file and CLAUDE.md per §4's instructions. +Load `.tfcore/templates/v4custom/app-coding-standards-tmpl.md` (reset Session 3, 2026-09-04: the standards live in the framework, `.tfcore/standards/coding-standards-core.md` plus `.tfcore/standards/coding-standards-.md` for the Stack answer set, and are not copied per project). Fill "Standards applied" with those two files and the per-project choices the stack file leaves open (for .NET: the instance-field prefix, default `obj` unless `CustomInstructions` pick no-prefix; record it in CLAUDE.md as before). Leave "Project rules" empty unless the concept demands a rule true of this project alone. Fill "Enforcement" from §5. Run `bash .tfcore/utils/tf-doc-check.sh docs/{AppName}-Coding-Standards.md`; fix any FAIL. ### 5. Create `.editorconfig` at repo root @@ -208,7 +205,9 @@ Do NOT auto-advance past day-1 (no split/build without the user). Rendering HTML - [ ] core-config.yaml has customTechnicalDocuments for this app - [ ] `docs/{AppName}-Architecture.md` (status: Target) with Mermaid -- [ ] `docs/{AppName}-BRD.md` with a §4 Development status table (one row per F-code, all `Planned` for greenfield) + a populated §9 Feature catalog (one `### F-…` per feature) + §10 BRD-N ledger + Mermaid diagrams (canonical three + per-feature where non-trivial), every diagram passing the §5.5 authoring self-check (quoted labels, no `end` ids) +- [ ] `docs/{AppName}-BRD.md` in template shape: header with Size and Kind, Screens and flow table, BRD-N ledger with one "When …, then …" acceptance line per item, Development status table (one row per screen, all `Planned`) +- [ ] `appSize` and `appKind` written to `.tfcore/core-config.yaml` +- [ ] `bash .tfcore/utils/tf-doc-check.sh --app {AppName}` prints no FAIL - [ ] If SourceDocs were harvested: BRD is a SUPERSET of their requirements content (no tables/detail dropped) - [ ] `docs/{AppName}-UIDesign.md` + `docs/mockups/*.html` produced (§3.6, TrBlazeUI-replicable, one per key screen) — or "skipped — no UI" recorded for an API-only app - [ ] `docs/{AppName}-Coding-Standards.md` diff --git a/.tfcore/templates/stack-defaults/dotnet.md b/.tfcore/templates/stack-defaults/dotnet.md new file mode 100644 index 0000000..5907503 --- /dev/null +++ b/.tfcore/templates/stack-defaults/dotnet.md @@ -0,0 +1,35 @@ +# TechieFlow — Stack Defaults: .NET + +| | | +|---|---| +| Purpose | An answer set for `TechieFlow-Stack-Questions.md`. These are the defaults the framework's author uses for their own .NET / Blazor / MAUI projects. Naming this answer set at day-1 fills in the questions it covers; the agent asks the rest. | +| Audience | Owners of .NET projects using TechieFlow. Owners in other stacks write their own answer set with the same structure. | +| Status | Drafted in Session 2 of the reset (2026-09-04) from the author's answers. Becomes a file under `.tfcore/templates/stack-defaults/` in Session 3. Configuration document read by agents; not rendered to HTML. | +| Applies to | Projects that declare `stack: dotnet` and name this answer set. Change any row for a project by overriding it in that project's Architecture Stack Decisions section; the override wins. | + +--- + +## Answers + +| Q | Topic | Default | Still asked at day-1? | +|---|---|---|---| +| Q1 | Configuration | Non-secret configuration lives in `appsettings.json`, layered by `appsettings.{Environment}.json`. No other configuration mechanism is created. | No | +| Q2 | Secrets in development | Project user secrets (`dotnet user-secrets`), visible to the developer, never committed. `appsettings.Development.json` is committed and therefore never holds a real secret. A `secrets.example.json` in the project lists every key the application reads. | No | +| Q3 | Database | PostgreSQL, running in Docker, for development and production. Docker is always running on the development machine because WSL, where the harness runs, depends on it. If PostgreSQL is unreachable the container is stopped: the agent starts it and continues. Only if no PostgreSQL container exists at all does the agent stop and ask the owner for connection details. It never substitutes another engine and never creates its own database container. | No, unless no container exists | +| Q4 | Authentication | Two choices only: **AppManager**, the author's shared platform for identity, roles, licences and subscriptions, which counts as part of the application and not as an external integration; or a different mechanism named by the owner. | **Yes**: "AppManager, or something else?" | +| Q5 | Logging | Serilog, always, with file logging wired at startup before anything else can fail. | No | +| Q6 | Tests | xUnit, always. A test project exists from day-1. | No | +| Q7 | Layout and naming | `src/` and `tests/` at the root. The primary executable project is named exactly ``; `.App` is banned. Secondary heads take a descriptive suffix (`.Api`, `.Desktop`). | No | +| Q8 | User interface | TrBlazeUI for every Blazor head and every MAUI Blazor Hybrid head. Native controls for WinForms, WPF and non-Blazor MAUI heads. Rendering mode is chosen per project and recorded. | Rendering mode only | +| Q9 | Hosting | Web applications run containerised on a Bluehost VPS. The owner supplies a pipeline guidance document at UAT go-ahead, and a deployment checklist is produced from it. | Asked at UAT go-ahead, as the questionnaire specifies | +| Q10 | Production secrets | Decided when the owner asks for the production pipeline, after UAT. Until then no document states where production secrets live. | Asked after UAT | +| Q11 | Standing rules and prohibitions | 1. Never add a NuGet package without a line in the Architecture document saying why. 2. Never create a second configuration mechanism. 3. When TechieFlow, TrBlazeUI, TechieRag or any other internal library lacks something, record it in that library's feedback file and hold the feature until the library is fixed. Never implement a workaround. 4. Data access is always Dapper. 5. Database migrations live in a dedicated project named `Db` (console or library) using the DbUp package. The web application runs the migration at startup, or the pipeline runs it after deployment. Never a `database` folder of loose scripts at the repository root. 6. Log files are written under the build output folder (`bin/`), which is git-ignored. Never at the repository root. 7. No unnecessary folders at the repository root (`data`, `database`, `scripts` and the like). Everything sits under `src/` or `tests/` according to what it belongs to. | No | + +--- + +## Notes + +- Q4 is asked on purpose even with this answer set, because the choice differs per product. +- Q3's "start the container, never create one" replaces the behaviour that produced TfLens's own database container: an agent that cannot reach the expected database restarts it or asks, and never builds an alternative. +- Q11.5 replaces the `database` folder of loose migration scripts that TfLens accumulated. +- Q11.3 is a standing framework convention restated as a stack rule so that it is recorded per project and its violations can be counted. diff --git a/.tfcore/templates/stack-questions.md b/.tfcore/templates/stack-questions.md new file mode 100644 index 0000000..4c50309 --- /dev/null +++ b/.tfcore/templates/stack-questions.md @@ -0,0 +1,77 @@ +# TechieFlow — Stack Questions + +| | | +|---|---| +| Purpose | The questions the framework asks the owner about a project's technology and conventions, when it asks them, and where the answers are recorded. The framework itself is technology-neutral; these answers are what make a project's documents and code specific. | +| Audience | Framework agents (the questionnaire is asked verbatim), project owners, contributors. | +| Status | Drafted in Session 2 of the reset (2026-09-04). Becomes a template under `.tfcore/templates/` in Session 3. Configuration document read by agents; not rendered to HTML. | +| Related | `TechieFlow-Stack-Defaults-DotNet.md` (an answer set), `TechieFlow-Requirements.md` (FR-01, FR-02) | + +--- + +## 1. Principle + +The framework never assumes a language, a UI library, a database, an authentication scheme, a test framework or a hosting target. Before any project document is written, the agent asks the questions below and records the answers in one place: the **Stack Decisions** section of the project's Architecture document. Every later command reads its stack facts from there and nowhere else. + +An owner who always works in one stack answers once by naming an **answer set**, a file that holds their standard answers. The framework ships one answer set, `TechieFlow-Stack-Defaults-DotNet.md`, described as the defaults its author uses for their own .NET projects. Any owner may copy it, change it, or write a different one. Naming an answer set fills in every question it covers; the agent then asks only the questions the set leaves open. + +--- + +## 2. When the questions are asked + +| Moment | Questions | Why then | +|---|---|---| +| Day-1, before any document is written | Q1 to Q8 | Every document depends on the answers. | +| When the owner gives the go-ahead for user acceptance testing (UAT) | Q9 | Hosting decisions made earlier are guesses; by UAT the application is real. | +| When the owner asks for a production deployment pipeline, after UAT | Q10 | Production secrets and infrastructure are decided by the people who run production. | + +In YOLO mode the agent does not skip the day-1 questions; it takes the answer set's value where one exists and stops with a clear message where none does. A document written without a recorded answer is a defect. + +--- + +## 3. The questions + +**Q1. Configuration.** Where does non-secret configuration live, and how is it layered per environment? + +**Q2. Secrets in development.** Where do secrets live on a developer machine, and what must never be committed? + +**Q3. Database.** Which database engine in development and which in production? Is a container for the database acceptable, and under what condition? What does the agent do if the database is not reachable? + +**Q4. Authentication and authorisation.** Which identity mechanism: a shared platform, the framework's built-in identity, an external provider, or none? Who owns roles, licences and subscriptions? + +**Q5. Logging.** Which logging library, which sinks, and is file logging mandatory? + +**Q6. Tests.** Which test framework? Is a test project mandatory from day one? Which categories of test are expected (unit, integration, browser)? + +**Q7. Solution layout and naming.** Folder layout for source and tests; naming rule for the primary executable project and for secondary heads. + +**Q8. User interface.** Which UI framework or component library for each kind of head (web, mobile, desktop)? Which rendering mode by default? + +**Q9. Hosting and deployment.** Where does the application run, in what form (container, service, static files), and is there a deployment guidance document to follow? Is a deployment checklist required? + +**Q10. Production secrets and pipeline.** Where do production secrets live, and how does the pipeline inject them? + +**Q11. Standing rules and prohibitions.** Anything an agent must always or never do in this repository that is not already enforced by a hook. Recorded as a list; the framework treats each item as a rule and tracks whether it is honoured. Two rules are present by default in every project, whatever the stack, and an owner may remove them only by saying so: + +1. Log files are written under the build output folder, which is git-ignored. Never at the repository root. +2. No unnecessary folders at the repository root (`data`, `database`, `scripts` and the like). Everything sits under the source folder or the tests folder according to what it belongs to. + +--- + +## 4. Where the answers go + +The Architecture document gains a section, **Stack Decisions**, placed first, one row per question: + +| Q | Decision | Source | Date | +|---|---|---|---| +| Q1 | `appsettings.json` plus `appsettings.{Environment}.json` | answer set: DotNet | 2026-09-04 | +| Q3 | PostgreSQL in a container for development and production | owner, day-1 | 2026-09-04 | +| … | | | | + +"Source" is either the answer set name or "owner" with the moment it was asked. Q9 and Q10 rows are added when they are answered; until then they read "not yet asked (UAT)" and "not yet asked (production)". The Coding Standards document references this section instead of restating it. + +--- + +## 5. What this replaces + +Before this document, the framework had one standing technology decision (Serilog file logging) written into the day-1 tasks, the BRD template and the Architecture template, and no place for any other. Projects therefore invented their own arrangements at build time; TfLens ended with settings in three places. This questionnaire moves every such decision to day-1 and to one recorded location. diff --git a/.tfcore/templates/v4custom/app-architecture-tmpl.md b/.tfcore/templates/v4custom/app-architecture-tmpl.md index f529ff3..29748c5 100644 --- a/.tfcore/templates/v4custom/app-architecture-tmpl.md +++ b/.tfcore/templates/v4custom/app-architecture-tmpl.md @@ -1,127 +1,117 @@ -# {AppName} — Architecture - -**Last updated:** {YYYY-MM-DD} -**Status:** Current (brownfield) | Target (greenfield) | Current + planned target (brownfield with structural change) - - + + +# {App} — Architecture + +| | | +|---|---| +| App | {App} | +| Kind | app or library | +| Size | Small, Medium or Large | +| Stack answer set | {name, or "none"} | +| Date | {YYYY-MM-DD} | + +## 1. Stack decisions + +One row per stack question. "Source" says where the answer came from: the answer set, the owner, or the existing code. + +| Q | Topic | Decision | Source | +|---|---|---|---| +| Q1 | Configuration | {…} | {answer set / owner / code} | +| Q2 | Secrets in development | {…} | {…} | +| Q3 | Database | {…} | {…} | +| Q4 | Authentication | {…} | {…} | +| Q5 | Logging | {…} | {…} | +| Q6 | Tests | {…} | {…} | +| Q7 | Layout and naming | {…} | {…} | +| Q8 | User interface | {…} | {…} | +| Q11 | Standing rules | {…} | {…} | + +## 2. Solution structure + +| Project | Kind | Purpose | +|---|---|---| +| `{App}` | {web app, desktop app, API, …} | {the primary head} | +| `{App}.Core` | class library | {…} | +| `{App}Db` | migrations project | {…} | +| `{App}.Tests` | test project | {…} | + +## 3. Component map -## Table of Contents - - - -1. [Tech stack](#tech-stack) -2. [Component map](#component-map) -3. [Data flow — primary path](#data-flow-primary-path) -4. [Module responsibilities](#module-responsibilities) -5. [Cross-cutting concerns](#cross-cutting-concerns) -6. [Deployment architecture](#deployment-architecture) -7. [Architectural decisions (ADR-style log)](#architectural-decisions-adr-style-log) -8. [Target architecture (brownfield only — if enhancement changes structure)](#target-architecture-brownfield-only-if-enhancement-changes-structure) -9. [Open questions / risks](#open-questions-risks) - -## 1. Tech stack -| Layer | Choice | Version | Notes | -|-------|--------|---------|-------| -| Runtime | .NET 9 | … | … | -| UI | Blazor [Server/WASM/Auto] + TrBlazeUI | … | … | -| AI/RAG | TechieRag | … | If applicable | -| DB | SQL Server / SQLite / Postgres | … | … | -| Vector store | SqliteVec / PgVector / Qdrant | … | If RAG | -| Auth | … | … | … | - -## 2. Component map ```mermaid flowchart TB - subgraph UI["Blazor UI"] - Dash["Dashboard"] - Settings["Settings"] - end - subgraph BE["Backend"] - API["API"] - Auth["Auth"] - Rag["RAG service"] - end - subgraph Data["Data"] - SQL[("SQL")] - Vec[("Vector")] - end - UI --> API - API --> Auth - API --> Rag - Rag --> Vec - API --> SQL + UI["UI"] --> Svc["Services"] + Svc --> Data["Data access"] + Data --> DB[("Database")] ``` -## 3. Data flow — primary path -```mermaid -sequenceDiagram - actor U - participant UI - participant API - participant Svc as Service - participant DB - U->>UI: action - UI->>API: HTTPS - API->>Svc: call - Svc->>DB: query - DB-->>Svc: rows - Svc-->>API: dto - API-->>UI: json - UI-->>U: render -``` +**How a request travels** (one typical request, in words; per-screen detail is in the DevGuide): +1. {The page calls …} +2. {The service …} +3. {The data access …} +4. {The result is shown …} + +## 4. Data model -## 4. Module responsibilities -| Module | Responsibility | Depends on | -|--------|----------------|------------| -| `src/{AppName}.Web` | UI host | Domain, Infra | -| `src/{AppName}.Domain` | Entities, business rules | (none) | -| `src/{AppName}.Infrastructure` | EF, external services | Domain | -| `src/{AppName}.Rag` | TechieRag wiring (if applicable) | Domain | - -## 5. Cross-cutting concerns -- Logging — Serilog file-based logging (rolling file sink under `logs/`, wired at startup in EVERY executable head — web, API, MAUI, desktop, console; app code logs via `ILogger`). This is a TechieFlow standing requirement, not a per-app choice — see Coding Standards §Logging. -- Error handling — global middleware; ProblemDetails responses -- Auth — JWT / cookie / Azure AD -- Caching — IMemoryCache / Redis -- Telemetry — OpenTelemetry / Application Insights - -## 6. Deployment architecture ```mermaid -flowchart LR - Dev["Dev"] --> CI["GitHub Actions"] - CI --> Reg["Container Reg"] - Reg --> AKS["Azure App Service / AKS"] - AKS --> ProdDB[("SQL")] +erDiagram + USER ||--o{ ENTRY : writes + ENTRY { + int EntryId PK + string Title + } ``` -## 7. Architectural decisions (ADR-style log) -- **ADR-001 — .** Reason: … -- **ADR-002 — .** Reason: … +| Entity | Key fields | Notes | +|---|---|---| +| {Entity} | {…} | {…} | -## 8. Target architecture (brownfield only — if enhancement changes structure) -```mermaid -flowchart TB - Existing["existing module"] --> New["new / changed module"] - New --> Removed["removed box (struck through in prose)"] -``` -Describe deltas: what's added, what's removed, what's renamed, migration path. +## 5. Cross-cutting + +- **Identity:** {AppManager API, or the mechanism chosen at Q4}. +- **Configuration:** {…} +- **Logging:** {…} +- **Errors:** {…} + +## 6. Decisions log + +One row per decision. Every package added to the project has a row saying why. + +| Date | Decision | Why | Status | +|---|---|---|---| +| {YYYY-MM-DD} | {…} | {…} | decided, planned, done | + +## 7. Module responsibilities + +| Module | Responsibility | Depends on | +|---|---|---| +| {…} | {…} | {…} | + +## 8. Open questions -## 9. Open questions / risks -- … +- {…} diff --git a/.tfcore/templates/v4custom/app-brd-tmpl.md b/.tfcore/templates/v4custom/app-brd-tmpl.md index 7d29604..4a90a11 100644 --- a/.tfcore/templates/v4custom/app-brd-tmpl.md +++ b/.tfcore/templates/v4custom/app-brd-tmpl.md @@ -1,204 +1,121 @@ -# {AppName} — Business Requirements - - + +# {App} — Business Requirements -## Table of Contents - - - -1. [Executive summary](#executive-summary) -2. [Business objectives](#business-objectives) -3. [Scope](#scope) -4. [Development status](#development-status) -5. [Stakeholders / users](#stakeholders-users) -6. [Context diagram](#context-diagram) -7. [User journey — primary use case](#user-journey-primary-use-case) -8. [Component sketch](#component-sketch) -9. [Feature catalog](#feature-catalog) -10. [Functional requirements (BRD ledger)](#functional-requirements-brd-ledger) -11. [Non-functional requirements](#non-functional-requirements) -12. [Constraints & assumptions](#constraints-assumptions) -13. [Success metrics](#success-metrics) -14. [Risks](#risks) -15. [Glossary](#glossary) - -## 1. Executive summary -<2-3 paragraphs: what we're building/changing and why it matters.> - -## 2. Business objectives -- -- - -## 3. Scope -**In scope:** … -**Out of scope (explicit):** … - -## 4. Development status - - - -**Snapshot as of {YYYY-MM-DD}.** Live, per-requirement status: see `PROJECT-STATUS.md` and the **Requirements Status** table in `docs/{AppName}-Checklist.md`. - -| Feature (F-code) | Phase | Status | % | Notes | -|------------------|-------|--------|---|-------| -| F-{CODE}: {name} | {0 / 1 / 2 … or MVP} | Done | 100 | {what works} | -| F-{CODE}: {name} | {phase} | Partial | {0–100} | {what's done / what's left} | -| F-{CODE}: {name} | {phase} | Planned | 0 | {not started} | - -**Legend:** **Done** = shipped & working · **In progress** = actively being built · **Partial** = some sub-features done, others pending · **Planned** = not started. (Maps to the checklist's `Done (pre-existing)` / `In Progress` / `PARTIAL` / `Not Started`.) - -## 5. Stakeholders / users - -| Role | Needs | -|------|-------| -| End user | … | -| Admin | … | - -## 6. Context diagram -```mermaid -flowchart LR - User(["End User"]) --> App["{AppName}"] - App --> DB[("Database")] - App --> LLM[/"LLM Provider"/] -``` +| | | +|---|---| +| App | {App} | +| Kind | app or library | +| Size | Small, Medium or Large | +| Stack answer set | {name of the answer set, or "none"} | +| Status | Draft, Approved | +| Date | {YYYY-MM-DD} | -## 7. User journey — primary use case -```mermaid -sequenceDiagram - actor U as User - participant W as Web UI - participant A as App API - U->>W: action - W->>A: request - A-->>W: response - W-->>U: result -``` +## 1. Summary -## 8. Component sketch -```mermaid -flowchart TB - UI["Blazor UI — TrBlazeUI"] --> API["ASP.NET API"] - API --> SQL[("SQL")] - API --> Rag["RAG — TechieRag"] - Rag --> Vec[("Vector store")] -``` +{What it is, for whom, why it exists. At most 200 words.} + +## 2. Scope + +**In:** +- {…} + +**Out:** +- {…} + +## 3. Users and roles + +| Role | Who they are | What they need | +|---|---|---| +| {Role} | {…} | {…} | -## 9. Feature catalog +## 4. Screens and flow - +One row per routed page. A dialog is a row under its parent screen with `on /route` in the Route column; it is not counted as a screen. -### F-{CODE}: {Feature name} +| Screen | Route | Role | Mockup | Fields | +|---|---|---|---|---| +| {Screen name} | `/route` | {Role} | [mockup](mockups/{screen-slug}.html) | {field, field, field} | +| {Dialog name} (dialog) | on `/route` | {Role} | [mockup](mockups/{screen-slug}.html) | {field, field} | -**Personas:** · **Phase:** <0/1/2/… or MVP/Later> +**Primary journey:** +1. {The user opens … and …} +2. {…} -<1-2 paragraphs: what this feature does and why it exists.> +## 5. Requirements -| Screen | Route | Description | -|--------|-------|-------------| -| … | `/…` | … | +One item per thing the verifier will test. Each names its screen, links the mockup, and states its acceptance in the form the checklist will carry. -**Workflow:** -1. -2. +- **BRD-1** — {Title}. *Screen:* {Screen name} · *Mockup:* [mockup](mockups/{screen-slug}.html) + - *Acceptance:* When {actor} {does what} on {screen}, then {a result a browser robot can observe}. +- **BRD-2** — {Title}. *Screen:* {Screen name} · *Mockup:* [mockup](mockups/{screen-slug}.html) + - *Acceptance:* When …, then …. + +## 6. Non-functional requirements + +| Id | Area | Requirement | Measure | +|---|---|---|---| +| BRD-{N} | Performance | {…} | perf-budget: p95 load <= 2000ms @ concurrency 1 | +| BRD-{N} | Security | {…} | {…} | +| BRD-{N} | Logging | {from the Stack answer set} | {…} | + +The `perf-budget:` measure is machine-read by the verifier, in exactly this form: `perf-budget: <= ms [@ concurrency ]`. Write one only where the owner stated a number. + +## 7. Development status + +Written by the status gate after every build, verify and handoff; not by hand. + +**Snapshot as of {YYYY-MM-DD}.** Live per-requirement status: `PROJECT-STATUS.md` and the Requirements Status table in `docs/{App}-Checklist.md`. + +| Screen | Requirements | Verified | Open | Status | +|---|---|---|---|---| +| {Screen} | {n} | {n} | {n} | Planned, In progress, Partial, Done | + +## 8. Context diagram ```mermaid flowchart LR - A["input"] --> B{"decision"} --> C["output"] + User(["User"]) --> App["{App}"] + App --> DB[("Database")] ``` -**Requirements:** BRD-x, BRD-y (see §10) - -## 10. Functional requirements (BRD ledger) - - - -- **BRD-1** — *(F-{CODE})* -- **BRD-2** — *(F-{CODE})* - -## 11. Non-functional requirements - -- **BRD-N** — Performance: … - - -- **BRD-N+1** — Security: … -- **BRD-N+2** — Accessibility: … -- **BRD-N+3** — Observability: Serilog file-based logging in every executable head — rolling file sink under `logs/`, wired at startup, unhandled exceptions logged (see Coding Standards §Logging). - -## 12. Constraints & assumptions -- … - -## 13. Success metrics -- … - -## 14. Risks +## 9. Constraints and assumptions + +- {…} + +## 10. Risks + | Risk | Likelihood | Impact | Mitigation | +|---|---|---|---| +| {…} | {…} | {…} | {…} | -## 15. Glossary -- TrBlazeUI, TechieRag, REQ-UI-*, REQ-FN-*, REQ-RAG-*, REQ-NFR-* +## 11. Glossary ---- -Last updated: {YYYY-MM-DD} -Highest BRD ID: BRD-{N} +- {Term} — {meaning} diff --git a/.tfcore/templates/v4custom/app-checklist-tmpl.md b/.tfcore/templates/v4custom/app-checklist-tmpl.md index 6178fe3..9c9fa6f 100644 --- a/.tfcore/templates/v4custom/app-checklist-tmpl.md +++ b/.tfcore/templates/v4custom/app-checklist-tmpl.md @@ -1,90 +1,62 @@ -# {AppName} — Checklist - -## Table of Contents - - - -1. [Goal](#goal) -2. [Requirements Status](#requirements-status) -3. [UI / Pages](#ui--pages) -4. [Functional requirements](#functional-requirements) -5. [RAG / AI requirements (→ /techierag)](#rag-ai-requirements-techierag) -6. [Non-functional](#non-functional) + + + +# {App} — Checklist + +| | | +|---|---| +| App | {App} | +| Size | Small, Medium or Large | ## Goal - -## Requirements Status +{One paragraph tying back to the BRD summary. This single checklist is the whole work list.} - +## Requirements Status | ID | Requirement | Status | % | Remarks | Details | |----|-------------|--------|---|---------|---------| -| REQ-UI-001 | Dashboard top nav | Not Started | 0% | — | [view](#d-req-ui-001) | -| REQ-FN-001 | | Not Started | 0% | — | [view](#d-req-fn-001) | -| REQ-RAG-001 | | Not Started | 0% | — | [view](#d-req-rag-001) | -| REQ-NFR-001 | | Not Started | 0% | — | [view](#d-req-nfr-001) | - -**Status values:** `Not Started` · `In Progress` · `Implemented` (code done, not yet verified) · `Verified` (self-smoke or verifier PASS — acceptance AND data-render AND visual gates all pass) · `Done (pre-existing)` (migrated from an earlier dev plan as already complete — build agents must NOT rebuild; terminal like `Verified`) · `Needs re-verify` (a defect or change was logged — must be re-run before it can return to `Verified`) · `PARTIAL` (some acceptance unmet — say what in Remarks) · `FAIL` (verifier ran and failed — bug in Remarks) · `Blocked` (external/library gap — link the TR-/TR-RAG- entry in Remarks) · `N/A`. - -**% guide:** `0` not started · `25` scaffolded · `50` in progress · `75` implemented-unverified · `100` verified. - -**Remarks:** date + what was done / what is missing / bug or library reference. This is the home for bugs and change notes — do not spawn a separate file. Visual-gate failures are prefixed `⚠ visual:`; security findings `⚠ SECURITY`. - -## UI / Pages +| REQ-UI-001 | {short name} | Not Started | 0% | — | [view](#d-req-ui-001) | +| REQ-FN-001 | {short name} | Not Started | 0% | — | [view](#d-req-fn-001) | +| REQ-NFR-001 | {short name} | Not Started | 0% | — | [view](#d-req-nfr-001) | - - - -### Page: Dashboard (`/dashboard`) +## Page: {Screen name} (`/route`) -- **REQ-UI-001** — TrBlazeUI top nav (logo, user menu, theme toggle). *Mockup:* docs/mockups/dashboard.html. - - *Acceptance:* page renders; nav fixed-top; theme toggle persists in localStorage; controls do not overlap at desktop + mobile widths (visual gate). - -## Functional requirements +- **REQ-UI-001** — {what the screen shows}. *BRD:* BRD-{N} · *Mockup:* mockups/{screen-slug}.html + - *Acceptance:* When {actor} opens {screen}, then {what is visible, with real data, at desktop and mobile widths}. -- **REQ-FN-001** — . - - - - -## RAG / AI requirements (→ /techierag) - - -- **REQ-RAG-001** — . +- **REQ-FN-001** — {what the user can do}. *BRD:* BRD-{N} + - *Acceptance:* When {actor} {does what} on {screen}, then {observable result}. ## Non-functional -- **REQ-NFR-001** — . +- **REQ-NFR-001** — {the requirement}. *BRD:* BRD-{N} + - *Acceptance:* When {the measurement is taken}, then {the value within its limit}; perf-budget: p95 load <= 2000ms @ concurrency 1 diff --git a/.tfcore/templates/v4custom/app-coding-standards-tmpl.md b/.tfcore/templates/v4custom/app-coding-standards-tmpl.md new file mode 100644 index 0000000..a1fd159 --- /dev/null +++ b/.tfcore/templates/v4custom/app-coding-standards-tmpl.md @@ -0,0 +1,52 @@ + + + +# {App} — Coding Standards + +| | | +|---|---| +| App | {App} | +| Stack answer set | {name, or "none"} | +| Date | {YYYY-MM-DD} | + +## Standards applied + +| File | Applies | Notes | +|---|---|---| +| `.tfcore/standards/coding-standards-core.md` | yes | every project | +| `.tfcore/standards/coding-standards-{stack}.md` | yes | {from the Stack answer set} | + +Per-project choices the stack file leaves open: + +| Choice | Decision | +|---|---| +| {e.g. instance-field prefix} | {…} | + +## Project rules + +Rules that hold in this project only. Empty is a valid answer. + +| Rule | Why | Since | +|---|---|---| +| {…} | {…} | {YYYY-MM-DD} | + +## Enforcement + +- **Editor configuration:** `.editorconfig` at the repository root carries the machine-checkable subset. +- **Analyzers:** {which, or "none"}. +- **Verifier checks:** the standards check runs the greps listed in the stack file's Enforcement section, plus: {any project-specific grep, or "none"}. diff --git a/.tfcore/templates/v4custom/app-devguide-tmpl.md b/.tfcore/templates/v4custom/app-devguide-tmpl.md index 5acb5ff..57f0b73 100644 --- a/.tfcore/templates/v4custom/app-devguide-tmpl.md +++ b/.tfcore/templates/v4custom/app-devguide-tmpl.md @@ -1,167 +1,86 @@ -# {AppName} — Developer Guide (Screen-by-Screen Code Map) - -> {Verification-status banner — REQUIRED, set by the OBSERVE pass (devguide §5a). One of:} -> ✅ **Runtime-verified {YYYY-MM-DD}** — exercised as: {roles}. Control render-status **and per-screen visual-status** below are **observed** (data renders + screen looks right), not inferred. Per-screen screenshots in `docs/screenshots/{AppName}/`. -> ⚠ **STATIC-ONLY ({YYYY-MM-DD})** — built from code reading; **NOT yet runtime-verified**. Render/visual status is unconfirmed and no screenshots were captured until `*verify` runs against the running app. - -> **Purpose — this is the document a HUMAN developer uses to trace any screen, control, or number on the page all the way down to the database, so they can find and fix a bug, or verify that AI-generated code is actually correct.** The BRD explains *what* the app does; the Architecture explains *how the system is shaped*; the database design explains *the data*. **None of those tell a developer "the dashboard's Ruling-Planet chart comes from `DashboardService.GetRulingPlanets()` → `KundliRepository.QueryRulingPlanets()` → `usp_RulingPlanets`."** This guide does exactly that, per user role, per screen, down to the stored procedure or query. -> -> It documents the **AS-BUILT code**, not the plan. Regenerate it with `*devguide {AppName}` after meaningful code changes (it is also refreshed automatically at handoff). It is a **human-readable** doc — it is rendered to HTML; it is NOT one of the AI checklists. - - - -## Table of Contents - -1. [How to use this guide](#how-to-use-this-guide) -2. [Architecture cheat-sheet](#architecture-cheat-sheet) -3. [Roles and menu map](#roles-and-menu-map) -4. [Screen-by-screen code map](#screen-by-screen-code-map) -5. [Cross-cutting flows](#cross-cutting-flows) -6. [How to fix a bug with this guide](#how-to-fix-a-bug-with-this-guide) - ---- - -## How to use this guide - -- **Find your screen** in §4, grouped by user role. Each screen tells you the route, the Razor file, every control, and where each control's data comes from. -- **Chasing a wrong number / missing data?** Find the control in that screen's *Data lineage* table → it names the service method → the data-access method → the stored proc / query. Open those files in order. -- **Verifying AI-generated code?** Compare what this guide claims against the actual files. If a row says `usp_GetDashboardKpis` but the service actually calls inline SQL, the guide (or the code) is wrong — that mismatch is exactly the kind of hallucination this guide is meant to catch. -- **For a large app (many roles / screens)** this guide is split into one file per role, kept together in the `docs/devguides/` subfolder (so they don't clutter `docs/`) — see the [index](#roles-and-menu-map). Open only the role file you need. -- **Library variant (a library is documented by its PUBLIC SURFACE, not its sample app's screens — `devguide.md §0`).** Replace "Roles and menu map" with a **catalog by category/module**, and each item block documents **Purpose · Public API surface** (the consumer's contract, read at file:line) **· Internal flow** (file:line) **· Demo & usage** (the `demos/`/`samples/` page or sample code + screenshot where it has UI + a copy-paste snippet) **· Known issues**. The unit depends on what the package ships: - - **UI-component library** (e.g. TrBlazeUI) → **component-by-component**; API surface = every `[Parameter]`/`EventCallback`/`@typeparam`; categories = Inputs / Layout / Overlays / Data display / Feedback / Icons; snippet = ``. - - **Service/SDK library** (e.g. TechieRag) → **service/API-by-service**; API surface = the `AddXxx(...)` DI registration + options + facade/interface method signatures; modules = e.g. Ingestion / Embedding / Query-RAG / LLM / Resilience+Token / Admin; snippet = `services.AddXxx(...)` + an `IFacade` call. - - The "find your screen / chase a wrong number" guidance above reads as "find your component/service / chase a broken parameter or method". Any public item the sample app does **not** exercise is marked **`⚠ no demo coverage`** (a sample gap logged to the checklist) — the sample app is part of the library's deliverable, not just a backdrop. + + +# {App} — Developer Guide + +| | | +|---|---| +| App | {App} | +| Kind | app or library | +| Size | Small, Medium or Large | +| Verified on | {YYYY-MM-DD, the run the screenshots and line numbers come from} | +| Date | {YYYY-MM-DD} | ## Architecture cheat-sheet -Brief — just enough to navigate the code. (Full detail lives in `docs/{AppName}-Architecture.md`.) - -| Layer | Project / folder | What lives here | Example types | -|-------|------------------|-----------------|---------------| -| UI (Blazor) | `src/{AppName}.Web` | Razor pages/components, layouts, nav menu | `Pages/`, `Components/`, `Shared/NavMenu.razor` | -| App services | `src/{AppName}.Application` (or `.Services`) | Business logic, orchestration | `DashboardService`, `KundliService` | -| Data access | `src/{AppName}.Infrastructure` (or `.Data`) | Repositories, DbContext, Dapper queries | `KundliRepository`, `AppDbContext` | -| Database | `database/` / `src/{AppName}.Db` | Tables, stored procs, seed scripts | `usp_*.sql`, migrations | +```mermaid +flowchart LR + UI["Pages"] --> Svc["Services"] --> Data["Data access"] --> DB[("Database")] +``` -- **Stored-proc vs ORM vs inline SQL:** {state the project's convention — e.g. "reads go through stored procs `usp_*`; writes via EF Core SaveChanges" — fill from the actual code}. -- **How a Razor component gets its data:** {e.g. "components inject a `*Service`; services inject a `*Repository`; repositories call `usp_*` via Dapper" — fill from the actual code}. +| Layer | Project or folder | What lives here | +|---|---|---| +| UI | `src/{App}/Pages` | {…} | +| Services | `src/{App}.Core/Services` | {…} | +| Data access | `src/{App}.Core/Data` | {…} | ## Roles and menu map -The app has these user roles (from `docs/{AppName}-UsageGuide.md` test-users + the authorization policies in code). For a **split** guide, this index and every role file live together in `docs/devguides/`, and each role links to its own file (relative `./{AppName}-DevGuide-{Role}.md`). - -| Role | Test user (see UsageGuide) | Authorization (policy / role claim in code) | Menus this role sees | Detail file | -|------|----------------------------|----------------------------------------------|----------------------|-------------| -| {App Admin} | {admin@…} | {`[Authorize(Roles="Admin")]` / policy name} | {Dashboard, Users, Settings, …} | [{App}-DevGuide-AppAdmin.md](./{AppName}-DevGuide-AppAdmin.md) | -| {App Manager} | {manager@…} | {…} | {Dashboard, Reports, …} | [{App}-DevGuide-AppManager.md](./{AppName}-DevGuide-AppManager.md) | -| {End user} | {user1@…} | {…} | {Home, Profile, …} | [{App}-DevGuide-EndUser.md](./{AppName}-DevGuide-EndUser.md) | - -For each role, list the **menu → menu-item → screen** mapping so a developer knows, for a given role, what appears and what each item opens: - -### {Role} — menu structure -- **{Menu group}** → **{Menu item}** → opens `{Route}` (`{RazorFile}`) — see [§4 {Role} · {Screen}](#role--screen) -- ... +| Role | Test user | Menu items and the screen each opens | +|---|---|---| +| {Role} | {user #} | {Menu → screen (`/route`)} | ## Screen-by-screen code map -One subsection per screen, grouped by role, in navigation order. **In a split guide, the per-role file carries only that role's screens.** Repeat the block below for every screen. - ---- - -### {Role} · {Screen / Page name} +### {Screen name} (`/route`) -- **Route:** `{@page "/dashboard"}` -- **Razor file:** `src/{AppName}.Web/Pages/Dashboard.razor` (+ code-behind `Dashboard.razor.cs` if present) -- **Reached via:** {Menu group → menu item}; **Log in as:** {test user from UsageGuide} -- **What this screen does:** {one or two lines} -- **Visual status:** {✅ looks-right (runtime-confirmed {date}) | ⚠ visual-broken (DEFECT — {what} @ {width}, {date}) | static-only (unconfirmed)} +![{Screen name}](screenshots/{App}/{screen-slug}.png) -**Screenshot** — the real rendered screen (captured by the OBSERVE pass, devguide §5a). Review this for layout/overlap issues. +**Call chain:** `{Page}.razor.cs:{HandleX}` → `{Service}.{Method}` → `{DataAccess}.{Method}` → `{table or procedure}` -![{Screen} — {Role}](../screenshots/{AppName}/{role}-{screen-slug}.png) +| File and line | Function | Watch | Expected value | +|---|---|---|---| +| `src/{App}/Pages/{Page}.razor.cs:127` | `HandleX` | `aModel.Email` | the email typed in the box | +| `src/{App}.Core/Services/{Service}.cs:54` | `{Method}` | `vResult` | {…} | +| `src/{App}.Core/Data/{DataAccess}.cs:31` | `{Method}` | `vRows.Count` | {…} | - - - -**Screen flowchart** — show every meaningful control on the screen and where its data comes from. (Follow the Mermaid authoring rules in `.tfcore/templates/v4custom/html-render-shell.md §5.5` — quote every label, never use `end` as a node id.) - -```mermaid -flowchart TD - P["Dashboard.razor"] --> C1["Kundli list (grid)"] - P --> C2["Ruling-Planet chart"] - P --> C3["Ruling-Planet table"] - C1 --> S1["DashboardService.GetKundliList()"] - C2 --> S2["DashboardService.GetRulingPlanets()"] - C3 --> S2 - S1 --> R1["KundliRepository.QueryList()"] - S2 --> R2["KundliRepository.QueryRulingPlanets()"] - R1 --> DB1[("usp_GetKundliList")] - R2 --> DB2[("usp_RulingPlanets")] -``` - -**Controls on this screen** - -| Control | Type | Purpose | Populated / calculated by | -|---------|------|---------|---------------------------| -| {Kundli list} | {grid} | {lists kundlis for the tenant} | {`DashboardService.GetKundliList()`} | -| {Ruling-Planet chart} | {chart} | {shows current ruling planets} | {`DashboardService.GetRulingPlanets()` — values computed in `RulingPlanetCalculator`} | - -**Data lineage** — the full path for each control/action. This is the heart of the guide. - -| Control / Action | Razor component (file) | Service method (file) | Data-access method (file) | Stored proc / Query | Notes / calculation | -|------------------|------------------------|-----------------------|---------------------------|---------------------|---------------------| -| {Load Kundli list} | {`Dashboard.razor` line ~40} | {`DashboardService.GetKundliList()`} | {`KundliRepository.QueryList()`} | {`usp_GetKundliList`} | {paged; tenant-filtered} | -| {Ruling-Planet chart} | {`Dashboard.razor` ``} | {`DashboardService.GetRulingPlanets()`} | {`KundliRepository.QueryRulingPlanets()`} | {`usp_RulingPlanets`} | {planet strengths computed in `RulingPlanetCalculator.Compute()` from the proc rows} | -| {Save / submit action} | {…} | {…} | {…} | {…} | {…} | - -**Business rules / calculations on this screen** -- {Any non-trivial computation, validation, or derived value — name the method that does it.} - -**Known issues / gotchas** -- {Anything fragile, any TR-NNN library gap, any `Blocked` REQ touching this screen.} - -_(Repeat the `### {Role} · {Screen}` block for every screen the role can reach.)_ - ---- +**Calculations on this screen:** {method name and what it computes, or "none"} ## Cross-cutting flows -Flows that span screens or run in the background (auth/login, token refresh, background jobs, notifications, file ingestion). One subsection each with a Mermaid diagram and the same service → data-access → proc lineage. Skip if the app has none. +### Sign-in +**Call chain:** {…} + +| File and line | Function | Watch | Expected value | +|---|---|---|---| +| {…} | {…} | {…} | {…} | -## How to fix a bug with this guide +### Configuration, logging, errors +{One line each: where it is wired, file and line.} -1. Reproduce the bug and note **which screen** and **which control** shows it. -2. Open §4, find that role + screen, find the control in the **Data lineage** table. -3. Walk the lineage **top-down**: Razor component → service method → data-access method → stored proc/query. The bug is in one of those four. -4. If the visible value is *calculated*, the lineage row names the calculator method — check it. -5. After fixing, re-run the screen's walkthrough in `docs/{AppName}-UsageGuide.md`, then re-generate this guide (`*devguide {AppName}`) if the code path changed. +## Known issues ---- -_Generated/refreshed by `*devguide {AppName}`. Reflects the code as built at the time shown in the subtitle — regenerate after code changes._ +- {one line each, with the REQ or feedback id} diff --git a/.tfcore/templates/v4custom/app-productguide-tmpl.md b/.tfcore/templates/v4custom/app-productguide-tmpl.md index 3c1588e..c3c82a2 100644 --- a/.tfcore/templates/v4custom/app-productguide-tmpl.md +++ b/.tfcore/templates/v4custom/app-productguide-tmpl.md @@ -1,54 +1,57 @@ -# {AppName} — Product Guide - -> **Audience: end users (not developers).** This is the how-to-use-the-app manual — task-oriented, screenshot-illustrated, plain language. It explains *what each screen is for* and *how to do each thing*, not how the code works (that's the DevGuide). It is a HUMAN document → always written as markdown AND rendered to HTML. - -## Table of Contents - - - -1. [Welcome](#welcome) -2. [Getting started](#getting-started) -3. [Roles at a glance](#roles-at-a-glance) -4. [Using {AppName}](#using-appname) + + + +# {App} — Product Guide + +| | | +|---|---| +| App | {App} | +| Size | Small, Medium or Large | +| Date | {YYYY-MM-DD} | ## Welcome - +{What it does and who it is for, at most 150 words.} ## Getting started -- **Sign in:** {how to reach the app + log in; the sign-in screen with its screenshot}. -- **What you'll see first:** {the landing screen per role — keep it accurate to the real post-login landing, the same one the DevGuide's LANDING-TRUTH established}. +1. {Open … and sign in.} +2. {What you see first.} -![Sign in](./screenshots/{AppName}/{anon}-login.png) +![Sign in](screenshots/{App}/login.png) ## Roles at a glance - - | Role | What you can do | -|------|-----------------| +|---|---| | {Role} | {one line} | -## Using {AppName} - - - -### {Feature / Screen name} +## Using {App} -**What it's for:** {one or two plain sentences — the user benefit, from the BRD feature catalog.} +### {Task name} -**How to use it:** -1. {step — what the user clicks/enters} +1. {step} 2. {step} -3. {what they should see / the result} +3. {what you should see} -![{Screen name}](./screenshots/{AppName}/{role}-{screen-slug}.png) +![{Task name}](screenshots/{App}/{screen-slug}.png) -**Tips & notes:** {gotchas, limits, anything from the UsageGuide's known-limitations that a user should know — plain language, no defect IDs.} +## Troubleshooting - +- {problem}: {what to do} diff --git a/.tfcore/templates/v4custom/app-project-status-tmpl.md b/.tfcore/templates/v4custom/app-project-status-tmpl.md index 33dfb77..bd16dae 100644 --- a/.tfcore/templates/v4custom/app-project-status-tmpl.md +++ b/.tfcore/templates/v4custom/app-project-status-tmpl.md @@ -1,98 +1,88 @@ + + + --- -project: {AppName} -stack: .NET 9 / Blazor [Server|WASM|Auto] / TrBlazeUI / TechieRag / [MAUI] +project: {App} last_updated: {YYYY-MM-DD} -current_phase: Discovery | UI build | UI verify | Functional build | Functional verify | Handoff | Released -last_verified_build: PASS | FAIL | not-run +current_phase: {Day-1 | Build | Verify | UAT | Handoff | Released} — {at most a half-line qualifier} +last_verified_build: {PASS | FAIL | not-run} last_verified_date: {YYYY-MM-DD} --- -# {AppName} — Status - - +# {App} — Status ## Where I am - + +{At most 80 words: which phase, what is built and verified, what is open. State, not story.} ## Next command to run - + +Claude Code: +``` +/TechieFlow:agents:flow-master *build-phase {App} +``` +OpenCode: ``` -/ (OpenCode: ) +/flow-master *build-phase {App} ``` - +{Optional one line naming the target REQ ids.} ## Open requirements -- [ ] REQ-UI-013 — -- [ ] REQ-FN-007 — + +| Status | Count | +|---|---| +| Not Started | {n} | +| In Progress | {n} | +| Implemented | {n} | +| Needs re-verify | {n} | +| Blocked | {n} | + +- [ ] REQ-UI-001 — {short name} (at most ten named rows) ## Known blockers -- None / - +- None ## Verification log - + +Last five passes; older passes live in `docs/metrics/gates.jsonl`. + | Date | Phase | Result | Status table | -|------|-------|--------|--------------| -| {YYYY-MM-DD} | UI verify | 14/14 Verified | docs/{AppName}-Checklist.md#requirements-status | +|---|---|---|---| +| {YYYY-MM-DD} | {verify all} | {14/14 Verified} | docs/{App}-Checklist.md#requirements-status | ## Library feedback summary -- TrBlazeUI: 0 major, 0 minor — docs/{AppName}-TrBlazeUI-Feedback.md -- TechieRag: 0 major, 0 minor — docs/{AppName}-TechieRag-Feedback.md - +- {Library}: {n} open — docs/{App}-{Library}-Feedback.md + +## Standards compliance -## Standards compliance (last verifier check) -- Underscore fields: not yet run -- Test method underscores: not yet run -- Mis-prefixed fields: not yet run +- Last check {YYYY-MM-DD}: {n} findings, see the checklist Remarks. ## Deferred / future -- + +- {parked ideas, one line each} diff --git a/.tfcore/templates/v4custom/app-uidesign-tmpl.md b/.tfcore/templates/v4custom/app-uidesign-tmpl.md index 2f3be9a..ae00c8a 100644 --- a/.tfcore/templates/v4custom/app-uidesign-tmpl.md +++ b/.tfcore/templates/v4custom/app-uidesign-tmpl.md @@ -1,48 +1,70 @@ -# {AppName} — UI Design Spec (Mockups) + + + +# {App} — UI Design + +| | | +|---|---| +| App | {App} | +| Kind | app or library | +| Size | Small, Medium or Large | +| UI library | {library and version} | +| Theme | {light, dark, both} | + +## Design system + +- **Layout shell:** {top nav, sidebar, both; the layout components used} +- **Theme:** {…} +- **Shared controls:** {the controls every screen uses} +- **Rules:** {spacing, density, responsive breakpoints in one line each} -> **What this is.** The approved visual design for {AppName}, produced at day-1 (greenfield) before any UI is built. Each screen has a **rendered mockup** (`docs/mockups/{screen}.html`, styled to look like TrBlazeUI) and a **component map** that ties every region to a real **TrBlazeUI control**, so the build (`/trblazeui`) reproduces it 1:1 and the verifier's visual-truth gate (`verify-phase.md §4b`) can diff the live screen against it. This is a HUMAN document → rendered to HTML. The owner APPROVES it (alongside the BRD + Architecture) before build. - -## Table of Contents - - - -1. [How to use](#how-to-use) -2. [Design system (TrBlazeUI)](#design-system-trblazeui) -3. [Screens](#screens) - -## How to use - -- Every screen below links to its rendered mockup in `docs/mockups/`. Open those `.html` files in a browser to see the intended layout. -- The **Component map** is the build contract: `region → TrBlazeUI control`. Only controls that actually exist in the TrBlazeUI library are used (the analyst read the component catalog first). If a screen needs something the library lacks, it is flagged here and logged to `docs/{AppName}-TrBlazeUI-Feedback.md`. -- To change a screen after approval: run `*mockups {AppName} --update` (or `*amend-docs` for a requirement change that adds screens). - -## Design system (TrBlazeUI) +## Screens -- **Source:** TrBlazeUI component library (`.trblazeui/TrBlazeUI-AI-Reference.md`). Mockups use its components and design language (spacing, color tokens, typography) so they are replicable in Blazor. -- **Layout shell:** {top nav / sidebar / both — name the TrBlazeUI layout components}. -- **Theme:** light/dark per the shared shell; warm off-white light default. -- **Controls inventory used:** {list the TrBlazeUI controls this app's screens rely on — e.g. `TrNavMenu`, `TrCard`, `TrDataGrid`, `TrForm`, `TrButton`, `TrChart`, `TrDialog`}. +### Screen: {Name} (`/route`) -## Screens +**Mockup:** [mockups/{screen-slug}.html](mockups/{screen-slug}.html) · **Roles:** {who reaches it} · **BRD:** BRD-{N} - +| Region | Control | Shows or binds | +|---|---|---| +| {Top nav} | {control} | {…} | +| {Main list} | {control} | {…} | -### Screen: Dashboard (`/dashboard`) +| Field | Type | Required | Validation | +|---|---|---|---| +| {Title} | text | yes | {1 to 120 characters} | -**Mockup:** [docs/mockups/dashboard.html](./mockups/dashboard.html) · **Role(s):** {who reaches it} · **BRD:** BRD-X · **REQ:** REQ-UI-001 +**Dialogs opened here:** {Dialog name: fields …; or "none"} -**Layout (one line):** top nav fixed; 3-card KPI row; full-width chart below; recent-activity table at the bottom. +**States:** empty: {…} · loading: {…} · error: {…} -**Component map:** +## Click-through flow -| Region | TrBlazeUI control | Shows / binds | States | -|--------|-------------------|---------------|--------| -| Top nav | `TrNavMenu` | logo, user menu, theme toggle | — | -| KPI row | 3× `TrCard` | {metric} each | loading skeleton; zero-state | -| Trend chart | `TrChart` (line) | {series} | empty: "no data yet" | -| Activity | `TrDataGrid` | {columns} | empty row message; paged | +```mermaid +flowchart LR + A["Login"] --> B["Home"] +``` -**Notes / interactions:** {sort, filter, drill-through, responsive behavior — what collapses/stacks at mobile width}. +## Branding guide -**Empty / loading / error:** {what each control shows when there's no data, while loading, on error — the verifier checks these aren't blank-but-broken}. +{Only when colours or type differ from the library defaults.} diff --git a/.tfcore/templates/v4custom/app-usageguide-tmpl.md b/.tfcore/templates/v4custom/app-usageguide-tmpl.md index 104404e..1292438 100644 --- a/.tfcore/templates/v4custom/app-usageguide-tmpl.md +++ b/.tfcore/templates/v4custom/app-usageguide-tmpl.md @@ -1,67 +1,81 @@ -# {AppName} — Usage Guide (Test Users · Test Plan · Setup) + + + +# {App} — Usage Guide + +| | | +|---|---| +| App | {App} | +| Kind | app or library | +| Size | Small, Medium or Large | +| Date | {YYYY-MM-DD} | + +## Test users + +| # | User | Password source | Role | Exists | +|---|---|---|---|---| +| 1 | {admin@app.test} | {user secrets key, seed script} | {Admin} | {yes, no} | + +## Execution guide + +Prerequisites: {runtime and version, database, anything else, one line}. -> The single source for **how to test and run** this app. Every agent (flow-master self-smoke, the verifier) **and** the human UAT use the SAME test users and the SAME walkthrough listed here — no one invents throwaway accounts (enforced by `.tfcore/tasks/_smoke-test-policy.md`). Keep the Test-users table current: when an account is actually created, flip its `Created?` to ✅. - -## Test users (canonical — use THESE for all smoke / verify / UAT) - -One row per account needed to exercise the app. Cover every distinct role/permission level. These are the ONLY accounts smoke/verify/UAT may use. - -| # | Username / Email | Password | Role / Permission | Created? | Notes | -|---|------------------|----------|-------------------|----------|-------| -| 1 | {admin@app.test} | {Pass!23} | Admin | ⬜ | {seeded by … / create on first build} | -| 2 | {user1@app.test} | {Pass!23} | Standard user | ⬜ | {…} | -| 3 | {…} | {…} | {role} | ⬜ | {…} | +``` +{restore command} +{database setup or migration command} +{build command} +{run command, with the URL it serves} +``` -- **Created?** — ✅ = the account exists in the database now (verified). ⬜ = planned; create it on first build, but **only after confirming with the owner** (see `_smoke-test-policy.md`). Never auto-create silently. -- **To add or confirm an account:** edit this table — it is the registry the whole pipeline reads from. -- **Seeding:** if the project seeds users via a migration / `database/*-seed-*.sql` / a DbUp step, reference it here so the accounts above are reproducible from a clean DB. +Open {http://localhost:port} and sign in as user 1. -## How to test — screen by screen / menu by menu +## How to test, screen by screen -One subsection per screen or top-level menu, in navigation order, so a tester (human or agent) can walk the whole app and exercise **every feature**. Each subsection names which test user to log in as. +### {Screen name} +- **Sign in as:** {user # from the table} +- **Steps:** 1) {action} 2) {action} 3) {action} +- **Expected:** {what proves it works} +- **Covers:** {REQ ids} -**Flowchart any complex flow.** When a flow is multi-step, multi-actor, or branches (approval queues, learning/feedback loops, ingestion→processing→review pipelines, auth/token handshakes, payment/consent gates), add a Mermaid `flowchart` (or `sequenceDiagram`) right above that flow's steps so the tester sees the path at a glance. Simple linear CRUD screens don't need one. **Every diagram MUST follow the authoring rules in `.tfcore/templates/v4custom/html-render-shell.md §5.5`** — quote every node/edge/subgraph label, never use `end` as a node id — or it will throw "Syntax error" in the rendered HTML. Example: +## Automated tests -```mermaid -flowchart LR - A["User submits form"] --> B{"Valid?"} - B -->|"yes"| C["Save + confirm"] - B -->|"no"| D["Show error"] ``` +{test command} +``` +{One line: what the suite covers.} -### {Screen / Menu name} -- **Log in as:** {user # from the table above} -- **Steps:** 1) {action} → 2) {action} → 3) {action} -- **Expected:** {observable result — what proves the feature works} -- **Covers:** {BRD-N / REQ-* IDs this walkthrough exercises} - -_(Repeat one block per screen/menu until every feature in the BRD feature catalog is covered. This is the human UAT script AND the map the verifier/smoke use to decide what to exercise.)_ - -## Prerequisites -- .NET {N} SDK -- {OtherRuntime — e.g. Node 20 for Playwright, PostgreSQL 16, etc. — one line each, only if actually required} - -## Setup / Deployment steps (runbook — one command per line, in order) +## Known limitations -Numbered, terse, copy-pasteable. No narrative. Omit any step that doesn't apply (no "N/A" placeholders). +- {one line each, with the REQ or feedback id} -1. `git clone && cd ` -2. `dotnet restore` -3. {database setup — one line per SQL file in lexicographic order, or a single DbUp `dotnet run --project src/{AppName}.Db` step. Include the test-user seed step if one exists.} -4. `dotnet build` -5. {run the backend — one command, e.g. `dotnet run --project src/{AppName}.Api --urls http://localhost:5100`} -6. {run the frontend — one command, e.g. `dotnet run --project src/{AppName}.Web --urls http://localhost:5099`} -7. Open `http://localhost:5099` in a browser; log in as a Test user from the table above. +## Platform notes -## Test (automated) -```bash -dotnet test -``` -{Add `npx playwright test` ONLY if the repo has Playwright tests checked in.} +{Only when the app runs on more than one platform.} -## Smoke checklist (quick capability pass) -- [ ] {one tight line per top-level BRD capability — 5-10 boxes max; each a user action using a Test user above, e.g. "Log in as Admin (user 1), open the dashboard"} +## Component map -## Known limitations -- {TR-NNN — short title — see docs/{AppName}-TrBlazeUI-Feedback.md (or TR-RAG-NNN → docs/{AppName}-TechieRag-Feedback.md)} -- {`Blocked` (library-gap) REQ-* items from the checklist Status tables, one line each} +{Service libraries only. One entry per service: what it does, how it is called, a short snippet.} diff --git a/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc b/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b61a149b76f3041bad0ba0e58b329af9539aeb26 GIT binary patch literal 57216 zcmcG%33yxAl_rRl1Obo$H*u#PcY@-+XrW}$6scXJ7HY92QWVGsQlz*@51=F>54KX} zo{+ZkiHe;PRJO}doGH`g^n~uDCv;V&qoiCFRaebd0702FMm>{G_ct@1{^lFUPEX3} z?wR?|eG5Peq@1drC-LGfcRlyqbI(2J+;jgnIoYhi^>OB#BNgA)X#O2ND3?jP(SA9q z*J$o)c+EKtujO^4+A-ZZotEzPqxy4t_G>t2V86z5M)sRCdI{seBs#(S16fel7#4GLR}$r*Roa zs-xv8cYG$^*D6oZ-w$-s{kiOQQ*L@j%A9pmW<4Wi?z$agehcxNkKfJwPQC!Y9KVZyjxT)0c&>=w&2Pq=V!n;%@LR$^&llmhlyB#Y@mt34 z;Y;va&Uf&o_^r@s_G|dES2TRN3ZqKCldr%x2m4luZw~ca6~C9S!nZB_K7I>+tNHzW zHGXUO1AGmBYx&w&H0SF0gM1yH>iK#+HSmY{!+Zm3*T^^GxruK=*vvO0Y~hdaEr_|5 zKgw^#?>4@R-}Z|3+;-Ove*5K0jYgmzOBnG)`%<~X`|Tgd4+?&1HB)a!6W6&*lVi?t4lyUkT;m?jIpiE3 zcYC;DkDK$j#;%MyJuc3TbYsq1vw80|moOE5KRM331g_RII4~i&>Xhj2x@*k?lWxz% zSRFTU#Wn8ct`B=Iq121JifU>&)Jw{btF5i&YHD_0H205A3|wyI1lQHcVZp_7Zr6Zk zcmfHB$GHgsWmI$5@yqp3@8W2kdA=N#ub&n1_6+&PAv z1hi0?9Cf*Cxl@;h-Drt0e8q#3oIKih$?4%(*(?I4@Gb)(cu!F6Rq@Ni=jyo(#0 z5a@&3L$Lw75%jd%0nF+^LjZ8kC1GN6=n^4G6nWj3YPsW6o=X$sTodQ^Aaf^>VRX1( zaH8WU#z&`W%_q^LPV@xFyQDq_{%{k63`%aGf`kRF5>%t;U(+cgeoZ&SaW}dLwUHjU zA;CE|<`lTf3H0{G%dV+b?wWIS(uLNe>lqGUunePJ)ENU4V>E;ut*j4G{Z_6Y^<3{7 z93FMa_4x*H!zUvLOMD5faS5#)-dA&n0o-b?8~7g0YH-*!s$lRK#v0cTbQD}}HiQ9u zWKQ559%tFfw$7c%Ij>wHY>-nkY^1h5hxZPYqjYGzDDtj&k$Dy{9wI9 z^Z+gPpqnwq$EP@gF~wlSiID;TaB%nrYmG!{TDfj+D~+h_6o}kW5YazXnkSDsLUm=l zv7xV0C$4gUP@WMODw0<)1f%B0!006J;-&Tw0ULI?QJO^eP||T$l~%bd!Y;WFxl0qH ztlET-jNRm1wMNG<^t zD2?(I6F*s+qenp%DM1p020eK*DTVqNlO!FO1>CJsh#`?JuYK)HZ$3-ECTdJJsWJto zd_0qs$&-}Tue`aG#p-u48Kl%qx}8I=A!-%X-5h;>DNR?nEjTjFLu3rhwKfLJ|e6 zI_xX0KqA`ii1uhid&H3t(Yhl>Mg=3xq86&iTYbY=5v$5Yj==MePpTM&-9D-k}Azk!MIfk{DdjSoyA0lqrgycIoF?E?d@E122` zT&-f51(Iep#E$1+T4q(#05=juGU!CBJ z=-re3AO#cGU4lawF?d`zJQ3|BH!Z$7?n!~NPMY*!6xXfdaIZ{7tm6~HSf9+42+c@y z6o2m55Zu)K%4EH5o-w~>T{h)~OnLr}u!+0b{@7%`oiLLy?e#bMuZB%U>YJdgaemK& zHf(B*c~w7OJnsyfwyUqEUk>O3?O{`S^v$(dw;yi`mrbQ1QzaeNyX8Vf8 zdh@`SsE%ep7AMesbbJVSSZ{np$AC~QYU7O(5dtD!m#Z03M+nQE1qy~JP}I`$ z82F;^ifF4o`+spc^bwP5oOcsL7cnzB$nJ$2d@~#2UX&2uIPCjA*AtZL09^@_( znh-H!!HYhbQ52rTn-}os-i6?%W+gFwIWa$!nD6giN-SEjSNA}mAz7-2b%o4Xp1N>)ZzDnT_^oqO*U@>%g&9vOEt!l#(TW}!C``U<+7=1TpBoYz{brjIt^uAVT zz>k}%z)4U@VQAeauW8)E9(=k$aY)>CQ}fu8e0%H6*4dgsTHw@=(|(fiVaAeWYtXRu z>2}gYDiKiJe4TbiiKM7ex;&=kaR}$?mV4Vzy2d81xwr$Tjvk5f+{AznV%h>`?6WUOBham=R1P^ImZ^vCMJ>1&Y?a#bJo(M_uErd67g~%!Aajx(jPtXQnj{&|(s6mV+o@ z2!HMZ1cVbQw}ji?8Lz+m`&;JtaB)-E)*QC91Pv`x%VP0bZP@`JU2MzN#eS_9q~T2s zuYX&ww7{o>JQb2wpV4RV=|x>U;t_3sr>6I`M&nBm^&={D7>HOP8bMSHV#3G7%}uu@Kcy9sicVhiqjL(PfU6uhEb>66G@_w&4oe+(zvO+}8u9~Ux8~p=;z4tErd*>>HnN4AH^UaQxyaNB#K82q`^$5qLVu0(MI`F5k?m!nsGTxt*G-DlvE=$ z_|{x1*Qpov!mB_$%bEyyZHE@7A%o>3#cXix}}&nOz*+FSXeF>a{_6#^R! z8Bx1V>S&!eNF7hDq`;PD{#&53ATM!LN>KCYly>k5q&4y-#I&5ycg@8oiU~>zz{-sj zXn)+6|C8&!PQG6YqCS%^QA|X?ob+TVO%M~6zKFi#P0G8=xtJ5Rw?@Q7QF~2`loKM> z1!5wD;3Oa@@%P6LFzF^_luU+6>n`rac1+c{8~1?o0tI2>GWLaJUxUNovbl>QcNkN( z$eke>EGJ%s)Cm&bT5tA+nwlE0={@6>n6n){h?81p3 zom+0%8*14bZrMMZFnjgx^*h)7S6}xoXH|rwmaMW4X?K@r083-HFH;gc>VqB^H)FcF4wh%>e|9}?bF@U`iLdv4Y$AP^&8)B3QWG+ z8qTVjFIhOV*x0d>TQI$Ew&~TwsEDzU>XCNy=+iWtXfb2VUct?Js~&}d(8`u)>y;Z? zcNG|ixGSJ2bw#YKbz`Z_4`LZ87H_yl>mXwn<5Zt$zwt!7%b}a38bVs_)kDkX&`HSF zig&ml#7{(fZPltLW!T*Z{ndbr@r65tX8-V0;D6d9FONyIw&5`$E$!wE;~KybT~pX zfi)Lzpi)pS-cwl$c}L1Zwfv2ypE%#5<;R4z|(@kXk#F98!!%%l~CB*9SlF$79v7K7>ltcEnj%sO^Zeu86h+)8==u`Y;aa{z!XQOXd1><3@&4 zRQfY9UC_;HZ(W;8m`WIcZ4I^IIzr{{pX;w{u{PNCBF(rQTN${7z5&rk-Hs| zVwUN0N!-j&T!+?-=~E$KrWHgjn-UQ?xj0g2nc-?97EtplqNVMn-(M5;fQu1l0efNRuTrPUPL?c^co*2=K8>gL`RQxb@m*>Y=Ow(PYL zf5Xgp*qpy&vCpQ?Y`@($(-yYmFIy@?mWnz3BTF@88rdIa2V1*>$4>{(oDH5kA3AeB zbiC(LL+^^CcJAu?**`3NzcB3BvFvCIIoclR!;a2n$Dxqp(8H#%t!X{k~BBz6b7b{o&>M6QTMO!IP)M^=CfG zKRegpKk?4lduQK%aWVhwlKq7hd+O|oI|;Me&yAY&j19|rRJIR`mUQp}v(<~%LZqB& z|CKcvkiks;d8Woroq_kV5NOkued;VKar5pN6`?U#Rtn{Opx9@T=J>0-zk5Pu_ivP{nO zK3!BG5}iQ6T{l5~3nE4fYF-;pJizN-!Lq+omEbFr$g2ra5z2JJAdJ;sF^T%8cOe5YAwyksE_tb;)kVaPt^2cg+TnlMy-@c!mUjxLqkLP zeQlRR%Q#0!HFdT1LL)Maoz0&l?IIhN8&`zg-l9uRcOM%neIyvI-3414u#3BE)hPEV z^(F*t+*^XN` zLMi1^jsGwuReNe5WOl@1GYSzgj7$uVNA#pdkV%eVU}DrQTt$qC;kpwdErD*0eMEGG zmvA3LcAl3-53AuR5Cce32X{XLP@a{|C7)PI{YBI5xA)KN|MtNkF|G(6=M)E;--MV! zZ_J)ao=%uGuB4>j9+?>djOX?(WjBRWnx^%Tp@mb*0~t#x<&P}opQdLK8=6gQXzEvA zSTxo$H;tM9Cab;3uu2NA$`A;E&labNS_sl~Z|gBuFgoax#@!`iBQR0ZgcEnPs8i#2 zbhk1MG|}j~m5GM%-r(KW4vQqnb1pIKEn2-X0hDB50ruJ}F6eM_*e^TC-1 z2ZBd>gVh&;7x^&f`ouEmZ;(26w*A|O7A=E8!{Ar=VPoX&?CpjDDnhwH+N4FoAxujk zZgEK&N%oaa;Pp&^p!3L`5?{I-j}L?FQqAS}PyLJ(aX6PKkN8h17fK%k(#tR#();vL zKASg!FvlUdSn4En35Nk%*71xji5LhyRuNko5RBNqfg6=NJx|ohgE!I@Pu3g{?Vpi? z`gzS2g^x0!$kcGpDryM9yKcfAG0=F4BrvtSJ7OYDAicp-jVL9fPqE}(qLR7LUrmf& zbH$&6@rfm7d9;08bc|2zH$@D z{-8af-K2FSNZi?LRQfFnUPcf}dwe5OBiraw3^6daxi zUY!h|xfZluzquDg6%Ab2yOgpgY}peu?2(qFsDOIO15W#8K3?6`xHRXqyylz^$^|XV za`hzT!><9xwF!{~$vSWq{f!QF+>x>S4Xvml4p+KxFE36>V}d7jfU_r+XbjUgns3>~ z)04WuWlz*c@;6q3ih78tN_APD>Y(+|Jjt#Rn&j79JSJ-o5l427WGi>kUuAgGoZz;2 zRCD*Jyy6eA_#Gh~(3&f{ycyZN<`W*3g;A$wR@eI#1|}_Ci`I|lGl&KuQ#6c}JROHu zC7N!igG9qMEf!SOh+7sXE-WDwg3EYABNlhYjY=M=csduQ_E6+(RA*XRVY&WV8xYdk}V}PSBWAl0< z>Z)Sttwq$x%xd&k>(s~+QzKIh2X7Kh8`VhW;FEkNk3#{CsJ4#RU+x6;$rh8OsZmH* z?l$U^aV>B4nR$)Rg4oHtCKudIFY-Yd(XmjalotKvQ$$nDSRewr=2FLkRkQ+@TRe42 z1kt+j2vDHov)tNez_^a5r5PcPWzB^a*oY|*&7x&c<-W8KZQXhcePU;Qa`oAL(u?J} zFIh~EhfNAD`%+^1AVsM$pGsw*wZ$>5O_BOTiL>fUGSyf4G6=JU5tTAU8z*9u3&k73 zB)#@h>O3srBlnI)ERZ;QO68c z$ENsoRN4hNNAX66g95KrUl=>6d|~CO^dIF>|7WuD(qdAn>SxqWogEPSM~J;?!`Nys zA@;vU>@14y*5N6HK7?>_?G+ba6E+%|N?U-xx?BI5SRr}MoT!ScOtE*4>@IHsnXy35 zr0yFb$&;k%fn|#~ZG0kTO9?Y%lo-RqsIGt(K&1i>1(uFbQM2{k6K!=QQC1xb;}pT9 z7^O2Ime&GnM$&haxtPRXCP*fapV34eNhG^t*lRO+yEjQSsrDwRR?%K-tU)vwC0If` za)o1p%@NA3Pm+nr*~w2vW)>}Jm=fGBGDBu6rihto z#`X%rguoU-0u#d#9g`$*H2FyD;r(<+vbc!$U__4<8I#0CjFZsY3l3Yv;C77;vK5r1 zZPE+Q>%ssfa#1iy0ZAPq+A9$w8I6L8CEKEiZtMpBpsAvl(DGvsh}(vU#0th-5?5&I zgY*u1{64Qu;;Cwt7ymtyxNQJ8TTlm0CDVpmrrG8s0YSyyrj z@RPDq(-1T@(49YHDW_^BGYj|T$JUhF2WJijH*H-o{IvgP{6~D)+A*#D)MkI2mLDu= z2&Xj$t&OX6NtL1W%5ZwsmzuJqy6KLU^i6kn+}ZK^&go99%l*yowB2i)Yg)>yn{Qg+ zKWtmEXRl=C-Syt_21)`~q1Bo>Fx@emzLJ%Dck0g6>)%+;stjdShO?^XI(~TY{evGI zo=r!dwzOH-tKv#t;jH1irj^tTf5LYPR`NIFDT$s;-`Tv<*zyy{hmM7gAJu+R*fn?J zLHjTF|9t;n9rRy)=lZ?tZ%-{2b|JQfVka%87Ohm*QTFo#A9Q_^y;G6oyqy!w-Z^VT z@OVq@+~fzX3)(;1z2JJ#{^yrwC;cbx+_?MFotKufDjpu1HLPUj%<5Kh3jJ68frt#NMj3Er0Vn2k#vW47^FYywp?L+0K=`&F{3{ zYkhOqZ0F;wTz~WHQ-Su^UwXWK=R)U$)Q|T6h4tfshouj%{!PVSjs~}N`wj0{?perC zeh8&ikex4=j}ZXyie|zZjn@o7etX<-_9O z=A*NQKeAF~EsLooYFEx09v7DdcnEEW-mU(ze!g=d^~3!?vIdKuL-Z6S`s1R~K=-@Z z%Bz&c)bhur5H7!aVAk-7y@;YEyiq_hUh^IEvb`u|FA9{r=eX~9cgrXC+TR@30_(o| zVv}a`G3{5s&ffXe7x|i;U2bB~Ki-kqwJG7RY-tGpT9?(8m+-d;If(x8QMqWQ!1&RA zNn@31D3vcUzu=3CAt6OPA5;dg*}$E2#VFQPD+DZR7l2`3Y)4W{5b;H^nxBy{rdVM=Dt4cR|D0?y|u6J>ZEg2`Xc;GioZaC zNI?q)#KQ=8m4ITn$@c3N&6m0cW8H5DwFaY9IE(a@oPu@Jze0jo!QC@tGV0WhWb)-v zC-A6PkJtV{l?%0y?8BnOhRB)fxaLw=6lJ0w(7`Mh|CElWt}f9ufE~P!ZhsNFTL`~3 zH}o%RpkdN?JuyyVn%)jeQ&{3k+fm5Yi!AJg*HBc9Fu{6i@6qE&+E4B6>pXap!3EPK zbUqMiCo9`_`e@FE;e%pBEg^}8_80WIu`dRys$U}4&++Ge9sz6+lag=s-yWJ7x;;8G z3XCWSr)+yrv6OV^bB)$m^sxW2EpxV?v@)*`hi%;N^mzF@`tk(@Puv8Z`JDE2{m;{l z?fHoVcvLSL^|fDecoicefMS7(2%t#7E_OXadIJz^tl6CoCf zCv)Sqq+=b>MTI0_I1NhX(cgHIPuNA1FdNpNZ))CF#Q=nrtTc5@L~~I~Q%m?gN`t09 zeraTYaRh}tF|r-7Pjs!E*U0w&f&dcuh5wNP>J-Ln34e#X_w`cu+`@fCKjt2iwIPz! zOaN`xcr9`#QQ!VQc;R*;kR)fjU){X|JDEeT9`YB>9Q*bm_Aqm3*;){?76gm|K5%BP zac*Glbg;1Lk+u0(zen5+`uGaUr#?ns;%@DwLRb9&=*nY9eTAENa@O_x687v8SO51& zDg+T=o>>FX5ZRUvQ%=Z~rg~}Sz+j?|B-_=AQOHRD0uRDZDfk~KU^4@CnXp8`n(5(h=p6GP`bV@Sfd)zcmwDIIX6rW z>Rs+(`Qqw#{r?w&aB@dHznAkV(4zu*CuSazQy+`xc<9p@-AYo+2sqb2HER zH$Ad&VDtTk*&U14d@|-O0OMfYBn<|+t;k=aQ3+lvQ2QU#C(r1`IMaKy7Zs@cv`Npx zh9p+oRlze#j@QpHwo(`XrEK?e&&cP$R>>;+qLeIv;CDZ(-Kx|)+VrSOh9ue2{vl!4 zGg=WRpJ$}_T0MsyOgf+T*8f5*Q=+^tOqfAkV@Hc{8!sY>&VD!3t{8{i(7!NE3}_#l zF|>Rn0qbmt3s%#;Br?Q@5_`fU{5#~4PUC`xImQLHj3v$_-nPxy-qO93crWoC+dUij z=Ik12kC!=xcJ^T8(0F(&jadQY-^&j;=W@b1O-q*MpXz>=_)+4|l0QoRxc`?!KOg$# z=+8%k-KTG#osZKq$yD=h z+nu)WH@>&+{)q)m$6gXZ-Z4*ps-_TLT775?cert^#NX(%;&( z0KJn}1LiK_e@1%RmX0mQ*!DX(JXAa792*sWhS$Oa`j&U05<5l6wS|aZat0xpWz(J{ zCW|92&*^jL|JF7 zv#0z!g5}%8rXAQ(nV9sg>)*Wot(U&}(yVhSG5c|H>THpJXeqh;bB!Ts_gpa~Sde)n z?}jizva1dj>|8h%&e$`njihHt1~`G#H*AM3%|p{oHxK>J5`BKcqz6B@7j_u*Kd(zi z_zQhA!oM^aJ5mx?@f_;>_J6R!_KaZ?he}3gEASQSFd9)g3;V~ldeMWt-m{@x~BSoBH6m0>to5ZiJxuW_{wkb`Wr(ndO#*9mr($&xEji_pN zO&5Fjju=*|o1H%W!E<|0B*moi^s37;^~8(%s+fVoRM)dW@}Z6*CpLt7od;5NKz`-IwpY z9Pos*s^?RGlJ#NMLh6rl=dBC-gSDMu^IniEd3xVHv)f;?WGR#~NlKm`@b6zrDkU*V z#T>+GdHy}`?7z4F&4XcEIf+RsNK8^eVv>p{ZYvt`mkBu?4*g#`j2(@M1L^?B2bFZ3 zVx$ABKm4w}q8q}ojiHilc6e5-OvH+BL8Z>f$P3F4QBP`c;cScqEl%Ds0#0>P(?-jq zczXoa4mBT?-C$2mm>Uy>O&Fe*we5x?mmD!9f@B!5k5yh5Ls|j*av^Qq6ef^F^{#_88`LyD@5cFGjKCSW0kV zo`XXou2LP)k^y!#bDjjhWo(Bid16Cea29?rxf2u8scz`u#`&jDcJ|3naX z|C4NV#F7daN1AOw1ot1|h9+-2?Gc?l11s=F^9up}1Ks693dGpdcQ!>DBqYpPb^_z4Z1W$M+V)^ac;`_(1y7t= zHur|iy?6+3dne^yN?>v+yJmVv(6D_To8Grey(tfIl%&xZ@W+-g zow)gyE>_$hU7fxfW6M6RgXOsvmglfwQQ#V7!vQy4*DAJ+7%?a#v|myIF;>}GvM>jG zrof)4C^Q3Urb2@t!pRWzvXx>EsdLEGT(a5LGHnj#z$s)K(O!<|`YuKEo*N#f4OZvB zo01~Li%8zhvTmDgG>>DVc$nbNA*O7>ZQpk<*PrU&6VU&7%fi5ehDVKi-$)PH_kHCi zmhlfWIqkhf4l*UCQli0s!vlXGiLw(9CNh#LL!*b|o|jm7YiuoqM3i|@Ef%A&)i{IM5s`pfGOWAE88r(z@u+-m zl)D8&*p)ouT6w6vs1l2IrGsOYG23U3;Yd|J)@Sjkyy?s8g3@Od4SeF|Z5X97mW0u| zjBBy`J=vq$5{gzaS+tO}2KG+|52+^DbxreuOj%${Q>46=VymkKo2Nwm60L#VQx@lK zDlB%&mG{wKEa7{wginKYmm~UCxf9bywkY?}U!@n+o=g|hZ)O1(YNBzJJJAf!NoimP zl46;GTRUUydD4}9FhKV|BNy|kTrLBFB_W&H~A2kw_ehsFO!)?k)BVvq#aN4v?wVR79b2(;*VudS(up~*>uzNM<^_n6>&x-w^O-DN zp>H#TYF7+Yxo1JuS0okzi}J*Rn6iraO*BxI66xMoEEbD}m(fxo_Zd*h5;p@j*>A@% zsJ9Lyb%t-VukhA!SURHbZ2t)?zCx+@tJ}p&K8NrGl&%7cfJ#1BdJ<@tbPAt$jlSfvFZuEpBc5!${N%Q> zZw0Hqu_s@ZSOvIscsi7xo!*Xlxe)Vmd30V@Y69$fL8%KkoV4+DezQ^=A<08CGrPnT zg7vsov;l(L+j`%Y4Pad{vR_UX^TQV@WfBDux2(a>H-VoAmFQ8MT3@x80yrO1zC?d9 z-p1m8k?P7Yuf|s^R*SXFF4wVMjm2ud_)P;}vbMFTXQ^1rzLkkJ?6>@FlR}rYn(-Rs zUBmLOMBX%)uemS}te+FQkUCz9cnkFvaG)p#qL~WxeYs+; zD953?BVNmTkl`h2;t+EIiz+1*dQP3+wVGR}Fm4h?j;v`q9)-8X)RALPzgOt($cd-F z@mrL7^3}we`Rc^F__;H<7q3@yu?{t^iJubnSNQ?-;vvKWq}N`;ZdP)fFRbLcb$gsj zu8p53zErMPCyc5Xs19j-^@w?DZLQ!YKqn-7&MNQ4P4Q#NbW-7Z8rF$b%_wN2*f6X8 z=C5Ps-v+);!E@|*&@zgKynIc*X0ZwLu32mp_KQs$aTFsjDs_zhij5nkPk9$1b`m)NM}M>h2v;UTD6ldmOaz6KYy_TroPG_ggQljG&a zH!J1E`>No)uSKjMAF+3(8YT9Hf7Eh8rSWGbkr)&NS7e{%(--XEH(2CskJkRNupg$1%=0tzK)|29&(c~pH#)MjH)SE><8 zAo`3yZ(_f%Xhh#B!p9hn?M0~fe2Fh5PRYO5wbx}O-dgqEY~rnQNmIb=_VvC@UxsY2 zZ}jC$JgWR`@RboyhdsfqUET^3Nv=9}LKQQTHONlReR&v`=e$?-*v)x44m+*kl#zo^ zX>#nZ+W*T(HdK1e5H`vQqtbpRm-nVf(o4z56a=Hz*fSQq+3X325MZmala6gr-QEzb z^QKCkFQCzY-p@nYb%C@}-ei)JG9MXej*Iz<_NMM~a>M*{=PP=Od-)pdBdGE2-id=M zh9u9mgAh{oLmk2mVV45_V>>h*^Z;+Rus(G;ac%@bhF+^~zU6HNvg87FmugG#$QK9O zPz`GsoKFF12(5P{OF*x!n0#F#XXhX}&?@#;7fXjkDAttHQ5tki1Z5(rZ>~G1-~a^Y zcnEJ(3sYA6`bPmI8em?lh5tl%{{=yOiPKhmiIF%V9Ta~u0oG_{u!L7gnJi;UTP0%> zw1^A4hAAoqLVRhXK|JRG3{ZUv{=^Y2y@u3G(;0*1F*39%>_`qkwXXU<2|fnb9yEKJ$ep{0I8@JNn4<5}kdpM(bw6 zVabQh9{Mg&z{Jk=bXQElC*eM)kUg4nqGCa^;s!do2Oa)pZ1jzgF{h`+Ij zi8N3`-^5_V#Qc8_^EYHqpfpEB41FYU{} z2GZ%~{K{fmv=qOw!++}aHYlKFRdU@-oxc>@?#*QojGM6sCv4_$mrSk#lQ-Yow^CC- zzxU62mQqUsolB{X1>xqQ$8dC=5HQbm%x5n&JjkN%qTx zP5Jghzb9-jy?GECv9#>F1$PR*&=`$Jw9{~ml0lA9{F(3M-OGCuCj1rCdT7U{cSdz; zU2`pS74!MQ+})3?ZI6@T5&cmzx8`%O;b1WL&?D>N&n=YiZ|$1x`?L>S4^M=*9a~Ou zzJF%!#JnE1YoYFoZ&=~g&=yDybS&B`gDK9N`$H+tmDJLpy=-A1xcBtp?$dL1AxG=d z`3vFm7ejXE&4Vjhgz^-H8WXY?2W_Wr z9thh`t>hMm3|Ti1Ob@SQ!v&4W=$NsxS{q<-cl+4PF?f`>a%?voRVroE8)Bdxo1!|g zk1do^#lSzCP1fd$QuAQI8R(v~EhNo2>!U@OyPKkoKK+Jls68m|nM%S48{g2!e;TGq!2NG(X$%IE9ccg+p;^oBUOw zv{Kj`r1RRjj9^21$kD#&c;Ny6a3r|z1?m0Drd+@4&e-CnmN~<`@%`j4^b~t#XWN4v zp`Aw-clHNQoDRO&A3D+hxn7fG`?V$Yt&(uTfrVX51qUAG9$3lF511FTtL_xf*9A|% z80;NeJU#ZY<6-A7>Vod;!S)-$8&ko0FLvq0w3>hK^8L&6CE@a|!Ln`h;)9L{Ey3J_ zkF1BNa}H~No2E(5f>Ozv4~Jukmg#G^eKS7zi(kpiyW4xGH_#l)tei2!>R3mf#~NR4 zjplti(BMB9==rf@q4UGKV8fmV)enW>$?k`P!Bf4#3w+Qu80;PTT%);!Efo04yR5_P zm^`XmNlu$R^Xi2SGTMSXPM>*rE_l`%>}MG{hk`?wg1N&>))9usM&r>a94e%`1{MwBbdpddj^rr`s=8ERh=l0GQ&8G*OJ0F~QaP{Y> z9_$Mi9)4s$Lc{N<_H(Pol(L+dwV0T-l9qS3?oM4etz1MM#j-tXlI>jBx4i3EXxFje&f~%3=Yr>*%jYkL&R-6m8x4+*1#`!jtP>zY>6teV zuOuda>!m+@>2X@2|N30Q4^!SxnZFvU+!0P|Mb%QX?iSuD3}{0{UQyAIrSNfn)!erE z_Hh2zFEy#jC$)D>vpQ^IOwXKcUCGG*G(9I!{9fh#%6F@li?@Y}w=E?6%=(e_FH)9@ z_pfARF>k8ff&FuP=9+_bdme0kQ2DSZSbStD<0vI=ZuRTlYVu!ubEkh-FuQU&yEc?v zyOdr3KsRfAoSpkl!o39lH|F-t8^hUKm$P?;vUkCLFniB(_JL6LfpGRAc$l*1E!&Gh z2LdficE?J34*a7ork6a;Xi>vG=}xygBcq2ocu1JA?m zN7*O7FjBnFH&X}xrc{%$InX@U^1bI`vAg_-weQz1q=u??E?4adRqc7u6t3DIbQ}mC zxDYJtTe4sLZ32vzXPf+|<}w!?56=dB2Uaq&{hqnf`BMw-;PLan)oU%i+M6A}vTwR; zyu#v@zhV=Jg%AU!bf$XZ2P54JE%k(-D9;kW1Yn&gZ(&IK(T zCCgBzhzx&DpgB+;tUT~A^`ZV@YcT7~lIiSYTl#;ZjKdt@hx7nJEdN--i{?yX5Tl23{dz|op=S*b|gZiSxc0uTn$Q42}(D@{^X zq*;5x-VtZuKu)pBJSs**t{Q7W!sNgr(-gHXB?p?KNs*U8mO@(Hjmki5l$5Ya>bZrM zrO>!bo>_XMCf@NZQ=z?$wkk0m)@%>gcYMW7qAH zV(+fKGe8Sf-8KqEw zJh3}&>Wa&|L(N%stS?D1EBDcoyqY_OIMvEQb9jcUkYS!3mgw-NOF0X!Av$$e${HtI zvByXv2|;lJm$z6gK#e#-`#R_&WQruDRk{K~B@aYDdaRenu6U+k!iO!}Fp0)9IVYj4 zR4}?Bg1k;A@!}v7IDfp3;&Sc0@4kb;gmN z*lvE~3ibj*g50In64_wmiDv>wJ0iJMK@Q-6ESb&%BC%ODw@=QfnmbDwRdajgjHHuZ z9ZLVgXX+@9V#EQz-t5Y@VLBV2s)}AhaEq68XkC>zPc6H+Bl?M9lFKWJv2fI)>x$dk zBpolnj;oW;xkCo7?QO*rd?#*+YhBVg?C*pTF4RUk@-F)N*Z%-kld5~N5b zh1rxT1rPD({%1O7wu$A{ON)sGkL}t14F6Qv?wB@yYRic7{s#i51N(zpdV;yVOV$gI zQ?vZW*CAujC7shww?9rNZYv{j^-;Pb%IKNqGUpQKUS7D0Re$^0VBrgo>@WVtNGZUK znNxzPC5uTVk0C|DDM!KFrukE0TPv)7ZRycc_XqX{wgsy?g1Mbb*1apKnN;HObxTZ~ zGX}2DjS-W*FcB>5dSpLF1s+!lEL%(}du%K4^H?UFS+X@p3zD112XkGItb;$wwNb3S3wM5!cEq2$ zaP^`7&&BUF1k;X8CnEUNlJ;~~5Ba|z{+r?83w`0Pi@~GLU~c~->i}}Lrn9<~E+&;q zb=fy}Xd&@I?vf2s4@HI`gX%^wcj}SVOZ7RbWyusMPt8mPbDHP*C3y5$n`&<`ci$uH zeoD2AT2Zi=R7gF;(j3Fef5~>{sYLC;+&z!19hB&-R8-MoQjwZ$`$FlGt?j8))R&K} zdnwiPT7>4}ufE99q#xC?jSwFxehw z>=uk0x`=Orb!uD>zu>=BJ3+EejdItDzWwPpzGT>STENSqn!XfhMy;?U(uv8i0xgKI>Mcybj7H?7~47l~K>1 z@Pe14l<%{P_M2MZNz`W7&NH6)1FQw<+5poElw1hYV4suf*({~OD6z-1?ANf`0`B_K zeCc8uS+BV1&$Z1I9|di*X~uARjR;KiE`BI|t(3O`#+56%6UND_x(Zf{ z`c<{4RN}E(6s^K;2VbSGcGst(Ta+APN~&fhZCh1RjS}yhRWTHtG>L02Siq^4k{h&8 zwK9t-x1N>TxT@R+rCip+l2t8igl$$c-=eN**Kg%kC6}1ewz1N7tSW805|5Sktd{P; z=$GfJ4O-f&#EB_;CoB8;XFzgS6q3FiF-NH2pA&Q9d9&nqD>wy@!s|n!Ely3IXEj-k zRqaZ=I9RoZ@Aw*6)v4qV)8@TY5-9YAXS90X>Q=i!wV%~$pFJi|xkqBrlHV`dJ?e{* zM)g*`QQG9ojiHVzedQ0Zy&#*{n_u~Z@lruHqe|Tf4$*xF{2?({fe@(jfIM%&x?aqW zx8p#e9GFvV@|*(8e4kCU#iN=^Y|J4Uq75^PWSMV^sbhhHFZ^z58De&<+qT1E9(aHw z@lvmrKHYMGa@t3Bs6bV3_~(@Je1-f`v5@a#6n_Jn4m?g_#4tW1?hMc?%00c=mmUL8b?*#+iq(95 zJUbmP6?*4PEKkH{`n@#Mi`l@g-MHhT>7FrC&WfDU7PZf?AA^5^YDQeiy1nwEScICK zi>(P})V-Kd_v7y+nNK^#M8NJ!{wd>j&rN67{dVxVPYu%dmv6^0tt*P3( zPFBBU&rSt-v8QX*D8@>rpT)`M2gHO8=`F9cOQC|y8d0a(BI|Iw?735T|F*gp(g4LX2 zk$2K{b#hp6kwp_M`yhyd9L<}@PvWr00Z77DS$qxi?p97{*RC<7BWZOL=}>4JntEUb zNx8K=){I2+WWzZ~xu>-393Y+7Ko#v`zQ$mzH-)6kLS~h#KU(y7i&mQf#W9=vFQ}0_ z3vs1GBRVEv8Ub~C!^4t~4~04=zWS2*`9H9w;`kwE_2NyBZI)ZIHGE>?2Ao%z725$= z#j(?c*?BKlV9W`*X{E~v5h{BC9C6w(&KW}!8~`LlSx%mgAI4$9*C8u}l_HGcaA>QmK5>lv1dK||bu~3_(TDZM9QXDpC+F^BZ#x^CW2DSUzM7Yow+bcSQw}I>1aN5Q{&K0NiOsW9Op51z2}PKWFp%L z)0wbPcR2?YxLprBU+nS_Y(|s27;~TkjFdKsp{;29CTtfY~-9k5Okoj=6-U zq&TJq#dP95rKH@seiux?U6_twfr^?_=H5f}7366GbNsV1R$8-8u3kHA!G{Nl5g@Wf zBieo@<_!(%&q#ah&7(rse&PuDFbdz#4(i;=U3R&yNOgAt*#?KNVF*(?n7?|^o@#NyNcTbK&S4ZTU zSt&DBs1ww8LYVSq698!!gvt%o(3uDg_1;v1(kh0*Tcqf~6b0D%w)5PL-nJh6cUPU< zd!ptuw(TPOsiYhk1Oq19WSEfJP^yia>z|@I10;;KTd6$R=2Ie`rSMADwnSiR=%_z$ zDw0Tkr)n4j0X8sAcFS0&eyZ~xWLQP{Fpv*oCUC(T{xX%wk~5YA9RTj*3V0u4c%IIN z4rF!|#;DYa(HX);1aNx7^O9a|FQ~fkXY_$Az9WeMM_)(#abXj^XG*&7(nn?({tDeq zQb2mSh!L}v>jvBjJ8wuf<787VnTyZT9jWXjM^|KR5z%{xuSf?)^wFbDViZ|O0CE&d z%DtkW#egEiX$(8aCDG9RzHxTLy9N^<7fUeRa#(j`fi`Ws4ZSDv@ zr|enk&=`4V-rOC2h#iN(S1~9ykMR2{`7?&ht zY5k|Rs7@`Idwj`y0`|C%H|4zUdaL`*)6*_kh^1!0*fpsUc9)xQ=)|4jnbUz&%huYE z6^FMyvNoX9q{e@1v8zg}VD7es(w{j#a)4)FdI5Fgmqx~xUKn5Mm5d1dK57fL9)7re>D2jP&&ALwXRy2f zb6U1sCd0nGQSwlhJf?$r-L!Vv@v2o(CPLNvmMM@qmlV!!45v1c0`%;<3Q+6ZiMfto z-Kk*V=|}c6r29NeXE&HrW;chEI5G*|7A)NgH}0wpcp7OG2Ljsx+e@+PI?0d|09>)B zO&cHQmr3VJBqdeO9Ko6H@S#}!Nm@++)~w`%^t(H7#Qk&!f)!hqzv|HpqAbfOs`MIN^=Z-!cSbFZ{N;bEWwK>obIC;P2y`A@WzWZD#tDfS1xc4WA zKRo=v5N_VP+CihEjPaqYJMU3;)SK=K1!I8bN9%dBQQ#Dyr20fqvAIfl>K=U zngh$~{33w7X3>!znwIOm@$g%uhWiUn)5mlN=_BJS9J*B%jS+Oe!Et|HMFk*isv` zw0_+9%N;-85j=W1ICgER<9bYDS@+xtZ}*7DN}t`lD0g+>SU9CQw(M&Q-iIy0ljoL7 z&d21TRG>$#04cEVD^i_ z^L#kPg%Jw-{g#0Ky`=jvaemhtO0SN|<=i9ddB7o~X1Ym+BGX(G6az=XDGjR|a&4(( zUrgfON7f4j#Qo7Kl!u|Z2aRA=IP!2fobrN9BhpqDP_3VK&Ry3yhwJCqYt3I_cL2lr*TRHNr$ zWygc|2Tj3!Q^9iYQua4K%`W&&5|aN$kKBHfh$>;AlhMhR#iT7VA>0;D*)C(%V|)4= zd+r{(b11mE<3V>g<6zi+NV3jveDzvP-M25KF6e_hgBtwSWPqK%=-emb%vWI}zEBRTnhk#8k zJr5@KV3L;3t5GZ=qOw;pkyo{3K3L>3nK4S0EMZ8HEptu~%x zNZ8Vvqr0zH>mW4+YR^TYP5d%$el{ zPSAwZ2i73wjVx&6edAS&*Le1@(XY^uB*$po=_J7oY;|R=dkTDcr7KX5T7AH46huZs z)w*zn4T)e>P6&i|COhQcsqu2c`?OV-&;JamG<N>&3t=xgHD7k10w z`sjkr$2tPt&)Q40DKVfEu_#~To}`L0)PCBuZrd`Mt;MET89B7*abOv(+eTh~J+LH@ zE=bJDhZV-g+6(^E$j4BYn2InPhok`dbipr1+A~U4tvaL>6vFmHhFQF{$~fY4pIM(g z=J_l?wm#85j)Ar6^)03j1!B6=3Z*viDwfF?G7ZJXaM&zn0uJ1&a-%)VMN3ic_#${h zlVJvXk4> zF{8hHZOr$0*ks#?nkelB>{j6t!+PzlJ-`>tGixukp1Cn)b8BOPa@1jePA*?c?IjOF zN}GLoVxEvg-;}Rn-bUq6`drO8?e@u~e3*8B!#Q1@Z$T|F-!?0C8!1xmqQ97JW%`EM zHiyl&`Iv1BkS>bPQ9XYtvtTG%ulE_{t&f)y97C+ntd~9HL4#fKe0z!o(s{XR-PWC9 zScwg>G-&(;X1^Uu?$KXf_2(o!?`c)u$AyVpdR!i%3A9M2j%(_exgk`VW9ta}j17CR z_j&3$z6Bf(F@b9?1p}FV-zEFXmZvXb!++m5+rqnw6E2_2^A{Vv zKCMTyV|>}fHLr$KU!c+kD6D`f>bi-H9b}bkBUG|FQt873P8Et4wjk(&Yis6FgeDKk zF^Ik6z}}9-M-FzM>N~Kvy>stLMd?pM)u(m0#cO1_akT!F$c=rJvhp_N!UN|m;r6we zYqNupu{F>cJ@4)a+iI2?+8^24Ln#+zt?t(CT5k*K9pZpg*5k@9m=sNnqCfH`Um)F- zD9iZS3Cy2?Ti1A@ExXP!z0MPDo7c$P$-tWp$#r2~;2yrg;jAO3pmXt0^cSl*Zw_e* zDo-C2_jQRU+xLrI`%XG)diZZN`Mic<=zP32szZE=dHP`8zOFh%tV6^aIDUeb04`x% zE{-1_f;j@b`aqITx`Sdbr=}z|lzh4c4kUvL;hJ+4`sgRxG8`GIt&iv$TOzs^{BCQB z=o{+my#}rf85GLOGbo{;IcaRE=34N(t%V%uBbLDQFofptEOe5NvczUH^8Rydt>R@X zk_}7>bj%a09x2A4hpU8n1SwsjN+0SDk82cWC?33)989?#UTaTfiMk8TTLXo!hmMJ4 za8+FPkVOS44#_41$Ex8w^WQV&wd{)ex03XxM&|1Zet?vGSlSYAsglV&YH+r=zYXdB#a(oOzV+;h)8_x#UKaz zf>!t?76uJ^J}BU-^NQrZPZYZ=ITpTSE6N!r7-VbU4Iqlf=JE7 zz<~iqZ;1$fzTFA4o#>&cImJVb2}=eo>bclF+bugUmX{7)tSTRZDhl^FFwomIM4o`y z-(}g^Tn#g-sT3Z%5Q7XoLCZ=}f{!grhZ9SQRgC6LTE9-iG=kN zdj<(Gy$L0p7eg;$T7!zTyPJ@LdmVfOvN|j%6j!|$lPGHX`jDL`a*n=v|BP?-m(lcP zJ{X#Aoqp+~W6-pf9ff{xQ;dGEU}Ebh01>z>Sjq$1Z2*E$XpYi6QV56dTbV*~om&YKBSVh}c0G0XtlkygOW)0anAoz@SN}o&RL}eCr*edY z-Ov;T^*t04sI1bL>dT$1@~cY`IH+S}o2SgHnn;(_63Rf*D#)b~yhuB&9VxqIB%ivW z;UOWX(w84J#sm(kLx>Bdu(nOeY7gpxyqR))=kQLCGN4;Et6SsOtqJJX-9P8o#W?)6 zd+NQJ6Wb;Vg)Na%Nw=$pt46E=UG}Ul&#%k#mIZVLv$_($t|Xw_Bve@aI&1U}sRom` z+iMlp??tqppnl)H-uQ-`fY&|SycL1u4FP>I`ctQO^Eb9FEtfOVIngeZzWNjz&l;xj z+#{dYC*5uuZW_7Zy&TYQ3WrUSyc%&aA-7q`YI&?bM2ecs(M->a_w(K^x>qFXPJA^# z+!t8CTPS*NN-1OmaN=>w!KK$+9gE#0>^vo8bw1X2J>5W${nKLL$3zE`xgn@-oL6bx zW#4%DjRMb_(UM21)sfS0rg_@`GQ*?wZn)p?Q{1~ClvMjpe{gwv;NjJ&3%H}h+R#2PHLzia-@GHJ-ua0!_2!^!@0>ApQI?HxRnS(6z2#Z?J%0Sk*p8<_#tWEypQeVeZ{?r=Z^H3^ft)9(V@6TEv$SQuE)G*TeBi(H29)IZ` z;ER;j-?$_sHMkQ%=(TX%tQ@&?W7m=gA>_6M548&&M*@eAPQ&4s^nD1hHk*=%5I{in zxiq#dn6iFJ5%gvri-MEAhs9^AMSP~@+b4%l3OS`g!{#3s&u%~H-wy3CLOXrb`-`(5 zofVFEBeqkZxlcIV?{7X6-2UQ{3WZ$q@D1vF=TgkGDY^cXTmWh^pJF*_dscc=#?!~r z-^zSsT>qmTvt{-EvU*G#WqTtUAbQ`>s&$gq>bFd5g%B~CLvlo|NlqnaW>4{iGGN@e z%$J!B<^4j|fuR22oI06MLp_S1dL@FI-6jCB!pFM&c~g$Z>TL>`HoEISPRm$wSz&FX zuxf8GxoLhywx|B}7tm}&12@9Wrz7^8w_{@SOnON)4pO_fejk!BZc|d!c|VD-^V>$NR*}2<8O=1RQYTnLuPE` z9K=bz$7LT)Q@poQ0s4M+`q7NUpUX3ku2la#M*%8SWgX2+^qUl*-C;f!g=DgbuIem4 z6JLzG^f@4Ba*22Tobyq%6eYZR0U65w=a7Q`kD-LPkZBRzq-!4oy@Kh+?1{``-cKlj zBo={O6FN60*Mu|)7&4K20$9aoHam1>(1yj4yxJppDH7Wn&yQDV8&;(1AZ4LJpA)e^yu8$NT(z^xHu`P=nFiI7GZVr6io zRf|e6htxx=KcZg6l6sSt*NbQS+K`eT`J87P(K20o6`-tGqb%R-5r5cg=VRKKAWGZ- zDwjOfNAazl-kaA<4^a7Lhg8sFj7- z<~wNJ{)`U)hX~E;Og_e6p%WCbRWwPg!k)95@qYcHc`uklgyJ5M1QU70$csK12zbI` zrij9c9d&bJ$6Rlo_x7Azw<{AIW z-Ou;~beU5igY=28fb|z6kmu4}ioQLl^;w|b$;=dtAWAL(_=OL+L&NfA9Vw~lrB2O{Y3}b%tsOG;W!Z={Y zU*Dud7GvfK?Oi9r5MFKABjiBG0D-1gQF;)h$QV)}Y_CJKWF4hs5fp` zAO};;cM3-fJ>{=&@@^fk9IKqz7s#*hnSPS-AY*DnpsLy3=1ygplf(Q-_np4cKF_&; zxmYk)`r4)vA3Q%@{%F^sxjpq$=lpv*9_y{X)?0124-X$6Y4FsF3&M#NZ>rj6M$=%$kWZVdGgL_uP#BJlUdN;67Pes3)dSn@2f%%v*>swfRCxmtYai z_&7}u_b)1KT+Tt}eFDpYmOr)GH4Jjd>?n#dDMY7m0apSkB} z2|)@@Ly>7r8F!jSn=qg!3w-&$EtAE;mAeC`-9Kv=T95wY3E{-aM+Z+n+$Pw2{iZ&5 zy>u6>K|)^b2es3x_YX`}3#}c3^?3pIu9g!){RWsNUSkdKf0KvjIY$*0}Ag462Vlyp~tym(@rJ2Y|_4TURw6*@Uwcfs&w2hOSeGPxR zTUs!;d6h6rE)YYmwh4vpGo}uh&!RM$cjbeqwa*sPWroAtvyi^=*+Q1g+|4mVt%n&q z_7y1J%PHPhteDnR?%S+bVyJ~_Ni@{bVz2_{5QpLxPxBE^=K%#yB-;N~ME=LeK>ls< zKOy|D2>&a?|B2y$Rrp^W{?~k4-lwLeF+RL#C4&$T5enLZ9=dEza8-cy8E8RpH<#kke0-_OFo%A=tyv*(9%0cz_nW z)>Q{~*F8AW=(dSct#V=2HU#oo>l+d(571l1HkG03Vtv}lGZpAwl?^eH4{XyP`1rG~8 zEPhx#J%Dh|fjviN%8uUJa=T)1xbhp!o({JYt=Zx5eoJ|2u zscX-?N^|}4waeFEx%SFPS5TEH$?LYbIMs z=gA&58gc532_tyfc0UqB3SRVP`1I4#X4%FUeL)+ERav&huSsezI7Y-Ui{Y^3XL6QR zfI}(wRRwB`V`;(L7^1BS)>4rv?2IK&J>evLKol`(unF;NmADlraMn?OA5AJy9m2O=TGl3!{@*GcNB>baEZKs!x5}Q)o}CJjn7LaHpmilevXMhQMvC@ZLy+$v zDvP*9KXmqNCeJVo5Szd@lvASS=xn5nhd7IVq>-YHj*_VD7}{@yRf{|x4y|KgNP)5! zzVb5wPyW^V`-U!xVC&Wa({v>;O+V*erI9I(0NDFlH14)1ZCDMhy?!YBfrFW$GRqy*l?Jp<1Ci#$Sm z;Ko=jqI)kDcMQTQ9(_$lG0qJ@i9=^1Qg2W9i`%2mw--*-=x&leh=E(+7+l2he}U&J zy1(ZyEr|P;o;HPgfk!?bnNt;hYNC82iF!9$C9{rUSXWC{OT<<%$TWF?TZDVQmlk{57VY!+6rl2MtQC)o%S ziAZ>B^yTN_$0Lh_x0a5$7U$!Ll{AQG{!Yc+3ZN&X1j2w4v)L8? z>-digZwvm8coWq#^Tplgp&TC=8S94 zoFegh%e9t~MsJx=vdh&HR8&Wjgggf&3`hxkh5U<@a49C?6eV;<61*pb%B6*xyk~`~ zla$Lgr`Fy!3>!x50d=;k=94gc1)0j5IYHgJd9^`E-tprDzG@+9U$C`fw)MEb_4thX z1ORB}))kERez!Nw{SfNX=I?B~yA6_<$u$Hrl`I-Q+&oAqJnHL%Umpb0#v^0i8~mLs zqgP)48vLr&hBq=10Xk)T#n_5KN`YTpz`{q*>r(_%g^&A*@_};lvafH-K6MzukKx8R z+x~*Tot!oyn)w(G*J9&u8L2shqqPn=TEOtRdo+-?&Q)czv zab;xl*RRg2O>jvs^Fn{S_6cx%;Cczia;3?wKu5dn-_(KgRGOaX^tUT+R6y4ZPex4v z;$+9RbH-?yH<|A=jyB$D8Ex^_cn1QeVp8X31+qG(^M6tFQBg3fV4Sm;AyqTQu+7j-I&2) zY(X)VM9KID92ZZvM~ZaN9y_UEsD&{VWui7z)Emhgsh)p8)g(q6$6xzwrX_9oGwFPh zT71XpmiX7CRQ~nIUgRPu|I?+ZcW?;x0F!`IdE|)fi+34L9bH;3d?U;9vQ&Ar96!6f zN;U=2M2B0YnTV$%qA;CJf04_eA(4GiZ^LfyWh%NOq_Po!r_C19+HAdp6tu}^<7u_c zzeDxiq&=nw_&Yjet-MWXY*xltpf4{nh#5pahx9+nSb z13G3($E@}KlujI^6ZeSRpu_7#+(d?nY^4*si0mO!N2H0!F(Q3L&Jr0U@+U;D6ZsYq zdM5mbL|B*1)84O${2P&96ZsvH|0Tjq3fTvO!rQYz6wF3V{7R4nip|;lKOi$O2(eUR zDk1h(FoD(Y)jJV}3V4sz{4{doCv*G~6t6SKeX7Xj6u+&MarwWJ8UIP9VL$DY@Jvhd z<`(*56BOG-WgvTNKv(WkhfxA&jjR2})n56yYD_h*AJb19ogDBTpI#rZSOdnxF8xBH z&LDSbU~7?dnsaHNq?v&kRR%MiTA|JAHyn0p=TbAh>WRyKeT7Sdm<)gkEAwj$T&gE& z`vh6?f;=Hb?lQm;+?eU^qPQ4yiW9=gQ?n<#{3p8>6XZs@O9uyT>8y}hJJt4K$3skg z`&_!YqztK0yI?&sYdz+-9*Y)A&T{i&kp8WcUGLi8u?LJhT>3@5nlmh#6S#s!J;zln zs^we*x2WZ~ZNE{gIpd<)$fYi>k#ikLOybfO^Afp~#Z)=hhvNjUmit|5C1?6n2D11P zC+Bj1mz>OP{!|9CxK7EL7n9W}WY(aj3@ul%kfp_5mYl0v(8{@H0QU`WoN?iN!dhH) z;SlHKQ1OB@p@HL8E*_PeI4ieU%9>Hi!!rsK L+^-c`YykWpF=2cv literal 0 HcmV?d00001 diff --git a/.tfcore/utils/tf-doc-check.py b/.tfcore/utils/tf-doc-check.py new file mode 100644 index 0000000..122d9d2 --- /dev/null +++ b/.tfcore/utils/tf-doc-check.py @@ -0,0 +1,784 @@ +#!/usr/bin/env python3 +"""tf-doc-check.py — check a TechieFlow human document against its template schema. + +Every template under .tfcore/templates/v4custom/ opens with a `` +block: required sections in order, word budgets per size, per-entry limits, and the +named row rules. This script reads that block and checks a generated document +against it. One line per problem, in plain words: + + FAIL docs/MyApp-BRD.md: section "Scope" is missing + WARN docs/MyApp-BRD.md: 6,410 words; the Small target is 6,000 (maximum 8,000) + +Exit 0 when nothing FAILs, 1 when something does, 2 when it could not run. +`--warn` prints every finding as WARN and exits 0 (report mode for existing projects). + +Called through tf-doc-check.sh. Python 3 standard library only. +Readable description of the same rules: docs/TechieFlow-Document-Schemas.md. + +Schema grammar (one `key: value` per line inside the comment): + doc: brd file: docs/{App}-BRD.md + header: App, Kind, Size fields that must be present and filled in + section: Name | flag | max N flag = required | optional | optional-small (required for M/L) + | app (required when Kind=app) | library (required when Kind=library) + a trailing * on the name matches any heading with that prefix + budget: S 6000 8000 | M … target and maximum words per size (code blocks and comments excluded) + entries: Section | Prefix: the section holding one ### entry per screen/task; optional H3 prefix + per-entry: 250 400 target and maximum words per entry + max-lines: 120 target-lines: 60 + rule: name a named check implemented below (rules starting entry- run per entry) +""" +from __future__ import annotations + +import argparse +import os +import re +import sys + +SELF_DIR = os.path.dirname(os.path.abspath(__file__)) +TEMPLATE_DIR = os.path.normpath(os.path.join(SELF_DIR, "..", "templates", "v4custom")) + +# file-name suffix (lower-case) -> (doc id, template file) +DOC_KINDS = [ + ("-brd.md", "brd", "app-brd-tmpl.md"), + ("-architecture.md", "architecture", "app-architecture-tmpl.md"), + ("-uidesign.md", "uidesign", "app-uidesign-tmpl.md"), + ("-checklist.md", "checklist", "app-checklist-tmpl.md"), + ("coding-standards.md", "coding-standards", "app-coding-standards-tmpl.md"), + ("project-status.md", "project-status", "app-project-status-tmpl.md"), + ("-usageguide.md", "usageguide", "app-usageguide-tmpl.md"), + ("-usage-guide.md", "usageguide", "app-usageguide-tmpl.md"), + ("-devguide.md", "devguide", "app-devguide-tmpl.md"), + ("-productguide.md", "productguide", "app-productguide-tmpl.md"), +] +SIZED_DOCS = {"brd", "architecture", "uidesign", "checklist", "usageguide", "devguide", "productguide"} + +SIZE_NAMES = {"s": "S", "small": "S", "m": "M", "medium": "M", "l": "L", "large": "L"} +SIZE_LONG = {"S": "Small", "M": "Medium", "L": "Large"} +REQ_CAP = {"S": 50, "M": 100, "L": 100} + +CHECKLIST_HEADER = "| ID | Requirement | Status | % | Remarks | Details |" +STATUS_VALUES = { + "not started", "in progress", "implemented", "verified", "done (pre-existing)", + "needs re-verify", "partial", "fail", "blocked", "n/a", +} +PERF_BUDGET = re.compile( + r"perf-budget:\s*(p50|p95|max)\s+(ttfb|load)\s*<=\s*\d+\s*ms(\s*@\s*concurrency\s+\d+)?", re.I +) +ACCEPT_LINE = re.compile(r"\*Acceptance:?\*|^\s*[-*]\s*Acceptance:", re.I) +ACCEPT_FORM = re.compile( + r"^\s*[-*]\s*\*?Acceptance:?\*?:?\s*(?:Given\b[^,]*,\s*)?When\b.+?,\s*then\b.+", re.I +) +NAMES_SCREEN = re.compile(r"\b(on|opens?|from|in)\b", re.I) + + +# ---------------------------------------------------------------------------- +# helpers +# ---------------------------------------------------------------------------- +def norm_heading(text: str) -> str: + """'## 3. Users and roles (all)' -> 'users and roles'.""" + h = text.strip().rstrip(":") + h = re.sub(r"^[#\s]*", "", h) + h = re.sub(r"^(?:§\s*)?\d+(?:\.\d+)*[.)]?\s+", "", h) # leading numbering + h = re.sub(r"[\U0001F300-\U0001FAFF☀-➿]", "", h) # emoji + h = re.sub(r"\s*[—(].*$", "", h) # trailing qualifiers: "— …" or "(…)" + h = re.sub(r"\s+", " ", h).strip().lower() + return h + + +def key_matches(declared_key: str, present_key: str) -> bool: + if declared_key.endswith("*"): + return present_key.startswith(declared_key[:-1].rstrip()) + return declared_key == present_key + + +def strip_comments(text: str) -> str: + return re.sub(r"", "", text, flags=re.S) + + +def strip_noise(text: str) -> str: + """Remove HTML comments and fenced code blocks (they are not prose).""" + return re.sub(r"```.*?```", "", strip_comments(text), flags=re.S) + + +def word_count(text: str) -> int: + return len(re.findall(r"\S+", strip_noise(text))) + + +def split_sections(body: str, level: int = 2): + """Return [(heading_text or None, section_text)] split on headings of `level`.""" + pat = re.compile(rf"(?m)^{'#' * level}\s+(.+)$") + out, last, last_head = [], 0, None + for m in pat.finditer(body): + out.append((last_head, body[last:m.start()])) + last_head, last = m.group(1).strip(), m.end() + out.append((last_head, body[last:])) + return out + + +def parse_header(body: str) -> dict: + """Header fields from YAML frontmatter and from the first `| Key | Value |` table.""" + fields = {} + text = strip_comments(body).lstrip() + fm = re.match(r"^---\s*\n(.*?)\n---\s*\n", text, re.S) + if fm: + for line in fm.group(1).splitlines(): + m = re.match(r"^\s*([A-Za-z_][\w -]*):\s*(.*?)\s*$", line) + if m: + fields[m.group(1).strip().lower()] = m.group(2).strip() + head = text.split("\n## ", 1)[0] + for m in re.finditer(r"(?m)^\|\s*([^|]+?)\s*\|\s*([^|]*?)\s*\|\s*$", head): + key, val = m.group(1).strip().lower(), m.group(2).strip() + if key and not set(key) <= set("-: ") and key not in fields: + fields[key] = val + return fields + + +def is_placeholder(val: str) -> bool: + v = val.strip() + return v == "" or v.startswith("{") or v.startswith("<") or v in ("…", "...") + + +def tables_in(text: str): + """Yield (header_cells, rows) for every markdown table in text.""" + lines = strip_noise(text).splitlines() + i = 0 + while i < len(lines) - 1: + if lines[i].lstrip().startswith("|") and re.match(r"^\s*\|?\s*:?-{2,}", lines[i + 1]): + header = [c.strip() for c in lines[i].strip().strip("|").split("|")] + rows, j = [], i + 2 + while j < len(lines) and lines[j].lstrip().startswith("|"): + rows.append([c.strip() for c in lines[j].strip().strip("|").split("|")]) + j += 1 + yield header, rows + i = j + else: + i += 1 + + +def has_table_with(text: str, *cols: str) -> bool: + want = [c.lower() for c in cols] + for header, _rows in tables_in(text): + low = " | ".join(h.lower() for h in header) + if all(w in low for w in want): + return True + return False + + +def links_to(text: str, folder: str): + """All paths in the text that point into `folder` (mockups, screenshots).""" + return {m.group(1) for m in re.finditer(rf"((?:\./|\.\./|docs/)?{folder}/[^\s)\]\"'`>|]+)", text)} + + +def resolve(root: str, doc_path: str, link: str) -> bool: + link = link.split("#", 1)[0] + cands = [os.path.join(root, link), os.path.join(os.path.dirname(doc_path), link), os.path.join(root, "docs", link)] + return any(os.path.exists(os.path.normpath(c)) for c in cands) + + +# ---------------------------------------------------------------------------- +# schema +# ---------------------------------------------------------------------------- +class Schema: + def __init__(self, text: str): + self.doc = self.file = None + self.header = [] + self.sections = [] # (name, flag, max_words) + self.budget = {} # size -> (target, max) + self.entries = None # (section name, h3 prefix) + self.per_entry = None # (target, max) + self.max_lines = self.target_lines = None + self.rules = [] + m = re.search(r"", text, re.S) + if not m: + raise ValueError("no tf-schema block") + for raw in m.group(1).splitlines(): + line = raw.strip() + if not line or line.startswith("#") or ":" not in line: + continue + key, val = line.split(":", 1) + key, val = key.strip().lower(), val.strip() + if key == "doc": + self.doc = val + elif key == "file": + self.file = val + elif key == "header": + self.header = [h.strip() for h in val.split(",") if h.strip()] + elif key == "section": + parts = [p.strip() for p in val.split("|")] + flag = parts[1].lower() if len(parts) > 1 and parts[1] else "required" + mx = None + for p in parts[2:]: + mm = re.match(r"max\s+(\d+)", p, re.I) + if mm: + mx = int(mm.group(1)) + self.sections.append((parts[0], flag, mx)) + elif key == "budget": + for part in val.split("|"): + bits = part.split() + if len(bits) == 3: + self.budget[bits[0].upper()] = (int(bits[1]), int(bits[2])) + elif key == "entries": + parts = [p.strip() for p in val.split("|")] + self.entries = (parts[0], parts[1] if len(parts) > 1 else "") + elif key == "per-entry": + bits = val.split() + self.per_entry = (int(bits[0]), int(bits[1])) + elif key == "max-lines": + self.max_lines = int(val) + elif key == "target-lines": + self.target_lines = int(val) + elif key == "rule": + self.rules.append(val.lower()) + + def declared(self, present_key: str): + for name, flag, mx in self.sections: + if key_matches(norm_heading(name), present_key): + return name, flag, mx + return None + + +def load_schema(template_file: str) -> Schema: + with open(os.path.join(TEMPLATE_DIR, template_file), encoding="utf-8") as fh: + return Schema(fh.read()) + + +# ---------------------------------------------------------------------------- +# reporting and context +# ---------------------------------------------------------------------------- +class Report: + def __init__(self, warn_only: bool): + self.warn_only = warn_only + self.fails = self.warns = 0 + self.lines = [] + + def fail(self, path, msg): + if self.warn_only: + self.warns += 1 + self.lines.append(f"WARN {path}: {msg}") + else: + self.fails += 1 + self.lines.append(f"FAIL {path}: {msg}") + + def warn(self, path, msg): + self.warns += 1 + self.lines.append(f"WARN {path}: {msg}") + + +def detect_kind(path: str): + name = os.path.basename(path).lower() + for suffix, doc, tmpl in DOC_KINDS: + if name.endswith(suffix): + return doc, tmpl + return None, None + + +def find_root(path: str) -> str: + d = os.path.dirname(os.path.abspath(path)) + while True: + if os.path.isdir(os.path.join(d, ".tfcore")) or os.path.isdir(os.path.join(d, "docs")): + return d + parent = os.path.dirname(d) + if parent == d: + return os.path.dirname(os.path.abspath(path)) + d = parent + + +def read_core_config(root: str) -> dict: + out = {} + p = os.path.join(root, ".tfcore", "core-config.yaml") + if os.path.exists(p): + with open(p, encoding="utf-8") as fh: + for line in fh: + m = re.match(r"^(appSize|appKind):\s*(\S+)", line) + if m and m.group(2) not in ("null", "~", "''", '""'): + out[m.group(1)] = m.group(2).strip("'\"") + return out + + +def sibling_brd_header(path: str) -> dict: + """Size/Kind from docs/-BRD.md when another document of the app lacks them.""" + base = os.path.basename(path) + m = re.match(r"(.+?)-(Architecture|UIDesign|Checklist|Coding-Standards|UsageGuide|Usage-Guide|DevGuide|ProductGuide)\.md$", base, re.I) + if not m: + return {} + brd = os.path.join(os.path.dirname(path), f"{m.group(1)}-BRD.md") + if not os.path.exists(brd): + return {} + with open(brd, encoding="utf-8") as fh: + return parse_header(fh.read()) + + +def resolve_size(header, brd_header, cfg, cli_size, rep, rel, need): + raw = header.get("size") or brd_header.get("size") or cfg.get("appSize") or cli_size + if raw: + size = SIZE_NAMES.get(str(raw).strip().lower()) + if size: + return size + if need: + rep.warn(rel, f'Size "{raw}" is not Small, Medium or Large; Small assumed') + elif need: + rep.warn(rel, "no Size recorded (document header, the BRD header, or appSize in core-config.yaml); Small assumed") + return "S" + + +def resolve_kind(header, brd_header, cfg) -> str: + raw = (header.get("kind") or brd_header.get("kind") or cfg.get("appKind") or "app").strip().lower() + return "library" if raw.startswith("lib") else "app" + + +def section_text(present, name): + key = norm_heading(name) + return next((txt for k, _h, txt in present if key_matches(key, k)), None) + + +# ---------------------------------------------------------------------------- +# the checker +# ---------------------------------------------------------------------------- +def check_document(path: str, rep: Report, cli_size=None, root=None): + doc, tmpl = detect_kind(path) + if not doc: + rep.warn(path, "not a TechieFlow document name; skipped") + return None + try: + schema = load_schema(tmpl) + except (OSError, ValueError) as e: + rep.warn(path, f"template {tmpl} has no usable tf-schema block ({e}); skipped") + return None + with open(path, encoding="utf-8") as fh: + body = fh.read() + root = root or find_root(path) + rel = os.path.relpath(path, root) + cfg = read_core_config(root) + header = parse_header(body) + brd_header = sibling_brd_header(path) if doc != "brd" else {} + size = resolve_size(header, brd_header, cfg, cli_size, rep, rel, doc in SIZED_DOCS) + kind = resolve_kind(header, brd_header, cfg) + clean = strip_noise(body) + nocomment = strip_comments(body) + + # 1. header fields + for field in schema.header: + val = header.get(field.lower()) + if val is None: + rep.fail(rel, f'header field "{field}" is missing') + elif is_placeholder(val): + rep.fail(rel, f'header field "{field}" is still a placeholder') + + # 2. sections: strangers, order, presence + present = [(norm_heading(h), h, txt) for h, txt in split_sections(clean, 2) if h is not None] + always_ok = {"table of contents"} | ({"recovery note"} if doc == "project-status" else set()) + for key, h, txt in present: + if schema.declared(key) or key in always_ok: + continue + if doc == "checklist" and re.search(r" mx: + rep.fail(rel, f"{total:,} words; the {SIZE_LONG[size]} maximum is {mx:,} (target {target:,}). Shorten prose; never drop a screen, field or requirement to fit") + elif total > target: + rep.warn(rel, f"{total:,} words; the {SIZE_LONG[size]} target is {target:,} (maximum {mx:,})") + for key, h, txt in present: + d = schema.declared(key) + if d and d[2]: + n = word_count(txt) + if n > d[2]: + rep.fail(rel, f'section "{h}" is {n} words; at most {d[2]}') + if schema.max_lines: + n_lines = nocomment.strip().count("\n") + 1 + if n_lines > schema.max_lines: + rep.fail(rel, f"{n_lines} lines; at most {schema.max_lines}") + elif schema.target_lines and n_lines > schema.target_lines: + rep.warn(rel, f"{n_lines} lines; the target is {schema.target_lines} (maximum {schema.max_lines})") + + # 4. per-entry checks (screens, tasks, components) + entry_names = [] + if schema.entries: + sec_txt = section_text(present, schema.entries[0]) + prefix = schema.entries[1] + if sec_txt is not None: + for h3, etxt in split_sections(sec_txt, 3): + if h3 is None: + continue + if prefix and not h3.lower().startswith(prefix.lower()): + rep.fail(rel, f'entry "{h3}" must start with "{prefix}"') + continue + ename = h3[len(prefix):].strip() if prefix else h3 + ename = re.sub(r"\s*\(.*$", "", ename).strip("` ").strip() + entry_names.append(ename) + if schema.per_entry: + n = word_count(etxt) + if n > schema.per_entry[1]: + rep.fail(rel, f'entry "{ename}" is {n} words; maximum {schema.per_entry[1]} (target {schema.per_entry[0]})') + elif n > schema.per_entry[0]: + rep.warn(rel, f'entry "{ename}" is {n} words; target {schema.per_entry[0]} (maximum {schema.per_entry[1]})') + for rule in schema.rules: + if rule.startswith("entry-"): + check_entry_rule(rule, ename, etxt, rel, root, path, rep) + if not entry_names and kind == "app": + rep.fail(rel, f'section "{schema.entries[0].rstrip("*")}" has no "###" entries') + + # 5. document rules + ctx = dict(doc=doc, body=body, clean=clean, nocomment=nocomment, present=present, header=header, + size=size, kind=kind, root=root, path=path, rel=rel, entry_names=entry_names) + for rule in schema.rules: + if not rule.startswith("entry-"): + check_doc_rule(rule, ctx, rep) + return ctx + + +def check_entry_rule(rule, ename, etxt, rel, root, path, rep): + if rule == "entry-mockup": + links = links_to(etxt, "mockups") + if not links: + rep.fail(rel, f'screen "{ename}" has no mockup link (docs/mockups/.html)') + for l in sorted(links): + if not resolve(root, path, l): + rep.fail(rel, f'screen "{ename}" links mockup {l}, which does not exist') + elif rule == "entry-screenshot": + imgs = re.findall(r"!\[[^\]]*\]\(([^)]+)\)", etxt) + if not imgs: + rep.fail(rel, f'entry "{ename}" has no screenshot image') + for l in imgs: + if not resolve(root, path, l): + rep.fail(rel, f'entry "{ename}" screenshot {l} does not exist') + elif rule == "entry-fields-table": + if not has_table_with(etxt, "field", "type"): + rep.fail(rel, f'screen "{ename}" has no fields table (Field, Type, Required, Validation)') + elif rule == "entry-regions-table": + if not has_table_with(etxt, "region", "control"): + rep.fail(rel, f'screen "{ename}" has no regions-to-controls table') + elif rule == "entry-states": + low = etxt.lower() + missing = [s for s in ("empty", "loading", "error") if s not in low] + if missing: + rep.fail(rel, f'screen "{ename}" does not say what it shows when {", ".join(missing)}') + elif rule == "entry-break-table": + if not has_table_with(etxt, "file", "function", "watch", "expected"): + rep.fail(rel, f'entry "{ename}" has no where-to-break table (File and line, Function, Watch, Expected value)') + elif rule == "entry-call-chain": + if not re.search(r"(?im)^\**call chain:?\**", etxt): + rep.fail(rel, f'entry "{ename}" has no "Call chain:" line') + elif rule == "entry-steps": + if not re.search(r"(?mi)^\s*(?:[-*]\s*[*_]*steps?[*_:]*\s*)?1[.)]\s+\S", etxt): + rep.fail(rel, f'entry "{ename}" has no numbered steps') + elif rule == "entry-expected": + if not re.search(r"(?im)^\s*[-*]?\s*\**expected", etxt): + rep.fail(rel, f'entry "{ename}" has no "Expected:" line') + + +def check_doc_rule(rule, c, rep): + rel, root, path, body, clean, nocomment, present, size = ( + c["rel"], c["root"], c["path"], c["body"], c["clean"], c["nocomment"], c["present"], c["size"]) + + if rule == "brd-ledger": + ids = re.findall(r"\*\*BRD-(\d+)\*\*", clean) + if not ids: + rep.fail(rel, "no **BRD-N** items found in the Requirements section") + return + seen, dupes = set(), set() + for i in ids: + (dupes if i in seen else seen).add(i) + if dupes: + rep.fail(rel, "duplicate requirement ids: " + ", ".join(f"BRD-{d}" for d in sorted(dupes, key=int))) + if len(seen) > REQ_CAP[size]: + rep.fail(rel, f"{len(seen)} requirements; the {SIZE_LONG[size]} cap is {REQ_CAP[size]}. Split into phases (each phase its own BRD) instead of growing this one") + c["brd_ids"] = seen + + elif rule == "mockup-links": + for l in sorted(links_to(clean, "mockups")): + if not resolve(root, path, l): + rep.fail(rel, f"mockup link {l} points at a file that does not exist") + + elif rule == "screens-table": + txt = section_text(present, "Screens and flow") + if txt is None: + return + if not has_table_with(txt, "screen", "route", "mockup"): + rep.fail(rel, 'the "Screens and flow" table needs the columns Screen, Route, Role, Mockup, Fields') + return + names = [] + for header, rows in tables_in(txt): + low = [h.lower() for h in header] + if "screen" in low and "route" in low: + sc, rc = low.index("screen"), low.index("route") + for r in rows: + if len(r) <= max(sc, rc) or not r[sc] or r[sc].startswith("{"): + continue + if "dialog" in r[sc].lower() or r[rc].strip("` ").lower().startswith("on "): + continue + names.append(r[sc]) + c["brd_screens"] = names + + elif rule == "stack-table": + txt = section_text(present, "Stack decisions") + if txt is not None and not any(True for _ in tables_in(txt)): + rep.fail(rel, 'the "Stack decisions" section needs a table (one row per stack question)') + + elif rule == "solution-table": + txt = section_text(present, "Solution structure") + if txt is not None and not has_table_with(txt, "project", "kind"): + rep.fail(rel, 'the "Solution structure" table needs the columns Project, Kind, Purpose') + + elif rule == "er-diagram": + if section_text(present, "Data model") is not None and not re.search(r"```mermaid\s*\n\s*erDiagram", nocomment): + rep.fail(rel, 'the "Data model" section needs a mermaid erDiagram') + + elif rule == "decisions-log": + txt = section_text(present, "Decisions log") + if txt is not None and not has_table_with(txt, "date", "decision", "why", "status"): + rep.fail(rel, 'the "Decisions log" table needs the columns Date, Decision, Why, Status') + + elif rule == "request-flow": + txt = section_text(present, "Component map") + if txt is not None and not re.search(r"(?im)^\**how a request travels", txt): + rep.fail(rel, 'the "Component map" section needs a "How a request travels" numbered list') + + elif rule == "execution-code": + seg = re.search(r"(?ms)^##\s+(?:\d+[.)]\s+)?Execution guide.*?\n(.*?)(?=^## |\Z)", nocomment) + if seg and "```" not in seg.group(1): + rep.fail(rel, 'the "Execution guide" needs the start commands in a code block') + + elif rule == "test-users-table": + txt = section_text(present, "Test users") + if txt is not None and not has_table_with(txt, "user", "role"): + rep.fail(rel, 'the "Test users" section needs a table with User and Role columns') + + elif rule == "next-command-blocks": + seg = re.search(r"(?ms)^## Next command to run\s*\n(.*?)(?=^## |\Z)", nocomment) + if not seg: + return + txt = seg.group(1) + blocks = re.findall(r"```[^\n]*\n(.*?)```", txt, re.S) + if len(blocks) != 2: + rep.fail(rel, f'"Next command to run" must hold exactly two code blocks, Claude Code then OpenCode; found {len(blocks)}') + return + for label, blk in zip(("Claude Code", "OpenCode"), blocks): + lines = [l for l in blk.splitlines() if l.strip()] + if len(lines) != 1: + rep.fail(rel, f"the {label} command block must be one line; found {len(lines)}") + parts = txt.split("```") + if "claude code" not in parts[0].lower(): + rep.fail(rel, 'the first command block must be labelled "Claude Code" on the line before it') + if len(parts) > 2 and "opencode" not in parts[2].lower(): + rep.fail(rel, 'the second command block must be labelled "OpenCode" on the line before it') + + elif rule == "verification-log": + txt = section_text(present, "Verification log") + if txt is None: + return + for _h, rows in tables_in(txt): + if len(rows) > 5: + rep.fail(rel, f"the Verification log holds {len(rows)} rows; keep the last five, the rest lives in gates.jsonl and runs.jsonl") + for r in rows: + for cell in r: + n = len(re.findall(r"\S+", cell)) + if n > 20: + rep.fail(rel, f"a Verification log cell is {n} words; at most 20, a result is a count not a story") + break + + elif rule == "open-requirements-max-10": + txt = section_text(present, "Open requirements") + if txt is not None: + n = len(re.findall(r"(?m)^\s*[-*]\s*\[?[ x]?\]?\s*REQ-", txt)) + if n > 10: + rep.fail(rel, f"Open requirements names {n} rows; show counts by status and at most ten named rows") + + elif rule == "checklist-rows": + check_checklist(c, rep) + + elif rule == "standards-pointer": + if ".tfcore/standards/" not in body: + rep.fail(rel, "must name the framework standard files it applies (.tfcore/standards/...)") + + +def check_checklist(c, rep): + rel, root, path, body, clean, present, size = ( + c["rel"], c["root"], c["path"], c["body"], c["clean"], c["present"], c["size"]) + txt = section_text(present, "Requirements Status") + if txt is None: + return + if not any(re.sub(r"\s+", " ", l.strip()) == CHECKLIST_HEADER for l in clean.splitlines()): + rep.fail(rel, f"the Requirements Status table header must be exactly {CHECKLIST_HEADER}") + rows = [l for l in txt.splitlines() if re.match(r"^\s*\|\s*REQ-", l)] + ids = [] + for l in rows: + cells = [x.strip() for x in l.strip().strip("|").split("|")] + if len(cells) < 6: + rep.fail(rel, f"row {cells[0] if cells else '?'} has {len(cells)} cells; six are needed") + continue + rid, _req, status, pct, remarks, details = cells[:6] + rid = rid.strip("`* ") + ids.append(rid) + if not re.fullmatch(r"REQ-(UI|FN|RAG|NFR)-\d{3}", rid): + rep.fail(rel, f'id "{rid}" is not REQ-UI/FN/RAG/NFR- plus three digits') + if status.strip("`* ").lower() not in STATUS_VALUES: + rep.fail(rel, f'{rid} status "{status}" is not one of the fixed values') + if pct.strip("`* ").rstrip("%").strip() not in ("0", "25", "50", "75", "100"): + rep.fail(rel, f'{rid} % "{pct}" must be 0, 25, 50, 75 or 100') + n = len(re.findall(r"\S+", remarks)) + if n > 60: + rep.fail(rel, f"{rid} Remarks is {n} words; at most 60, current state only (history lives in the telemetry streams)") + m = re.search(r"\(#([^)]+)\)", details) + if not m: + rep.fail(rel, f"{rid} Details cell has no link to its detail entry") + elif not re.search(rf" REQ_CAP[size]: + rep.fail(rel, f"{len(ids)} rows; the {SIZE_LONG[size]} cap is {REQ_CAP[size]}. Split into phases instead of growing this checklist") + dupes = sorted({i for i in ids if ids.count(i) > 1}) + if dupes: + rep.fail(rel, "duplicate rows: " + ", ".join(dupes)) + + brd_refs = set() + for rid in ids: + m = re.search(rf" on , then "') + elif rid.startswith(("REQ-UI", "REQ-FN")) and not NAMES_SCREEN.search(acc[0].split(", then", 1)[0]): + rep.fail(rel, f'{rid} acceptance line must name the screen ("… on , then …")') + for l in entry.splitlines(): + if "perf-budget:" in l.lower() and not PERF_BUDGET.search(l): + rep.fail(rel, f"{rid} perf-budget line is not in the form perf-budget: <= ms [@ concurrency ]") + refs = re.findall(r"BRD-(\d+)", entry) + if not refs: + rep.fail(rel, f"{rid} detail entry does not name its BRD-N item") + brd_refs.update(refs) + if rid.startswith("REQ-UI"): + links = links_to(entry, "mockups") + if not links: + rep.fail(rel, f"{rid} is a UI row without a mockup link") + for l in sorted(links): + if not resolve(root, path, l): + rep.fail(rel, f"{rid} mockup {l} does not exist") + c["checklist_brd_refs"] = brd_refs + c["checklist_ids"] = ids + + +# ---------------------------------------------------------------------------- +# cross-document rules +# ---------------------------------------------------------------------------- +def cross_checks(ctxs: dict, rep: Report, root: str): + brd, ui, cl = ctxs.get("brd"), ctxs.get("uidesign"), ctxs.get("checklist") + if brd and ui and brd.get("brd_screens") is not None and ui["kind"] == "app": + b = {re.sub(r"\s+", " ", s.strip("`* ")).lower() for s in brd["brd_screens"]} + u = {re.sub(r"\s+", " ", s).lower() for s in ui["entry_names"]} + for s in sorted(b - u): + rep.fail(brd["rel"], f'screen "{s}" is in the BRD but has no "### Screen:" entry in the UIDesign') + for s in sorted(u - b): + rep.fail(ui["rel"], f'screen "{s}" is in the UIDesign but not in the BRD screens table') + if brd and cl and brd.get("brd_ids") is not None and cl.get("checklist_brd_refs") is not None: + missing = sorted(brd["brd_ids"] - cl["checklist_brd_refs"], key=int) + if missing: + shown = ", ".join(f"BRD-{m}" for m in missing[:20]) + (" …" if len(missing) > 20 else "") + rep.fail(cl["rel"], f"BRD items with no checklist row ({len(missing)}): {shown}") + if ui: + mock_dir = os.path.join(root, "docs", "mockups") + if os.path.isdir(mock_dir): + linked = {os.path.basename(l.split("#")[0]) for l in links_to(ui["clean"], "mockups")} + for f in sorted(os.listdir(mock_dir)): + if f.lower().endswith(".html") and f not in linked: + rep.warn(ui["rel"], f"mockup {f} is not linked from any screen") + + +# ---------------------------------------------------------------------------- +def app_files(root: str, app: str): + docs = os.path.join(root, "docs") + names = [f"{app}-BRD.md", f"{app}-Architecture.md", f"{app}-UIDesign.md", f"{app}-Checklist.md", + f"{app}-Coding-Standards.md", f"{app}-UsageGuide.md", f"{app}-DevGuide.md", f"{app}-ProductGuide.md"] + out = [os.path.join(docs, n) for n in names if os.path.exists(os.path.join(docs, n))] + ps = os.path.join(root, "PROJECT-STATUS.md") + if os.path.exists(ps): + out.append(ps) + return out + + +def main(argv=None): + ap = argparse.ArgumentParser(description="Check TechieFlow documents against their template schemas.") + ap.add_argument("files", nargs="*", help="document paths") + ap.add_argument("--app", help="check every human document of this app under /docs plus PROJECT-STATUS.md") + ap.add_argument("--root", help="project root (default: found from the first file, or the current directory)") + ap.add_argument("--size", help="Small|Medium|Large when no header or core-config carries it") + ap.add_argument("--warn", action="store_true", help="report only: every finding is WARN and the exit code is 0") + ap.add_argument("--quiet", action="store_true", help="print findings and the summary only") + a = ap.parse_args(argv) + + if not os.path.isdir(TEMPLATE_DIR): + print(f"tf-doc-check: template folder not found at {TEMPLATE_DIR}", file=sys.stderr) + return 2 + root = os.path.abspath(a.root) if a.root else None + files = list(a.files) + if a.app: + root = root or os.getcwd() + files += app_files(root, a.app) + if not files: + print(f"tf-doc-check: no documents for app {a.app} under {root}", file=sys.stderr) + return 2 + if not files: + ap.print_help() + return 2 + root = root or find_root(files[0]) + + rep = Report(a.warn) + ctxs, checked = {}, 0 + for f in files: + if not os.path.exists(f): + rep.fail(os.path.relpath(f, root), "file not found") + continue + ctx = check_document(f, rep, a.size, root) + checked += 1 + if ctx: + ctxs[ctx["doc"]] = ctx + if len(ctxs) > 1: + cross_checks(ctxs, rep, root) + + for line in rep.lines: + print(line) + if not a.quiet: + for f in files: + if os.path.exists(f): + r = os.path.relpath(f, root) + if not any(l.split(": ", 1)[0].endswith(" " + r) for l in rep.lines): + print(f"OK {r}") + print(f"tf-doc-check: {rep.fails} FAIL, {rep.warns} WARN in {checked} document(s)") + return 1 if rep.fails else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.tfcore/utils/tf-doc-check.sh b/.tfcore/utils/tf-doc-check.sh new file mode 100644 index 0000000..9907b94 --- /dev/null +++ b/.tfcore/utils/tf-doc-check.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# tf-doc-check.sh — check TechieFlow human documents against their template schemas. +# +# bash .tfcore/utils/tf-doc-check.sh docs/MyApp-BRD.md [more.md ...] +# bash .tfcore/utils/tf-doc-check.sh --app MyApp # every document of the app + PROJECT-STATUS.md +# bash .tfcore/utils/tf-doc-check.sh --app MyApp --warn # report mode: findings as WARN, exit 0 +# bash .tfcore/utils/tf-doc-check.sh --size Medium docs/MyApp-BRD.md +# +# Each template under .tfcore/templates/v4custom/ opens with a `` +# block (required sections in order, budgets per size, row rules). This script +# prints one line per problem — FAIL blocks the phase, WARN does not — and exits +# 0 when nothing FAILs, 1 when something does, 2 when it could not run. +# +# Run by the status gate (_status-update-gate.md) on every human document a +# command wrote, before the HTML render. Readable version of the rules: +# docs/TechieFlow-Document-Schemas.md. +# +# Thin wrapper over tf-doc-check.py, matching the tf-render-html.sh idiom. +# Python 3 standard library only. +set -uo pipefail + +SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +if ! command -v python3 >/dev/null 2>&1; then + echo "tf-doc-check: python3 is required (standard library only)." >&2 + exit 2 +fi + +exec python3 "$SELF_DIR/tf-doc-check.py" "$@" diff --git a/docs/TechieFlow-Document-Schemas.html b/docs/TechieFlow-Document-Schemas.html new file mode 100644 index 0000000..9607289 --- /dev/null +++ b/docs/TechieFlow-Document-Schemas.html @@ -0,0 +1,630 @@ + + + + + +TechieFlow — Document Schemas + + + + + +
+ +
+

TechieFlow — Document Schemas

+
Rendered 2026-09-04 · source TechieFlow-Document-Schemas.md
+ + +
+ + + + + + + +
PurposeFor each human document the framework produces: which sections it must have, how big it may be, and what every row must contain. A checker script enforces this, so the AI cannot drift from the shape.
AudienceThe owner (reviews the section lists, the size limits and the decisions in §7). Agents read the same rules in machine form at the top of each template.
StatusBuilt in Session 3 of the reset and closed 2026-09-04. Nine templates carry a schema block; tf-doc-check.sh enforces it; the status gate runs it; day-1 asks the size. §6 holds the real results on fourteen projects. The tenth document, the Deployment Checklist (§3.10), is agreed and is built in Sitting 4b.
CompanionTechieFlow-Reset-Plan-2026-09-04.md (Session 3), TechieFlow-Requirements.md (FR-07 to FR-09, FR-14, FR-15, FR-17), TechieFlow-How-It-Works.md §8 (D-1, D-2, D-3, D-20).
+
+

1. How a schema works#

+

Each template opens with a short block inside an HTML comment, so it never shows in a rendered page. The block says, in a fixed form:

+
    +
  • which file the template produces (docs/<App>-BRD.md); +
  • +
  • the required sections, in order, and the optional ones; +
  • +
  • the word budget for each size (Small, Medium, Large), as a target and a maximum; +
  • +
  • the rules every row or entry must follow. +
  • +
+

One script, tf-doc-check.sh, reads the block for a document and checks the document against it. It prints one line per problem, in words a person can act on, and exits with a failure when anything is wrong. The status gate runs it on every human document a command wrote. A failing document means the phase does not close.

+

The owner does not read the machine blocks. This page is the readable version of the same rules.

+
+

2. Size, kind, and what counts as a screen#

+

The size is chosen at day-1 and written into core-config.yaml and every document header. The definition was agreed on 2026-09-04 (D-3, FR-09):

+ + + + + + + +
SizeScreensRolesRequirements
Smallup to 10oneup to 50
Mediumup to 20anyup to 100
Largemore than 20, or more than 100 requirementsanysplit into phases; each phase is its own Small or Medium BRD, checklist and build
+

What counts as a screen (owner, 2026-09-04). A screen is a page with its own route. Every routed page counts, including sign in, register, forgot password, reset password, and any licence, subscription or role screens. AppManager provides the API behind those screens; the application builds the screens. Dialogs, tabs and panels inside a page are regions of that page, not screens. They are listed under their parent screen in the UIDesign, drawn in that screen's mockup, and verified on that page. By this rule TfLens has thirteen screens, which is Medium by screens; its 169 requirements are beyond Medium in any case, which is the D-3 complaint.

+

A known defect follows from this and is logged as a miss (§8): the verifier today cannot drive a dialog, so dialogs have been built as separate routed screens to get them verified. Session 4c rewrites the verify task so a dialog is verified on its parent page, and the build never promotes a dialog to a route to make it testable.

+

Kind. A project is an app or a library. Kind sits beside Size. For a library the UIDesign and mockups are optional, and the screen maps become component maps:

+
    +
  • A UI library with a sample app (TrBlazeUI): the DevGuide's component map links every component to the sample-app screen that shows it, with that screen's screenshot. +
  • +
  • A service library (TechieRag): the component map is the consumer's view, one entry per service with how it is called, and it lives in the UsageGuide. +
  • +
+

Where the size and kind are asked. Greenfield day-1 asks one more question after the concept: "Size: Small, Medium or Large? From the concept I count N screens and N roles, so I propose X." Brownfield day-1 counts the routes in the code and confirms the same way. The answer is written to appSize: and appKind: in core-config.yaml and into every document header. *amend-docs reads the cap from there and proposes a phase split when an addition would pass it (FR-10; wired in Session 4a).

+
+

3. The documents#

+

For each document: what it is for, the sections a Small app needs, what Medium and Large add, the budget, and the row rules. "Removed" means the section was in the old template and is no longer allowed; existing content in a project is not deleted by anything.

+

Budgets are a target and a maximum (owner, 2026-09-04: a single hard number makes the AI truncate). The checker warns above the target and fails only above the maximum. Word counts exclude code blocks, diagrams and comments. Truncation is stopped by the content rules, not the budget: a document that drops a screen, a field, a requirement or a row to fit fails on those rules first, so the only way to meet a budget is shorter prose. The only fixed numbers are Coding Standards and the checklist Remarks cell.

+

3.1 BRD — docs/<App>-BRD.md#

+

What it is for: the owner's statement of what the product does, screen by screen, with one numbered requirement per thing the verifier will test. Produced at day-1 together with the Architecture and the mockups (D-1, D-2).

+ + + + + + + + + + + + + + + + +
OrderSectionSmallMedium / LargeContent rule
0Header tablerequiredrequiredApp, Kind, Size, Stack answer set, Status, Date
1SummaryrequiredrequiredWhat it is, for whom, why. At most 200 words.
2ScoperequiredrequiredTwo lists: in, out.
3Users and rolesrequiredrequiredOne table.
4Screens and flowrequiredrequiredOne table: screen, route, role, mockup link, fields. A dialog is a row under its parent screen with on /route in the Route column. Then the primary journey as a numbered list.
5RequirementsrequiredrequiredThe BRD-N ledger. Every item: id, title, screen, mockup link, one acceptance line in the "When …, then …" form. Ids never renumbered.
6Non-functional requirementsrequiredrequiredOne table. A speed requirement uses the perf-budget: form, only where the owner gave a number.
7Development statusrequiredrequiredOne row per screen with counts. Written by the status gate, not by hand.
8Context diagramoptionalrequired
9Constraints and assumptionsoptionalrequired
10Risksoptionalrequired
11Glossaryoptionaloptional
+

Removed: Business objectives (folded into Summary), Component sketch (the Architecture owns it), Feature catalog (repeated the ledger), Success metrics, Table of Contents (the renderer builds it), the footer.

+

Budget: Small 6,000 target, 8,000 maximum. Medium 10,000 target, 15,000 maximum. Large: the Medium figures per phase. Checks: requirement ids unique and within the size cap; every mockup link points at a file that exists; the screens table has the five columns; the set of screens equals the UIDesign's (dialog rows excluded).

+

3.2 Architecture — docs/<App>-Architecture.md#

+

What it is for: the technical decisions, once, so no phase re-invents them. Produced at day-1. Read at every build.

+ + + + + + + + + + + + + +
OrderSectionSmallMedium / LargeContent rule
0Header tablerequiredrequiredApp, Kind, Size, Stack answer set, Date
1Stack decisionsrequiredrequiredOne row per stack question with its source: the answer set, the owner, or the existing code (FR-01).
2Solution structurerequiredrequiredOne table: project, kind (web app, class library, test project, migrations project), purpose.
3Component maprequiredrequiredOne diagram, then "How a request travels": one typical request as a numbered list in words. No sequence diagram; per-screen detail belongs to the DevGuide.
4Data modelrequiredrequiredOne ER diagram, then a table of entities with key fields.
5Cross-cuttingrequiredrequiredIdentity (AppManager API), configuration, logging, errors. One short paragraph each.
6Decisions logrequiredrequiredOne row per decision: date, decision, why, status (decided, planned, done). Every package added has a row. A planned brownfield change is a row with status "planned" naming the BRD item that drives it.
7Module responsibilitiesoptionalrequired
8Open questionsoptionaloptional
+

Removed: Deployment (decided after UAT; how a developer runs the application is in the UsageGuide), Primary data flow (folded into the Component map as the request list), Target architecture (replaced by the status column), Sources harvested, Table of Contents.

+

Budget: Small 2,500 target, 3,500 maximum. Medium 4,000 target, 6,000 maximum. Checks: the stack table, the solution table, the request list, the ER diagram and the four-column decisions table are all present.

+

3.3 UIDesign — docs/<App>-UIDesign.md#

+

What it is for: one section per screen, mapping every region to a control and listing every field, beside the mockup. The build implements from it; the verifier compares the built screen to it. Optional for libraries.

+ + + + + + + + + +
OrderSectionSmallMedium / LargeContent rule
0Header tablerequiredrequiredApp, Kind, Size, UI library, Theme
1Design systemrequiredrequiredLayout rules, theme, shared controls. At most 300 words.
2ScreensrequiredrequiredOne ### Screen: Name (/route) per screen. Each: mockup link (file must exist), roles, a regions-to-controls table, a fields table (field, type, required, validation), the dialogs the screen opens with their fields, and one line each for the empty, loading and error states. Target 250 words per screen, maximum 400.
3Click-through flowoptionalrequiredWhich screen leads to which.
4Branding guideoptionaloptionalColours and type when they differ from the library defaults.
+

Removed: How to use, Library gaps (the library feedback file owns them), Table of Contents.

+

Budget: 300 words plus the per-screen figures, so a ten-screen Small app is 2,800 words target and 4,300 maximum. Checks: every screen has its mockup, its two tables and its three states; every mockup file in docs/mockups/ is linked from a screen (a warning otherwise).

+

3.4 Checklist — docs/<App>-Checklist.md#

+

An agent document. The owner does not review it and it is never rendered. Only the row rules matter.

+

Sections, in order: Goal, Requirements Status (the one table), then one section per page or group holding the detail entries. No other sections: a UAT Bugs or Feedback tracker section goes into the misses stream instead.

+

Row rules, each checked by script:

+
    +
  1. Table header is exactly | ID | Requirement | Status | % | Remarks | Details |. +
  2. +
  3. ID matches REQ-UI-, REQ-FN-, REQ-RAG- or REQ-NFR- plus three digits. No duplicates. +
  4. +
  5. Status is one of the fixed values. Verified is written only by a verify run (hook exists). +
  6. +
  7. % is 0, 25, 50, 75 or 100. +
  8. +
  9. The Details link resolves to an anchor in the same file. +
  10. +
  11. Every detail entry names its BRD-N item, and every BRD-N item in the BRD has at least one row. +
  12. +
  13. Every UI row carries a mockup link to a file that exists. +
  14. +
  15. Every row has exactly one acceptance line, and it reads "When <actor> <does what> on <screen>, then <a result a browser robot can observe>". An optional "Given …," may precede it. UI and functional rows name the screen; a NFR row names the measurement instead and, for speed, carries perf-budget: in the fixed form. +
  16. +
  17. Remarks holds the current state only, at most 60 words. The history of a row lives in the telemetry streams. +
  18. +
  19. Row count is within the size cap. +
  20. +
+

3.5 Coding Standards — docs/<App>-Coding-Standards.md#

+

Decided 2026-09-04: the standards are baked into the framework, and the .NET set is a framework document with guidance on how to edit it, the way model routing is done.

+ + + + + + + +
WhereFileContentSize
Framework, every project.tfcore/standards/coding-standards-core.mdTechnology-neutral rules: naming style, layout, one configuration mechanism, no package without a decisions-log row, logging, testability, what the verifier's standards check reads.about 500 words
Framework, .NET answer set.tfcore/standards/coding-standards-dotnet.mdThe .NET rules, seeded from the two sample files in this repository's docs/ folder. Header explains how to edit it. Loaded when the project's Stack answer set is .NET.about 800 words
Per projectdocs/<App>-Coding-Standards.mdSections: Standards applied (which files, and the choices the stack file leaves open, such as the instance-field prefix), Project rules (often empty), Enforcement.800 target, 1,200 maximum, every size
+

The per-project file stays because the verifier's standards check and the build read it, and because a developer joining the project needs one starting point. update-framework.sh now copies the standards folder into every project.

+

3.6 PROJECT-STATUS — PROJECT-STATUS.md#

+

The owner's report (2026-09-04): agents fill it with long accounts of what they did and fixed, and the next-command section is not in a fixed place and shows one harness only. Two causes were found: the hook watches only the harness's file-write tool, so a write through the shell is not seen; and it checks headings and length, not content.

+

Sections, fixed, in order, with a word limit each: Where I am (80), Next command to run (60), Open requirements (200), Known blockers (150), Verification log (250), Library feedback summary (60), Standards compliance (60), Deferred / future (100). Same for every size and kind.

+

Rules: at most 120 lines, 60 as the target. Next command to run holds exactly two one-line code blocks, the first labelled Claude Code and the second OpenCode, so each can be copied on its own. Verification log keeps the last five rows, and no cell in it exceeds 20 words: a result is a count, not a story. Open requirements shows counts by status and at most ten named rows.

+

The fix, built: the checker enforces all of the above at the status gate whichever way the file was written; a shell write to PROJECT-STATUS (redirection, tee, cp, mv, sed -i, a script) is refused by the guard hook in both harnesses; the checker becomes a Stop hook in Session 6.

+

3.7 UsageGuide — docs/<App>-UsageGuide.md#

+

What it is for: the owner's test plan. Who to sign in as, how to start it, what to click on every screen and what should happen. Started at day-1, finished at handoff.

+ + + + + + + + + + + +
OrderSectionSmallMedium / LargeContent rule
1Test usersrequiredrequiredOne table: user, password source, role, exists.
2Execution guiderequiredrequiredPrerequisites, then start commands in a code block, one per line.
3How to test, screen by screenrequiredrequiredOne ### per screen, at most 120 words: who to sign in as, numbered steps, the expected result, the REQ ids covered.
4Automated testsrequiredrequiredThe command and what it covers.
5Known limitationsrequiredrequired
6Platform notesoptionaloptionalOnly when the app runs on more than one platform.
7Component mapservice librariesservice librariesOne entry per service: what it does, how it is called.
+

Removed: Smoke checklist (folded into section 3), the separate Setup and Deployment sections (folded into the Execution guide), the long section titles.

+

Budget: Small 2,500 target, 3,500 maximum. Medium 4,000 target, 6,000 maximum.

+

3.8 DevGuide — docs/<App>-DevGuide.md#

+

What it is for: a developer's map from each screen to the code that serves it, written so the developer can set breakpoints and debug it. Produced automatically when the build completes the checklist (D-7), refreshed at handoff.

+ + + + + + + + + + +
OrderSectionSmallMedium / LargeContent rule
0Header tablerequiredrequiredApp, Kind, Size, Verified on (the run the screenshots and line numbers come from), Date
1Architecture cheat-sheetrequiredrequiredOne diagram and at most 300 words.
2Roles and menu mapappsapps
3Screen-by-screen code maprequiredrequiredOne ### per screen (per component for a UI library). Each entry: the screenshot (file must exist); one "Call chain:" line, page method to service class and method to data-access class and method; a where-to-break table, one row per step: file and line, function, the variable to watch, the value it should hold. Example row: Login.razor.cs:127, HandleLogin, aLogin.Email, the email typed in the box. Target 300 words per screen, maximum 450.
4Cross-cutting flowsrequiredrequiredSign-in, configuration, logging, errors, each with its own call chain and where-to-break table.
5Known issuesrequiredrequired
+

Line numbers are taken from the code at the time of writing and refreshed at handoff; the function name is what a developer searches for when a line has moved.

+

Removed: How to use this guide, How to fix a bug with this guide (boilerplate), Table of Contents.

+

Budget: Small 4,000 target, 6,000 maximum. Medium 7,000 target, 10,000 maximum.

+

3.9 ProductGuide — docs/<App>-ProductGuide.md#

+

What it is for: the end user's manual, task by task, with a screenshot per task. On demand.

+ + + + + + + + + +
OrderSectionSmallMedium / LargeContent rule
1WelcomerequiredrequiredWhat it does, at most 150 words.
2Getting startedrequiredrequiredSign in, first task.
3Roles at a glanceonly with more than one rolerequired
4Using <App>requiredrequiredOne ### per task: numbered steps, one screenshot each.
5Troubleshootingoptionaloptional
+

Budget: Small 2,500 target, 3,500 maximum. Medium 4,500 target, 6,500 maximum.

+

3.10 Deployment Checklist — docs/<App>-Deployment-Checklist.md (agreed 2026-09-04; template and command built in Sitting 4b)#

+

Raised by the owner in this session: the two existing deployment checklists (TfLens, TechieBlog) are a mess and the document needs a schema like the others. The survey of those two confirmed it. Both are long (6,500 and 12,200 words), one has eight checkboxes and the other none, both re-describe the same secrets in three or four places, one mixes a local Docker path with the production path in one file, one carries a variable that another section says was deleted, and one says on its first page that the pipeline is settled and on its last that it has never run against the real server.

+

What it is for: the steps to put the application on its host, produced after UAT from the pipeline guidance document the owner supplies (Stack Q9 and Q10). One document per hosting target. Running the application locally is not in it; that is the UsageGuide's Execution guide.

+ + + + + + + + + + + + + + +
OrderSectionContent rule
0Header tableApp, Hosting target, Pipeline document (the source), Date, Proven (never, or the date of the last real deploy)
1Who does whatOne table: step, done by the pipeline or by the owner.
2Secrets and settingsOne table: name, where it is set, what breaks without it. Each name appears exactly once in the document.
3Before the first deployA checkbox list. Each box is one action with one observable result.
4DeployA checkbox list.
5After the deployA checkbox list: the command to run and the output that proves it worked.
6RollbackA checkbox list, and one line saying what a rollback does not undo (migrations).
7Routine operationsOne table: task, command.
8TroubleshootingOne table: symptom, cause, fix.
9ProvenOne table: what has been executed for real and when; what remains unverified.
+

Rules: every item in sections 3 to 6 is a checkbox; no narrative sections; one hosting target per document. Budget 2,500 target, 4,000 maximum, every size.

+

Produced by a small new command, *deploy-checklist <App> <pipeline-document>, built in Sitting 4b beside the handoff task, because handoff runs before UAT and deployment comes after.

+
+

4. The checker, as built#

+

bash .tfcore/utils/tf-doc-check.sh takes document paths, or --app <App> to check every human document of that app plus PROJECT-STATUS.md. For each document it finds the template by file name, reads the schema block, and checks:

+
    +
  1. Header fields present and filled in (Size, Kind where required). A document without a Size falls back to the BRD's header, then to core-config.yaml. +
  2. +
  3. Required sections present, in order, no top-level section outside the allowed set. Numbering and trailing qualifiers in headings are ignored, so "## 3. Scope (v2)" still counts as Scope. +
  4. +
  5. Word budget for the document's size: WARN above the target, FAIL above the maximum. Per-section and per-screen limits the same way. +
  6. +
  7. The row rules of §3 for that document. +
  8. +
  9. Across documents: BRD screens equal UIDesign screens; every BRD-N has a checklist row; every mockup file has a screen. +
  10. +
+

It prints one line per problem, for example:

+
WARN docs/TrSetup-BRD.md: 7,599 words; the Small target is 6,000 (maximum 8,000)
+FAIL docs/TrSetup-Checklist.md: REQ-FN-012 acceptance line does not read "When <actor> <does what> on <screen>, then <observable result>"
+FAIL docs/TrSetup-UIDesign.md: screen "Settings" has no mockup link (docs/mockups/<screen>.html)
+FAIL PROJECT-STATUS.md: "Next command to run" must hold exactly two code blocks, Claude Code then OpenCode; found 1
+

It exits with failure when any line says FAIL. --warn prints every finding as WARN and exits clean: report mode for an existing project, after which each finding is logged as a miss and fixed through *amend-docs.

+

Where it runs: step 7b of the status gate (_status-update-gate.md), on the documents the command wrote, before the HTML render. Both harnesses run it, because it is a shell script called from the task. It becomes a Stop hook in Session 6, after the fixture projects pass.

+

Self-test: bash tests/doc-check/run.sh builds a minimal Small app document set (nine documents, two mockups, two screenshots) that passes with no findings, and a broken twin that fails on thirteen lines, one per planted defect. This is the FR-14 check; the distribution pipeline runs it.

+
+

5. What changed in the framework#

+ + + + + + + + + + + + +
ChangeWhere
Nine templates rewritten with a schema block and a lean skeleton.tfcore/templates/v4custom/app-*-tmpl.md; Coding Standards is new; templates total 3,400 words against 8,300 before
The checker.tfcore/utils/tf-doc-check.sh and tf-doc-check.py
Standards moved into the framework.tfcore/standards/coding-standards-core.md, coding-standards-dotnet.md; update-framework.sh copies the folder
Stack documents installed as templates.tfcore/templates/stack-questions.md, .tfcore/templates/stack-defaults/dotnet.md (copies of the two Stack documents in docs/)
Status gate step 7b.tfcore/tasks/_status-update-gate.md and its Claude Code mirror
Day-1 size and kind question, and the coding-standards step.tfcore/tasks/day1-greenfield.md, day1-brownfield.md and their mirrors; the 150-line standards block left the brownfield task
appSize, appKind keys.tfcore/core-config.yaml
Shell writes to PROJECT-STATUS refused.tfcore/hooks/guard-status.sh now also runs on Bash; registered in .claude/settings.json, the OpenCode plugin, and the three scaffold and update scripts
+

Task references to the old template sections (for example "BRD §4 Development status" in the status gate and split-brd's reading of "§10 Functional requirements") are updated when those tasks are shrunk in Session 4. Until then the day-1 tasks point at the new sections and the other tasks still name the old ones.

+
+

6. What the checker says about existing documents#

+

Run on 2026-09-04 in report mode on fourteen projects. Every project fails, as expected: the shape did not exist before today. The count is findings, one line each.

+ + + + + + + + + + + +
ProjectFindingsThe main reasons
TfLens577BRD 29,400 words and 169 requirements against Medium limits; UIDesign screens without a fields table; all 178 acceptance lines out of form; 178 Remarks cells over 60 words; six mockup links to files that do not exist; DevGuide in its own section set; PROJECT-STATUS with narrative log cells and one command block.
TechieBlog54738 screens, so Large; 135 Remarks cells over limit; 87 rows with no acceptance line; 38 screens without a fields table; 31 Details links unresolved; Verification log over five rows.
TrStudio310112 acceptance lines out of form; 22 screens without a fields table; Architecture with twelve sections outside the list; PROJECT-STATUS with twelve narrative log cells.
TechieRag and TechieDesk231 and 651TechieDesk 153 Remarks cells over limit, 118 rows without an acceptance line, 94 rows with a % outside the five values; TechieRag DevGuide entries without screenshots, call chains or where-to-break tables.
TrBlazeUI19834 rows not naming their BRD item; 19 UI rows without a mockup; acceptance lines out of form; needs the library kind.
TrSetup20142 acceptance lines out of form; UsageGuide and DevGuide in their own section sets; a mockup link still holding the template placeholder.
Seven private projects50 to 733 eachThe same pattern. One checklist has 354 acceptance lines out of form and 132 UI rows without a mockup; one BRD has 80 requirements against a Small cap of 50; two use the older BRD template; every PROJECT-STATUS fails the two-block rule.
+

Every existing project fails on the acceptance line and on the two-block next command, because neither rule existed until today. That is not evidence the rules are too strict; it is why they exist. The owner's decision (§7.1, item 8) is that existing projects are reported with --warn, each finding logged as a miss, and repaired through *amend-docs when the project is next worked on.

+
+

7. Decisions#

+

7.1 Taken on 2026-09-04#

+ + + + + + + + + + + + + + + + + + +
#DecisionResult
1Small section listsAccepted as listed, with the Architecture changes (solution structure and ER diagram added, Deployment removed) and the DevGuide debugging shape.
2BudgetsA target and a maximum for every document, not one number. Fixed numbers only for Coding Standards and the Remarks cell.
3Acceptance line"When … on <screen>, then …"; "Given" allowed as a prefix; NFR rows name the measurement.
4What counts as a screenEvery routed page counts, sign-in and AppManager-backed screens included. Dialogs are regions of their page.
5Kindapp or library. TrBlazeUI's component map links to the sample app; TechieRag's lives in the UsageGuide.
6Coding StandardsBaked into the framework: a neutral core file plus a .NET file in .tfcore/standards/, and a short per-project file.
7RemarksCurrent state only, at most 60 words.
8Block or warnBlock on shape and row rules and on the maximum budget; --warn for existing projects; Stop hook in Session 6.
9ScopeAll nine documents from the start.
10UsageGuide section 2Named "Execution guide".
APrimary data flowFolded into the Component map as "How a request travels", a numbered list in words. No sequence diagram: the owner's view is that sequence diagrams belong to low-level design, and the maintainer agrees; the DevGuide holds the detailed flows.
BTarget architectureReplaced by a Status column in the Decisions log (decided, planned, done).
CCoding Standards shapeAs in §3.5.
DPROJECT-STATUS"Spoiled" means long accounts of what was done, and a next-command section that moves and shows one harness. Fixed by per-section word limits, the two labelled command blocks, the five-row log with 20-word cells, the shell-write guard, and the checker at the gate.
+

| E | Deployment Checklist schema (§3.10) | Accepted as listed. | +| F | Which command produces it | A small new command, *deploy-checklist <App> <pipeline-document>, built in Sitting 4b. | +| G | Tenth document in the Reset Plan | Yes; the plan now lists it under Session 3 and Sitting 4b. |

+

7.2 Still open#

+

Nothing. Session 3 closed on 2026-09-04.

+
+

8. Misses logged in this session#

+

Framework defects found during Session 3, each recorded in docs/metrics/misses.jsonl with the sentence below (the sentence itself is stored in a readable file from Session 5, per D-10).

+ + + + + + + +
MissWhat
MISS-TechieFlow-20260904-24The verifier cannot drive a dialog, so dialogs have been built as separate routed screens to get them verified; a dialog must be verified on its parent page.
MISS-TechieFlow-20260904-25Coding Standards had no template file: the content was a prose block inside the brownfield day-1 task, so it could not be checked, word-counted or kept in one place.
MISS-TechieFlow-20260904-26The PROJECT-STATUS guard watched only the harness's file-write tool, so a write through the shell bypassed it, and it checked headings and length but not content; both are how status files were spoiled.
+ + + + + + + + diff --git a/docs/TechieFlow-Document-Schemas.md b/docs/TechieFlow-Document-Schemas.md new file mode 100644 index 0000000..bb74001 --- /dev/null +++ b/docs/TechieFlow-Document-Schemas.md @@ -0,0 +1,333 @@ +# TechieFlow — Document Schemas + +| | | +|---|---| +| Purpose | For each human document the framework produces: which sections it must have, how big it may be, and what every row must contain. A checker script enforces this, so the AI cannot drift from the shape. | +| Audience | The owner (reviews the section lists, the size limits and the decisions in §7). Agents read the same rules in machine form at the top of each template. | +| Status | **Built in Session 3 of the reset and closed 2026-09-04.** Nine templates carry a schema block; `tf-doc-check.sh` enforces it; the status gate runs it; day-1 asks the size. §6 holds the real results on fourteen projects. The tenth document, the Deployment Checklist (§3.10), is agreed and is built in Sitting 4b. | +| Companion | `TechieFlow-Reset-Plan-2026-09-04.md` (Session 3), `TechieFlow-Requirements.md` (FR-07 to FR-09, FR-14, FR-15, FR-17), `TechieFlow-How-It-Works.md` §8 (D-1, D-2, D-3, D-20). | + +--- + +## 1. How a schema works + +Each template opens with a short block inside an HTML comment, so it never shows in a rendered page. The block says, in a fixed form: + +- which file the template produces (`docs/-BRD.md`); +- the required sections, in order, and the optional ones; +- the word budget for each size (Small, Medium, Large), as a target and a maximum; +- the rules every row or entry must follow. + +One script, `tf-doc-check.sh`, reads the block for a document and checks the document against it. It prints one line per problem, in words a person can act on, and exits with a failure when anything is wrong. The status gate runs it on every human document a command wrote. A failing document means the phase does not close. + +The owner does not read the machine blocks. This page is the readable version of the same rules. + +--- + +## 2. Size, kind, and what counts as a screen + +The size is chosen at day-1 and written into `core-config.yaml` and every document header. The definition was agreed on 2026-09-04 (D-3, FR-09): + +| Size | Screens | Roles | Requirements | +|---|---|---|---| +| Small | up to 10 | one | up to 50 | +| Medium | up to 20 | any | up to 100 | +| Large | more than 20, or more than 100 requirements | any | split into phases; each phase is its own Small or Medium BRD, checklist and build | + +**What counts as a screen** (owner, 2026-09-04). A screen is a page with its own route. Every routed page counts, including sign in, register, forgot password, reset password, and any licence, subscription or role screens. AppManager provides the API behind those screens; the application builds the screens. Dialogs, tabs and panels inside a page are regions of that page, not screens. They are listed under their parent screen in the UIDesign, drawn in that screen's mockup, and verified on that page. By this rule TfLens has thirteen screens, which is Medium by screens; its 169 requirements are beyond Medium in any case, which is the D-3 complaint. + +A known defect follows from this and is logged as a miss (§8): the verifier today cannot drive a dialog, so dialogs have been built as separate routed screens to get them verified. Session 4c rewrites the verify task so a dialog is verified on its parent page, and the build never promotes a dialog to a route to make it testable. + +**Kind.** A project is an `app` or a `library`. Kind sits beside Size. For a library the UIDesign and mockups are optional, and the screen maps become component maps: + +- A UI library with a sample app (TrBlazeUI): the DevGuide's component map links every component to the sample-app screen that shows it, with that screen's screenshot. +- A service library (TechieRag): the component map is the consumer's view, one entry per service with how it is called, and it lives in the UsageGuide. + +**Where the size and kind are asked.** Greenfield day-1 asks one more question after the concept: "Size: Small, Medium or Large? From the concept I count N screens and N roles, so I propose X." Brownfield day-1 counts the routes in the code and confirms the same way. The answer is written to `appSize:` and `appKind:` in `core-config.yaml` and into every document header. `*amend-docs` reads the cap from there and proposes a phase split when an addition would pass it (FR-10; wired in Session 4a). + +--- + +## 3. The documents + +For each document: what it is for, the sections a Small app needs, what Medium and Large add, the budget, and the row rules. "Removed" means the section was in the old template and is no longer allowed; existing content in a project is not deleted by anything. + +**Budgets are a target and a maximum** (owner, 2026-09-04: a single hard number makes the AI truncate). The checker warns above the target and fails only above the maximum. Word counts exclude code blocks, diagrams and comments. Truncation is stopped by the content rules, not the budget: a document that drops a screen, a field, a requirement or a row to fit fails on those rules first, so the only way to meet a budget is shorter prose. The only fixed numbers are Coding Standards and the checklist Remarks cell. + +### 3.1 BRD — `docs/-BRD.md` + +What it is for: the owner's statement of what the product does, screen by screen, with one numbered requirement per thing the verifier will test. Produced at day-1 together with the Architecture and the mockups (D-1, D-2). + +| Order | Section | Small | Medium / Large | Content rule | +|---|---|---|---|---| +| 0 | Header table | required | required | App, Kind, Size, Stack answer set, Status, Date | +| 1 | Summary | required | required | What it is, for whom, why. At most 200 words. | +| 2 | Scope | required | required | Two lists: in, out. | +| 3 | Users and roles | required | required | One table. | +| 4 | Screens and flow | required | required | One table: screen, route, role, mockup link, fields. A dialog is a row under its parent screen with `on /route` in the Route column. Then the primary journey as a numbered list. | +| 5 | Requirements | required | required | The `BRD-N` ledger. Every item: id, title, screen, mockup link, one acceptance line in the "When …, then …" form. Ids never renumbered. | +| 6 | Non-functional requirements | required | required | One table. A speed requirement uses the `perf-budget:` form, only where the owner gave a number. | +| 7 | Development status | required | required | One row per screen with counts. Written by the status gate, not by hand. | +| 8 | Context diagram | optional | required | | +| 9 | Constraints and assumptions | optional | required | | +| 10 | Risks | optional | required | | +| 11 | Glossary | optional | optional | | + +Removed: Business objectives (folded into Summary), Component sketch (the Architecture owns it), Feature catalog (repeated the ledger), Success metrics, Table of Contents (the renderer builds it), the footer. + +Budget: Small 6,000 target, 8,000 maximum. Medium 10,000 target, 15,000 maximum. Large: the Medium figures per phase. Checks: requirement ids unique and within the size cap; every mockup link points at a file that exists; the screens table has the five columns; the set of screens equals the UIDesign's (dialog rows excluded). + +### 3.2 Architecture — `docs/-Architecture.md` + +What it is for: the technical decisions, once, so no phase re-invents them. Produced at day-1. Read at every build. + +| Order | Section | Small | Medium / Large | Content rule | +|---|---|---|---|---| +| 0 | Header table | required | required | App, Kind, Size, Stack answer set, Date | +| 1 | Stack decisions | required | required | One row per stack question with its source: the answer set, the owner, or the existing code (FR-01). | +| 2 | Solution structure | required | required | One table: project, kind (web app, class library, test project, migrations project), purpose. | +| 3 | Component map | required | required | One diagram, then "How a request travels": one typical request as a numbered list in words. No sequence diagram; per-screen detail belongs to the DevGuide. | +| 4 | Data model | required | required | One ER diagram, then a table of entities with key fields. | +| 5 | Cross-cutting | required | required | Identity (AppManager API), configuration, logging, errors. One short paragraph each. | +| 6 | Decisions log | required | required | One row per decision: date, decision, why, status (decided, planned, done). Every package added has a row. A planned brownfield change is a row with status "planned" naming the BRD item that drives it. | +| 7 | Module responsibilities | optional | required | | +| 8 | Open questions | optional | optional | | + +Removed: Deployment (decided after UAT; how a developer runs the application is in the UsageGuide), Primary data flow (folded into the Component map as the request list), Target architecture (replaced by the status column), Sources harvested, Table of Contents. + +Budget: Small 2,500 target, 3,500 maximum. Medium 4,000 target, 6,000 maximum. Checks: the stack table, the solution table, the request list, the ER diagram and the four-column decisions table are all present. + +### 3.3 UIDesign — `docs/-UIDesign.md` + +What it is for: one section per screen, mapping every region to a control and listing every field, beside the mockup. The build implements from it; the verifier compares the built screen to it. Optional for libraries. + +| Order | Section | Small | Medium / Large | Content rule | +|---|---|---|---|---| +| 0 | Header table | required | required | App, Kind, Size, UI library, Theme | +| 1 | Design system | required | required | Layout rules, theme, shared controls. At most 300 words. | +| 2 | Screens | required | required | One `### Screen: Name (/route)` per screen. Each: mockup link (file must exist), roles, a regions-to-controls table, a fields table (field, type, required, validation), the dialogs the screen opens with their fields, and one line each for the empty, loading and error states. Target 250 words per screen, maximum 400. | +| 3 | Click-through flow | optional | required | Which screen leads to which. | +| 4 | Branding guide | optional | optional | Colours and type when they differ from the library defaults. | + +Removed: How to use, Library gaps (the library feedback file owns them), Table of Contents. + +Budget: 300 words plus the per-screen figures, so a ten-screen Small app is 2,800 words target and 4,300 maximum. Checks: every screen has its mockup, its two tables and its three states; every mockup file in `docs/mockups/` is linked from a screen (a warning otherwise). + +### 3.4 Checklist — `docs/-Checklist.md` + +An agent document. The owner does not review it and it is never rendered. Only the row rules matter. + +Sections, in order: Goal, Requirements Status (the one table), then one section per page or group holding the detail entries. No other sections: a `UAT Bugs` or `Feedback tracker` section goes into the misses stream instead. + +Row rules, each checked by script: + +1. Table header is exactly `| ID | Requirement | Status | % | Remarks | Details |`. +2. ID matches `REQ-UI-`, `REQ-FN-`, `REQ-RAG-` or `REQ-NFR-` plus three digits. No duplicates. +3. Status is one of the fixed values. `Verified` is written only by a verify run (hook exists). +4. `%` is 0, 25, 50, 75 or 100. +5. The Details link resolves to an anchor in the same file. +6. Every detail entry names its `BRD-N` item, and every `BRD-N` item in the BRD has at least one row. +7. Every UI row carries a mockup link to a file that exists. +8. Every row has exactly one acceptance line, and it reads **"When `` `` on ``, then ``"**. An optional "Given …," may precede it. UI and functional rows name the screen; a NFR row names the measurement instead and, for speed, carries `perf-budget:` in the fixed form. +9. Remarks holds the current state only, at most 60 words. The history of a row lives in the telemetry streams. +10. Row count is within the size cap. + +### 3.5 Coding Standards — `docs/-Coding-Standards.md` + +Decided 2026-09-04: the standards are baked into the framework, and the .NET set is a framework document with guidance on how to edit it, the way model routing is done. + +| Where | File | Content | Size | +|---|---|---|---| +| Framework, every project | `.tfcore/standards/coding-standards-core.md` | Technology-neutral rules: naming style, layout, one configuration mechanism, no package without a decisions-log row, logging, testability, what the verifier's standards check reads. | about 500 words | +| Framework, .NET answer set | `.tfcore/standards/coding-standards-dotnet.md` | The .NET rules, seeded from the two sample files in this repository's `docs/` folder. Header explains how to edit it. Loaded when the project's Stack answer set is .NET. | about 800 words | +| Per project | `docs/-Coding-Standards.md` | Sections: Standards applied (which files, and the choices the stack file leaves open, such as the instance-field prefix), Project rules (often empty), Enforcement. | 800 target, 1,200 maximum, every size | + +The per-project file stays because the verifier's standards check and the build read it, and because a developer joining the project needs one starting point. `update-framework.sh` now copies the `standards` folder into every project. + +### 3.6 PROJECT-STATUS — `PROJECT-STATUS.md` + +The owner's report (2026-09-04): agents fill it with long accounts of what they did and fixed, and the next-command section is not in a fixed place and shows one harness only. Two causes were found: the hook watches only the harness's file-write tool, so a write through the shell is not seen; and it checks headings and length, not content. + +Sections, fixed, in order, with a word limit each: Where I am (80), Next command to run (60), Open requirements (200), Known blockers (150), Verification log (250), Library feedback summary (60), Standards compliance (60), Deferred / future (100). Same for every size and kind. + +Rules: at most 120 lines, 60 as the target. **Next command to run holds exactly two one-line code blocks, the first labelled Claude Code and the second OpenCode**, so each can be copied on its own. Verification log keeps the last five rows, and no cell in it exceeds 20 words: a result is a count, not a story. Open requirements shows counts by status and at most ten named rows. + +The fix, built: the checker enforces all of the above at the status gate whichever way the file was written; a shell write to PROJECT-STATUS (redirection, tee, cp, mv, sed -i, a script) is refused by the guard hook in both harnesses; the checker becomes a Stop hook in Session 6. + +### 3.7 UsageGuide — `docs/-UsageGuide.md` + +What it is for: the owner's test plan. Who to sign in as, how to start it, what to click on every screen and what should happen. Started at day-1, finished at handoff. + +| Order | Section | Small | Medium / Large | Content rule | +|---|---|---|---|---| +| 1 | Test users | required | required | One table: user, password source, role, exists. | +| 2 | Execution guide | required | required | Prerequisites, then start commands in a code block, one per line. | +| 3 | How to test, screen by screen | required | required | One `###` per screen, at most 120 words: who to sign in as, numbered steps, the expected result, the REQ ids covered. | +| 4 | Automated tests | required | required | The command and what it covers. | +| 5 | Known limitations | required | required | | +| 6 | Platform notes | optional | optional | Only when the app runs on more than one platform. | +| 7 | Component map | service libraries | service libraries | One entry per service: what it does, how it is called. | + +Removed: Smoke checklist (folded into section 3), the separate Setup and Deployment sections (folded into the Execution guide), the long section titles. + +Budget: Small 2,500 target, 3,500 maximum. Medium 4,000 target, 6,000 maximum. + +### 3.8 DevGuide — `docs/-DevGuide.md` + +What it is for: a developer's map from each screen to the code that serves it, written so the developer can set breakpoints and debug it. Produced automatically when the build completes the checklist (D-7), refreshed at handoff. + +| Order | Section | Small | Medium / Large | Content rule | +|---|---|---|---|---| +| 0 | Header table | required | required | App, Kind, Size, Verified on (the run the screenshots and line numbers come from), Date | +| 1 | Architecture cheat-sheet | required | required | One diagram and at most 300 words. | +| 2 | Roles and menu map | apps | apps | | +| 3 | Screen-by-screen code map | required | required | One `###` per screen (per component for a UI library). Each entry: the screenshot (file must exist); one "Call chain:" line, page method to service class and method to data-access class and method; a where-to-break table, one row per step: file and line, function, the variable to watch, the value it should hold. Example row: `Login.razor.cs:127`, `HandleLogin`, `aLogin.Email`, the email typed in the box. Target 300 words per screen, maximum 450. | +| 4 | Cross-cutting flows | required | required | Sign-in, configuration, logging, errors, each with its own call chain and where-to-break table. | +| 5 | Known issues | required | required | | + +Line numbers are taken from the code at the time of writing and refreshed at handoff; the function name is what a developer searches for when a line has moved. + +Removed: How to use this guide, How to fix a bug with this guide (boilerplate), Table of Contents. + +Budget: Small 4,000 target, 6,000 maximum. Medium 7,000 target, 10,000 maximum. + +### 3.9 ProductGuide — `docs/-ProductGuide.md` + +What it is for: the end user's manual, task by task, with a screenshot per task. On demand. + +| Order | Section | Small | Medium / Large | Content rule | +|---|---|---|---|---| +| 1 | Welcome | required | required | What it does, at most 150 words. | +| 2 | Getting started | required | required | Sign in, first task. | +| 3 | Roles at a glance | only with more than one role | required | | +| 4 | Using `` | required | required | One `###` per task: numbered steps, one screenshot each. | +| 5 | Troubleshooting | optional | optional | | + +Budget: Small 2,500 target, 3,500 maximum. Medium 4,500 target, 6,500 maximum. + +### 3.10 Deployment Checklist — `docs/-Deployment-Checklist.md` (agreed 2026-09-04; template and command built in Sitting 4b) + +Raised by the owner in this session: the two existing deployment checklists (TfLens, TechieBlog) are a mess and the document needs a schema like the others. The survey of those two confirmed it. Both are long (6,500 and 12,200 words), one has eight checkboxes and the other none, both re-describe the same secrets in three or four places, one mixes a local Docker path with the production path in one file, one carries a variable that another section says was deleted, and one says on its first page that the pipeline is settled and on its last that it has never run against the real server. + +What it is for: the steps to put the application on its host, produced after UAT from the pipeline guidance document the owner supplies (Stack Q9 and Q10). One document per hosting target. Running the application locally is not in it; that is the UsageGuide's Execution guide. + +| Order | Section | Content rule | +|---|---|---| +| 0 | Header table | App, Hosting target, Pipeline document (the source), Date, Proven (never, or the date of the last real deploy) | +| 1 | Who does what | One table: step, done by the pipeline or by the owner. | +| 2 | Secrets and settings | One table: name, where it is set, what breaks without it. Each name appears exactly once in the document. | +| 3 | Before the first deploy | A checkbox list. Each box is one action with one observable result. | +| 4 | Deploy | A checkbox list. | +| 5 | After the deploy | A checkbox list: the command to run and the output that proves it worked. | +| 6 | Rollback | A checkbox list, and one line saying what a rollback does not undo (migrations). | +| 7 | Routine operations | One table: task, command. | +| 8 | Troubleshooting | One table: symptom, cause, fix. | +| 9 | Proven | One table: what has been executed for real and when; what remains unverified. | + +Rules: every item in sections 3 to 6 is a checkbox; no narrative sections; one hosting target per document. Budget 2,500 target, 4,000 maximum, every size. + +Produced by a small new command, `*deploy-checklist `, built in Sitting 4b beside the handoff task, because handoff runs before UAT and deployment comes after. + +--- + +## 4. The checker, as built + +`bash .tfcore/utils/tf-doc-check.sh` takes document paths, or `--app ` to check every human document of that app plus `PROJECT-STATUS.md`. For each document it finds the template by file name, reads the schema block, and checks: + +1. Header fields present and filled in (Size, Kind where required). A document without a Size falls back to the BRD's header, then to `core-config.yaml`. +2. Required sections present, in order, no top-level section outside the allowed set. Numbering and trailing qualifiers in headings are ignored, so "## 3. Scope (v2)" still counts as Scope. +3. Word budget for the document's size: WARN above the target, FAIL above the maximum. Per-section and per-screen limits the same way. +4. The row rules of §3 for that document. +5. Across documents: BRD screens equal UIDesign screens; every `BRD-N` has a checklist row; every mockup file has a screen. + +It prints one line per problem, for example: + +``` +WARN docs/TrSetup-BRD.md: 7,599 words; the Small target is 6,000 (maximum 8,000) +FAIL docs/TrSetup-Checklist.md: REQ-FN-012 acceptance line does not read "When on , then " +FAIL docs/TrSetup-UIDesign.md: screen "Settings" has no mockup link (docs/mockups/.html) +FAIL PROJECT-STATUS.md: "Next command to run" must hold exactly two code blocks, Claude Code then OpenCode; found 1 +``` + +It exits with failure when any line says FAIL. `--warn` prints every finding as WARN and exits clean: report mode for an existing project, after which each finding is logged as a miss and fixed through `*amend-docs`. + +Where it runs: step 7b of the status gate (`_status-update-gate.md`), on the documents the command wrote, before the HTML render. Both harnesses run it, because it is a shell script called from the task. It becomes a Stop hook in Session 6, after the fixture projects pass. + +Self-test: `bash tests/doc-check/run.sh` builds a minimal Small app document set (nine documents, two mockups, two screenshots) that passes with no findings, and a broken twin that fails on thirteen lines, one per planted defect. This is the FR-14 check; the distribution pipeline runs it. + +--- + +## 5. What changed in the framework + +| Change | Where | +|---|---| +| Nine templates rewritten with a schema block and a lean skeleton | `.tfcore/templates/v4custom/app-*-tmpl.md`; Coding Standards is new; templates total 3,400 words against 8,300 before | +| The checker | `.tfcore/utils/tf-doc-check.sh` and `tf-doc-check.py` | +| Standards moved into the framework | `.tfcore/standards/coding-standards-core.md`, `coding-standards-dotnet.md`; `update-framework.sh` copies the folder | +| Stack documents installed as templates | `.tfcore/templates/stack-questions.md`, `.tfcore/templates/stack-defaults/dotnet.md` (copies of the two Stack documents in `docs/`) | +| Status gate step 7b | `.tfcore/tasks/_status-update-gate.md` and its Claude Code mirror | +| Day-1 size and kind question, and the coding-standards step | `.tfcore/tasks/day1-greenfield.md`, `day1-brownfield.md` and their mirrors; the 150-line standards block left the brownfield task | +| `appSize`, `appKind` keys | `.tfcore/core-config.yaml` | +| Shell writes to PROJECT-STATUS refused | `.tfcore/hooks/guard-status.sh` now also runs on Bash; registered in `.claude/settings.json`, the OpenCode plugin, and the three scaffold and update scripts | + +Task references to the old template sections (for example "BRD §4 Development status" in the status gate and split-brd's reading of "§10 Functional requirements") are updated when those tasks are shrunk in Session 4. Until then the day-1 tasks point at the new sections and the other tasks still name the old ones. + +--- + +## 6. What the checker says about existing documents + +Run on 2026-09-04 in report mode on fourteen projects. Every project fails, as expected: the shape did not exist before today. The count is findings, one line each. + +| Project | Findings | The main reasons | +|---|---|---| +| TfLens | 577 | BRD 29,400 words and 169 requirements against Medium limits; UIDesign screens without a fields table; all 178 acceptance lines out of form; 178 Remarks cells over 60 words; six mockup links to files that do not exist; DevGuide in its own section set; PROJECT-STATUS with narrative log cells and one command block. | +| TechieBlog | 547 | 38 screens, so Large; 135 Remarks cells over limit; 87 rows with no acceptance line; 38 screens without a fields table; 31 Details links unresolved; Verification log over five rows. | +| TrStudio | 310 | 112 acceptance lines out of form; 22 screens without a fields table; Architecture with twelve sections outside the list; PROJECT-STATUS with twelve narrative log cells. | +| TechieRag and TechieDesk | 231 and 651 | TechieDesk 153 Remarks cells over limit, 118 rows without an acceptance line, 94 rows with a `%` outside the five values; TechieRag DevGuide entries without screenshots, call chains or where-to-break tables. | +| TrBlazeUI | 198 | 34 rows not naming their BRD item; 19 UI rows without a mockup; acceptance lines out of form; needs the `library` kind. | +| TrSetup | 201 | 42 acceptance lines out of form; UsageGuide and DevGuide in their own section sets; a mockup link still holding the template placeholder. | +| Seven private projects | 50 to 733 each | The same pattern. One checklist has 354 acceptance lines out of form and 132 UI rows without a mockup; one BRD has 80 requirements against a Small cap of 50; two use the older BRD template; every PROJECT-STATUS fails the two-block rule. | + +Every existing project fails on the acceptance line and on the two-block next command, because neither rule existed until today. That is not evidence the rules are too strict; it is why they exist. The owner's decision (§7.1, item 8) is that existing projects are reported with `--warn`, each finding logged as a miss, and repaired through `*amend-docs` when the project is next worked on. + +--- + +## 7. Decisions + +### 7.1 Taken on 2026-09-04 + +| # | Decision | Result | +|---|---|---| +| 1 | Small section lists | Accepted as listed, with the Architecture changes (solution structure and ER diagram added, Deployment removed) and the DevGuide debugging shape. | +| 2 | Budgets | A target and a maximum for every document, not one number. Fixed numbers only for Coding Standards and the Remarks cell. | +| 3 | Acceptance line | "When … on ``, then …"; "Given" allowed as a prefix; NFR rows name the measurement. | +| 4 | What counts as a screen | Every routed page counts, sign-in and AppManager-backed screens included. Dialogs are regions of their page. | +| 5 | Kind | `app` or `library`. TrBlazeUI's component map links to the sample app; TechieRag's lives in the UsageGuide. | +| 6 | Coding Standards | Baked into the framework: a neutral core file plus a .NET file in `.tfcore/standards/`, and a short per-project file. | +| 7 | Remarks | Current state only, at most 60 words. | +| 8 | Block or warn | Block on shape and row rules and on the maximum budget; `--warn` for existing projects; Stop hook in Session 6. | +| 9 | Scope | All nine documents from the start. | +| 10 | UsageGuide section 2 | Named "Execution guide". | +| A | Primary data flow | Folded into the Component map as "How a request travels", a numbered list in words. No sequence diagram: the owner's view is that sequence diagrams belong to low-level design, and the maintainer agrees; the DevGuide holds the detailed flows. | +| B | Target architecture | Replaced by a Status column in the Decisions log (decided, planned, done). | +| C | Coding Standards shape | As in §3.5. | +| D | PROJECT-STATUS | "Spoiled" means long accounts of what was done, and a next-command section that moves and shows one harness. Fixed by per-section word limits, the two labelled command blocks, the five-row log with 20-word cells, the shell-write guard, and the checker at the gate. | + +| E | Deployment Checklist schema (§3.10) | Accepted as listed. | +| F | Which command produces it | A small new command, `*deploy-checklist `, built in Sitting 4b. | +| G | Tenth document in the Reset Plan | Yes; the plan now lists it under Session 3 and Sitting 4b. | + +### 7.2 Still open + +Nothing. Session 3 closed on 2026-09-04. + +--- + +## 8. Misses logged in this session + +Framework defects found during Session 3, each recorded in `docs/metrics/misses.jsonl` with the sentence below (the sentence itself is stored in a readable file from Session 5, per D-10). + +| Miss | What | +|---|---| +| MISS-TechieFlow-20260904-24 | The verifier cannot drive a dialog, so dialogs have been built as separate routed screens to get them verified; a dialog must be verified on its parent page. | +| MISS-TechieFlow-20260904-25 | Coding Standards had no template file: the content was a prose block inside the brownfield day-1 task, so it could not be checked, word-counted or kept in one place. | +| MISS-TechieFlow-20260904-26 | The PROJECT-STATUS guard watched only the harness's file-write tool, so a write through the shell bypassed it, and it checked headings and length but not content; both are how status files were spoiled. | diff --git a/docs/TechieFlow-Reset-Plan-2026-09-04.html b/docs/TechieFlow-Reset-Plan-2026-09-04.html index a4f068a..980ca0f 100644 --- a/docs/TechieFlow-Reset-Plan-2026-09-04.html +++ b/docs/TechieFlow-Reset-Plan-2026-09-04.html @@ -225,9 +225,9 @@

Session 3 — Templates become schemas#

Goal: every human document the framework produces gets a short required-shape list and a check script, so the AI cannot drift from the format. This comes before any task is shrunk, because the tasks exist to produce these documents.

Owner brings: a git branch created. For each document, which sections a small app truly needs and a rough maximum size. Claude proposes defaults from the existing documents across projects (TfLens, TechieBlog, Lekhak, AstroLyfe, TrBlazeUI have full sets); the owner adjusts.

-

The documents, in the order a project produces them: BRD, Architecture, UIDesign, Checklist (row rules only; it stays an agent document), Coding Standards, PROJECT-STATUS, UsageGuide, DevGuide, ProductGuide.

+

The documents, in the order a project produces them: BRD, Architecture, UIDesign, Checklist (row rules only; it stays an agent document), Coding Standards, PROJECT-STATUS, UsageGuide, DevGuide, ProductGuide. Added by the owner during Session 3 (2026-09-04): a tenth document, the Deployment Checklist, produced after UAT from the owner's pipeline guidance document; its schema is agreed in TechieFlow-Document-Schemas.md §3.10 and its template and command are built in Sitting 4b.

We do: for each template, write a schema block at the top: required sections in order, word or row budgets by app size, per-row rules such as "acceptance line contains when … then". Write one script, tf-doc-check.sh, that reads the schema and fails the phase if a generated document breaks it. Wire it into the status gate. Add an app size (S, M, L) question to day-1 that sets the budgets. Test the checker against existing documents from at least three projects and report which would fail today and why.

-

Output: nine schema-backed templates, one checker script, size caps at day-1. Both harnesses can run the checker.

+

Output: nine schema-backed templates, one checker script, size caps at day-1. Both harnesses can run the checker. Done 2026-09-04: nine templates with schema blocks, tf-doc-check.sh plus its self-test, status-gate step 7b, the day-1 size and kind question, the standards moved into .tfcore/standards/, and a shell-write guard on PROJECT-STATUS. Fourteen projects checked in report mode; the results and the owner's decisions are in TechieFlow-Document-Schemas.md. Three misses logged (24 to 26).

Session 4 — Shrink every task, in life-cycle order#

Goal: every task file the owner fully understands, roughly one third its current size overall, with prose that needed judgement kept, mechanical steps turned into scripts, and duplicates deleted.

The method, same for every file: Claude prints the task as a table, one row per block, with a one-line plain summary and a proposed verdict: keep as words, turn into a script, or delete as duplicate or obvious. The owner rules on each row and can ask "show me the block" or "why delete" on any row. Claude applies the verdicts, writes the replacement scripts, copies the file to the Claude Code mirror, confirms opencode.jsonc still points at it, and runs the command for real in both harnesses on a project at that life-cycle stage.

@@ -246,6 +246,8 @@

Session 4 — Shrink ev -

The mechanical parts already work: 8 hooks, 17 scripts, 16 templates, 3 scaffold scripts. The prose is what grew, and the prose is what the reset removes.

+

The mechanical parts already work: 11 hooks, 17 scripts, 16 templates, 3 scaffold scripts. The prose is what grew, and the prose is what the reset removes.


8. Defects and gaps found in the Session 1 review#

These are inputs to TechieFlow-Requirements.md (Session 2). Each becomes a requirement line with a check, or is closed as not a defect.

diff --git a/docs/TechieFlow-How-It-Works.md b/docs/TechieFlow-How-It-Works.md index 1c2d3c5..19dac98 100644 --- a/docs/TechieFlow-How-It-Works.md +++ b/docs/TechieFlow-How-It-Works.md @@ -30,7 +30,7 @@ The framework runs in two harnesses, Claude Code and OpenCode, and must work ide | **Command** | An instruction typed to a persona, prefixed with `*`, such as `*build-phase TechieRag`. Each command maps to one task file. | A CLI sub-command. | | **Task** | A markdown file holding the step-by-step procedure for one command. The agent reads it and follows it. | A runbook, or a script written in prose. | | **Template** | The blueprint for one document: a skeleton with placeholders and guidance for each section. The agent fills it to produce the BRD, the Architecture, and the other documents. Sixteen exist under `.tfcore/templates/v4custom/`. At present the guidance is loose prose, so a generated document may add, skip, or over-fill sections without anything stopping it. Session 3 of the reset gives each template a strict structure (required sections in order, size limits, row rules) and a script that rejects a document that breaks it. | A document template plus a validator. | -| **Hook** | A small script the harness runs automatically before or after an agent action, with power to block it. Eight exist. Example: every `git` command is blocked. | A pre-commit hook or a CI policy check. Runs regardless of the agent's intent. | +| **Hook** | A small script the harness runs automatically before or after an agent action, with power to block it. Eleven exist (2026-09-06). Eight refuse an action and print why: every `git` write; a test folder at the repository root; a PROJECT-STATUS write in the wrong shape, from any tool or the shell; a hand edit of a telemetry file; `Verified` written without a verify run; a database write outside build-phase and fix-issues; a build, test or app run started in the background while YOLO is on; and ending a turn while the status gate is incomplete (stale HTML, a failing PROJECT-STATUS or checklist, a stale BRD table, no run record). Three do housekeeping: the session pointer at start and on every prompt, the sweep of old test artefacts at start, and the session telemetry line at end. All run in both harnesses. | A pre-commit hook or a CI policy check. Runs regardless of the agent's intent. | | **Utility script** | A shell or Python script the agent runs instead of doing a step by hand, such as the HTML renderer or the telemetry writer. Seventeen exist under `.tfcore/utils/`. | A build tool or CLI utility. | | **`.tfcore/`** | The framework's folder inside each project. Hidden, ignored by git, refreshed by `update-framework.sh`. Holds personas, tasks, templates, hooks, scripts. Never edited inside a project. | The installed copy of a library. | | **Harness mirror** | Claude Code discovers commands only under `.claude/commands/`, so every persona and task is copied there byte for byte. OpenCode instead reads file paths from `opencode.jsonc`. Both must stay in step with `.tfcore/`. | Two build configurations pointing at one source tree. | @@ -39,7 +39,7 @@ The framework runs in two harnesses, Claude Code and OpenCode, and must work ide | **Status gate** | The rule that every command ends by rewriting `PROJECT-STATUS.md` in a fixed shape. A hook rejects a malformed write. | A mandatory end-of-job report. | | **YOLO mode** | Run to completion with no questions and all permissions except git writes. Set by a flag file with a 24-hour expiry. The owner's preferred mode for most commands. | An unattended batch run. | | **Model routing** | A YAML file mapping each command to a cost tier (frontier, standard, economy) and each tier to a model per harness. Currently disabled. | Selecting machine size per pipeline stage. | -| **Telemetry streams** | The measurement system. Five files under `docs/metrics/`, one line appended per event, never edited: command runs (command, model, time, tokens), verification verdicts (first check that failed), misses (kind, phase, finder, fix cost), chat sessions (tokens in and out), and the owner's git commits. A report reads them into five figures; see section 6. | An append-only event log with a reporting query. | +| **Telemetry streams** | The measurement system. Five files under `docs/metrics/`, one line appended per event, never edited: command runs (command, model, time, tokens), verification verdicts (first check that failed), misses (kind, phase, finder, whose gap, the owner's sentence, fix cost; mirrored into a readable `docs/-Misses.md`), chat sessions (tokens in and out), and the owner's git commits. A report reads them into five figures; see section 6. | An append-only event log with a reporting query. | --- @@ -162,7 +162,7 @@ flowchart LR **`*fix-issues {App} {folder}`**: takes a folder of screenshots and an optional notes file, reproduces each bug, fixes the code (UI via the TrBlazeUI sub-agent), re-smokes, re-verifies the touched rows, updates the documents. -**`*log-miss {App} "description"`**: the twenty-second record. Writes one miss line classifying the miss and a remark on the checklist row. No reproduction, no code. **Defect:** D-10 (the record stores categories only, not the description). +**`*log-miss {App} "description"`**: the twenty-second record. First sorts the miss with four questions, asked in order (did the app's spec say it; did the framework say it; was there a check that did not catch it; was it written and ignored), because the answer decides the fix: a checklist line, a requirement line plus a check, a fixed check, or a hook. Then writes one miss record carrying the sentence and the answer, a remark on the checklist row, and one row in the readable `docs/-Misses.md`, which is rebuilt from the records after every miss. No reproduction, no code. D-10 closed in Session 5 (2026-09-07). ### 3.8 Ship — flow-master @@ -172,6 +172,8 @@ flowchart LR **`*productguide {App}`**: the end-user manual, screenshot-illustrated, task by task. On demand. Owner usage: not yet used. +**`*deploy-checklist {App} {pipeline-document}`** (added in Sitting 4b, 2026-09-06): the steps to put the application on its host, one document per hosting target, written after UAT from the owner's pipeline guidance document and the Stack answers about hosting and production secrets. Every step is a checkbox, every secret is named once, and the document says what has been executed for real. Schema: `TechieFlow-Document-Schemas.md` §3.10. + ### 3.9 Maintenance — flow-master **`*refresh-status {App}`**: rebuilds PROJECT-STATUS from evidence (checklist table, file modification times, a fresh build) after an interrupted run. Never reads git. Owner usage: after brownfield onboarding of older projects. @@ -180,6 +182,12 @@ flowchart LR **`*generate-html`, `*render-workflow-docs`**: render markdown to styled HTML through `tf-render-html.sh`. The checklist is never rendered. +### 3.10 Running unattended — the supervisor + +`bash .tfcore/utils/tf-goal.sh [--harness opencode] [--model ] ""` (or `@goal.md` for a goal written in a file) starts a harness session in YOLO mode with that goal and keeps it running until the agent writes the done sentinel. It waits out a usage limit (reads the reset time, sleeps until reset plus 15 minutes, resumes the same session), retries a crash with a growing pause, and re-prompts an agent that stopped early. `--resume` picks up after a reboot. The state and the full log live under `/.tfcore/.session/` (`goal.log`, `goal.json`, `goal-done.json`); nothing is committed. Exit codes: 0 complete, 3 blocked on the owner, 4 too many cycles. + +Examples from the reset's own test runs (2026-09-05): `bash .tfcore/utils/tf-goal.sh --model sonnet /mnt/c/1MyCode/MyDiary @goal.md` for Claude Code, and the same with `--harness opencode --model opencode-go/mimo-v2.5` for OpenCode. The interactive alternative is to start Claude Code with `--permission-mode bypassPermissions`, type `/goal `, then `*yolo` and the command; that gives YOLO and the goal loop but not the limit wait. (Moved here from `_yolo-mode.md` in Sitting 4b, 2026-09-05.) + --- ## 4. What surrounds every command @@ -195,7 +203,7 @@ flowchart TD E --> H{"Hook check
on every file write
and shell command"} H -->|"allowed"| E H -->|"refused, reason printed"| E - H --- N["Always refused:
git · writing Verified without a verify run ·
malformed PROJECT-STATUS · test output outside tests/.artifacts"] + H --- N["Always refused:
git · writing Verified without a verify run ·
malformed PROJECT-STATUS · test output outside tests/.artifacts ·
hand edits of telemetry files · database writes outside build and fix ·
a backgrounded build in YOLO ·
ending the turn with the status gate incomplete
(stale HTML, a failing PROJECT-STATUS or checklist, stale BRD table, no run record)"] E --> F["Status gate
PROJECT-STATUS rewritten · HTML re-rendered"] F --> G["Telemetry
one line appended to runs.jsonl"] G --> Z["Next command printed"] @@ -297,7 +305,7 @@ Five files under `docs/metrics/`. Each line is one event. Nothing is edited afte |---|---|---| | `runs.jsonl` | command run: command, model, harness, start, end, tokens, sub-agents | every task, at the status gate | | `gates.jsonl` | requirement graded in a verify run, with the first check that failed | verify-phase, triage-issues | -| `misses.jsonl` | thing the agent got wrong, plus a second line when it is fixed | log-miss, triage-issues, fix-issues | +| `misses.jsonl` | thing the agent got wrong, plus a second line when it is fixed; every line is also one row in the readable `docs/-Misses.md`, rebuilt after each write (Session 5) | log-miss, triage-issues, fix-issues | | `sessions.jsonl` | chat session: tokens in and out | a hook at session end | | `commits.jsonl` | one git commit by the owner | a git hook the owner installs; agents never run it | @@ -327,7 +335,7 @@ Fix, verify and build account for 90 of the 142 recorded runs. The run record do The report (`*metrics`) answers five questions. -1. **First-pass rate.** Of all requirements, the share marked Verified on their first verification. It measures how often the agent gets a requirement right without rework. Example: 100 requirements, 70 verified first time, first-pass rate 70 percent. +1. **First-pass rate.** Of all requirements, the share marked Verified on their first verification. It measures how often the agent gets a requirement right without rework. Example: 100 requirements, 70 verified first time, first-pass rate 70 percent. Across projects a requirement is counted by its project and its id together, because every project has a REQ-UI-001; until 2026-09-07 the rollup counted by id alone and printed 72 percent where the figure is 48 (Session 5). The five numbers, with real figures and the sentence to say about each, are in `TechieFlow-Telemetry-Explained.md`. 2. **Which check caught it.** The verifier applies seven checks to every requirement in a fixed order, and the first to fail is recorded. The checks: build (does it compile), acceptance (does the requirement's automated test pass), data (does the screen show real data), visual (does the screen look right and match the mockup), assets (did the stylesheet and scripts load), speed (within the declared budget), standards (does the code follow the coding standards). The distribution shows which checks do the work. Failures concentrated in "data" mean screens are wired to nothing. Failures concentrated in "visual" mean working screens that look broken. Few failures caught by any check while people keep finding bugs means the checks are not looking at the right things, which is the TfLens pattern. @@ -353,7 +361,7 @@ An analogy: a new team member is handed a sixty-page manual to read before every | The verify task | 11,850 words | The largest task and the source of most "verify method insufficient" misses; the instructions for what to check are interleaved with instructions for how to set up. | | All tasks together | 70,000 words | Tasks repeat one another, so a change in one leaves the others stale. | | Rules stated as MUST or NEVER in prose | 622 | Each depends on the agent remembering it. | -| Rules enforced by hooks | 8 | These always hold. No agent has run git since the hook was installed. | +| Rules enforced by hooks | 8 (11 hook scripts, 3 of them housekeeping) | These always hold. No agent has run git since the hook was installed. | | `WorkFlow-Context.md`, the session briefing | 344 KB | Mostly a six-month incident log. A session that reads it spends attention on history. | | `README.md` | 121 KB | The same problem for a human reader. | @@ -365,7 +373,7 @@ Three moves shrink a task, applied block by block in Session 4 of the reset: - A rule the agent has ignored more than once becomes a hook, which cannot be ignored, or is deleted. - Explanation and history move to human documents. The task keeps the step, not the story. -The mechanical parts already work: 8 hooks, 17 scripts, 16 templates, 3 scaffold scripts. The prose is what grew, and the prose is what the reset removes. +The mechanical parts already work: 11 hooks, 17 scripts, 16 templates, 3 scaffold scripts. The prose is what grew, and the prose is what the reset removes. --- diff --git a/docs/TechieFlow-Misses.html b/docs/TechieFlow-Misses.html new file mode 100644 index 0000000..f031c9e --- /dev/null +++ b/docs/TechieFlow-Misses.html @@ -0,0 +1,430 @@ + + + + + +TechieFlow — Misses + + + + + +
+
+

TechieFlow — Misses

+
Rendered 2026-09-07 · source TechieFlow-Misses.md
+ +
+
Contents
+
    +
  1. Open (35)
  2. +
  3. Fixed (82)
  4. +
  5. Will not fix (1)
  6. +
+
+ + + + + + + + +
AppTechieFlow
Count118 logged: 35 open, 82 fixed, 1 will not fix
Sourcedocs/metrics/misses.jsonl, one row per miss record. Rewritten by tf-misses-md.sh on every new record. Never edit it: a wrong row is corrected by a new record.
Updated2026-09-07
+

Whose gap answers the four questions of the miss protocol: the app's spec did not say it, so the checklist line is fixed; the framework never said it, so one requirement line and a check are added; the check was too weak (a review, or a script that did not fire), so the check is fixed; said and ignored, so the rule becomes a hook or is deleted. not sorted means the record predates the sort or nobody has answered yet; bash .tfcore/utils/tf-emit.sh --amend <miss> sort <spec|unsaid|weak-check|ignored> completes it.

+

Open (35)#

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MissFoundWhose gapWhat went wrong
MISS-TechieFlow-20260907-122026-09-07 by ownersaid and ignoredThe D-21 rule that hidden framework folders are invisible to search was written down and still cost 58 percent of a folder's true size when OpenCode measured the Playbook with a shell glob.
MISS-TechieFlow-20260907-102026-09-07 by ownerthe check was too weakWORKFLOW.html is force-deployed to every project and still teaches three commands removed in Sitting 4c, because the removed-command check looks at the task files and the harness registrations but never at the human reference.
MISS-TechieFlow-20260907-042026-09-07 by gatethe check was too weakThe readable miss file's unchanged check compared only the header above the Updated line, so an amend that changed a row but no count left the file stale; the bugs self-test caught it before release and the check now compares everything but the date.
MISS-TechieFlow-20260907-032026-09-07 by agent-reviewthe check was too weakThe cross-project rollup keyed a requirement by its id alone, so REQ-UI-001 of TfLens and REQ-UI-001 of TechieBlog counted as one requirement and the combined first-pass rate printed 72% where the true figure is 48%; found by re-reading the numbers before the explainer, fixed by keying on project and id.
MISS-TechieFlow-20260907-012026-09-07 by agent-reviewsaid and ignoredThe OpenCode verify on TechieBlog-oc (gpt-5.6-terra, 2026-09-07) skipped step 4 of the verify task: the old specs died on missing environment variables and no test was written for the 101 rows without one, so the run ended in twelve minutes with 101 rows not tested and only the screens checks graded; the task's step 4 was read and not done, which is the instruction-ignored pattern, and the Claude run on the same project wrote and repaired tests for three hours.
MISS-TechieFlow-20260906-262026-09-06 by agent-reviewthe framework never said itThe TechieBlog verify (Sonnet, 2026-09-06) wrote 86 rows FAIL on the acceptance check and 87 regression misses because the local database had no published post and the seeded staff accounts sat behind a must-change-password screen, and nothing in the verify task or the smoke policy says what a verify does when the test data the acceptance needs is missing: create it through the application as the test user, pass a password gate through its screen, and record a row as not observable with the environment reason when neither is possible, never as a failure of the code; for Session 5's sort.
MISS-TechieFlow-20260906-08 (FR-41)2026-09-06 by agent-reviewsaid and ignoredThe MyDiary build agent wrote the sentinel as complete with 69 of 77 rows still Implemented, although the yolo rule says blocked when everything left needs the owner, and here the owner must set up Appium or FlaUI before any UI row can be verified; the honest outcome was blocked.
MISS-TechieFlow-20260905-17 (FR-01)2026-09-06 by ownerthe framework never said itThe analyst brainstorm and project-brief steps proposed project names (src/MyDiary.App) without reading the stack answer set, so the MiMo day-1 copied a banned name from the brief; the brief template asks for repository thoughts and the idea-stage commands have no run record kind, so nothing checked it.
MISS-TechieFlow-20260904-262026-09-04 by ownernot sortedno sentence recorded (wrong-behaviour, other, why: insufficient-verify-method)
MISS-TechieFlow-20260904-252026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-242026-09-04 by ownernot sortedno sentence recorded (missed-requirement, other, why: insufficient-verify-method)
MISS-TechieFlow-20260904-232026-09-04 by ownernot sortedno sentence recorded (missed-requirement, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-222026-09-04 by ownernot sortedno sentence recorded (scope-creep, other, why: instruction-ignored)
MISS-TechieFlow-20260904-212026-09-04 by ownernot sortedno sentence recorded (wrong-behaviour, other, why: instruction-ignored)
MISS-TechieFlow-20260904-202026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, checklist, why: ambiguous-acceptance)
MISS-TechieFlow-20260904-192026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-182026-09-04 by ownernot sortedno sentence recorded (wrong-behaviour, other, why: instruction-ignored)
MISS-TechieFlow-20260904-172026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-162026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-152026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-142026-09-04 by ownernot sortedno sentence recorded (scope-creep, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-132026-09-04 by agent-reviewnot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-122026-09-04 by agent-reviewnot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-112026-09-04 by agent-reviewnot sortedno sentence recorded (unspecified-gap, other, why: insufficient-verify-method)
MISS-TechieFlow-20260904-102026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-092026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
MISS-TechieFlow-20260904-082026-09-04 by agent-reviewnot sortedno sentence recorded (partial-implementation, other, why: insufficient-verify-method)
MISS-TechieFlow-20260904-072026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, devguide, why: missing-checklist-item)
MISS-TechieFlow-20260904-062026-09-04 by ownernot sortedno sentence recorded (wrong-behaviour, other, why: ambiguous-acceptance)
MISS-TechieFlow-20260904-052026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, uidesign, why: missing-checklist-item)
MISS-TechieFlow-20260904-042026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, architecture, why: missing-checklist-item)
MISS-TechieFlow-20260904-032026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, brd, why: missing-checklist-item)
MISS-TechieFlow-20260904-022026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, brd, why: missing-checklist-item)
MISS-TechieFlow-20260904-012026-09-04 by ownernot sortedno sentence recorded (wrong-behaviour, other, why: instruction-ignored)
(no id, record 55)2026-09-05 by ownernot sortedThe first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule.
+

Fixed (82)#

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MissFoundClosedWhose gapWhat went wrong
MISS-TechieFlow-20260907-112026-09-07 by owner2026-09-07 by fix-issuesthe check was too weakThe Playbook review prompt was drafted from folder word counts taken only at the top level, so 174 files and 196,498 words counted as zero and the prompt nearly shipped the wrong headline finding.
MISS-TechieFlow-20260907-092026-09-07 by owner2026-09-07 by fix-issuesthe check was too weakA run record with no ended at all was accepted and could never be costed: the guard only replaced an ended that lied, so this session's own record landed with no duration.
MISS-TechieFlow-20260907-082026-09-07 by owner2026-09-07 by fix-issuesthe check was too weakThe updater kept a project's root opencode.jsonc because its dead BMAD-era registrations looked like project content, so that repo loaded no framework agents in OpenCode at all and only a warning was printed.
MISS-TechieFlow-20260907-072026-09-07 by owner2026-09-07 by fix-issuesthe framework never said itNothing capped or checked the two files a person reads first, so the briefing reached 344 KB and the README 121 KB and both still named commands the framework had removed.
MISS-TechieFlow-20260907-062026-09-07 by owner2026-09-07 by fix-issuesthe check was too weaktf-log-miss.sh printed 'Miss logged' with an id and said the readable file was rewritten after the emitter had refused the record for a bad artifact value and appended nothing.
MISS-TechieFlow-20260907-052026-09-07 by owner2026-09-07 by fix-issuesthe check was too weakFR-47 says a script greps every public document for private project names, but no such script was ever written, so a private project was named in the public README for months.
MISS-TechieFlow-20260907-022026-09-07 by gate2026-09-07 by fix-issuesthe check was too weakThe checklist-edit helper shared by the triage and verdict scripts cut a long Remarks cell to sixty words and then added the ellipsis as a word of its own, so three rows the TechieBlog triage wrote carried 61 words and the Stop hook refused the turn until the agent trimmed them; the ellipsis now rides on the last word, and the self-tests did not cover a remark over the limit.
MISS-TechieFlow-20260906-272026-09-06 by gate2026-09-07 by fix-issuesthe check was too weakRemoving the seven never-used commands left every project's own routing file naming author-brd, and the binding generator wrote an OpenCode file reference to the vanished task, so OpenCode refused to start on TechieBlog-oc; the generator now skips a phase whose task file does not exist and says so, and the stale line was removed from the three projects refreshed today (the other projects hit the warning, not the error, at their next update).
MISS-TechieFlow-20260906-252026-09-06 by gate2026-09-07 by fix-issuesthe check was too weakThe fix close script passed the verify ledger's raw verdict (RENDER-FAIL) as a miss-fix verdict_after, a field whose vocabulary is the five checklist statuses, so the emitter refused all twenty miss-fix records of the MyDiary fix run and the cost of the fix was not attached to any miss; the self-test had covered only PASS and FAIL, and the script now maps every ledger verdict to a status and writes one run record per start.
MISS-TechieFlow-20260906-242026-09-06 by agent-review2026-09-07 by fix-issuessaid and ignoredThe maintainer ran a process clean-up that filtered by working directory while its own shell had drifted into the MyDiary folder from an earlier cd, and killed its own command (exit 144); the 4b watch-out that the shell's working directory persists between commands was repeated, so every clean-up now runs from a script file that excludes its own shell and every command names absolute paths.
MISS-TechieFlow-20260906-232026-09-06 by gate2026-09-07 by fix-issuesthe check was too weakThe verify boot script wrapped the Windows project path in quote characters inside the cmd.exe command, and the WSL bridge handed those quotes to dotnet as part of the path, so the MyDiary Windows head never started and the fix agent spent its first cycle launching it by hand; the self-test could not catch it because the fixture has no Windows head, and the path is now passed bare unless it holds a space.
MISS-TechieFlow-20260906-222026-09-06 by gate2026-09-07 by fix-issuessaid and ignoredThe TechieBlog verify (Sonnet) started the browser tests in the background and ended its turn waiting for them, so the supervisor had to re-prompt a second cycle; the build guard knew only dotnet, msbuild and npm verbs, not npx playwright test or the verify scripts, and now refuses those backgrounded too (third occurrence of the backgrounded-run failure after misses 23 of 2026-09-05 and 05 of 2026-09-06).
MISS-TechieFlow-20260906-212026-09-06 by gate2026-09-07 by fix-issuesthe check was too weakThe document checker cut a checklist detail entry at the next anchor only when no list dash preceded it, so two consecutive list-item entries were read as one and a row could borrow the next row's BRD id or acceptance line (the third parser with this boundary bug after tf-build-list.py and the new list script, miss 18); the bugs self-test caught it on a row triage added, and the checker now stops at a dash-prefixed anchor too.
MISS-TechieFlow-20260906-202026-09-06 by gate2026-09-07 by fix-issuesthe check was too weakThe database guard matched the name of the migration tool inside an echo string and refused a verify run's read of two seed SQL files and a select on TechieBlog, because it matched words anywhere in the command text; it now strips echo and printf strings and comment lines before matching, so only a command that runs a migrator or writes through a client is refused.
MISS-TechieFlow-20260906-192026-09-06 by agent-review2026-09-07 by fix-issuesthe check was too weakThe Stop hook and the status gate ran the document checker on every checklist a command wrote and blocked on any FAIL line, so on an existing project whose checklist predates the schemas (TechieBlog: 376 old findings) no verify or build could ever end its turn without repairing rows it must not touch, against the Session 3 decision that old findings warn and are repaired through amend-docs; tf-phase.sh start now records a baseline of the findings present when a command starts and the checker prints those as OLD and blocks only on new ones.
MISS-TechieFlow-20260906-182026-09-06 by agent-review2026-09-07 by fix-issuesthe check was too weakThe checklist entry parser in tf-build-list.py read past the end of a row's detail entry into the next one when the next entry started with a list dash, so a row with no mockup or BRD id of its own could inherit the next row's; the verify self-test caught the same bug in the new list script, and both now stop at the next entry.
MISS-TechieFlow-20260906-172026-09-06 by agent-review2026-09-07 by fix-issuesthe check was too weakThe old verify task described driving MAUI Android, iOS and Mac Catalyst heads over Appium with the same render and visual checks, but no script in the framework ever implemented that drive, so the prose promised a verify that could not happen; the shrunk task now says those heads have no driver yet and their rows are recorded as not verified.
MISS-TechieFlow-20260906-162026-09-06 by owner2026-09-07 by fix-issuessaid and ignoredThe maintainer reported no monitors running while the watch on the MyDiary devguide run from 10:28 was still alive after five hours, because it stopped only the monitors it remembered instead of listing them; a running-work report is read from the harness list, never from memory (second occurrence after miss 24 of 2026-09-05).
MISS-TechieFlow-20260906-15 (FR-20)2026-09-06 by agent-review2026-09-07 by fix-issuesthe check was too weakThe second OpenCode build on the MyDiary copy (gpt-5.6-terra) marked 85 rows Implemented, ran the verifier on 28 rows and wrote the status file, but left no build-phase run record and no gate records, and the new sentinel guard accepted it because any run record since the goal start satisfied it; the guard now requires a run record for the command the phase marker names.
MISS-TechieFlow-20260906-14 (FR-40)2026-09-06 by agent-review2026-09-06 by fix-issuesthe check was too weakOpenAI models in OpenCode edit files only through apply_patch, and the OpenCode plugin refused every apply_patch that touched the checklist or the status file and told the agent to use edit or write tools it does not have, so no OpenAI-model run could ever update a row or the status; the plugin now maps an apply_patch onto the same write guards per file instead of refusing it.
MISS-TechieFlow-20260906-13 (FR-20)2026-09-06 by agent-review2026-09-06 by fix-issuessaid and ignoredThe OpenCode build on the MyDiary copy (gpt-5.6-terra) wrote code across three fresh sessions, built and tested it, then wrote the sentinel blocked with all 87 rows Not Started, the status file untouched since day-1 and no build-phase run record, because nothing mechanical tied the sentinel to the status gate; tf-yolo.sh done now refuses the sentinel until the status file and a run record are newer than the goal start.
MISS-TechieFlow-20260906-122026-09-06 by agent-review2026-09-07 by fix-issuesthe framework never said itThe maintainer ran a relative-path patch and a miss emit from whatever folder the previous command had left as the working directory, so a supervisor patch landed in the MyDiary copy and a framework miss record landed in TfLens-oc's stream; every script edit and emit now uses the absolute framework path.
MISS-TechieFlow-20260906-112026-09-06 by agent-review2026-09-06 by fix-issuesthe framework never said itWhen the opencode-go provider refuses a model (monthly usage limit reached, resets in six days) opencode run prints only its header and waits, and the refusal appears only in the OpenCode log file, so three fifteen-minute stalls on MyDiary-oc and a one-line probe looked like hangs; the supervisor now reads that log after a silent stall and stops with exit 5 and the provider message.
MISS-TechieFlow-20260906-10 (FR-55)2026-09-06 by agent-review2026-09-06 by fix-issuesthe check was too weakThe devguide and refresh-status run records on MyDiary carried no duration_s because the tasks left it out and the emitter only recomputed one that was present; the emitter now derives it from started and ended.
MISS-TechieFlow-20260906-092026-09-06 by agent-review2026-09-06 by fix-issuesthe framework never said itOn the MyDiary copy the glm-5.2 build's first cycle went silent after 15 minutes and both continue cycles (opencode run -c) printed only their header for 15 minutes each, so a resumed OpenCode session can hang forever; the new stall watchdog caught all three, and the supervisor now starts a fresh session after two stalled resumes and accepts --resume --fresh.
MISS-TechieFlow-20260906-07 (FR-18)2026-09-06 by agent-review2026-09-07 by fix-issuesthe framework never said itThe smoke policy names a desktop and a mobile browser width and no evidence file, and FR-18's check reads a smoke log that no task writes, so the MyDiary MAUI build wrote no smoke evidence at all and marked 69 rows Implemented on a code trace against the mockups; the policy needs a native-head path and a named evidence file.
MISS-TechieFlow-20260906-06 (FR-14)2026-09-06 by agent-review2026-09-06 by fix-issuessaid and ignoredThe MyDiary build closed its status gate with three checklist Remarks cells over 60 words (REQ-NFR-006 to 008), because the Stop hook ran the checker on PROJECT-STATUS only and the agent skipped step 7b on the checklist; the hook now checks every checklist written in the session.
MISS-TechieFlow-20260906-05 (FR-46)2026-09-06 by agent-review2026-09-06 by fix-issuessaid and ignoredFor the second time after miss 23 the Sonnet build agent started the build as a background job and ended its turn with Waiting for the build, three cycles in a row, and each turn end killed the job; the supervisor's prompt now says builds run in the foreground, and the second occurrence makes this a hook candidate under FR-46.
MISS-TechieFlow-20260906-042026-09-06 by agent-review2026-09-07 by fix-issuesthe check was too weakThe emitter named the app from the one *-Checklist.md in docs, so once TfLens-oc had a Deployment Checklist beside it the run record and the next miss id fell back to the folder name TfLens-oc, and a Large project's phase-2 checklist would do the same; deployment and phase-n checklists are now excluded.
MISS-TechieFlow-20260906-032026-09-06 by agent-review2026-09-07 by fix-issuesthe check was too weakThe build ladder counted only CS, MSB and NU codes as code errors, so a MyDiary build failing on sixteen Razor RZ9991 errors was taken for a wrong rung on every rung and reported NOT-RUN, a host issue and never a project blocker, while the agent carried on as if the code were fine; RZ, BL and XAML codes now count.
MISS-TechieFlow-20260906-022026-09-06 by agent-review2026-09-07 by fix-issuesthe check was too weakKilling the supervisor with TERM ran a trap that only cleared the YOLO flag and did not exit, and bash held the signal until the running sleep ended, so the stopped MyDiary supervisor woke, launched cycle 8 without its flag and left a harness child to be killed by hand; the trap now stops the child, records stopped and exits 130, and every sleep is interruptible.
MISS-TechieFlow-20260906-012026-09-06 by agent-review2026-09-07 by fix-issuesthe check was too weakThe supervisor's crash pattern api_error also matched the harmless field api_error_status that every clean Claude result line carries, so cycles 5 to 7 of the MyDiary build, each a clean early stop, were called harness errors and backed off 2, 4 and 8 minutes instead of being re-prompted after 30 seconds; the classifier now reads the result line first.
MISS-TechieFlow-20260905-242026-09-06 by owner2026-09-07 by fix-issuesthe framework never said itAsked which of the three background shells in Claude Code was doing what, the maintainer listed operating-system processes instead and called the interactive session a third shell; the owner reads the harness shell list, so a running-work report names each background command by its launch line and its folder.
MISS-TechieFlow-20260905-23 (FR-41)2026-09-06 by agent-review2026-09-07 by fix-issuessaid and ignoredThe MyDiary build agent started the build as a background task and ended its turn with "Waiting for the build to finish", which killed the task; the supervisor then labelled the clean early stop a harness error and backed off 120 then 240 seconds instead of re-prompting after 30; an early stop with exit 0 is a stop, not a crash, and a task never waits on a background job.
MISS-TechieFlow-20260905-22 (FR-40)2026-09-06 by agent-review2026-09-07 by fix-issuesthe framework never said itThe OpenCode run of deploy-checklist on TfLens initialised and then produced no output for 43 minutes, and the supervisor has no stall watchdog, so it waited forever; a cycle whose output does not grow for fifteen minutes must be killed and re-prompted, and the OpenCode hang on this repository is unexplained.
MISS-TechieFlow-20260905-212026-09-06 by agent-review2026-09-07 by fix-issuesthe framework never said itThe maintainer ran the supervisor in dry-run mode against TfLens while a real run was active there, which rewrote the live goal.json and appended a fake cycle line to its log; a dry run must never touch a folder with an active run, and the supervisor should refuse it.
MISS-TechieFlow-20260905-20 (FR-55)2026-09-06 by agent-review2026-09-07 by fix-issuessaid and ignoredThe MiMo stage 2 run on the MyDiary copy ran step 0 last, so its run record says it started at 05:37 and ended at 05:38 after 38 minutes of work, and the review record copied that as the cost to correct; the supervisor now writes the start marker when its first cycle begins and the command claims it.
MISS-TechieFlow-20260905-192026-09-06 by owner2026-09-07 by fix-issuessaid and ignoredThe maintainer compressed a Session 3 decision (a service library's map lives in the UsageGuide) into half a table cell and a decision line, so the owner could not see where it came from or why, and questioned it as invented; a decision is restated with its source and reason, never as a half sentence.
MISS-TechieFlow-20260905-18 (FR-54)2026-09-06 by agent-review2026-09-07 by fix-issuesthe framework never said itThe Phases table allowed one id range per phase, so an item added to phase 1 after phase 2 existed had no legal id; a row may now carry several ranges and the checker reads them all.
MISS-TechieFlow-20260905-16 (FR-14)2026-09-06 by owner2026-09-07 by fix-issuesthe check was too weakThe checker only asks whether the Architecture has a Stack decisions table, so the MiMo run named the head MyDiary.App against the .NET answer set and left questions out of the table, and the owner found both by reading; a row per stack question and the head named exactly <App> are the candidate checks.
MISS-TechieFlow-20260905-15 (FR-53)2026-09-06 by owner2026-09-07 by fix-issuesthe check was too weakThe mockup click-through check passed the MiMo set although its menus navigated by script, its Settings item went nowhere and thirteen screens could not be reached by clicking; the check resolved links by file name and counted a script as a link, so the owner found the set broken by hand.
MISS-TechieFlow-20260905-142026-09-06 by owner2026-09-07 by fix-issuessaid and ignoredThe maintainer ended a report with a one-line pointer to three open decisions made two messages earlier; the owner called it dense chatting and could not tell what was being asked, so every open decision is now restated in full.
MISS-TechieFlow-20260905-13 (FR-15)2026-09-05 by agent-review2026-09-07 by fix-issuesthe framework never said itThe MyDiary BRD from MiMo packs four or five testable behaviours into one acceptance line (slots shown; Enter advances; empty slots dropped; timer starts), so 50 items stand where about 100 belong and the verifier grades a bundle; nothing in the checker sees a bundled then-clause.
MISS-TechieFlow-20260905-12 (FR-14)2026-09-05 by agent-review2026-09-07 by fix-issuesthe check was too weakThe checker counted only the bold BRD-N ledger items and ignored the ids in the Non-functional table, so seven MyDiary NFR requirements escaped the cap, the checklist cross-check and the phase range check until the reading of the output found them.
MISS-TechieFlow-20260905-11 (FR-39)2026-09-05 by agent-review2026-09-07 by fix-issuesthe check was too weakThe MyDiary day-1 run in OpenCode wrote a run record whose ended time (17:05) was 25 minutes after the record was appended (16:40), an invented duration; the emitter accepted it, so it must set ended to now when it lies in the future or before started.
MISS-TechieFlow-20260905-10 (FR-10)2026-09-05 by agent-review2026-09-07 by fix-issuesthe framework never said itamend-docs proposed a phase split whenever a project passed its size cap, so a Small project growing past 50 requirements (Xpenser with family scope) would have been split into phases instead of first becoming Medium; the split belongs only past Medium, and FR-10 and D-3 did not say which cap.
MISS-TechieFlow-20260905-09 (FR-21)2026-09-05 by agent-review2026-09-07 by fix-issuessaid and ignoredThe Xpenser day-1 run in Claude Code created a test user and patched two stored procedures in the development database although the task says create no user and day-1 writes documents only; a hook that refuses database writes outside build and fix is the candidate fix.
MISS-TechieFlow-20260905-08 (FR-41)2026-09-05 by owner2026-09-07 by fix-issuesthe framework never said itThe unattended Xpenser day-1 run was launched with a bare claude -p instead of the goal supervisor tf-goal.sh, so the usage-limit halt was not survived automatically; unattended runs go through the supervisor, and the YOLO rule will say so in 4c.
MISS-TechieFlow-20260905-07 (FR-39)2026-09-05 by agent-review2026-09-07 by fix-issuesthe check was too weakThe emitter accepted a run record whose build_result was free text ('PASS (API+Web, DbMigration excluded)') instead of pass, fail or not-run, in the Xpenser OpenCode run; it now refuses it.
MISS-TechieFlow-20260905-06 (FR-14)2026-09-05 by agent-review2026-09-07 by fix-issuesthe check was too weakThe checker compared BRD screen names with their qualifier attached, so 'Profile (planned)' and 'Profile' were reported as two different screens during the Xpenser brownfield run; found by reading the run log, fixed the same hour.
MISS-TechieFlow-20260905-052026-09-05 by owner2026-09-07 by fix-issuesthe framework never said itMockups were repeatedly delivered as unlinked HTML files with no navigation and dead buttons; nothing in the framework required a click-through set where every link and button behaves.
MISS-TechieFlow-20260905-04 (FR-41)2026-09-05 by owner2026-09-07 by fix-issuesthe framework never said itThe YOLO rule and the flow-master run-workflow command say YOLO logs a phase boundary and continues, but the owner's rule is that YOLO runs one command to completion and never crosses an owner review into the next phase; fixed in 4c when YOLO is made uniform.
MISS-TechieFlow-20260905-03 (FR-39)2026-09-05 by agent-review2026-09-07 by fix-issuesthe check was too weaktf-emit.sh appends a miss record that has no miss_id, although miss_id is the join key to its fix record, so a caller that skips --next-miss-id writes an orphan.
MISS-TechieFlow-20260905-02 (FR-40)2026-09-05 by agent-review2026-09-07 by fix-issuesthe check was too weakSix task files edited in .tfcore on 2026-08-31 were never copied to the Claude Code mirror, so the two harnesses ran different smoke, metrics, mockup, render and verify rules for five days; no parity check ran.
MISS-TechieFlow-20260905-012026-09-05 by owner2026-09-07 by fix-issuessaid and ignoredThe first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule.
MISS-TechieFlow-20260831-102026-08-31 by agent-review2026-08-31 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, src, why: insufficient-verify-method)
MISS-TechieFlow-20260831-092026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (unspecified-gap, src, why: insufficient-verify-method)
MISS-TechieFlow-20260831-082026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (spec-contradiction, src, why: missing-checklist-item)
MISS-TechieFlow-20260831-072026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, architecture, why: insufficient-verify-method)
MISS-TechieFlow-20260831-062026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, architecture, why: insufficient-verify-method)
MISS-TechieFlow-20260831-052026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, architecture, why: insufficient-verify-method)
MISS-TechieFlow-20260831-042026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (unspecified-gap, architecture, why: insufficient-verify-method)
MISS-TechieFlow-20260831-032026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (unspecified-gap, config, why: missing-checklist-item)
MISS-TechieFlow-20260831-022026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (unspecified-gap, architecture, why: missing-checklist-item)
MISS-TechieFlow-20260831-012026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, src, why: insufficient-verify-method)
MISS-TechieFlow-20260828-182026-08-28 by owner2026-08-28 by fix-issuesnot sortedno sentence recorded (partial-implementation, src, why: missing-checklist-item)
MISS-TechieFlow-20260828-172026-08-28 by agent-review2026-08-28 by fix-issuesnot sortedno sentence recorded (partial-implementation, config, why: insufficient-verify-method)
MISS-TechieFlow-20260828-162026-08-28 by agent-review2026-08-28 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, src, why: missing-checklist-item)
MISS-TechieFlow-20260828-152026-08-28 by agent-review2026-08-28 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, src, why: insufficient-verify-method)
MISS-TechieFlow-20260828-142026-08-28 by agent-review2026-08-28 by fix-issuesnot sortedno sentence recorded (partial-implementation, src, why: missing-checklist-item)
MISS-TechieFlow-20260828-132026-08-28 by owner2026-08-28 by log-missnot sortedno sentence recorded (scope-creep, config, why: instruction-ignored)
MISS-TechieFlow-20260828-122026-08-28 by agent-review2026-08-28 by log-missnot sortedno sentence recorded (partial-implementation, config, why: insufficient-verify-method)
MISS-TechieFlow-20260828-112026-08-28 by owner2026-08-28 by fix-issuesnot sortedno sentence recorded (scope-creep, config, why: instruction-ignored)
MISS-TechieFlow-20260828-102026-08-28 by agent-review2026-08-28 by fix-issuesnot sortedno sentence recorded (spec-contradiction, config, why: ambiguous-acceptance)
MISS-TechieFlow-20260828-092026-08-28 by agent-review2026-08-28 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, devguide, why: instruction-ignored)
MISS-TechieFlow-20260828-082026-08-28 by agent-review2026-08-28 by fix-issuesnot sortedno sentence recorded (partial-implementation, config, why: insufficient-verify-method)
MISS-TechieFlow-20260828-072026-08-28 by agent-review2026-08-28 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, config, why: insufficient-verify-method)
MISS-TechieFlow-20260828-062026-08-28 by library-feedback2026-08-28 by log-missnot sortedno sentence recorded (wrong-behaviour, src, why: insufficient-verify-method)
MISS-TechieFlow-20260828-052026-08-28 by library-feedback2026-08-28 by log-missnot sortedno sentence recorded (spec-contradiction, architecture, why: missing-checklist-item)
MISS-TechieFlow-20260828-042026-08-28 by owner2026-08-28 by log-missnot sortedno sentence recorded (partial-implementation, src, why: instruction-ignored)
MISS-TechieFlow-20260828-022026-08-28 by agent-review2026-08-28 by log-missnot sortedno sentence recorded (wrong-behaviour, src)
MISS-TechieFlow-20260828-012026-08-28 by owner2026-08-28 by log-missnot sortedno sentence recorded (partial-implementation, src)
+

Will not fix (1)#

+ + + + + +
MissFoundClosedWhose gapWhat went wrong
MISS-TechieFlow-20260828-032026-08-28 by agent-review2026-08-28 by log-missnot sortedno sentence recorded (other, other)
+
+
+ + + + + + diff --git a/docs/TechieFlow-Misses.md b/docs/TechieFlow-Misses.md new file mode 100644 index 0000000..48ac4bf --- /dev/null +++ b/docs/TechieFlow-Misses.md @@ -0,0 +1,143 @@ +# TechieFlow — Misses + +| | | +|---|---| +| App | TechieFlow | +| Count | 118 logged: 35 open, 82 fixed, 1 will not fix | +| Source | `docs/metrics/misses.jsonl`, one row per miss record. Rewritten by `tf-misses-md.sh` on every new record. Never edit it: a wrong row is corrected by a new record. | +| Updated | 2026-09-07 | + +**Whose gap** answers the four questions of the miss protocol: **the app's spec** did not say it, so the checklist line is fixed; **the framework never said it**, so one requirement line and a check are added; **the check was too weak** (a review, or a script that did not fire), so the check is fixed; **said and ignored**, so the rule becomes a hook or is deleted. **not sorted** means the record predates the sort or nobody has answered yet; `bash .tfcore/utils/tf-emit.sh --amend sort ` completes it. + +## Open (35) + +| Miss | Found | Whose gap | What went wrong | +|---|---|---|---| +| MISS-TechieFlow-20260907-12 | 2026-09-07 by owner | said and ignored | The D-21 rule that hidden framework folders are invisible to search was written down and still cost 58 percent of a folder's true size when OpenCode measured the Playbook with a shell glob. | +| MISS-TechieFlow-20260907-10 | 2026-09-07 by owner | the check was too weak | WORKFLOW.html is force-deployed to every project and still teaches three commands removed in Sitting 4c, because the removed-command check looks at the task files and the harness registrations but never at the human reference. | +| MISS-TechieFlow-20260907-04 | 2026-09-07 by gate | the check was too weak | The readable miss file's unchanged check compared only the header above the Updated line, so an amend that changed a row but no count left the file stale; the bugs self-test caught it before release and the check now compares everything but the date. | +| MISS-TechieFlow-20260907-03 | 2026-09-07 by agent-review | the check was too weak | The cross-project rollup keyed a requirement by its id alone, so REQ-UI-001 of TfLens and REQ-UI-001 of TechieBlog counted as one requirement and the combined first-pass rate printed 72% where the true figure is 48%; found by re-reading the numbers before the explainer, fixed by keying on project and id. | +| MISS-TechieFlow-20260907-01 | 2026-09-07 by agent-review | said and ignored | The OpenCode verify on TechieBlog-oc (gpt-5.6-terra, 2026-09-07) skipped step 4 of the verify task: the old specs died on missing environment variables and no test was written for the 101 rows without one, so the run ended in twelve minutes with 101 rows not tested and only the screens checks graded; the task's step 4 was read and not done, which is the instruction-ignored pattern, and the Claude run on the same project wrote and repaired tests for three hours. | +| MISS-TechieFlow-20260906-26 | 2026-09-06 by agent-review | the framework never said it | The TechieBlog verify (Sonnet, 2026-09-06) wrote 86 rows FAIL on the acceptance check and 87 regression misses because the local database had no published post and the seeded staff accounts sat behind a must-change-password screen, and nothing in the verify task or the smoke policy says what a verify does when the test data the acceptance needs is missing: create it through the application as the test user, pass a password gate through its screen, and record a row as not observable with the environment reason when neither is possible, never as a failure of the code; for Session 5's sort. | +| MISS-TechieFlow-20260906-08 (FR-41) | 2026-09-06 by agent-review | said and ignored | The MyDiary build agent wrote the sentinel as complete with 69 of 77 rows still Implemented, although the yolo rule says blocked when everything left needs the owner, and here the owner must set up Appium or FlaUI before any UI row can be verified; the honest outcome was blocked. | +| MISS-TechieFlow-20260905-17 (FR-01) | 2026-09-06 by owner | the framework never said it | The analyst brainstorm and project-brief steps proposed project names (src/MyDiary.App) without reading the stack answer set, so the MiMo day-1 copied a banned name from the brief; the brief template asks for repository thoughts and the idea-stage commands have no run record kind, so nothing checked it. | +| MISS-TechieFlow-20260904-26 | 2026-09-04 by owner | not sorted | no sentence recorded (wrong-behaviour, other, why: insufficient-verify-method) | +| MISS-TechieFlow-20260904-25 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-24 | 2026-09-04 by owner | not sorted | no sentence recorded (missed-requirement, other, why: insufficient-verify-method) | +| MISS-TechieFlow-20260904-23 | 2026-09-04 by owner | not sorted | no sentence recorded (missed-requirement, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-22 | 2026-09-04 by owner | not sorted | no sentence recorded (scope-creep, other, why: instruction-ignored) | +| MISS-TechieFlow-20260904-21 | 2026-09-04 by owner | not sorted | no sentence recorded (wrong-behaviour, other, why: instruction-ignored) | +| MISS-TechieFlow-20260904-20 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, checklist, why: ambiguous-acceptance) | +| MISS-TechieFlow-20260904-19 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-18 | 2026-09-04 by owner | not sorted | no sentence recorded (wrong-behaviour, other, why: instruction-ignored) | +| MISS-TechieFlow-20260904-17 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-16 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-15 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-14 | 2026-09-04 by owner | not sorted | no sentence recorded (scope-creep, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-13 | 2026-09-04 by agent-review | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-12 | 2026-09-04 by agent-review | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-11 | 2026-09-04 by agent-review | not sorted | no sentence recorded (unspecified-gap, other, why: insufficient-verify-method) | +| MISS-TechieFlow-20260904-10 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-09 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-08 | 2026-09-04 by agent-review | not sorted | no sentence recorded (partial-implementation, other, why: insufficient-verify-method) | +| MISS-TechieFlow-20260904-07 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, devguide, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-06 | 2026-09-04 by owner | not sorted | no sentence recorded (wrong-behaviour, other, why: ambiguous-acceptance) | +| MISS-TechieFlow-20260904-05 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, uidesign, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-04 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, architecture, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-03 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, brd, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-02 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, brd, why: missing-checklist-item) | +| MISS-TechieFlow-20260904-01 | 2026-09-04 by owner | not sorted | no sentence recorded (wrong-behaviour, other, why: instruction-ignored) | +| (no id, record 55) | 2026-09-05 by owner | not sorted | The first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule. | + +## Fixed (82) + +| Miss | Found | Closed | Whose gap | What went wrong | +|---|---|---|---|---| +| MISS-TechieFlow-20260907-11 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the check was too weak | The Playbook review prompt was drafted from folder word counts taken only at the top level, so 174 files and 196,498 words counted as zero and the prompt nearly shipped the wrong headline finding. | +| MISS-TechieFlow-20260907-09 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the check was too weak | A run record with no ended at all was accepted and could never be costed: the guard only replaced an ended that lied, so this session's own record landed with no duration. | +| MISS-TechieFlow-20260907-08 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the check was too weak | The updater kept a project's root opencode.jsonc because its dead BMAD-era registrations looked like project content, so that repo loaded no framework agents in OpenCode at all and only a warning was printed. | +| MISS-TechieFlow-20260907-07 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the framework never said it | Nothing capped or checked the two files a person reads first, so the briefing reached 344 KB and the README 121 KB and both still named commands the framework had removed. | +| MISS-TechieFlow-20260907-06 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the check was too weak | tf-log-miss.sh printed 'Miss logged' with an id and said the readable file was rewritten after the emitter had refused the record for a bad artifact value and appended nothing. | +| MISS-TechieFlow-20260907-05 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the check was too weak | FR-47 says a script greps every public document for private project names, but no such script was ever written, so a private project was named in the public README for months. | +| MISS-TechieFlow-20260907-02 | 2026-09-07 by gate | 2026-09-07 by fix-issues | the check was too weak | The checklist-edit helper shared by the triage and verdict scripts cut a long Remarks cell to sixty words and then added the ellipsis as a word of its own, so three rows the TechieBlog triage wrote carried 61 words and the Stop hook refused the turn until the agent trimmed them; the ellipsis now rides on the last word, and the self-tests did not cover a remark over the limit. | +| MISS-TechieFlow-20260906-27 | 2026-09-06 by gate | 2026-09-07 by fix-issues | the check was too weak | Removing the seven never-used commands left every project's own routing file naming author-brd, and the binding generator wrote an OpenCode file reference to the vanished task, so OpenCode refused to start on TechieBlog-oc; the generator now skips a phase whose task file does not exist and says so, and the stale line was removed from the three projects refreshed today (the other projects hit the warning, not the error, at their next update). | +| MISS-TechieFlow-20260906-25 | 2026-09-06 by gate | 2026-09-07 by fix-issues | the check was too weak | The fix close script passed the verify ledger's raw verdict (RENDER-FAIL) as a miss-fix verdict_after, a field whose vocabulary is the five checklist statuses, so the emitter refused all twenty miss-fix records of the MyDiary fix run and the cost of the fix was not attached to any miss; the self-test had covered only PASS and FAIL, and the script now maps every ledger verdict to a status and writes one run record per start. | +| MISS-TechieFlow-20260906-24 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | said and ignored | The maintainer ran a process clean-up that filtered by working directory while its own shell had drifted into the MyDiary folder from an earlier cd, and killed its own command (exit 144); the 4b watch-out that the shell's working directory persists between commands was repeated, so every clean-up now runs from a script file that excludes its own shell and every command names absolute paths. | +| MISS-TechieFlow-20260906-23 | 2026-09-06 by gate | 2026-09-07 by fix-issues | the check was too weak | The verify boot script wrapped the Windows project path in quote characters inside the cmd.exe command, and the WSL bridge handed those quotes to dotnet as part of the path, so the MyDiary Windows head never started and the fix agent spent its first cycle launching it by hand; the self-test could not catch it because the fixture has no Windows head, and the path is now passed bare unless it holds a space. | +| MISS-TechieFlow-20260906-22 | 2026-09-06 by gate | 2026-09-07 by fix-issues | said and ignored | The TechieBlog verify (Sonnet) started the browser tests in the background and ended its turn waiting for them, so the supervisor had to re-prompt a second cycle; the build guard knew only dotnet, msbuild and npm verbs, not npx playwright test or the verify scripts, and now refuses those backgrounded too (third occurrence of the backgrounded-run failure after misses 23 of 2026-09-05 and 05 of 2026-09-06). | +| MISS-TechieFlow-20260906-21 | 2026-09-06 by gate | 2026-09-07 by fix-issues | the check was too weak | The document checker cut a checklist detail entry at the next anchor only when no list dash preceded it, so two consecutive list-item entries were read as one and a row could borrow the next row's BRD id or acceptance line (the third parser with this boundary bug after tf-build-list.py and the new list script, miss 18); the bugs self-test caught it on a row triage added, and the checker now stops at a dash-prefixed anchor too. | +| MISS-TechieFlow-20260906-20 | 2026-09-06 by gate | 2026-09-07 by fix-issues | the check was too weak | The database guard matched the name of the migration tool inside an echo string and refused a verify run's read of two seed SQL files and a select on TechieBlog, because it matched words anywhere in the command text; it now strips echo and printf strings and comment lines before matching, so only a command that runs a migrator or writes through a client is refused. | +| MISS-TechieFlow-20260906-19 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The Stop hook and the status gate ran the document checker on every checklist a command wrote and blocked on any FAIL line, so on an existing project whose checklist predates the schemas (TechieBlog: 376 old findings) no verify or build could ever end its turn without repairing rows it must not touch, against the Session 3 decision that old findings warn and are repaired through amend-docs; tf-phase.sh start now records a baseline of the findings present when a command starts and the checker prints those as OLD and blocks only on new ones. | +| MISS-TechieFlow-20260906-18 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The checklist entry parser in tf-build-list.py read past the end of a row's detail entry into the next one when the next entry started with a list dash, so a row with no mockup or BRD id of its own could inherit the next row's; the verify self-test caught the same bug in the new list script, and both now stop at the next entry. | +| MISS-TechieFlow-20260906-17 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The old verify task described driving MAUI Android, iOS and Mac Catalyst heads over Appium with the same render and visual checks, but no script in the framework ever implemented that drive, so the prose promised a verify that could not happen; the shrunk task now says those heads have no driver yet and their rows are recorded as not verified. | +| MISS-TechieFlow-20260906-16 | 2026-09-06 by owner | 2026-09-07 by fix-issues | said and ignored | The maintainer reported no monitors running while the watch on the MyDiary devguide run from 10:28 was still alive after five hours, because it stopped only the monitors it remembered instead of listing them; a running-work report is read from the harness list, never from memory (second occurrence after miss 24 of 2026-09-05). | +| MISS-TechieFlow-20260906-15 (FR-20) | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The second OpenCode build on the MyDiary copy (gpt-5.6-terra) marked 85 rows Implemented, ran the verifier on 28 rows and wrote the status file, but left no build-phase run record and no gate records, and the new sentinel guard accepted it because any run record since the goal start satisfied it; the guard now requires a run record for the command the phase marker names. | +| MISS-TechieFlow-20260906-14 (FR-40) | 2026-09-06 by agent-review | 2026-09-06 by fix-issues | the check was too weak | OpenAI models in OpenCode edit files only through apply_patch, and the OpenCode plugin refused every apply_patch that touched the checklist or the status file and told the agent to use edit or write tools it does not have, so no OpenAI-model run could ever update a row or the status; the plugin now maps an apply_patch onto the same write guards per file instead of refusing it. | +| MISS-TechieFlow-20260906-13 (FR-20) | 2026-09-06 by agent-review | 2026-09-06 by fix-issues | said and ignored | The OpenCode build on the MyDiary copy (gpt-5.6-terra) wrote code across three fresh sessions, built and tested it, then wrote the sentinel blocked with all 87 rows Not Started, the status file untouched since day-1 and no build-phase run record, because nothing mechanical tied the sentinel to the status gate; tf-yolo.sh done now refuses the sentinel until the status file and a run record are newer than the goal start. | +| MISS-TechieFlow-20260906-12 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the framework never said it | The maintainer ran a relative-path patch and a miss emit from whatever folder the previous command had left as the working directory, so a supervisor patch landed in the MyDiary copy and a framework miss record landed in TfLens-oc's stream; every script edit and emit now uses the absolute framework path. | +| MISS-TechieFlow-20260906-11 | 2026-09-06 by agent-review | 2026-09-06 by fix-issues | the framework never said it | When the opencode-go provider refuses a model (monthly usage limit reached, resets in six days) opencode run prints only its header and waits, and the refusal appears only in the OpenCode log file, so three fifteen-minute stalls on MyDiary-oc and a one-line probe looked like hangs; the supervisor now reads that log after a silent stall and stops with exit 5 and the provider message. | +| MISS-TechieFlow-20260906-10 (FR-55) | 2026-09-06 by agent-review | 2026-09-06 by fix-issues | the check was too weak | The devguide and refresh-status run records on MyDiary carried no duration_s because the tasks left it out and the emitter only recomputed one that was present; the emitter now derives it from started and ended. | +| MISS-TechieFlow-20260906-09 | 2026-09-06 by agent-review | 2026-09-06 by fix-issues | the framework never said it | On the MyDiary copy the glm-5.2 build's first cycle went silent after 15 minutes and both continue cycles (opencode run -c) printed only their header for 15 minutes each, so a resumed OpenCode session can hang forever; the new stall watchdog caught all three, and the supervisor now starts a fresh session after two stalled resumes and accepts --resume --fresh. | +| MISS-TechieFlow-20260906-07 (FR-18) | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the framework never said it | The smoke policy names a desktop and a mobile browser width and no evidence file, and FR-18's check reads a smoke log that no task writes, so the MyDiary MAUI build wrote no smoke evidence at all and marked 69 rows Implemented on a code trace against the mockups; the policy needs a native-head path and a named evidence file. | +| MISS-TechieFlow-20260906-06 (FR-14) | 2026-09-06 by agent-review | 2026-09-06 by fix-issues | said and ignored | The MyDiary build closed its status gate with three checklist Remarks cells over 60 words (REQ-NFR-006 to 008), because the Stop hook ran the checker on PROJECT-STATUS only and the agent skipped step 7b on the checklist; the hook now checks every checklist written in the session. | +| MISS-TechieFlow-20260906-05 (FR-46) | 2026-09-06 by agent-review | 2026-09-06 by fix-issues | said and ignored | For the second time after miss 23 the Sonnet build agent started the build as a background job and ended its turn with Waiting for the build, three cycles in a row, and each turn end killed the job; the supervisor's prompt now says builds run in the foreground, and the second occurrence makes this a hook candidate under FR-46. | +| MISS-TechieFlow-20260906-04 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The emitter named the app from the one *-Checklist.md in docs, so once TfLens-oc had a Deployment Checklist beside it the run record and the next miss id fell back to the folder name TfLens-oc, and a Large project's phase-2 checklist would do the same; deployment and phase-n checklists are now excluded. | +| MISS-TechieFlow-20260906-03 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The build ladder counted only CS, MSB and NU codes as code errors, so a MyDiary build failing on sixteen Razor RZ9991 errors was taken for a wrong rung on every rung and reported NOT-RUN, a host issue and never a project blocker, while the agent carried on as if the code were fine; RZ, BL and XAML codes now count. | +| MISS-TechieFlow-20260906-02 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the check was too weak | Killing the supervisor with TERM ran a trap that only cleared the YOLO flag and did not exit, and bash held the signal until the running sleep ended, so the stopped MyDiary supervisor woke, launched cycle 8 without its flag and left a harness child to be killed by hand; the trap now stops the child, records stopped and exits 130, and every sleep is interruptible. | +| MISS-TechieFlow-20260906-01 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The supervisor's crash pattern api_error also matched the harmless field api_error_status that every clean Claude result line carries, so cycles 5 to 7 of the MyDiary build, each a clean early stop, were called harness errors and backed off 2, 4 and 8 minutes instead of being re-prompted after 30 seconds; the classifier now reads the result line first. | +| MISS-TechieFlow-20260905-24 | 2026-09-06 by owner | 2026-09-07 by fix-issues | the framework never said it | Asked which of the three background shells in Claude Code was doing what, the maintainer listed operating-system processes instead and called the interactive session a third shell; the owner reads the harness shell list, so a running-work report names each background command by its launch line and its folder. | +| MISS-TechieFlow-20260905-23 (FR-41) | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | said and ignored | The MyDiary build agent started the build as a background task and ended its turn with "Waiting for the build to finish", which killed the task; the supervisor then labelled the clean early stop a harness error and backed off 120 then 240 seconds instead of re-prompting after 30; an early stop with exit 0 is a stop, not a crash, and a task never waits on a background job. | +| MISS-TechieFlow-20260905-22 (FR-40) | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the framework never said it | The OpenCode run of deploy-checklist on TfLens initialised and then produced no output for 43 minutes, and the supervisor has no stall watchdog, so it waited forever; a cycle whose output does not grow for fifteen minutes must be killed and re-prompted, and the OpenCode hang on this repository is unexplained. | +| MISS-TechieFlow-20260905-21 | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the framework never said it | The maintainer ran the supervisor in dry-run mode against TfLens while a real run was active there, which rewrote the live goal.json and appended a fake cycle line to its log; a dry run must never touch a folder with an active run, and the supervisor should refuse it. | +| MISS-TechieFlow-20260905-20 (FR-55) | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | said and ignored | The MiMo stage 2 run on the MyDiary copy ran step 0 last, so its run record says it started at 05:37 and ended at 05:38 after 38 minutes of work, and the review record copied that as the cost to correct; the supervisor now writes the start marker when its first cycle begins and the command claims it. | +| MISS-TechieFlow-20260905-19 | 2026-09-06 by owner | 2026-09-07 by fix-issues | said and ignored | The maintainer compressed a Session 3 decision (a service library's map lives in the UsageGuide) into half a table cell and a decision line, so the owner could not see where it came from or why, and questioned it as invented; a decision is restated with its source and reason, never as a half sentence. | +| MISS-TechieFlow-20260905-18 (FR-54) | 2026-09-06 by agent-review | 2026-09-07 by fix-issues | the framework never said it | The Phases table allowed one id range per phase, so an item added to phase 1 after phase 2 existed had no legal id; a row may now carry several ranges and the checker reads them all. | +| MISS-TechieFlow-20260905-16 (FR-14) | 2026-09-06 by owner | 2026-09-07 by fix-issues | the check was too weak | The checker only asks whether the Architecture has a Stack decisions table, so the MiMo run named the head MyDiary.App against the .NET answer set and left questions out of the table, and the owner found both by reading; a row per stack question and the head named exactly are the candidate checks. | +| MISS-TechieFlow-20260905-15 (FR-53) | 2026-09-06 by owner | 2026-09-07 by fix-issues | the check was too weak | The mockup click-through check passed the MiMo set although its menus navigated by script, its Settings item went nowhere and thirteen screens could not be reached by clicking; the check resolved links by file name and counted a script as a link, so the owner found the set broken by hand. | +| MISS-TechieFlow-20260905-14 | 2026-09-06 by owner | 2026-09-07 by fix-issues | said and ignored | The maintainer ended a report with a one-line pointer to three open decisions made two messages earlier; the owner called it dense chatting and could not tell what was being asked, so every open decision is now restated in full. | +| MISS-TechieFlow-20260905-13 (FR-15) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the framework never said it | The MyDiary BRD from MiMo packs four or five testable behaviours into one acceptance line (slots shown; Enter advances; empty slots dropped; timer starts), so 50 items stand where about 100 belong and the verifier grades a bundle; nothing in the checker sees a bundled then-clause. | +| MISS-TechieFlow-20260905-12 (FR-14) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The checker counted only the bold BRD-N ledger items and ignored the ids in the Non-functional table, so seven MyDiary NFR requirements escaped the cap, the checklist cross-check and the phase range check until the reading of the output found them. | +| MISS-TechieFlow-20260905-11 (FR-39) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The MyDiary day-1 run in OpenCode wrote a run record whose ended time (17:05) was 25 minutes after the record was appended (16:40), an invented duration; the emitter accepted it, so it must set ended to now when it lies in the future or before started. | +| MISS-TechieFlow-20260905-10 (FR-10) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the framework never said it | amend-docs proposed a phase split whenever a project passed its size cap, so a Small project growing past 50 requirements (Xpenser with family scope) would have been split into phases instead of first becoming Medium; the split belongs only past Medium, and FR-10 and D-3 did not say which cap. | +| MISS-TechieFlow-20260905-09 (FR-21) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | said and ignored | The Xpenser day-1 run in Claude Code created a test user and patched two stored procedures in the development database although the task says create no user and day-1 writes documents only; a hook that refuses database writes outside build and fix is the candidate fix. | +| MISS-TechieFlow-20260905-08 (FR-41) | 2026-09-05 by owner | 2026-09-07 by fix-issues | the framework never said it | The unattended Xpenser day-1 run was launched with a bare claude -p instead of the goal supervisor tf-goal.sh, so the usage-limit halt was not survived automatically; unattended runs go through the supervisor, and the YOLO rule will say so in 4c. | +| MISS-TechieFlow-20260905-07 (FR-39) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The emitter accepted a run record whose build_result was free text ('PASS (API+Web, DbMigration excluded)') instead of pass, fail or not-run, in the Xpenser OpenCode run; it now refuses it. | +| MISS-TechieFlow-20260905-06 (FR-14) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the check was too weak | The checker compared BRD screen names with their qualifier attached, so 'Profile (planned)' and 'Profile' were reported as two different screens during the Xpenser brownfield run; found by reading the run log, fixed the same hour. | +| MISS-TechieFlow-20260905-05 | 2026-09-05 by owner | 2026-09-07 by fix-issues | the framework never said it | Mockups were repeatedly delivered as unlinked HTML files with no navigation and dead buttons; nothing in the framework required a click-through set where every link and button behaves. | +| MISS-TechieFlow-20260905-04 (FR-41) | 2026-09-05 by owner | 2026-09-07 by fix-issues | the framework never said it | The YOLO rule and the flow-master run-workflow command say YOLO logs a phase boundary and continues, but the owner's rule is that YOLO runs one command to completion and never crosses an owner review into the next phase; fixed in 4c when YOLO is made uniform. | +| MISS-TechieFlow-20260905-03 (FR-39) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the check was too weak | tf-emit.sh appends a miss record that has no miss_id, although miss_id is the join key to its fix record, so a caller that skips --next-miss-id writes an orphan. | +| MISS-TechieFlow-20260905-02 (FR-40) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the check was too weak | Six task files edited in .tfcore on 2026-08-31 were never copied to the Claude Code mirror, so the two harnesses ran different smoke, metrics, mockup, render and verify rules for five days; no parity check ran. | +| MISS-TechieFlow-20260905-01 | 2026-09-05 by owner | 2026-09-07 by fix-issues | said and ignored | The first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule. | +| MISS-TechieFlow-20260831-10 | 2026-08-31 by agent-review | 2026-08-31 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, src, why: insufficient-verify-method) | +| MISS-TechieFlow-20260831-09 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (unspecified-gap, src, why: insufficient-verify-method) | +| MISS-TechieFlow-20260831-08 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (spec-contradiction, src, why: missing-checklist-item) | +| MISS-TechieFlow-20260831-07 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, architecture, why: insufficient-verify-method) | +| MISS-TechieFlow-20260831-06 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, architecture, why: insufficient-verify-method) | +| MISS-TechieFlow-20260831-05 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, architecture, why: insufficient-verify-method) | +| MISS-TechieFlow-20260831-04 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (unspecified-gap, architecture, why: insufficient-verify-method) | +| MISS-TechieFlow-20260831-03 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (unspecified-gap, config, why: missing-checklist-item) | +| MISS-TechieFlow-20260831-02 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (unspecified-gap, architecture, why: missing-checklist-item) | +| MISS-TechieFlow-20260831-01 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, src, why: insufficient-verify-method) | +| MISS-TechieFlow-20260828-18 | 2026-08-28 by owner | 2026-08-28 by fix-issues | not sorted | no sentence recorded (partial-implementation, src, why: missing-checklist-item) | +| MISS-TechieFlow-20260828-17 | 2026-08-28 by agent-review | 2026-08-28 by fix-issues | not sorted | no sentence recorded (partial-implementation, config, why: insufficient-verify-method) | +| MISS-TechieFlow-20260828-16 | 2026-08-28 by agent-review | 2026-08-28 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, src, why: missing-checklist-item) | +| MISS-TechieFlow-20260828-15 | 2026-08-28 by agent-review | 2026-08-28 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, src, why: insufficient-verify-method) | +| MISS-TechieFlow-20260828-14 | 2026-08-28 by agent-review | 2026-08-28 by fix-issues | not sorted | no sentence recorded (partial-implementation, src, why: missing-checklist-item) | +| MISS-TechieFlow-20260828-13 | 2026-08-28 by owner | 2026-08-28 by log-miss | not sorted | no sentence recorded (scope-creep, config, why: instruction-ignored) | +| MISS-TechieFlow-20260828-12 | 2026-08-28 by agent-review | 2026-08-28 by log-miss | not sorted | no sentence recorded (partial-implementation, config, why: insufficient-verify-method) | +| MISS-TechieFlow-20260828-11 | 2026-08-28 by owner | 2026-08-28 by fix-issues | not sorted | no sentence recorded (scope-creep, config, why: instruction-ignored) | +| MISS-TechieFlow-20260828-10 | 2026-08-28 by agent-review | 2026-08-28 by fix-issues | not sorted | no sentence recorded (spec-contradiction, config, why: ambiguous-acceptance) | +| MISS-TechieFlow-20260828-09 | 2026-08-28 by agent-review | 2026-08-28 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, devguide, why: instruction-ignored) | +| MISS-TechieFlow-20260828-08 | 2026-08-28 by agent-review | 2026-08-28 by fix-issues | not sorted | no sentence recorded (partial-implementation, config, why: insufficient-verify-method) | +| MISS-TechieFlow-20260828-07 | 2026-08-28 by agent-review | 2026-08-28 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, config, why: insufficient-verify-method) | +| MISS-TechieFlow-20260828-06 | 2026-08-28 by library-feedback | 2026-08-28 by log-miss | not sorted | no sentence recorded (wrong-behaviour, src, why: insufficient-verify-method) | +| MISS-TechieFlow-20260828-05 | 2026-08-28 by library-feedback | 2026-08-28 by log-miss | not sorted | no sentence recorded (spec-contradiction, architecture, why: missing-checklist-item) | +| MISS-TechieFlow-20260828-04 | 2026-08-28 by owner | 2026-08-28 by log-miss | not sorted | no sentence recorded (partial-implementation, src, why: instruction-ignored) | +| MISS-TechieFlow-20260828-02 | 2026-08-28 by agent-review | 2026-08-28 by log-miss | not sorted | no sentence recorded (wrong-behaviour, src) | +| MISS-TechieFlow-20260828-01 | 2026-08-28 by owner | 2026-08-28 by log-miss | not sorted | no sentence recorded (partial-implementation, src) | + +## Will not fix (1) + +| Miss | Found | Closed | Whose gap | What went wrong | +|---|---|---|---|---| +| MISS-TechieFlow-20260828-03 | 2026-08-28 by agent-review | 2026-08-28 by log-miss | not sorted | no sentence recorded (other, other) | diff --git a/docs/TechieFlow-Permissions-And-YOLO.html b/docs/TechieFlow-Permissions-And-YOLO.html new file mode 100644 index 0000000..7002acc --- /dev/null +++ b/docs/TechieFlow-Permissions-And-YOLO.html @@ -0,0 +1,353 @@ + + + + + +TechieFlow — Permissions and YOLO mode + + + + + +
+
+

TechieFlow — Permissions and YOLO mode

+
Rendered 2026-09-07 · source TechieFlow-Permissions-And-YOLO.md
+ + + + + + + + + +
PurposeWhy the harness stops asking you for permission, how the git ban is enforced, and what YOLO mode changes. Includes the shipped settings.json and every guard hook with the incident that produced it.
AudienceThe owner, and anyone who has to explain why an agent was refused.
StatusMoved out of README.md on 2026-09-07 (Session 6 of the reset), unedited. Written before the reset: the hook list grew afterwards to eleven, adding the database, build and metrics guards. docs/TechieFlow-How-It-Works.md §2 carries the current list.
CompanionREADME.md, docs/TechieFlow-How-It-Works.md, .tfcore/tasks/_yolo-mode.md.
+
+

12. Permissions (yolo-except-git-writes)#

+

The pre-built config auto-allows Read/Glob/Grep/Edit/Write/MultiEdit and all Bash (bare "Bash") — so create/update/move run with zero prompts. Deletes and sudo (rm, rmdir, find -delete, sudo) ask — via the hook, not via a settings ask rule (see the YOLO box below for why). Denies catastrophic rm -rf root/home paths and every git/gh WRITE subcommand (git commit|push|add|reset|checkout|switch|restore|merge|rebase|stash|clean|pull|fetch|…, gh pr|issue|repo|release create|merge|close|…) — git is manual in TechieFlow; agents never write it, so it is a hard deny, in every permission mode including bypass. Permission precedence is deny → ask → hook → allow. (Cross-project tip: to let a session work in another app's folder without per-path prompts, add that root to permissions.additionalDirectories in this project's settings.json — keep those machine-specific paths out of any shared template.)

+

The git ban is TWO layers, because prefix rules alone leak. Bash(git commit*) is a literal prefix match — it never sees cd src && git commit or echo done; git add -A, which the bare "Bash" allow would wave straight through. That is exactly how agents kept "accidentally" running git during status updates. So the config also wires a PreToolUse hook.tfcore/hooks/block-git.sh — that parses every Bash call (compound forms, bash -c "…", eval, $(…), wrappers like sudo/env/xargs) and classifies each git/gh node as read (status/log/diff/show/blame/grep/branch/tag -l/stash list/remote -v/config --get, gh pr list|view…) or write (everything else). Writes are blocked always; reads are blocked outside YOLO and allowed in YOLO. The block message carries the local-evidence recipe (checklist tables + working-tree files + fresh build) so the agent continues correctly instead of flailing. You still run git yourself: in a separate terminal, or by typing !git … in the session (user-typed bang commands bypass agent tool permissions).

+

12a. YOLO / goal mode — "I've given you all the permissions; run until it's done" (2026-08-21)#

+

*yolo used to be agent-side only (skip elicitation) and the owner still got prompted for every delete and blocked on every git read — which is how a VM goal run took 3 days, mostly waiting for a human. Now *yolo, the word YOLO anywhere in a command, an active Claude Code /goal, or a tf-goal.sh run all mean the same thing, defined in .tfcore/tasks/_yolo-mode.md:

+ + + + + + + + + + +
NormalYOLO
rm / rmdir / sudohook asksallowed, no prompt (catastrophic rm -rf //~ still denied)
git/gh reads (status/log/diff/blame, gh pr view)blockedallowed
git/gh writes (commit/push/add/reset/checkout/stash/tag, `gh pr createmerge`)blockedblocked — always
Elicitation, phase-boundary pauses, "confirm the BRD-N list", "ask once" questionspausedecide the default, record it, continue
Build pass scopewhole checklist (§2b)whole checklist + automatic FIX loop on verifier FAIL rows (build-phase §6c, ≤5 cycles)
Turn endingmay hand backonly when the goal is complete (tf-yolo.sh done complete) or every remaining REQ is owner-gated (done blocked)
+

Mechanics. The flag is .tfcore/.session/yolo.json (bash .tfcore/utils/tf-yolo.sh on|off|status; never committed). block-git.sh reads it (plus TF_YOLO=1 and the hook payload's permission_mode — Claude Code's bypassPermissions/auto count as YOLO); the OpenCode plugin reads the same flag and auto-approves its rm */sudo * asks via permission.ask. Why the delete prompt moved out of settings.json: Claude Code honours a settings ask rule even in bypassPermissions mode and even when a hook says allow — so as long as Bash(rm *) sat in ask, no mode could stop the prompt. A hook-issued ask can be withheld; a settings ask cannot.

+

Usage limits (5-hour / weekly). Nothing inside a session can wait a limit out, so the wait lives in a supervisor: bash .tfcore/utils/tf-goal.sh <app-dir> "<goal>" runs the goal headless (claude -p --permission-mode bypassPermissions, or --harness opencodeopencode run --auto), parses the reset time from the limit message (resets 7pm (Asia/Kolkata), resets in 2h 14m, usage limit reached|<epoch>, weekly resets Tue 3pm), sleeps until reset + 15 min (--buffer-min), logs RETRY AT … to .tfcore/.session/goal.log, and resumes the same session (--resume <id>). Crashes back off 2 → 30 min; an agent that stops without finishing is re-prompted; it exits only on the agent's sentinel (0 complete, 3 owner-blocked, 4 max cycles). --resume <app-dir> picks up after a reboot. The agent's part of the bargain is the status gate: every phase ends with PROJECT-STATUS + checklist written, so a resume is lossless.

+

Build passes are whole-checklist, YOLO or not. The other 3-day culprit: build-phase runs that implemented a few REQs, wrote "next command: *build-phase for the remaining REQs" and stopped. build-phase.md §2b now bans that ending — a pass is done when every working-list REQ is ≥ Implemented (or a logged Blocked/owner-gated blocker), the verifier has been chained, and (in YOLO) its FAIL rows have been looped. Long list ⇒ more sub-agent clusters, never a shorter pass. _status-update-gate.md item 5 carries the matching rule for the next-command line.

+

Codex permission difference. .codex/hooks.json routes shell and file changes through .tfcore/hooks/codex-adapter.py, while .codex/rules/techieflow.rules provides the command policy. Codex keeps every agent-issued git and gh command blocked in normal and YOLO modes (including reads); this is intentionally stricter than the Claude/OpenCode YOLO table above. Trust the repository and inspect /hooks after scaffold/update. $techieflow-yolo changes TechieFlow pause/delete behavior, but never relaxes Codex's version-control boundary.

+

Q: Config (canonical version in scaffold-brownfield.sh / scaffold-greenfield.sh)

+
{
+  "permissions": {
+    "defaultMode": "acceptEdits",
+    "allow": [
+      "Bash",
+      "Edit", "Write", "MultiEdit", "NotebookEdit",
+      "Read", "Glob", "Grep", "TodoWrite", "WebFetch", "WebSearch", "Task"
+    ],
+    "ask": [],
+    "deny": [
+      "Bash(rm -rf /)", "Bash(rm -rf /*)", "Bash(rm -rf ~)", "Bash(rm -rf ~/*)",
+      "Bash(git commit*)", "Bash(git push*)", "Bash(git add*)", "Bash(git reset*)",
+      "Bash(git checkout*)", "Bash(git switch*)", "Bash(git restore*)", "Bash(git merge*)",
+      "… every other git WRITE subcommand (rebase, cherry-pick, revert, clean, pull, fetch, init, clone, …) …",
+      "Bash(gh pr create*)", "Bash(gh pr merge*)", "Bash(gh issue create*)", "… every gh WRITE verb …"
+    ]
+  },
+  "hooks": {
+    "PreToolUse": [
+      { "matcher": "Bash",
+        "hooks": [ { "type": "command",
+                     "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/block-git.sh\"" },
+                   { "type": "command",
+                     "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-artifacts.sh\"" } ] },
+      { "matcher": "Write|Edit|MultiEdit",
+        "hooks": [ { "type": "command",
+                     "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" },
+                   { "type": "command",
+                     "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify.sh\"" } ] }
+    ],
+    "Stop": [
+      { "hooks": [ { "type": "command",
+                     "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status-html.sh\"" } ] }
+    ]
+  }
+}
+

Repo-root artifact directories are blocked mechanically (2026-08-25). A fourth PreToolUse hook — .tfcore/hooks/guard-artifacts.sh, matcher Bash — blocks any command that points --output / --output-dir at a root-level test-results* or scripts-* directory, or mkdirs one. The prose rule in verify-phase.md §1 had been strengthened twice and broken three times (fourteen test-results-* dirs in one app, four scripts-cluster-* in the next fan-out, ten test-results-* in TechieBlog) — every time from an agent passing --output test-results-<slug> and overriding the config's pinned outputDir. The sanctioned isolation form --output tests/.artifacts/<slug> passes, as do tests/, docs/screenshots/ and the project's own tracked scripts/. A bare -o is deliberately not matched (Playwright has no -o; grep -o / curl -o are legitimate).

+

A stale PROJECT-STATUS.html blocks the end of the turn (2026-08-25). The first Stop hook — .tfcore/hooks/guard-status-html.sh — refuses to let a turn end while PROJECT-STATUS.html is older than PROJECT-STATUS.md, or missing. _status-update-gate.md §8 ("re-render in the same turn, full stop") had failed twice in a row; the owner spent 4h40m reading a page that still listed retracted owner-actions. Stop, not PostToolUse, because the rule is about the turn — an agent legitimately renders the HTML several tool calls after writing the markdown. It honours stop_hook_active so a turn that genuinely cannot render still terminates. mtime only: content parity stays an agent responsibility.

+

Expired run material is deleted automatically (2026-08-26). Pinning artifacts under tests/.artifacts/ fixed where they land but not that they ever leave — Playwright wipes only its own outputDir, so per-cluster subfolders, harness scripts and multi-hundred-MB host logs piled up (TechieBlog: 1.1 GB under tests/.artifacts/ + 101 MB of .verify/*.log, mostly two weeks stale). A SessionStart hook — .tfcore/hooks/sweep-artifacts.sh, no veto, exit 0 always — deletes files under tests/.artifacts/ and .verify/ older than the retention window (default 7 days; artifactRetentionDays: N in .tfcore/core-config.yaml or TF_ARTIFACT_RETENTION_DAYS=N; 0 disables the age sweep), prunes emptied dirs, and removes banned repo-root legacy dirs (test-results*/, scripts-*/, playwright-report/) regardless of age. Files newer than the window are untouched, so a run in flight is never disturbed and a mixed-age harness/ keeps its recent scripts. Never follows symlinks, never leaves the project root, never touches tracked tests/verify/ or the project's own scripts/. Throttled to once per hour per project (.tfcore/.session/sweep.stamp); TF_SWEEP_DRY_RUN=1 previews. Codex runs it from codex-adapter.py session-start; OpenCode from the plugin on the first root session.created. The one-line summary of what was removed is surfaced into the session.

+

Both new guards run in every harness: Codex through codex-adapter.py (pre-tool and the new stop mode wired in .codex/hooks.json), OpenCode through .opencode/plugin/techieflow.js (the bash guard in tool.execute.before; the Stop check as a one-shot follow-up prompt on root session.idle, since OpenCode has no blocking Stop hook). Hooks load at session start — neither takes effect in an already-running session.

+

PROJECT-STATUS shape is enforced mechanically too (2026-07-09). A second PreToolUse hook — .tfcore/hooks/guard-status.sh, matcher Write|Edit|MultiEdit — blocks any write to PROJECT-STATUS.md that violates the crisp fixed-shape snapshot rule: an H2 outside the template's section set (per-run dated sections like ## *verify all — coverage matrix (DATE) are the classic disease), a heading naming a command run, a paragraph stuffed into current_phase:, or a full-file write past ~120 lines. The block message tells the agent exactly how to reshape (overwrite the template sections in place, ONE Verification-log row per run, detail into the checklist Remarks). Same philosophy as the git ban: prose rules kept failing, so the harness enforces it. See .tfcore/tasks/_status-update-gate.md.

+

Verified verdicts are enforced mechanically too (2026-07-10). A third PreToolUse hook — .tfcore/hooks/guard-verify.sh, matcher Write|Edit|MultiEdit — blocks any write to a *-Checklist.md that introduces a Verified status cell unless a same-day run ledger docs/.last-verify.json exists, which only an executed verify-phase run writes (verify-phase §6: boot → scoped tests → §4a data-render + §4b visual-truth gates → ledger → verdicts). This exists because a build orchestrator did its own smoke and wrote the Verified verdicts itself (TrSetup, 2026-07-09) — self-attestation the "chain the verifier" prose didn't stop. A self-smoke's ceiling is Implemented (_smoke-test-policy.md §"Smoke is NOT verify"); *refresh-status may reconcile a lagging Status column to a row's pre-existing dated verdict by writing the ledger with "mode":"reconcile". Demotions (e.g. Verified → Needs re-verify) are never blocked.

+

WSL (Windows):

+
/mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/app
+

macOS:

+
/Volumes/MacD/MyCode/TechieFlow/update-framework.sh /path/to/app
+
+
+ + + + + + diff --git a/docs/TechieFlow-Permissions-And-YOLO.md b/docs/TechieFlow-Permissions-And-YOLO.md new file mode 100644 index 0000000..1241c8f --- /dev/null +++ b/docs/TechieFlow-Permissions-And-YOLO.md @@ -0,0 +1,103 @@ +# TechieFlow — Permissions and YOLO mode + +| | | +|---|---| +| Purpose | Why the harness stops asking you for permission, how the git ban is enforced, and what YOLO mode changes. Includes the shipped `settings.json` and every guard hook with the incident that produced it. | +| Audience | The owner, and anyone who has to explain why an agent was refused. | +| Status | Moved out of `README.md` on 2026-09-07 (Session 6 of the reset), unedited. Written before the reset: the hook list grew afterwards to eleven, adding the database, build and metrics guards. `docs/TechieFlow-How-It-Works.md` §2 carries the current list. | +| Companion | `README.md`, `docs/TechieFlow-How-It-Works.md`, `.tfcore/tasks/_yolo-mode.md`. | + +--- + +## 12. Permissions (yolo-except-git-writes) + +The pre-built config **auto-allows** Read/Glob/Grep/Edit/Write/MultiEdit and **all Bash** (bare `"Bash"`) — so create/update/**move** run with zero prompts. **Deletes and `sudo`** (`rm`, `rmdir`, `find -delete`, `sudo`) **ask — via the hook, not via a settings `ask` rule** (see the YOLO box below for why). **Denies** catastrophic `rm -rf` root/home paths **and every git/gh WRITE subcommand** (`git commit|push|add|reset|checkout|switch|restore|merge|rebase|stash|clean|pull|fetch|…`, `gh pr|issue|repo|release create|merge|close|…`) — git is manual in TechieFlow; agents never write it, so it is a hard deny, in **every** permission mode including bypass. Permission precedence is `deny → ask → hook → allow`. *(Cross-project tip: to let a session work in another app's folder without per-path prompts, add that root to `permissions.additionalDirectories` in this project's settings.json — keep those machine-specific paths out of any shared template.)* + +**The git ban is TWO layers, because prefix rules alone leak.** `Bash(git commit*)` is a literal prefix match — it never sees `cd src && git commit` or `echo done; git add -A`, which the bare `"Bash"` allow would wave straight through. That is exactly how agents kept "accidentally" running git during status updates. So the config also wires a **PreToolUse hook** — `.tfcore/hooks/block-git.sh` — that parses every Bash call (compound forms, `bash -c "…"`, `eval`, `$(…)`, wrappers like `sudo`/`env`/`xargs`) and classifies each `git`/`gh` node as **read** (`status`/`log`/`diff`/`show`/`blame`/`grep`/`branch`/`tag -l`/`stash list`/`remote -v`/`config --get`, `gh pr list|view`…) or **write** (everything else). Writes are blocked always; reads are blocked outside YOLO and allowed in YOLO. The block message carries the local-evidence recipe (checklist tables + working-tree files + fresh build) so the agent continues correctly instead of flailing. You still run git yourself: in a separate terminal, or by typing `!git …` in the session (user-typed bang commands bypass agent tool permissions). + +### 12a. YOLO / goal mode — "I've given you all the permissions; run until it's done" (2026-08-21) + +`*yolo` used to be agent-side only (skip elicitation) and the owner still got prompted for every delete and blocked on every git read — which is how a VM goal run took **3 days**, mostly waiting for a human. Now `*yolo`, the word **YOLO** anywhere in a command, an active Claude Code **`/goal`**, or a `tf-goal.sh` run all mean the same thing, defined in **`.tfcore/tasks/_yolo-mode.md`**: + +| | Normal | YOLO | +|---|---|---| +| `rm` / `rmdir` / `sudo` | hook asks | **allowed, no prompt** (catastrophic `rm -rf /`/`~` still denied) | +| git/gh **reads** (`status`/`log`/`diff`/`blame`, `gh pr view`) | blocked | **allowed** | +| git/gh **writes** (`commit`/`push`/`add`/`reset`/`checkout`/`stash`/`tag`, `gh pr create|merge`) | blocked | **blocked — always** | +| Elicitation, phase-boundary pauses, "confirm the BRD-N list", "ask once" questions | pause | **decide the default, record it, continue** | +| Build pass scope | whole checklist (§2b) | whole checklist **+ automatic FIX loop** on verifier FAIL rows (build-phase §6c, ≤5 cycles) | +| Turn ending | may hand back | **only** when the goal is complete (`tf-yolo.sh done complete`) or every remaining REQ is owner-gated (`done blocked`) | + +**Mechanics.** The flag is `.tfcore/.session/yolo.json` (`bash .tfcore/utils/tf-yolo.sh on|off|status`; never committed). `block-git.sh` reads it (plus `TF_YOLO=1` and the hook payload's `permission_mode` — Claude Code's `bypassPermissions`/`auto` count as YOLO); the OpenCode plugin reads the same flag and auto-approves its `rm */sudo *` asks via `permission.ask`. **Why the delete prompt moved out of `settings.json`:** Claude Code honours a settings `ask` rule *even in `bypassPermissions` mode and even when a hook says allow* — so as long as `Bash(rm *)` sat in `ask`, no mode could stop the prompt. A hook-issued `ask` can be withheld; a settings `ask` cannot. + +**Usage limits (5-hour / weekly).** Nothing inside a session can wait a limit out, so the wait lives in a supervisor: `bash .tfcore/utils/tf-goal.sh ""` runs the goal headless (`claude -p --permission-mode bypassPermissions`, or `--harness opencode` → `opencode run --auto`), parses the reset time from the limit message (`resets 7pm (Asia/Kolkata)`, `resets in 2h 14m`, `usage limit reached|`, weekly `resets Tue 3pm`), **sleeps until reset + 15 min** (`--buffer-min`), logs `RETRY AT …` to `.tfcore/.session/goal.log`, and **resumes the same session** (`--resume `). Crashes back off 2 → 30 min; an agent that stops without finishing is re-prompted; it exits only on the agent's sentinel (`0` complete, `3` owner-blocked, `4` max cycles). `--resume ` picks up after a reboot. The agent's part of the bargain is the status gate: every phase ends with PROJECT-STATUS + checklist written, so a resume is lossless. + +**Build passes are whole-checklist, YOLO or not.** The other 3-day culprit: build-phase runs that implemented a few REQs, wrote "next command: `*build-phase` for the remaining REQs" and stopped. `build-phase.md §2b` now bans that ending — a pass is done when **every** working-list REQ is ≥ `Implemented` (or a logged `Blocked`/owner-gated blocker), the verifier has been chained, and (in YOLO) its FAIL rows have been looped. Long list ⇒ more sub-agent clusters, never a shorter pass. `_status-update-gate.md` item 5 carries the matching rule for the next-command line. + +**Codex permission difference.** `.codex/hooks.json` routes shell and file changes through `.tfcore/hooks/codex-adapter.py`, while `.codex/rules/techieflow.rules` provides the command policy. Codex keeps every agent-issued `git` and `gh` command blocked in normal and YOLO modes (including reads); this is intentionally stricter than the Claude/OpenCode YOLO table above. Trust the repository and inspect `/hooks` after scaffold/update. `$techieflow-yolo` changes TechieFlow pause/delete behavior, but never relaxes Codex's version-control boundary. + +**Q: Config (canonical version in scaffold-brownfield.sh / scaffold-greenfield.sh)** + +```json +{ + "permissions": { + "defaultMode": "acceptEdits", + "allow": [ + "Bash", + "Edit", "Write", "MultiEdit", "NotebookEdit", + "Read", "Glob", "Grep", "TodoWrite", "WebFetch", "WebSearch", "Task" + ], + "ask": [], + "deny": [ + "Bash(rm -rf /)", "Bash(rm -rf /*)", "Bash(rm -rf ~)", "Bash(rm -rf ~/*)", + "Bash(git commit*)", "Bash(git push*)", "Bash(git add*)", "Bash(git reset*)", + "Bash(git checkout*)", "Bash(git switch*)", "Bash(git restore*)", "Bash(git merge*)", + "… every other git WRITE subcommand (rebase, cherry-pick, revert, clean, pull, fetch, init, clone, …) …", + "Bash(gh pr create*)", "Bash(gh pr merge*)", "Bash(gh issue create*)", "… every gh WRITE verb …" + ] + }, + "hooks": { + "PreToolUse": [ + { "matcher": "Bash", + "hooks": [ { "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/block-git.sh\"" }, + { "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-artifacts.sh\"" } ] }, + { "matcher": "Write|Edit|MultiEdit", + "hooks": [ { "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" }, + { "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify.sh\"" } ] } + ], + "Stop": [ + { "hooks": [ { "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status-html.sh\"" } ] } + ] + } +} +``` + +**Repo-root artifact directories are blocked mechanically (2026-08-25).** A fourth PreToolUse hook — `.tfcore/hooks/guard-artifacts.sh`, matcher `Bash` — blocks any command that points `--output` / `--output-dir` at a root-level `test-results*` or `scripts-*` directory, or `mkdir`s one. The prose rule in `verify-phase.md` §1 had been strengthened twice and broken three times (fourteen `test-results-*` dirs in one app, four `scripts-cluster-*` in the next fan-out, ten `test-results-*` in TechieBlog) — every time from an agent passing `--output test-results-` and overriding the config's pinned `outputDir`. The sanctioned isolation form `--output tests/.artifacts/` passes, as do `tests/`, `docs/screenshots/` and the project's own tracked `scripts/`. A bare `-o` is deliberately *not* matched (Playwright has no `-o`; `grep -o` / `curl -o` are legitimate). + +**A stale `PROJECT-STATUS.html` blocks the end of the turn (2026-08-25).** The first **Stop** hook — `.tfcore/hooks/guard-status-html.sh` — refuses to let a turn end while `PROJECT-STATUS.html` is older than `PROJECT-STATUS.md`, or missing. `_status-update-gate.md` §8 ("re-render in the same turn, full stop") had failed twice in a row; the owner spent 4h40m reading a page that still listed retracted owner-actions. Stop, not PostToolUse, because the rule is about the turn — an agent legitimately renders the HTML several tool calls after writing the markdown. It honours `stop_hook_active` so a turn that genuinely cannot render still terminates. mtime only: content parity stays an agent responsibility. + +**Expired run material is deleted automatically (2026-08-26).** Pinning artifacts under `tests/.artifacts/` fixed *where* they land but not that they ever leave — Playwright wipes only its own `outputDir`, so per-cluster subfolders, harness scripts and multi-hundred-MB host logs piled up (TechieBlog: 1.1 GB under `tests/.artifacts/` + 101 MB of `.verify/*.log`, mostly two weeks stale). A **SessionStart** hook — `.tfcore/hooks/sweep-artifacts.sh`, no veto, exit 0 always — deletes files under `tests/.artifacts/` and `.verify/` older than the retention window (default **7 days**; `artifactRetentionDays: N` in `.tfcore/core-config.yaml` or `TF_ARTIFACT_RETENTION_DAYS=N`; `0` disables the age sweep), prunes emptied dirs, and removes banned repo-root legacy dirs (`test-results*/`, `scripts-*/`, `playwright-report/`) regardless of age. Files newer than the window are untouched, so a run in flight is never disturbed and a mixed-age `harness/` keeps its recent scripts. Never follows symlinks, never leaves the project root, never touches tracked `tests/verify/` or the project's own `scripts/`. Throttled to once per hour per project (`.tfcore/.session/sweep.stamp`); `TF_SWEEP_DRY_RUN=1` previews. Codex runs it from `codex-adapter.py session-start`; OpenCode from the plugin on the first root `session.created`. The one-line summary of what was removed is surfaced into the session. + +Both new guards run in every harness: Codex through `codex-adapter.py` (`pre-tool` and the new `stop` mode wired in `.codex/hooks.json`), OpenCode through `.opencode/plugin/techieflow.js` (the bash guard in `tool.execute.before`; the Stop check as a one-shot follow-up prompt on root `session.idle`, since OpenCode has no blocking Stop hook). Hooks load at session start — neither takes effect in an already-running session. + +**PROJECT-STATUS shape is enforced mechanically too (2026-07-09).** A second PreToolUse hook — `.tfcore/hooks/guard-status.sh`, matcher `Write|Edit|MultiEdit` — blocks any write to `PROJECT-STATUS.md` that violates the crisp fixed-shape snapshot rule: an H2 outside the template's section set (per-run dated sections like `## *verify all — coverage matrix (DATE)` are the classic disease), a heading naming a command run, a paragraph stuffed into `current_phase:`, or a full-file write past ~120 lines. The block message tells the agent exactly how to reshape (overwrite the template sections in place, ONE Verification-log row per run, detail into the checklist Remarks). Same philosophy as the git ban: prose rules kept failing, so the harness enforces it. See `.tfcore/tasks/_status-update-gate.md`. + +**`Verified` verdicts are enforced mechanically too (2026-07-10).** A third PreToolUse hook — `.tfcore/hooks/guard-verify.sh`, matcher `Write|Edit|MultiEdit` — blocks any write to a `*-Checklist.md` that *introduces* a `Verified` status cell unless a same-day run ledger `docs/.last-verify.json` exists, which only an *executed* `verify-phase` run writes (verify-phase §6: boot → scoped tests → §4a data-render + §4b visual-truth gates → ledger → verdicts). This exists because a build orchestrator did its own smoke and wrote the `Verified` verdicts itself (TrSetup, 2026-07-09) — self-attestation the "chain the verifier" prose didn't stop. A self-smoke's ceiling is `Implemented` (`_smoke-test-policy.md §"Smoke is NOT verify"`); `*refresh-status` may reconcile a lagging Status column to a row's *pre-existing* dated verdict by writing the ledger with `"mode":"reconcile"`. Demotions (e.g. `Verified → Needs re-verify`) are never blocked. + +**WSL (Windows):** + +```bash +/mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/app +``` + +**macOS:** + +```bash +/Volumes/MacD/MyCode/TechieFlow/update-framework.sh /path/to/app +``` + diff --git a/docs/TechieFlow-Requirements.md b/docs/TechieFlow-Requirements.md index 098da5d..bd724fd 100644 --- a/docs/TechieFlow-Requirements.md +++ b/docs/TechieFlow-Requirements.md @@ -4,7 +4,7 @@ |---|---| | Purpose | The list of things the framework must do, each with a way to check it. This is the framework's checklist. | | Audience | Agents, and the framework maintainer who reviews it (Claude). **The owner does not review this list.** The owner reviews the descriptive documents it is derived from: `TechieFlow-How-It-Works.md` (including its defect table), `TechieFlow-Stack-Questions.md` and `TechieFlow-Stack-Defaults-DotNet.md`. Every line below traces to a statement in one of those documents or to a decision the owner gave in conversation. When a line needs a decision only the owner can make, the maintainer asks it as a plain question, never as "review this line". | -| Status | Session 2 of the reset (2026-09-04). Reviewed by the maintainer for traceability; owner decisions of 2026-09-04 applied. Checks marked "script" are written in Sessions 3 and 4. Group I (distribution) added 2026-09-04 after freeze, via miss 23. Agent document; not rendered to HTML. | +| Status | Session 2 of the reset (2026-09-04). Reviewed by the maintainer for traceability; owner decisions of 2026-09-04 applied. Checks marked "script" are written in Sessions 3 and 4. Group I (distribution) added 2026-09-04 after freeze, via miss 23; FR-53 added 2026-09-05 via miss 05 of that day; FR-54 (Large layout), FR-55 (honest `ended` and the command marker), FR-56 (database writes only from build and fix) and FR-57 (Architecture stack rows and head name) added in Sitting 4b, 2026-09-05 and 06, from the owner's decisions and misses 9, 11, 16; FR-10, FR-15, FR-36 and FR-53 reworded from misses 10, 13, 15 and the review record build. **Session 5 (2026-09-07):** FR-31 and FR-32 built; FR-58 to FR-61 added from the four-question sort of Session 4's misses (04, 08, 17 of 09-05 and 26 of 09-06, all sorted `unsaid`); FR-18 and FR-40 checks reworded from misses 07 of 09-06 and 02 of 09-05. **Session 6 (2026-09-07):** FR-62 added from miss 07 of that day (sorted `unsaid`); FR-47's check rewritten and built from miss 05, which found that it named a script nobody had written. Agent document; not rendered to HTML. | | Sources | The conventions in `WorkFlow-Context.md` §2; the 128 recorded misses across all repositories; the incident log; defects D-1 to D-22 in `TechieFlow-How-It-Works.md` §8; the Stack documents; owner decisions of 2026-09-04. | --- @@ -27,7 +27,7 @@ The projects on which checks run. Agreed with the owner on 2026-09-04. All publi | Fixture | Kind | Used for | |---|---|---| -| MyDiary | small greenfield application (a journal site the owner is about to build; the first real project on the reset framework) | day-1 greenfield, mockups, build, verify, bugs | +| MyDiary | greenfield application (a journal app the owner is about to build; the first real project on the reset framework). Its brief of 2026-09-05 lists 25 screens in two phases, so it is Large and also the first phased project (Sitting 4b) | day-1 greenfield, mockups, the Large layout, build, verify, bugs | | TrStudio | brownfield application | day-1 brownfield, amend-docs, DevGuide | | Xpenser | brownfield application | second brownfield sample, so that a check passing on one codebase is confirmed on another | | TrBlazeUI | UI component library | library modes of DevGuide and verify, library feedback | @@ -66,20 +66,22 @@ The same four questions are asked for a miss in an application, against that app | FR-07 | writes the BRD so that every use case links its mockup and lists the fields of every screen, beside the acceptance criteria. | script: every use-case section in the BRD contains a link to `docs/mockups/`; every screen in the UIDesign appears in the BRD. | D-2 | | FR-08 | records an application size at day-1 stage 1 and applies that size's requirement cap and document budgets. | script: the BRD header carries `Size:`; requirement count and document word counts are within budget for that size. | D-3 | | FR-09 | defines Small as up to 10 screens, one role, up to 50 requirements; Medium as up to 20 screens, up to 100 requirements; Large as anything beyond, to be split into phase-wise BRDs. AppManager does not count as an external integration. | review: the definition appears once, in the BRD template, and nowhere else. | D-3; owner 2026-09-04 | -| FR-10 | proposes a phase split, rather than growing the single BRD, when `*amend-docs` would take a project past its size's requirement cap. | fixture run: `*amend-docs TrStudio` with additions that exceed the cap; the run stops and proposes a phase split. | D-3 | +| FR-10 | raises a Small project to Medium when `*amend-docs` would take it past 50 requirements, and proposes a phase split, rather than growing the single BRD, only when a Medium project or a phase would pass 100 requirements or 20 screens. | fixture run: `*amend-docs Xpenser` with additions past 50 raises the size; `*amend-docs TrStudio` with additions past 100 stops and proposes a phase split. | D-3; owner 2026-09-05 (Schemas §7.2 K; MISS-TechieFlow-20260905-10) | | FR-11 | applies mockups to any project, not only greenfield; brownfield day-1 finds existing mockups, records their location, and links them from the documents. | fixture run: `*day1-brownfield Xpenser` with a `docs/mockups/` folder present; the UIDesign and BRD link them and no mockup is regenerated. | D-5 | | FR-12 | produces the checklist automatically once the owner approves the BRD; the owner never types `*split-brd`. | fixture run: after the stage 2 go-ahead on MyDiary the checklist exists without a separate command. | D-6 | | FR-13 | produces the DevGuide automatically when the build phase completes the checklist, for every project type, and refreshes it at handoff. | fixture run: `*build-phase MyDiary` to completion; `docs/MyDiary-DevGuide.md` exists at the end. | D-7 | | FR-14 | gives every human document template a strict structure (required sections in order, size budget per size class, row rules) and refuses to close a phase whose document breaks it. | script: `tf-doc-check.sh` exits 0 on every document the fixture runs produce, exits non-zero on a deliberately broken one, and the status gate refuses to close. | How-It-Works §2 Template; Session 3 | -| FR-15 | requires every checklist row to carry an acceptance line of the form "when … then …" naming an observable result. | script: every `REQ-` row in the fixture checklists matches the pattern. | D-20 | +| FR-15 | requires every checklist row and every BRD item to carry one acceptance line of the form "when … then …" naming an observable result, of at most 30 words (target 20) and holding one behaviour, under a title in everyday words; BRD items sit under a heading per screen that opens with one plain sentence. | script: every `REQ-` row and `BRD-N` item in the fixture documents matches the pattern and the word cap; the broken twin's 42-word line fails. | D-20; owner 2026-09-06 (miss 13) | | FR-16 | keeps one checklist per application as the single source of truth, in markdown only, and never creates dated `docs/qa/` or `docs/verify/` files or `-v2` document copies. | script: no `docs/qa/`, `docs/verify/`, `*-v2.*` or `*-Checklist.html` in any fixture. | conventions | | FR-17 | renders every human document to HTML by script, never by hand; configuration and agent documents are not rendered. | script: every human `docs/*.md` has a sibling `.html` newer than itself; no `.html` exists for the checklist, the Stack documents or this file. | TF-003; owner 2026-09-04 | +| FR-54 | lays a Large project out by phase: BRD, checklist, UIDesign and DevGuide are one file per phase (phase 1 under the plain names, phase n as `-Pn-…`), `BRD-N` and `REQ-` ids run on across phases and are never reused, a `docs/-Phases.md` table names every phase's screens and id range, `appPhase` in `core-config.yaml` selects the phase every command works on, and the Architecture, Coding Standards, PROJECT-STATUS, UsageGuide, ProductGuide and the mockup folder stay single. | script: `bash tests/doc-check/run.sh` passes the two-phase fixture clean and fails its broken twin on a screen in two phases, an id in two phases, an id outside its phase's range and a phase without its BRD; `tf-split-brd.sh --all-phases` numbers phase 2 after phase 1. | owner 2026-09-05 (Schemas §2, §3.11, §7.2 H to L) | +| FR-53 | produces mockups as one click-through set: every link and form action resolves to a mockup that exists when opened from the mockup folder, navigation is a link or a form action and never a script, every menu item leads to its screen, every stylesheet exists, every screen is reachable by clicking from the entry screen, every screen has a way out, every button navigates or shows a message, and every mockup carries `data-testid` anchors. Existing mockups in a brownfield repository are moved into `docs/mockups/` before anything is linked. | script: `tf-doc-check.sh --app ` FAILs on a broken link, an unreachable or dead-end screen, an inert button or an unanchored mockup; `tf-mockups-locate.sh` leaves no mockup outside `docs/mockups/`. | owner 2026-09-05 (MISS-TechieFlow-20260905-05); D-5 | ### C. Build | ID | The framework … | Check | Source | |---|---|---|---| -| FR-18 | builds UI requirements from the approved mockups and nothing else, and compares the built screen to its mockup before marking it implemented. | fixture run: `*build-phase MyDiary`; the smoke log names the mockup compared for every UI row. | 39 partial-implementation misses; How-It-Works §3.4 | +| FR-18 | builds UI requirements from the approved mockups and nothing else, and compares the built screen to its mockup before marking it implemented. | fixture run: `*build-phase MyDiary`; `tests/.artifacts/verify/screens.json` (written by `tf-verify-screens.sh`, the smoke evidence since Sitting 4c) has an entry for every screen the build touched, and `tests/.artifacts/verify/parity.json` names the mockup compared for every UI row. Reworded 2026-09-07 from MISS-TechieFlow-20260906-07: the old check read a smoke log no task wrote. | 39 partial-implementation misses; How-It-Works §3.4 | | FR-19 | never writes `Verified` from a build, a fix or a status refresh; only an executed verify may. | script (hook exists): a write of `Verified` without a same-day verify ledger is refused. | convention; guard-verify hook | | FR-20 | ends every command by rewriting PROJECT-STATUS in template shape and appending one run record. | script: after any fixture command, PROJECT-STATUS matches the template shape and `runs.jsonl` has one new line. | status gate; D-11 | | FR-21 | records a library gap in that library's feedback file and holds the feature; it never implements a workaround. | fixture run: build a MyDiary requirement needing a TrBlazeUI control that does not exist; the row is `BLOCKED-BY-LIBRARY`, the feedback file has the entry, no workaround code exists. | Stack Defaults Q11.3 | @@ -102,8 +104,8 @@ The same four questions are asked for a miss in an application, against that app | FR-28 | never edits source or spawns builders during `*triage-issues`. | script: no file under `src/` or `tests/` changes during a fixture triage run. | convention; How-It-Works §3.7 | | FR-29 | records a miss automatically from triage (discovery cost) and from fix (fix cost); the owner never types `*log-miss` for a bug that went through either. | fixture run: triage then fix one bug on MyDiary; `misses.jsonl` gains a `miss` and a `miss-fix` with no manual log command. | D-15 | | FR-30 | offers one command that runs the owner's bug sequence end to end in YOLO mode: compare screens to mockups, triage, log discovery cost, fix, log fix cost, metrics, with a summary per step. | fixture run: `*triage-and-fix MyDiary `; the final summary has six sections. | D-16 | -| FR-31 | stores the owner's one-sentence description of every miss in a human-readable file beside the record. | script: every `miss` record has a matching line in `docs/-Misses.md`. | D-10 | -| FR-32 | sorts every miss with the four questions of §3 and records the answer. | script: every `miss` record carries the sort field. | §3 | +| FR-31 | stores the owner's one-sentence description of every miss in a human-readable file beside the record. | script (built 2026-09-07): `tf-emit.sh` rebuilds `docs/-Misses.md` and its HTML from the stream after every write to it; `bash tests/bugs/run.sh` checks that the row count equals the record count and that the sentence, the row and whose gap are in the row. | D-10 | +| FR-32 | sorts every miss with the four questions of §3 and records the answer. | script (built 2026-09-07): `tf-log-miss.sh` refuses a miss without `--sort` and prints the four questions; `tf-triage.sh` defaults it; `tf-emit.sh --amend sort ` sorts an older record once and never twice; `tf-metrics.sh` reports the distribution over the records that carry it. `bash tests/bugs/run.sh`. | §3 | | FR-33 | records issues found by people in UAT as misses, never as reviews. | script: every record from `*triage-issues` is of kind `miss`. | owner 2026-09-04 | ### F. Telemetry @@ -112,18 +114,25 @@ The same four questions are asked for a miss in an application, against that app |---|---|---|---| | FR-34 | emits one run record for every command, including day-1, mockups, DevGuide, ProductGuide and the idea-stage commands. | script: after each fixture command, `runs.jsonl` has a record with that command's name. | D-11; D-13 | | FR-35 | records on every run whether YOLO mode was on. | script: every new run record carries `yolo: true|false`. | D-12 | -| FR-36 | records the outcome of every owner review as a record of kind `review`, named by its phase (`day1-review`, `build-review`, `verify-review`, `handoff-review`), carrying the number of corrections given, the cost of producing the reviewed output, and the cost of applying the corrections. | fixture run: day-1 stage 1 on MyDiary followed by an owner correction; the stream gains a `day1-review` record with those three fields. | D-17; owner 2026-09-04 | +| FR-36 | records the outcome of every owner review as a record of kind `review` on the misses stream, named by its phase (`day1-review`, `build-review`, `verify-review`, `handoff-review`), carrying the number of corrections given, and the cost of producing the reviewed output and of applying the corrections, both copied by the emitter from the two runs the record names. | script: the emitter refuses a review without a phase from the list or without a corrections count, and copies the costs from the runs named (built 2026-09-06); fixture run: MyDiary stage 2 after the owner's review of stage 1 leaves a `day1-review` record. | D-17; owner 2026-09-04; built Sitting 4b | | FR-37 | records framework maintenance work under the command value `framework-reset`. | script: the schema lists the value; the report accepts it. | D-19 | | FR-38 | never merges provenance in a report: live with backfilled, or one project type with another. | script (exists in `tf-metrics.sh`): the report prints separate figures. | schema §0 | | FR-39 | never blocks, fails or changes a verdict because a telemetry write failed. | review: `tf-emit.sh` exits 0 on every path. | schema | +| FR-55 | records a run's `ended` as the moment the record is written, never a value the agent guessed: an `ended` in the future or before `started` is replaced with now and the duration recomputed; `started` comes from the command marker `tf-phase.sh start` writes at step 0 of every task when the record leaves it out. | script: emit a run record with `ended` an hour ahead on a fixture; the appended record carries now; a record without `started` carries the marker's time. | MISS-TechieFlow-20260905-11 | +| FR-56 | writes to an application's database only from `*build-phase` and `*fix-issues`, and only through the migration path the Stack decisions name; a direct SQL write through a client is refused from every command, and a migration runner is refused unless the command marker says build-phase or fix-issues. | script (hook `guard-db.sh`, both harnesses): an SQL update is refused with and without a marker; a migration runner is refused under a day-1 marker and allowed under a build marker; a select and a build pass. | MISS-TechieFlow-20260905-09; owner 2026-09-05 | +| FR-57 | checks the Architecture's Stack decisions table for a row per question 1 to 8 and 11, and refuses an app whose Solution structure names `.App` or has no project named exactly the app. | script: the broken fixture fails on a missing Q3 row and on `MyDiary.App`. | MISS-TechieFlow-20260905-16; owner 2026-09-06 | ### G. Harnesses | ID | The framework … | Check | Source | |---|---|---|---| -| FR-40 | works identically in Claude Code and OpenCode; every task is registered in both, and every hook has an OpenCode equivalent or a documented gap. | script: every task under `.tfcore/tasks/` is byte-identical in the Claude mirror and referenced in `opencode.jsonc`; the hook parity table has no undocumented row. | owner 2026-09-04 | +| FR-40 | works identically in Claude Code and OpenCode; every task is registered in both, and every hook has an OpenCode equivalent or a documented gap. | script (built 2026-09-07 from MISS-TechieFlow-20260905-02): `bash tests/mirror/run.sh` fails when a persona or task differs from the Claude Code mirror, when the mirror holds a removed file, when a command task is not referenced from `opencode.jsonc`, or when a reference there does not resolve; it also holds FR-43 and FR-44 to their budgets. The hook parity table has no undocumented row (review). | owner 2026-09-04 | | FR-41 | honours YOLO mode in every command; `*build-phase` and `*verify` default to it. | fixture run: each command with the flag on completes without a prompt; build and verify complete without the flag. | D-18 | | FR-42 | carries no Codex-specific code path once the Codex adapter is removed. | script: no `codex` reference outside the changelog. | D-14 | +| FR-58 | runs one command to completion in YOLO and stops at the next owner review; it never starts the following phase on its own. | fixture run: `*day1-greenfield MyDiary` in YOLO ends after stage 1 with no checklist; `*build-phase` in YOLO ends after its verify with no handoff. Script candidate: `tf-yolo.sh done` refuses `complete` when the phase marker names a command other than the goal's. | MISS-TechieFlow-20260905-04 (sorted `unsaid`, Session 5); D-18 | +| FR-59 | starts every unattended run through the goal supervisor `tf-goal.sh`, never through a bare harness command, so a usage limit, a crash or an early stop is survived. | review, to become a script: every run record with `yolo: true` written outside an interactive session has a `goal.json` under `.tfcore/.session/` whose start precedes it. | MISS-TechieFlow-20260905-08 (sorted `unsaid`, Session 5); How-It-Works §3.10 | +| FR-60 | reads the project's Stack answer set before any command proposes a project, folder or head name, the idea-stage commands included, so a banned name such as `.App` never enters a brief. | script candidate: the document checker refuses a project brief that names `.App` when the .NET answer set is chosen. Until then, review. Not built: the idea-stage commands also emit no run record yet (FR-34 unmet for them). | MISS-TechieFlow-20260905-17 (sorted `unsaid`, Session 5) | +| FR-61 | when a verify's acceptance line needs test data the application does not hold, creates it through the application as a UsageGuide test user, passes a password gate through its own screen, and otherwise grades the row `not observable, environment`; it never grades it FAIL or logs a regression for missing data. | fixture run: `*verify all TechieBlog` on an empty database ends with rows `not observable`, not 86 FAIL and 87 regression misses. Script candidate: `tf-verify-verdict.sh` maps an acceptance failure whose test output is a sign-in redirect or an empty list to `not observable, environment`. | MISS-TechieFlow-20260906-26 (sorted `unsaid`, Session 5) | ### H. Instruction budget @@ -133,7 +142,8 @@ The same four questions are asked for a miss in an application, against that app | FR-44 | keeps the shared rule files under 3,000 words in total and every persona under 1,500. | script: word counts. | How-It-Works §5 | | FR-45 | keeps explanation and history out of task files; a task file contains steps only. | review, to become a script: no paragraph in a task file begins with "Why", "Because", "This exists", or a date. | How-It-Works §7 | | FR-46 | converts a prose rule to a hook or deletes it after the second recorded `instruction-ignored` miss against it. | script: the miss report lists prose rules with two or more `instruction-ignored` misses; the list is empty. | §3 question 4 | -| FR-47 | names only public repositories in every document under the framework's `docs/`, README and templates. | script: grep for the private project names; zero hits. | owner 2026-09-04 | +| FR-47 | names only public repositories in the documents a person outside the owner's machines reads: the README, the briefing, and every template that ships into a project. The reset's own working documents name fixtures on purpose and are out of scope until the owner rules otherwise. | script (built 2026-09-07): `bash tests/mirror/run.sh` reads the owner's private names from `~/.techieflow/private-names.txt`, a per-machine file that is in no repository, and fails when one appears in the README, `WorkFlow-Context.md` or a template. Without that file the check says it was skipped. Reworded from MISS-TechieFlow-20260907-05: the old check named a script that was never written, and a private project sat in the public README for months. | owner 2026-09-04 | +| FR-62 | keeps the two files a person reads first short and true: the briefing at most 3,000 words, the README at most 4,000, neither naming a command the framework has removed, with the maintenance history in `docs/CHANGELOG.md` and the machine-specific detail in its own document under `docs/`. | script (built 2026-09-07): `bash tests/mirror/run.sh` counts both files and greps them for the seven removed command names; a planted 6,167-word briefing and a planted `*author-brd` both fail it. | MISS-TechieFlow-20260907-07 (sorted `unsaid`); Reset Plan Session 6 | ### I. Distribution diff --git a/docs/TechieFlow-Reset-Plan-2026-09-04.html b/docs/TechieFlow-Reset-Plan-2026-09-04.html index 980ca0f..c42a576 100644 --- a/docs/TechieFlow-Reset-Plan-2026-09-04.html +++ b/docs/TechieFlow-Reset-Plan-2026-09-04.html @@ -106,7 +106,7 @@

TechieFlow — Reset Plan (started 2026-09-04)

-
Rendered 2026-09-04 · source TechieFlow-Reset-Plan-2026-09-04.md
+
Rendered 2026-09-07 · source TechieFlow-Reset-Plan-2026-09-04.md
Contents
@@ -241,6 +241,8 @@

Session 4 — Shrink ev
  • Output: the document-phase tasks shrunk and proven.
  • +
  • Done 2026-09-05: the three shared rule files and day1-greenfield, day1-brownfield, mockups, split-brd and amend-docs shrunk from 23,172 words to 4,237; six scripts, two hooks, five checker rules and FR-53 added; day1-brownfield and amend-docs ran clean on Xpenser in both harnesses; author-brd skipped, it is removed in 4c; misses 01 to 09 logged. +
  • Sitting 4b — the build phase and the guides.

      @@ -252,6 +254,8 @@

      Session 4 — Shrink ev
    • Output: the build and handoff tasks shrunk and proven.
    • +
    • Done 2026-09-06: build-phase, devguide, refresh-status and the flow-master persona shrunk from 15,649 words to 2,373 (productguide and handoff-phase left for later); the Large layout, the Phases document and the Deployment Checklist with *deploy-checklist built; tf-phase.sh, tf-build.sh, tf-build-list.sh, tf-devguide-list.sh, tf-status-evidence.sh and the goal supervisor with its 26-check self-test added; three hooks added: guard-db.sh, guard-build.sh, and the Stop hook now checks the checklist; deploy-checklist ran in both harnesses on TfLens and its copy; build, devguide and refresh-status ran for real on MyDiary in Claude Code (the build's screens turned out blank at runtime, which the DevGuide run caught); the same three commands on the MyDiary copy through OpenCode with an OpenAI model were started at the close of the sitting, their result is in the run record; FR-54 to FR-57 added; misses 10 to 24 of 2026-09-05 and 01 to 12 of 2026-09-06 logged. +

    Sitting 4c — verification and bug handling, last.

      @@ -265,6 +269,8 @@

      Session 4 — Shrink ev
    • Output: all tasks shrunk. Total task words near 20,000. Both harnesses tested end to end.
    • +
    • Done 2026-09-07: verify-phase, triage-issues, fix-issues and log-miss shrunk from 17,552 words to 2,230, plus the new *triage-and-fix (283) and the verifier persona (388); all task files now total 20,282 words; twelve scripts added (the verify chain from work list to telemetry, the Windows head over the WebView2 DevTools port, triage, log-miss and fix-close) with two self-tests, 103 checks; the seven never-used commands removed from both harnesses; the verify hook checks every row against the ledger, the build guard covers browser tests, the checker takes a baseline so old findings warn instead of block; real runs in both harnesses: verify and triage on TechieBlog, fix-issues on MyDiary (the blank screens' root cause found and fixed); misses 17 to 27 of 2026-09-06 and 01 to 02 of 2026-09-07 logged. +

    Session 5 — The miss protocol and the telemetry explainer#

    Goal: misses become readable by a human, and the owner can present every telemetry number.

    @@ -280,6 +286,7 @@

    Session 5 — T

    Decided 2026-09-04 (FR-36): an owner review is recorded as a new record kind review, named by phase (day1-review, build-review, …), with corrections given, cost to produce, cost to correct. UAT issues stay misses.

    We do, part two: Claude writes docs/TechieFlow-Telemetry-Explained.md: each of the five report numbers, what it means, how it is calculated, one real figure from the combined data and one from a named project, and the one sentence the owner says about it in a talk. The owner reads it and rewrites any sentence they would not say.

    Output: a miss log a human can read, and a telemetry page the owner can present from.

    +

    Done 2026-09-07: every miss carries whose gap it was (sort: spec, unsaid, weak-check, ignored), required by tf-log-miss.sh, defaulted by triage, amendable once, reported; docs/<App>-Misses.md and its HTML rebuilt by the emitter after every miss record (FR-31, FR-32, D-10 closed); tests/mirror/run.sh added; the rollup keyed by project and id (the combined first-pass rate is 48%, not the 72% it printed); TechieFlow-Telemetry-Explained.md written with the five numbers and the owner's stage sentences; Session 4's 53 misses sorted (26 weak-check, 15 unsaid, 14 ignored) and 42 closed; FR-58 to FR-61 added; TechieBlog's 87 empty-database regressions closed as will-not-fix; a real *log-miss ran through OpenCode on the MyDiary copy; misses 03 and 04 of 2026-09-07 logged.

    Session 6 — Make the repository readable again, and deploy#

    Goal: the "read this first" file is short, the six-month log is archived, and every repo has the new framework.

    We do: split WorkFlow-Context.md into a briefing of at most 3,000 words (what it is, how it is used, conventions, repo map, open items, maintenance contract) and docs/CHANGELOG.md holding the full maintenance log untouched. Trim README to what a new user needs, moving the rest to docs/. Run update-framework.sh against the projects the owner will build next (TechieRag, AstroLyfe, TrStudio), against one library repo, then the rest.

    diff --git a/docs/TechieFlow-Reset-Plan-2026-09-04.md b/docs/TechieFlow-Reset-Plan-2026-09-04.md index 9839677..2c55898 100644 --- a/docs/TechieFlow-Reset-Plan-2026-09-04.md +++ b/docs/TechieFlow-Reset-Plan-2026-09-04.md @@ -110,12 +110,14 @@ The ten questions: - Then `day1-greenfield` (3,400 words), `day1-brownfield` (7,300), `mockups`, `split-brd`, `amend-docs`, `author-brd`. - **Test on:** a project in design or redevelopment, TechieRag or TrStudio, the owner picks. Day-1 or amend-docs runs for real in both harnesses, and the Session 3 checker passes on what it produces. - **Output:** the document-phase tasks shrunk and proven. +- **Done 2026-09-05:** the three shared rule files and day1-greenfield, day1-brownfield, mockups, split-brd and amend-docs shrunk from 23,172 words to 4,237; six scripts, two hooks, five checker rules and FR-53 added; day1-brownfield and amend-docs ran clean on Xpenser in both harnesses; author-brd skipped, it is removed in 4c; misses 01 to 09 logged. **Sitting 4b — the build phase and the guides.** - `build-phase` (4,400 words), `devguide` (5,300), `productguide`, `handoff-phase`, `refresh-status`. - **Added 2026-09-04:** the Deployment Checklist template (schema in `TechieFlow-Document-Schemas.md` §3.10) and a small new command, `*deploy-checklist {App} {pipeline-document}`, that fills it from the owner's pipeline guidance and the Stack Q9 and Q10 answers after UAT. It sits beside handoff because handoff runs before UAT and deployment after. - **Test on:** the same design-stage project once it has a checklist, or a small project the owner chooses. A real build runs in both harnesses. - **Output:** the build and handoff tasks shrunk and proven. +- **Done 2026-09-06:** build-phase, devguide, refresh-status and the flow-master persona shrunk from 15,649 words to 2,373 (productguide and handoff-phase left for later); the Large layout, the Phases document and the Deployment Checklist with `*deploy-checklist` built; `tf-phase.sh`, `tf-build.sh`, `tf-build-list.sh`, `tf-devguide-list.sh`, `tf-status-evidence.sh` and the goal supervisor with its 26-check self-test added; three hooks added: `guard-db.sh`, `guard-build.sh`, and the Stop hook now checks the checklist; deploy-checklist ran in both harnesses on TfLens and its copy; build, devguide and refresh-status ran for real on MyDiary in Claude Code (the build's screens turned out blank at runtime, which the DevGuide run caught); the same three commands on the MyDiary copy through OpenCode with an OpenAI model were started at the close of the sitting, their result is in the run record; FR-54 to FR-57 added; misses 10 to 24 of 2026-09-05 and 01 to 12 of 2026-09-06 logged. **Sitting 4c — verification and bug handling, last.** - `verify-phase` (11,850 words, the largest), `fix-issues`, `triage-issues`, `log-miss`. @@ -123,6 +125,7 @@ The ten questions: - The seven commands the owner decided to remove on 2026-09-04 are removed here, with their registrations, from both harnesses: create-brd (author-brd), elicit (advanced-elicitation), document-project, index-docs, shard-doc, execute-checklist, kb-mode-interaction. `create-doc` stays because brainstorm's brief, competitor analysis and market research depend on it. - YOLO handling is made uniform across every task (D-18): every command honours the flag; build-phase and verify default to it. - **Output:** all tasks shrunk. Total task words near 20,000. Both harnesses tested end to end. +- **Done 2026-09-07:** verify-phase, triage-issues, fix-issues and log-miss shrunk from 17,552 words to 2,230, plus the new `*triage-and-fix` (283) and the verifier persona (388); all task files now total 20,282 words; twelve scripts added (the verify chain from work list to telemetry, the Windows head over the WebView2 DevTools port, triage, log-miss and fix-close) with two self-tests, 103 checks; the seven never-used commands removed from both harnesses; the verify hook checks every row against the ledger, the build guard covers browser tests, the checker takes a baseline so old findings warn instead of block; real runs in both harnesses: verify and triage on TechieBlog, fix-issues on MyDiary (the blank screens' root cause found and fixed); misses 17 to 27 of 2026-09-06 and 01 to 02 of 2026-09-07 logged. ### Session 5 — The miss protocol and the telemetry explainer @@ -140,6 +143,8 @@ The ten questions: **Output:** a miss log a human can read, and a telemetry page the owner can present from. +**Done 2026-09-07:** every miss carries whose gap it was (`sort`: spec, unsaid, weak-check, ignored), required by `tf-log-miss.sh`, defaulted by triage, amendable once, reported; `docs/-Misses.md` and its HTML rebuilt by the emitter after every miss record (FR-31, FR-32, D-10 closed); `tests/mirror/run.sh` added; the rollup keyed by project and id (the combined first-pass rate is 48%, not the 72% it printed); `TechieFlow-Telemetry-Explained.md` written with the five numbers and the owner's stage sentences; Session 4's 53 misses sorted (26 weak-check, 15 unsaid, 14 ignored) and 42 closed; FR-58 to FR-61 added; TechieBlog's 87 empty-database regressions closed as will-not-fix; a real `*log-miss` ran through OpenCode on the MyDiary copy; misses 03 and 04 of 2026-09-07 logged. + ### Session 6 — Make the repository readable again, and deploy **Goal:** the "read this first" file is short, the six-month log is archived, and every repo has the new framework. @@ -148,6 +153,8 @@ The ten questions: **Output:** a framework a newcomer, or the owner in six months, can pick up in twenty minutes, deployed everywhere. +**Done 2026-09-07:** `WorkFlow-Context.md` cut from 48,593 words to 1,961 and `README.md` from 16,963 to 1,864, with the six-month log moved verbatim to `docs/CHANGELOG.md` and the machine setup, permissions and gotchas into their own documents (`TechieFlow-Setup.md`, `TechieFlow-Permissions-And-YOLO.md`, `TechieFlow-FAQ.md`); `update-framework.sh` run over all 23 projects carrying `.tfcore/` in three passes, all exit 0, verified by content, every project's readable miss file written, no dead `opencode.jsonc` reference left and the dead `author-brd` routing entry removed from twenty; five framework defects logged (misses 05 to 10 of 2026-09-07), four fixed and proven and the stale `WORKFLOW.html` left for the owner's decision; five checks added, each proven by planting its defect; FR-62 added and FR-47's check rewritten; self-tests mirror 12, doc-check 12, bugs 51, verify 67, goal 29. + ### Session 7 — Write the Playbook review prompt, version 2 **Goal:** carry what these sessions taught into the Playbook review. diff --git a/docs/TechieFlow-Routing-Guide.html b/docs/TechieFlow-Routing-Guide.html index be91534..73b754d 100644 --- a/docs/TechieFlow-Routing-Guide.html +++ b/docs/TechieFlow-Routing-Guide.html @@ -3,8 +3,10 @@ -TechieFlow Model Routing Guide — TechieFlow +TechieFlow — Model Routing Guide - +@media(max-width:900px){.layout{grid-template-columns:1fr}nav.side{position:static;height:auto}} + - +
    -
    -

    TechieFlow Model Routing Guide

    -
    Run cheap phases on cheap models — the owner's guide to per-phase model tiers
    - -

    Audience: the framework owner. TL;DR: run cheap phases on cheap models, expensive thinking on expensive models — one script controls everything. Design doc: docs/Adapter-Design.md §5 (the reasoning) · quick summary: README §17b / WORKFLOW.html §17b.

    -

    1. The problem routing solves#

    -

    Every TechieFlow phase used to run on whatever model your session happened to be using. Day-1 architecture — where a wrong decision costs days of rework — and re-rendering markdown to HTML — pure mechanics — cost exactly the same per token. Over a project's life the mechanical phases (renders, status refreshes, reports) plus the bulk token spend of builder subagents dominate the bill, while the genuinely hard thinking is a handful of runs.

    -

    Routing assigns every phase and every subagent a tier, and maps each tier to a real model per harness. It is:

    -
    • OFF by default — a freshly scaffolded or updated app changes nothing until you turn it on.
    • Per-app — enable it on one project, leave the others alone.
    • Reversibleoff removes every generated file and your map survives for the next on.
    • Observed, never enforced — nothing blocks a phase from running on the "wrong" model; the telemetry records what actually ran so drift is visible, not hidden.
    -

    A real measured datapoint (TechieBlog pilot, 2026-08-20): one complete metrics-report phase on the economy tier — 14,000 output tokens, full report written — cost $0.036. The same phase on the frontier model is roughly 10× that for identical output.

    -

    2. The three tiers#

    - - - - - -
    TierMental modelClaude Code modelOpenCode model (shipped defaults)
    frontierThe expensive thinking. Mistakes here are the costliest to discover late.opusopencode-go/kimi-k3
    standardThe everyday building. Needs real competence, not brilliance.sonnetopencode-go/kimi-k2.7-code
    economyThe mechanical work. Format, assemble, scan, report.haikuopencode-go/deepseek-v4-flash
    -

    The models are starting values, yours to change (§6). Claude side accepts the aliases opus / sonnet / haiku or a full model id; OpenCode side takes provider/model ids — list everything your account offers with opencode models.

    -

    3. The complete map — what runs on what#

    -

    3.1 Phases (the commands you type)#

    + +
    +

    TechieFlow — Model Routing Guide

    +
    Rendered 2026-09-06 · source TechieFlow-Routing-Guide.md
    + + - - - - - - - - - - - - - - - - - - + + + + + + +
    PhaseTierWhy this tier
    day1-greenfieldfrontierArchitecture + BRD from nothing; errors here poison everything downstream.
    day1-brownfieldfrontierSame, plus whole-codebase comprehension of an existing app.
    author-brdfrontierRequirements authoring — bad acceptance criteria fail every later gate.
    amend-docsfrontierSurgical edits to the day-1 docs; must understand the whole picture.
    fix-issuesfrontierThe diagnosis half (root-causing from screenshots) is frontier work; the fixes themselves fan out to standard builders.
    mockupsstandardBounded design from a known component catalog. Promote to frontier if greenfield first-pass mockups disappoint — it's the easiest override.
    split-brdstandardBRD → checklist rows with acceptance authoring; not mechanical enough for economy.
    build-phasestandardThe orchestrator: clustering, fan-out, FIX-mode detection. The expensive judgement already lives in the BRD/architecture.
    verify-phasestandardTest generation and the visual-truth eyeball need a capable vision model; the gates themselves are deterministic scripts.
    triage-issuesstandardReproduce, classify, log — no code changes.
    devguidestandardLarge code-tracing; cheap models lose the thread across page → service → query.
    productguideeconomyScreenshot-illustrated how-to assembly from existing material.
    handoff-phaseeconomyWrap-up docs and re-renders.
    refresh-statuseconomyEvidence gathering (build + mtimes + tables). Promote to standard if recovery notes look wrong — the reconcile judgement is occasionally subtle.
    render-workflow-docseconomyMarkdown → HTML.
    generate-htmleconomyMarkdown → HTML.
    metrics-reporteconomyRuns tf-metrics.sh and formats the output.
    PurposeWhich model runs which command, where that is written down, and how to change it.
    AudienceThe framework owner.
    StatusRewritten in plain words 2026-09-06 (Sitting 4b), when the owner found the old guide confusing. Default OpenCode models changed to the OpenAI ones the same day.
    Companion.tfcore/routing.yaml (the file itself, with the same how-to in its header), TechieFlow-How-It-Works.md §2 "Model routing".
    -

    3.2 Personas and subagents (the agents that do the work)#

    -

    Personas get the tier of their primary phase; subagents get their own row in the map. This matters because typing *verify all MyApp inside the flow-verifier persona is routed through the persona's model, and every builder the build orchestrator spawns runs on the subagent's model.

    +
    +

    1. What routing is, in one paragraph#

    +

    Every command and every sub-agent is given a tier: frontier for the expensive thinking, standard for everyday building, economy for mechanical work. Each tier is then mapped to one real model per harness. Day-1 documents run on the frontier tier; a build runs on standard; rendering HTML runs on economy. Routing only sets the model a command starts on. It never switches a model mid-run, and it never blocks anything: the run record says which model actually ran, so a drift is visible, not hidden.

    +

    2. The current defaults#

    - - - - - - - - - - - + + + + + +
    AgentKindTierClaude modelOpenCode modelWhen it runs
    flow-masterpersonastandard (= build-phase)sonnetopencode-go/kimi-k2.7-codeThe super-agent: build, fix, triage, utilities typed as *commands
    flow-analystpersonafrontier (= day-1)opusopencode-go/kimi-k3Day-1 docs, mockups, split-brd, amendments
    flow-architectpersonafrontier (= day-1)opusopencode-go/kimi-k3Optional deep architecture dives
    flow-verifierpersonastandard (= verify-phase)sonnetopencode-go/kimi-k2.7-code*verify ui/functional/all
    tf-buildersubagentstandardsonnetopencode-go/kimi-k2.7-codeOne per FN/NFR cluster in build-phase §3 and fix-issues §4
    trblazeuisubagentstandardsonnetopencode-go/kimi-k2.7-codeUI clusters (REQ-UI-*) — wraps the NuGet-deployed persona
    techieragsubagentstandardsonnetopencode-go/kimi-k2.7-codeRAG clusters (REQ-RAG-*) — wraps the NuGet-deployed persona
    tf-test-writersubagentstandardsonnetopencode-go/kimi-k2.7-codeVerify-phase §4 test generation, one per cluster
    tf-explorersubagenteconomyhaikuopencode-go/deepseek-v4-flashRead-only scans (devguide OBSERVE, index-docs)
    build (OpenCode) / your session (Claude)default chatnever routedyour /model choiceyour TUI selectionYour normal conversation — routing deliberately leaves it alone
    TierClaude CodeOpenCodeUsed for
    frontiersonnetopenai/gpt-5.6-terraday1-greenfield, day1-brownfield, amend-docs, fix-issues
    standardsonnetopenai/gpt-5.6-terrabuild-phase, verify-phase, mockups, split-brd, triage-issues, devguide, and the builder sub-agents
    economyhaikuopenai/gpt-5.6-lunarefresh-status, handoff-phase, productguide, metrics-report, HTML rendering, the explorer sub-agent
    -

    Why builders are standard, not economy — the single most-challenged row. A cheap builder that ships a page with a blank data table doesn't save money: it costs a full verify → fix-issues → re-verify cycle, which dwarfs the per-token saving. This is a hypothesis with a measurement attached: the rework ratio in runs.jsonl (§8) confirms or overturns it with your own data.

    -

    4. Turning it on and off#

    -

    Everything is one script, run from the app repo. It edits .tfcore/routing.yaml for you and regenerates all harness bindings — you never touch a generated file.

    -
    cd /mnt/c/1MyCode/TechieBlog
    -bash .tfcore/utils/tf-routing.sh status     # read-only: what routing is/would be doing
    -bash .tfcore/utils/tf-routing.sh on         # enable  → generates ~23 binding files
    -bash .tfcore/utils/tf-routing.sh off        # disable → removes exactly those files
    -

    status prints the live tier/model/phase table for THIS app, whether the bindings on disk agree with the flag, the advisory escalation policy (§6.4), and where routing shows up in the TUI.

    -

    After on, you keep using the same commands you always used:

    +

    Why these: the owner's decisions of 2026-09-05 (Sonnet for every long Claude run, Haiku for the cheap ones) and 2026-09-06 (OpenAI models for OpenCode, after the OpenCode Go monthly limit was reached with MiMo). Codex is frozen; its column in the file is kept but not maintained.

    +

    Your normal chat is never routed. Opening OpenCode or Claude Code looks exactly as before; routing shows only when a command runs or a persona is selected.

    +

    3. Where the models are written down#

    +

    There are two copies of the same file, and knowing which one you are editing is the whole trick.

    - - - - - + + + + +
    HarnessYou typeWhat changed
    OpenCode/techieflow:tasks:verify-phase all MyApp — unchangedThe phase now executes on its tier's model
    OpenCodeTab to flow-verifier, then *verify all MyApp — unchangedThe persona carries its tier's model
    Claude Code/tf:verify-phase all MyApp — new short wrapperRuns the phase on the tier model for that turn
    Claude Code/TechieFlow:agents:verifier *verify all MyApp — the old wayStill works, unrouted (session model)
    FileWhat it isWho changes it
    TechieFlow/.tfcore/routing.yamlThe framework default. Copied into a project when the project is scaffolded.Edit it to change what every future project starts with.
    <project>/.tfcore/routing.yamlThe project's own copy. update-framework.sh never overwrites it.Edit it to change this project.
    -

    5. What you will — and won't — see in the TUI#

    -

    Opening OpenCode looks exactly the same as before. That is deliberate. Your normal chat runs on the default build agent, and routing never binds it — your conversation stays on the model YOU picked. If you enable routing, open the TUI and see your usual model in the status bar: that is correct behavior, not a failure.

    -

    Routing is visible in exactly three places:

    -
    1. Running a phase command. /techieflow:tasks:metrics-report MyApp → the footer shows the run executing on deepseek-v4-flash while it works.
    2. Switching persona. Tab to flow-master / flow-verifier / flow-analyst — each shows and uses its bound model.
    3. The telemetry. Every run lands in docs/metrics/runs.jsonl with declared tier, observed model, routed: true/false, tokens, and (OpenCode) real dollar cost.
    -

    Verified gotcha #1 — the model sticks (OpenCode). After a routed command finishes, that TUI session continues on the phase's model. It does not bounce back to your selection. If you finish an economy phase and keep chatting, you are chatting with the economy model until you pick another from the model list or start a new session.

    -

    Verified gotcha #2 — turn-scoped (Claude Code). The mirror image: a /tf:<phase> wrapper's model lasts exactly one turn; your next plain prompt reverts to the session model automatically.

    -

    6. Changing the map — every case, with examples#

    -

    Each command edits routing.yaml and immediately regenerates the bindings. Verify any change with status.

    -

    6.1 Moving a phase between tiers#

    -
    # Greenfield mockups keep missing the mark → give them the frontier model:
    -bash .tfcore/utils/tf-routing.sh set-tier mockups frontier
    -
    -# Verify feels like overkill on standard for a stable app → try economy:
    -bash .tfcore/utils/tf-routing.sh set-tier verify-phase economy
    -
    -# Recovery notes from refresh-status look sloppy → promote it:
    -bash .tfcore/utils/tf-routing.sh set-tier refresh-status standard
    -
    -# Take a phase out of routing entirely (runs on the session model again):
    -bash .tfcore/utils/tf-routing.sh set-tier devguide inherit
    -

    6.2 Moving a subagent between tiers#

    -
    # Test the "cheap builders" hypothesis yourself (watch the rework ratio!):
    -bash .tfcore/utils/tf-routing.sh set-tier tf-builder economy
    -
    -# UI builders struggle with a complex design system → promote just them:
    -bash .tfcore/utils/tf-routing.sh set-tier trblazeui frontier
    -

    6.3 Changing which model a tier means — per tier, per harness#

    -
    # FRONTIER — try a different top model on OpenCode:
    -bash .tfcore/utils/tf-routing.sh set-model frontier opencode opencode-go/qwen3.8-max
    -# ...and pin Claude's frontier to opus explicitly:
    -bash .tfcore/utils/tf-routing.sh set-model frontier claude opus
    -
    -# STANDARD — swap the workhorse:
    -bash .tfcore/utils/tf-routing.sh set-model standard opencode opencode-go/kimi-k2.7-code
    -bash .tfcore/utils/tf-routing.sh set-model standard claude sonnet
    -
    -# ECONOMY — chase the cheapest model that doesn't degrade output:
    -bash .tfcore/utils/tf-routing.sh set-model economy opencode opencode-go/mimo-v2.5
    +

    The file is short and flat: a tiers: block (tier to model, per harness), a phases: block (command to tier), a subagents: block, and an effort: block. Its header repeats the steps below.

    +

    Nothing happens until the bindings are regenerated. The harnesses do not read routing.yaml; they read files generated from it (.opencode/opencode.json, .claude/commands/tf/*.md, .claude/agents/*.md). Every change ends with the bind step.

    +

    4. How to change a model, the four cases#

    +

    All commands run inside the project folder.

    +

    Case 1, this project, one tier. The script edits the file and regenerates the bindings in one go:

    +
    bash .tfcore/utils/tf-routing.sh set-model standard opencode openai/gpt-5.6-terra
     bash .tfcore/utils/tf-routing.sh set-model economy claude haiku
    -

    Find OpenCode ids with opencode models. On the Claude side you can also repoint what the aliases mean machine-wide with environment variables: ANTHROPIC_DEFAULT_OPUS_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL, ANTHROPIC_DEFAULT_HAIKU_MODEL.

    -

    6.4 Escalation — when the base tier isn't cutting it#

    -

    routing.yaml carries an advisory escalation policy, ported from the AI-First Playbook's model-tiers.yml:

    -
    escalation:
    -  fix-issues:
    -    after_attempts: 2
    -    tier: frontier
    -

    Meaning: if the same REQs have already been through fix-issues twice without reaching Verified, launch the third run on the frontier tier. Three things it is not:

    +

    Model ids: for Claude Code opus, sonnet, haiku or a full id; for OpenCode provider/model exactly as opencode models prints it.

    +

    Case 2, this project, move a command or a sub-agent to another tier:

    +
    bash .tfcore/utils/tf-routing.sh set-tier mockups frontier
    +bash .tfcore/utils/tf-routing.sh set-tier tf-builder economy
    +bash .tfcore/utils/tf-routing.sh set-tier devguide inherit     # take it out of routing
    +

    Case 3, edit the file by hand (either copy), then regenerate:

    +
    bash .tfcore/utils/tf-routing.sh bind
    +

    For the framework default there is nothing to bind; the next scaffold copies the file. To bring an existing project up to a changed default, change its own copy (case 1 or 3), because the updater leaves it alone on purpose.

    +

    Case 4, one unattended run only. Pass the model to the supervisor; the file is untouched:

    +
    bash .tfcore/utils/tf-goal.sh --harness opencode --model openai/gpt-5.6-terra /path/to/App @goal.md
    +

    The main agent uses that model. The sub-agents it spawns still use the project's bindings, so when a provider is down, change the project's tiers too (case 1), or the sub-agents will fail while the main agent works. That is what happened on 2026-09-06.

    +

    5. Turning routing on and off#

    +
    bash .tfcore/utils/tf-routing.sh status   # what this project would do, and whether the bindings match
    +bash .tfcore/utils/tf-routing.sh on       # write the bindings
    +bash .tfcore/utils/tf-routing.sh off      # remove exactly those files; the map stays for next time
    +

    With routing off, every command runs on whatever model the session is on. status is the first thing to run when a model looks wrong.

    +

    6. How to see what ran#

    +

    Every run appends a line to docs/metrics/runs.jsonl with the tier the file declared, the model the harness actually used, and routed: true when they match. On OpenCode the line also carries the real dollar cost. After a few weeks, *metrics shows whether cheaper tiers cost more rework; that data, not opinion, decides the map.

    +

    7. Two things that surprise people#

      -
    • Not runtime. Nothing switches a running phase's model — neither harness can, and the one place it could be faked (OpenCode's undocumented chat.message mutation) has no Claude Code equivalent (DECISIONS.md 2026-08-21).
    • -
    • Not enforced. It is applied by whoever launches the command — you, or a wrapper script — by reading the attempt history before the run starts.
    • -
    • Not a binding. set-escalation edits routing.yaml only; it generates no files, so it needs no bind.
    • +
    • OpenCode keeps the model. After a routed command finishes, that OpenCode session stays on the command's model. Pick your model again, or start a new session, before chatting on. +
    • +
    • Claude Code reverts. A /tf:<phase> wrapper's model lasts one turn; the next prompt is back on the session model. The old long command form (/TechieFlow:agents:verifier *verify …) still works but is not routed. +
    -

    How to apply it at launch:

    -
    # 1. What attempt would the next fix-issues run on these REQs be?
    -bash .tfcore/utils/tf-emit.sh --next-run-attempt fix-issues REQ-UI-009 REQ-FN-011   # → 3
    -# 2. status prints the policy next to the base tier:
    -bash .tfcore/utils/tf-routing.sh status
    -#    Escalation (ADVISORY ...):
    -#      fix-issues   after 2 attempt(s) on the same REQs -> launch the next on frontier (base tier: frontier)
    -# 3. If the answer exceeds after_attempts, launch on the escalation tier:
    -#    Claude Code:  /model opus  then  /tf:fix-issues ...   (or run the old command form on opus)
    -#    OpenCode:     pick the tier model from the model list, then /techieflow:tasks:fix-issues ...
    -

    The attempt history is the checklist's own record: attempt on each runs.jsonl record (§2.5 of SCHEMA.md1 + prior non-backfilled runs of the same cmd touching any of the same REQs), with the per-REQ verdict history in gates.jsonl and the Verification Log in PROJECT-STATUS.md as the human-readable view. Tune the threshold from the data — if third attempts on the base tier usually succeed anyway, raise it; if second attempts mostly fail, lower it:

    -
    bash .tfcore/utils/tf-routing.sh set-escalation fix-issues 3 frontier   # raise the threshold
    -bash .tfcore/utils/tf-routing.sh set-escalation build-phase 2 frontier  # add a policy for another phase
    -

    With the shipped map fix-issues is already frontier, so the default row only bites after you demote it (set-tier fix-issues standard) — which is exactly the experiment it exists to make safe.

    -

    6.5 Editing routing.yaml by hand#

    -

    The file is deliberately human-editable (flat, two-space indent, commented). After any manual edit:

    -
    bash .tfcore/utils/tf-routing.sh bind      # re-apply → regenerates all bindings
    -

    7. Under the hood — what actually gets generated#

    -

    You never need this section to use routing; it is here so nothing is a black box.

    - - - - - - -
    FileHarnessWhat it does
    .opencode/opencode.jsonOpenCodePure-JSON binding file loaded alongside the framework config. Adds model to each persona (deep-merges — prompt/permission preserved), registers tf-builder/tf-test-writer/tf-explorer as subagents with their models, and re-declares each techieflow:tasks:* command with its tier model.
    .claude/commands/tf/<phase>.md × 17ClaudeWrapper commands: frontmatter model: + effort: from the tier; body loads the owner persona then executes the task with your arguments.
    .claude/agents/{tf-builder, tf-test-writer, tf-explorer, trblazeui, techierag}.mdClaudeTier-bound subagent definitions (the library two adopt the NuGet-deployed personas).
    .tfcore/.session/routing-bind.manifestbothThe exact list of generated files. off deletes precisely this list — never anything else.
    -

    All generated files live under gitignored paths — nothing to commit in the app. update-framework.sh re-runs the generator on every refresh, so framework updates and your routing coexist: your routing.yaml is never overwritten, your bindings are always regenerated from it (verified in the TechieBlog pilot).

    -

    8. Reading the results — the tuning loop#

    -

    Every phase run appends a record to docs/metrics/runs.jsonl (full field reference: docs/TechieFlow-Telemetry-Guide.md):

    -
    {"kind":"run","cmd":"verify-phase","app":"TechieBlog","harness":"opencode",
    - "tier":"standard",
    - "tier_model":"opencode-go/kimi-k2.7-code",
    - "model":"opencode-go/kimi-k2.7-code",
    - "routed":true,
    - "tokens_in":784,"tokens_out":42310,"tokens_cache_read":310221,
    - "cost_usd":0.41,"tokens_scope":"tree", "...":"..."}
    -
    • tier / tier_model — what routing declared should run.
    • model — what actually ran (from the harness's own store, never self-reported).
    • routed — do they match. false = drift (someone ran the phase unrouted); visible, never blocked.
    • cost_usd — real dollars on OpenCode; always null on Claude (no cost source exists — the framework never estimates).
    -

    The deciding question after ~2 weeks: does the rework ratio rise on cheaper tiers? Run *metrics (or read runs.jsonl): if standard-tier builders hold the first-pass rate, consider demoting more phases; if mode:"fix" re-entries climb after a demotion, promote back. Data corrects the map — not opinion, and not this guide.

    -

    9. Troubleshooting#

    +

    8. When something is wrong#

    - - - - - - - - - + + + + + + + +
    SymptomCauseFix
    "I turned it on and the TUI looks the same"Expected — the default chat agent is never routed (§5)Run a phase command or Tab to a persona
    status says flag and bindings disagreeAn update or manual edit got out of syncbash .tfcore/utils/tf-routing.sh bind
    A phase ran on the wrong modelInvoked unrouted (old Claude command form, or model picked manually)Check runs.jsonlrouted:false confirms; use /tf:<phase> (Claude) or the /techieflow:tasks:* command (OpenCode)
    fix-issues keeps failing on the same REQsThat's what escalation is for (§6.4) — it is advisory, so nothing happens until you act on ittf-emit.sh --next-run-attempt fix-issues <REQs>; if it exceeds after_attempts, launch the next run on the escalation tier
    Follow-up chat is on the phase's model (OpenCode)Verified behavior — the session keeps the command's modelPick your model from the model list, or start a new session
    "I want my normal chat cheaper too"That's not routing's jobTUI model list, or "model" in ~/.config/opencode/opencode.jsonc
    Config error mentioning .opencode/opencode.jsonHand-edited generated fileNever edit generated files — bind regenerates them
    Want a clean slateoff removes everything generated; your routing.yaml map survives for the next on
    You seeWhyDo
    A command ran on the wrong modelIt was started unrouted, or the bindings are stalestatus; then bind
    Sub-agents fail while the main agent worksSub-agents use the project's bindings, not --modelChange the tiers in the project's copy, then bind
    OpenCode prints its header and nothing elseThe provider refused the model; OpenCode says so only in its own log (~/.local/share/opencode/log/opencode.log)The supervisor stops with exit 5 and the provider's message; pick another model or wait for the reset
    The TUI looks unchanged after onExpected: your chat is never routedRun a command or select a persona
    An error mentions .opencode/opencode.jsonA generated file was edited by handNever edit generated files; bind
    -
    +
    + - +} + diff --git a/docs/TechieFlow-Routing-Guide.md b/docs/TechieFlow-Routing-Guide.md index 5fda7fa..9756957 100644 --- a/docs/TechieFlow-Routing-Guide.md +++ b/docs/TechieFlow-Routing-Guide.md @@ -1,259 +1,105 @@ -# TechieFlow Model Routing Guide +# TechieFlow — Model Routing Guide -> **Codex:** each tier also has a `codex:` model slug and uses the same effort -> map through `model_reasoning_effort`. `tf-codex-bind.py` pins routed custom -> subagents. A skill invoked in an existing main thread inherits that thread's -> model; use a routed subagent or `tf-goal.sh --harness codex` when the phase -> must run on the declared model. +| | | +|---|---| +| Purpose | Which model runs which command, where that is written down, and how to change it. | +| Audience | The framework owner. | +| Status | Rewritten in plain words 2026-09-06 (Sitting 4b), when the owner found the old guide confusing. Default OpenCode models changed to the OpenAI ones the same day. | +| Companion | `.tfcore/routing.yaml` (the file itself, with the same how-to in its header), `TechieFlow-How-It-Works.md` §2 "Model routing". | -**Audience:** the framework owner. **TL;DR:** run cheap phases on cheap models, expensive thinking on expensive models — one script controls everything. **Design doc:** `docs/Adapter-Design.md §5` (the reasoning) · **quick summary:** README §17b / WORKFLOW.html §17b. +--- -## 1. The problem routing solves +## 1. What routing is, in one paragraph -Every TechieFlow phase used to run on whatever model your session happened to be using. Day-1 architecture — where a wrong decision costs days of rework — and re-rendering markdown to HTML — pure mechanics — cost exactly the same per token. Over a project's life the mechanical phases (renders, status refreshes, reports) plus the bulk token spend of builder subagents dominate the bill, while the genuinely hard thinking is a handful of runs. +Every command and every sub-agent is given a **tier**: `frontier` for the expensive thinking, `standard` for everyday building, `economy` for mechanical work. Each tier is then mapped to one real model per harness. Day-1 documents run on the frontier tier; a build runs on standard; rendering HTML runs on economy. Routing only sets the model a command starts on. It never switches a model mid-run, and it never blocks anything: the run record says which model actually ran, so a drift is visible, not hidden. -Routing assigns every phase and every subagent a **tier**, and maps each tier to a real model per harness. It is: +## 2. The current defaults -- **OFF by default** — a freshly scaffolded or updated app changes nothing until you turn it on. -- **Per-app** — enable it on one project, leave the others alone. -- **Reversible** — `off` removes every generated file and your map survives for the next `on`. -- **Observed, never enforced** — nothing blocks a phase from running on the "wrong" model; the telemetry records what actually ran so drift is visible, not hidden. +| Tier | Claude Code | OpenCode | Used for | +|---|---|---|---| +| `frontier` | `sonnet` | `openai/gpt-5.6-terra` | day1-greenfield, day1-brownfield, amend-docs, fix-issues | +| `standard` | `sonnet` | `openai/gpt-5.6-terra` | build-phase, verify-phase, mockups, split-brd, triage-issues, devguide, and the builder sub-agents | +| `economy` | `haiku` | `openai/gpt-5.6-luna` | refresh-status, handoff-phase, productguide, metrics-report, HTML rendering, the explorer sub-agent | -A real measured datapoint (TechieBlog pilot, 2026-08-20): one complete `metrics-report` phase on the economy tier — 14,000 output tokens, full report written — cost **$0.036**. The same phase on the frontier model is roughly 10× that for identical output. +Why these: the owner's decisions of 2026-09-05 (Sonnet for every long Claude run, Haiku for the cheap ones) and 2026-09-06 (OpenAI models for OpenCode, after the OpenCode Go monthly limit was reached with MiMo). Codex is frozen; its column in the file is kept but not maintained. -## 2. The three tiers +Your normal chat is never routed. Opening OpenCode or Claude Code looks exactly as before; routing shows only when a command runs or a persona is selected. -| Tier | Mental model | Claude Code | OpenCode (shipped default) | Codex | -|---|---|---|---|---| -| `frontier` | The expensive thinking. Mistakes here are the costliest to discover late. | `opus` | `opencode-go/kimi-k3` | `gpt-5.6` | -| `standard` | The everyday building. Needs real competence, not brilliance. | `sonnet` | `opencode-go/kimi-k2.7-code` | `gpt-5.6-terra` | -| `economy` | The mechanical work. Format, assemble, scan, report. | `haiku` | `opencode-go/deepseek-v4-flash` | `gpt-5.6-luna` | +## 3. Where the models are written down -The models are **starting values, yours to change** (§6). Claude accepts aliases or a full model id; OpenCode takes `provider/model` ids; Codex takes a model slug available to the installed Codex CLI. +There are two copies of the same file, and knowing which one you are editing is the whole trick. -## 3. The complete map — what runs on what - -### 3.1 Phases (the commands you type) - -| Phase | Tier | Why this tier | +| File | What it is | Who changes it | |---|---|---| -| `day1-greenfield` | frontier | Architecture + BRD from nothing; errors here poison everything downstream. | -| `day1-brownfield` | frontier | Same, plus whole-codebase comprehension of an existing app. | -| `author-brd` | frontier | Requirements authoring — bad acceptance criteria fail every later gate. | -| `amend-docs` | frontier | Surgical edits to the day-1 docs; must understand the whole picture. | -| `fix-issues` | frontier | The *diagnosis* half (root-causing from screenshots) is frontier work; the fixes themselves fan out to standard builders. | -| `mockups` | standard | Bounded design from a known component catalog. Promote to frontier if greenfield first-pass mockups disappoint — it's the easiest override. | -| `split-brd` | standard | BRD → checklist rows with acceptance authoring; not mechanical enough for economy. | -| `build-phase` | standard | The orchestrator: clustering, fan-out, FIX-mode detection. The expensive judgement already lives in the BRD/architecture. | -| `verify-phase` | standard | Test *generation* and the visual-truth eyeball need a capable vision model; the gates themselves are deterministic scripts. | -| `triage-issues` | standard | Reproduce, classify, log — no code changes. | -| `devguide` | standard | Large code-tracing; cheap models lose the thread across page → service → query. | -| `productguide` | economy | Screenshot-illustrated how-to assembly from existing material. | -| `handoff-phase` | economy | Wrap-up docs and re-renders. | -| `refresh-status` | economy | Evidence gathering (build + mtimes + tables). Promote to standard if recovery notes look wrong — the reconcile judgement is occasionally subtle. | -| `render-workflow-docs` | economy | Markdown → HTML. | -| `generate-html` | economy | Markdown → HTML. | -| `metrics-report` | economy | Runs `tf-metrics.sh` and formats the output. | - -### 3.2 Personas and subagents (the agents that do the work) - -Personas get the tier of their *primary phase*; subagents get their own row in the map. This matters because typing `*verify all MyApp` inside the flow-verifier persona is routed through the **persona's** model, and every builder the build orchestrator spawns runs on the **subagent's** model. - -| Agent | Kind | Tier | Claude model | OpenCode model | When it runs | -|---|---|---|---|---|---| -| `flow-master` | persona | standard (= build-phase) | sonnet | opencode-go/kimi-k2.7-code | The super-agent: build, fix, triage, utilities typed as `*commands` | -| `flow-analyst` | persona | frontier (= day-1) | opus | opencode-go/kimi-k3 | Day-1 docs, mockups, split-brd, amendments | -| `flow-architect` | persona | frontier (= day-1) | opus | opencode-go/kimi-k3 | Optional deep architecture dives | -| `flow-verifier` | persona | standard (= verify-phase) | sonnet | opencode-go/kimi-k2.7-code | `*verify ui/functional/all` | -| `tf-builder` | subagent | standard | sonnet | opencode-go/kimi-k2.7-code | One per FN/NFR cluster in build-phase §3 and fix-issues §4 | -| `trblazeui` | subagent | standard | sonnet | opencode-go/kimi-k2.7-code | UI clusters (REQ-UI-*) — wraps the NuGet-deployed persona | -| `techierag` | subagent | standard | sonnet | opencode-go/kimi-k2.7-code | RAG clusters (REQ-RAG-*) — wraps the NuGet-deployed persona | -| `tf-test-writer` | subagent | standard | sonnet | opencode-go/kimi-k2.7-code | Verify-phase §4 test generation, one per cluster | -| `tf-explorer` | subagent | economy | haiku | opencode-go/deepseek-v4-flash | Read-only scans (devguide OBSERVE, index-docs) | -| `build` (OpenCode) / your session (Claude) | default chat | **never routed** | your `/model` choice | your TUI selection | Your normal conversation — routing deliberately leaves it alone | - -> **Why builders are standard, not economy** — the single most-challenged row. A cheap builder that ships a page with a blank data table doesn't save money: it costs a full verify → fix-issues → re-verify cycle, which dwarfs the per-token saving. This is a *hypothesis with a measurement attached*: the rework ratio in `runs.jsonl` (§8) confirms or overturns it with your own data. - -## 4. Turning it on and off - -Everything is one script, run from the app repo. It edits `.tfcore/routing.yaml` for you and regenerates all harness bindings — you never touch a generated file. - -```bash -cd /mnt/c/1MyCode/TechieBlog -bash .tfcore/utils/tf-routing.sh status # read-only: what routing is/would be doing -bash .tfcore/utils/tf-routing.sh on # enable → generates ~23 binding files -bash .tfcore/utils/tf-routing.sh off # disable → removes exactly those files -``` - -`status` prints the live tier/model/phase table for THIS app, whether the bindings on disk agree with the flag, the advisory escalation policy (§6.4), and where routing shows up in the TUI. - -After `on`, **you keep using the same commands you always used**: - -| Harness | You type | What changed | -|---|---|---| -| OpenCode | `/techieflow:tasks:verify-phase all MyApp` — unchanged | The phase now executes on its tier's model | -| OpenCode | Tab to `flow-verifier`, then `*verify all MyApp` — unchanged | The persona carries its tier's model | -| Claude Code | `/tf:verify-phase all MyApp` — new short wrapper | Runs the phase on the tier model for that turn | -| Claude Code | `/TechieFlow:agents:verifier *verify all MyApp` — the old way | Still works, **unrouted** (session model) | -| Codex | `$techieflow-verify all MyApp` | Runs the project skill; the main conversation inherits its active model | -| Codex | `bash .tfcore/utils/tf-goal.sh --harness codex . "verify all requirements"` | Runs/resumes headless Codex with routed custom-agent bindings | - -Codex routing generates `.codex/agents/*.toml` and `.agents/skills/techieflow-*/`. Custom agents carry the mapped model; a skill invoked directly in the main Codex conversation cannot switch that turn's model and therefore inherits the active selection. Use a routed agent or the Codex goal supervisor when exact tier pinning matters. - -## 5. What you will — and won't — see in the TUI +| `TechieFlow/.tfcore/routing.yaml` | The **framework default**. Copied into a project when the project is scaffolded. | Edit it to change what every *future* project starts with. | +| `/.tfcore/routing.yaml` | The **project's own copy**. `update-framework.sh` never overwrites it. | Edit it to change *this* project. | -**Opening OpenCode looks exactly the same as before. That is deliberate.** Your normal chat runs on the default `build` agent, and routing never binds it — your conversation stays on the model YOU picked. If you enable routing, open the TUI and see your usual model in the status bar: that is correct behavior, not a failure. +The file is short and flat: a `tiers:` block (tier to model, per harness), a `phases:` block (command to tier), a `subagents:` block, and an `effort:` block. Its header repeats the steps below. -Routing is visible in exactly three places: +**Nothing happens until the bindings are regenerated.** The harnesses do not read `routing.yaml`; they read files generated from it (`.opencode/opencode.json`, `.claude/commands/tf/*.md`, `.claude/agents/*.md`). Every change ends with the bind step. -1. **Running a phase command.** `/techieflow:tasks:metrics-report MyApp` → the footer shows the run executing on `deepseek-v4-flash` while it works. -2. **Switching persona.** Tab to `flow-master` / `flow-verifier` / `flow-analyst` — each shows and uses its bound model. -3. **The telemetry.** Every run lands in `docs/metrics/runs.jsonl` with declared tier, observed model, `routed: true/false`, tokens, and (OpenCode) real dollar cost. +## 4. How to change a model, the four cases -**Verified gotcha #1 — the model sticks (OpenCode).** After a routed command finishes, that TUI session **continues on the phase's model**. It does not bounce back to your selection. If you finish an economy phase and keep chatting, you are chatting with the economy model until you pick another from the model list or start a new session. +All commands run inside the project folder. -**Verified gotcha #2 — turn-scoped (Claude Code).** The mirror image: a `/tf:` wrapper's model lasts exactly one turn; your next plain prompt reverts to the session model automatically. - -## 6. Changing the map — every case, with examples - -Each command edits `routing.yaml` and immediately regenerates the bindings. Verify any change with `status`. - -### 6.1 Moving a phase between tiers +**Case 1, this project, one tier.** The script edits the file and regenerates the bindings in one go: ```bash -# Greenfield mockups keep missing the mark → give them the frontier model: -bash .tfcore/utils/tf-routing.sh set-tier mockups frontier - -# Verify feels like overkill on standard for a stable app → try economy: -bash .tfcore/utils/tf-routing.sh set-tier verify-phase economy - -# Recovery notes from refresh-status look sloppy → promote it: -bash .tfcore/utils/tf-routing.sh set-tier refresh-status standard - -# Take a phase out of routing entirely (runs on the session model again): -bash .tfcore/utils/tf-routing.sh set-tier devguide inherit +bash .tfcore/utils/tf-routing.sh set-model standard opencode openai/gpt-5.6-terra +bash .tfcore/utils/tf-routing.sh set-model economy claude haiku ``` -### 6.2 Moving a subagent between tiers +Model ids: for Claude Code `opus`, `sonnet`, `haiku` or a full id; for OpenCode `provider/model` exactly as `opencode models` prints it. + +**Case 2, this project, move a command or a sub-agent to another tier:** ```bash -# Test the "cheap builders" hypothesis yourself (watch the rework ratio!): +bash .tfcore/utils/tf-routing.sh set-tier mockups frontier bash .tfcore/utils/tf-routing.sh set-tier tf-builder economy - -# UI builders struggle with a complex design system → promote just them: -bash .tfcore/utils/tf-routing.sh set-tier trblazeui frontier +bash .tfcore/utils/tf-routing.sh set-tier devguide inherit # take it out of routing ``` -### 6.3 Changing which model a tier means — per tier, per harness +**Case 3, edit the file by hand** (either copy), then regenerate: ```bash -# FRONTIER — try a different top model on OpenCode: -bash .tfcore/utils/tf-routing.sh set-model frontier opencode opencode-go/qwen3.8-max -# ...and pin Claude's frontier to opus explicitly: -bash .tfcore/utils/tf-routing.sh set-model frontier claude opus - -# STANDARD — swap the workhorse: -bash .tfcore/utils/tf-routing.sh set-model standard opencode opencode-go/kimi-k2.7-code -bash .tfcore/utils/tf-routing.sh set-model standard claude sonnet - -# ECONOMY — chase the cheapest model that doesn't degrade output: -bash .tfcore/utils/tf-routing.sh set-model economy opencode opencode-go/mimo-v2.5 -bash .tfcore/utils/tf-routing.sh set-model economy claude haiku +bash .tfcore/utils/tf-routing.sh bind ``` -Find OpenCode ids with `opencode models`. On the Claude side you can also repoint what the aliases mean machine-wide with environment variables: `ANTHROPIC_DEFAULT_OPUS_MODEL`, `ANTHROPIC_DEFAULT_SONNET_MODEL`, `ANTHROPIC_DEFAULT_HAIKU_MODEL`. - -### 6.4 Escalation — when the base tier isn't cutting it +For the framework default there is nothing to bind; the next scaffold copies the file. To bring an existing project up to a changed default, change its own copy (case 1 or 3), because the updater leaves it alone on purpose. -`routing.yaml` carries an **advisory** escalation policy, ported from the AI-First Playbook's `model-tiers.yml`: - -```yaml -escalation: - fix-issues: - after_attempts: 2 - tier: frontier -``` - -Meaning: if the same REQs have already been through `fix-issues` twice without reaching `Verified`, launch the **third** run on the frontier tier. Three things it is *not*: - -- **Not runtime.** Nothing switches a running phase's model — neither harness can, and the one place it could be faked (OpenCode's undocumented `chat.message` mutation) has no Claude Code equivalent (DECISIONS.md 2026-08-21). -- **Not enforced.** It is applied by whoever launches the command — you, or a wrapper script — by reading the attempt history *before* the run starts. -- **Not a binding.** `set-escalation` edits `routing.yaml` only; it generates no files, so it needs no `bind`. - -How to apply it at launch: +**Case 4, one unattended run only.** Pass the model to the supervisor; the file is untouched: ```bash -# 1. What attempt would the next fix-issues run on these REQs be? -bash .tfcore/utils/tf-emit.sh --next-run-attempt fix-issues REQ-UI-009 REQ-FN-011 # → 3 -# 2. status prints the policy next to the base tier: -bash .tfcore/utils/tf-routing.sh status -# Escalation (ADVISORY ...): -# fix-issues after 2 attempt(s) on the same REQs -> launch the next on frontier (base tier: frontier) -# 3. If the answer exceeds after_attempts, launch on the escalation tier: -# Claude Code: /model opus then /tf:fix-issues ... (or run the old command form on opus) -# OpenCode: pick the tier model from the model list, then /techieflow:tasks:fix-issues ... +bash .tfcore/utils/tf-goal.sh --harness opencode --model openai/gpt-5.6-terra /path/to/App @goal.md ``` -The attempt history is the checklist's own record: `attempt` on each `runs.jsonl` record (§2.5 of `SCHEMA.md` — `1 +` prior non-backfilled runs of the same `cmd` touching any of the same REQs), with the per-REQ verdict history in `gates.jsonl` and the Verification Log in `PROJECT-STATUS.md` as the human-readable view. Tune the threshold from the data — if third attempts on the base tier usually succeed anyway, raise it; if second attempts mostly fail, lower it: +The main agent uses that model. The sub-agents it spawns still use the project's bindings, so when a provider is down, change the project's tiers too (case 1), or the sub-agents will fail while the main agent works. That is what happened on 2026-09-06. -```bash -bash .tfcore/utils/tf-routing.sh set-escalation fix-issues 3 frontier # raise the threshold -bash .tfcore/utils/tf-routing.sh set-escalation build-phase 2 frontier # add a policy for another phase -``` - -With the shipped map `fix-issues` is already `frontier`, so the default row only bites after you demote it (`set-tier fix-issues standard`) — which is exactly the experiment it exists to make safe. - -### 6.5 Editing routing.yaml by hand - -The file is deliberately human-editable (flat, two-space indent, commented). After any manual edit: +## 5. Turning routing on and off ```bash -bash .tfcore/utils/tf-routing.sh bind # re-apply → regenerates all bindings +bash .tfcore/utils/tf-routing.sh status # what this project would do, and whether the bindings match +bash .tfcore/utils/tf-routing.sh on # write the bindings +bash .tfcore/utils/tf-routing.sh off # remove exactly those files; the map stays for next time ``` -## 7. Under the hood — what actually gets generated - -You never need this section to *use* routing; it is here so nothing is a black box. +With routing off, every command runs on whatever model the session is on. `status` is the first thing to run when a model looks wrong. -| File | Harness | What it does | -|---|---|---| -| `.opencode/opencode.json` | OpenCode | Pure-JSON binding file loaded alongside the framework config. Adds `model` to each persona (deep-merges — prompt/permission preserved), registers `tf-builder`/`tf-test-writer`/`tf-explorer` as subagents with their models, and re-declares each `techieflow:tasks:*` command with its tier model. | -| `.claude/commands/tf/.md` × 17 | Claude | Wrapper commands: frontmatter `model:` + `effort:` from the tier; body loads the owner persona then executes the task with your arguments. | -| `.claude/agents/{tf-builder, tf-test-writer, tf-explorer, trblazeui, techierag}.md` | Claude | Tier-bound subagent definitions (the library two adopt the NuGet-deployed personas). | -| `.tfcore/.session/routing-bind.manifest` | both | The exact list of generated files. `off` deletes precisely this list — never anything else. | - -All generated files live under gitignored paths — **nothing to commit in the app**. `update-framework.sh` re-runs the generator on every refresh, so framework updates and your routing coexist: your `routing.yaml` is never overwritten, your bindings are always regenerated from it (verified in the TechieBlog pilot). - -## 8. Reading the results — the tuning loop +## 6. How to see what ran -Every phase run appends a record to `docs/metrics/runs.jsonl` (full field reference: `docs/TechieFlow-Telemetry-Guide.md`): - -```json -{"kind":"run","cmd":"verify-phase","app":"TechieBlog","harness":"opencode", - "tier":"standard", - "tier_model":"opencode-go/kimi-k2.7-code", - "model":"opencode-go/kimi-k2.7-code", - "routed":true, - "tokens_in":784,"tokens_out":42310,"tokens_cache_read":310221, - "cost_usd":0.41,"tokens_scope":"tree", "...":"..."} -``` +Every run appends a line to `docs/metrics/runs.jsonl` with the tier the file declared, the model the harness actually used, and `routed: true` when they match. On OpenCode the line also carries the real dollar cost. After a few weeks, `*metrics` shows whether cheaper tiers cost more rework; that data, not opinion, decides the map. -- `tier` / `tier_model` — what routing **declared** should run. -- `model` — what **actually** ran (from the harness's own store, never self-reported). -- `routed` — do they match. `false` = drift (someone ran the phase unrouted); visible, never blocked. -- `cost_usd` — real dollars on OpenCode; always `null` on Claude (no cost source exists — the framework never estimates). +## 7. Two things that surprise people -**The deciding question after ~2 weeks:** does the rework ratio rise on cheaper tiers? Run `*metrics` (or read `runs.jsonl`): if standard-tier builders hold the first-pass rate, consider demoting more phases; if `mode:"fix"` re-entries climb after a demotion, promote back. Data corrects the map — not opinion, and not this guide. +- **OpenCode keeps the model.** After a routed command finishes, that OpenCode session stays on the command's model. Pick your model again, or start a new session, before chatting on. +- **Claude Code reverts.** A `/tf:` wrapper's model lasts one turn; the next prompt is back on the session model. The old long command form (`/TechieFlow:agents:verifier *verify …`) still works but is not routed. -## 9. Troubleshooting +## 8. When something is wrong -| Symptom | Cause | Fix | +| You see | Why | Do | |---|---|---| -| "I turned it on and the TUI looks the same" | Expected — the default chat agent is never routed (§5) | Run a phase command or Tab to a persona | -| `status` says flag and bindings disagree | An update or manual edit got out of sync | `bash .tfcore/utils/tf-routing.sh bind` | -| A phase ran on the wrong model | Invoked unrouted (old Claude command form, or model picked manually) | Check `runs.jsonl` → `routed:false` confirms; use `/tf:` (Claude) or the `/techieflow:tasks:*` command (OpenCode) | -| `fix-issues` keeps failing on the same REQs | That's what escalation is for (§6.4) — it is advisory, so nothing happens until you act on it | `tf-emit.sh --next-run-attempt fix-issues `; if it exceeds `after_attempts`, launch the next run on the escalation tier | -| Follow-up chat is on the phase's model (OpenCode) | Verified behavior — the session keeps the command's model | Pick your model from the model list, or start a new session | -| "I want my normal chat cheaper too" | That's not routing's job | TUI model list, or `"model"` in `~/.config/opencode/opencode.jsonc` | -| Config error mentioning `.opencode/opencode.json` | Hand-edited generated file | Never edit generated files — `bind` regenerates them | -| Want a clean slate | — | `off` removes everything generated; your `routing.yaml` map survives for the next `on` | +| A command ran on the wrong model | It was started unrouted, or the bindings are stale | `status`; then `bind` | +| Sub-agents fail while the main agent works | Sub-agents use the project's bindings, not `--model` | Change the tiers in the project's copy, then `bind` | +| OpenCode prints its header and nothing else | The provider refused the model; OpenCode says so only in its own log (`~/.local/share/opencode/log/opencode.log`) | The supervisor stops with exit 5 and the provider's message; pick another model or wait for the reset | +| The TUI looks unchanged after `on` | Expected: your chat is never routed | Run a command or select a persona | +| An error mentions `.opencode/opencode.json` | A generated file was edited by hand | Never edit generated files; `bind` | diff --git a/docs/TechieFlow-Session-5-Restart-Prompt.md b/docs/TechieFlow-Session-5-Restart-Prompt.md new file mode 100644 index 0000000..1adcae3 --- /dev/null +++ b/docs/TechieFlow-Session-5-Restart-Prompt.md @@ -0,0 +1,38 @@ +# TechieFlow — Session 5, restart prompt + +| | | +|---|---| +| Purpose | The text the owner pastes into a fresh Claude Code window to start Session 5 of the reset. Written 2026-09-07 at the close of Sitting 4c. | +| Audience | The owner, and the maintainer session that reads it. | +| Companion | `TechieFlow-Reset-Plan-2026-09-04.md` (Session 5), `TechieFlow-How-It-Works.md`, `TechieFlow-Document-Schemas.md`, `TechieFlow-Requirements.md`, `TechieFlow-Sitting-4c-Restart-Prompt.md` (the previous sitting) | + +--- + +## The prompt (paste from here) + +We are on the TechieFlow reset, Session 5: the miss protocol and the telemetry explainer. Branch: dev, everything uncommitted since Session 3; I commit when all sessions are done, agents never run git. Read, in this order: docs/TechieFlow-Reset-Plan-2026-09-04.md, docs/TechieFlow-How-It-Works.md, docs/TechieFlow-Document-Schemas.md, docs/TechieFlow-Requirements.md, then docs/TechieFlow-Session-5-Restart-Prompt.md in full. + +State on 2026-09-07 00:45 UTC. Sessions 1 to 4 are done; the Reset Plan carries a Done line for each sitting. Sitting 4c left these on disk, all proven by self-tests and real runs: + +- `verify-phase` is 964 words on nine scripts under `.tfcore/utils/`: `tf-verify-list.sh` (the work list), `tf-verify-env.sh`, `tf-verify-boot.sh` (web head, a MAUI Blazor Hybrid Windows head over the WebView2 DevTools port through `tf-cdp-relay.ps1`, or a static folder), `tf-verify-screens.sh` (render and visual at two widths, a screenshot per screen; also the smoke evidence), `tf-verify-tests.sh`, `tf-perf-grade.sh`, `tf-verify-verdict.sh` (the seven checks in order, the ledger with every row's verdict, the checklist cells) and `tf-verify-emit.sh`. Android, iOS and Mac heads have no driver: their rows are written "not verified". Self-test `bash tests/verify/run.sh`, 67 checks. +- `triage-issues` (448 words), `fix-issues` (386), `log-miss` (430) and the new `triage-and-fix` (283) run on `tf-triage.sh` (demote, new, note, close), `tf-log-miss.sh`, `tf-fix-close.sh` and the shared `tf-checklist-edit.py`; the emitter has `--origin-of` and fills `yolo` on every run record. A row logged from UAT starts `Not Started` with the marker `BRD-pending`. Self-test `bash tests/bugs/run.sh`, 36 checks. All task files together: 20,282 words. +- The seven never-used commands are gone from both harnesses; the routing bind generator skips a phase whose task file no longer exists and says so. Projects not refreshed since the removal still list `author-brd` in their own `routing.yaml`; the next `update-framework.sh` prints the warning. +- Hooks: the verify hook refuses a hand-written `Verified` unless today's ledger lists the row as PASS; the build guard also refuses a backgrounded `npx playwright test` or verify script; the database guard ignores echo strings and comments. The document checker takes a baseline at every `tf-phase.sh start` and prints findings that predate the command as OLD, which do not block. +- Real runs: MyDiary `*fix-issues` found the blank-screen root cause (a LoggingErrorBoundary with no markup shadowed ErrorBoundary's render); all 20 screens render; rows stay `Needs re-verify` until the app carries the mockups' `data-testid` anchors. TechieBlog `*verify all` ran in Claude Code (Sonnet, three hours: 15 PASS, 86 FAIL on acceptance because the local database has no published post and the staff accounts sat behind a password gate) and in OpenCode on TechieBlog-oc (gpt-5.6-terra, twelve minutes: it wrote no tests, 101 rows not tested). `*triage-issues uiIssues` (five owner screenshots of 2026-08-24) ran in both: Claude found none reproducing and wrote three notes; OpenCode demoted two and noted three. TechieBlog and TechieBlog-oc share one database; the UsageGuide test-user rows were aligned by hand after two runs rotated passwords. +- Misses 17 to 27 of 2026-09-06 and 01 to 02 of 2026-09-07 are logged in this repository's stream. Two are for this session's sort: 26 (what a verify does when the test data an acceptance line needs is missing: create it through the app, pass a password gate through its screen, or write "not observable, environment") and 20260907-01 (the OpenCode verify skipped writing tests). + +Open work for this session, in the plan's order: + +1. `*log-miss` gains the readable file: every `miss` record's `what` sentence is written to `docs/-Misses.md` beside the record (FR-31; the sentence is already in the record since 4c). The three-question sort becomes the first step of `tf-log-miss.sh` and the record carries the answer (FR-32): the app's spec did not say it (fix the checklist line), the framework did not say it (one requirement line plus a check, no prose in task files), it was said and ignored (a hook or script, or delete the rule). +2. Sort the misses of Session 4 with the three questions, five real ones from different projects first, and apply the outcomes in a batch. +3. Write `docs/TechieFlow-Telemetry-Explained.md`: the five report numbers, each with its plain definition, how it is calculated, one figure from the combined data and one from a named public project, and the sentence the owner would say on stage. The owner rewrites any sentence they would not say. +4. Close the session: one `framework-reset` run record, mode `session-5`; propose the Done line; refresh the memory file. + +Method, unchanged: tables for anything the owner rules on, questions numbered after the table with a suggested answer, every script proven by a real run with its output shown, files mirrored to `.claude/commands/TechieFlow/`, `opencode.jsonc` checked, both harnesses. Plain words. Owner-reviewed documents change only after the owner's yes. Every gap is logged as a miss, the maintainer's own included. Every open decision is restated in full at the end of a message as a yes-or-no question. Fable 5.1 only in the reset session; Sonnet for long Claude runs; OpenCode through `tf-goal.sh --harness opencode --model openai/gpt-5.6-terra`. + +Watch-outs paid for in 4c, on top of 4b's: + +- The framework's own hooks fire in the maintainer's session: a command whose text names `docs/metrics/*.jsonl`, a migration tool, or a verify script together with `&`, `nohup` or `setsid` is refused whatever it does. Write such steps into a script file with the Write tool and run the file. +- `pgrep -f ` matches the maintainer's own shell, whose command line holds the pattern; exclude `bash -c` before killing or waiting on anything. +- A copy for OpenCode (`-oc`) shares the original's database; a password rotated by one run must be written into both UsageGuides. +- The rsync of a project on `/mnt/c` takes hours; exclude `node_modules`, `bin`, `obj`, `.git` and `tests/.artifacts`, and check completion by the folder's contents, not by a process match. diff --git a/docs/TechieFlow-Session-6-Restart-Prompt.md b/docs/TechieFlow-Session-6-Restart-Prompt.md new file mode 100644 index 0000000..acef7ab --- /dev/null +++ b/docs/TechieFlow-Session-6-Restart-Prompt.md @@ -0,0 +1,40 @@ +# TechieFlow — Session 6, restart prompt + +| | | +|---|---| +| Purpose | The text the owner pastes into a fresh Claude Code window to start Session 6 of the reset. Written 2026-09-07 at the close of Session 5. | +| Audience | The owner, and the maintainer session that reads it. | +| Companion | `TechieFlow-Reset-Plan-2026-09-04.md` (Session 6), `TechieFlow-How-It-Works.md`, `TechieFlow-Document-Schemas.md`, `TechieFlow-Requirements.md`, `TechieFlow-Telemetry-Explained.md`, `TechieFlow-Session-5-Restart-Prompt.md` (the previous session) | + +--- + +## The prompt (paste from here) + +We are on the TechieFlow reset, Session 6: make the repository readable again, and deploy. Branch: dev, everything uncommitted since Session 3; I commit when all sessions are done, agents never run git. Read, in this order: docs/TechieFlow-Reset-Plan-2026-09-04.md, docs/TechieFlow-How-It-Works.md, docs/TechieFlow-Document-Schemas.md, docs/TechieFlow-Requirements.md, then docs/TechieFlow-Session-6-Restart-Prompt.md in full. + +State on 2026-09-07 at the close of Session 5. Sessions 1 to 5 are done; the Reset Plan carries a Done line for each. Session 5 left these on disk, all proven by self-tests and real runs: + +- Every miss record carries `sort`, whose gap it was: `spec`, `unsaid`, `weak-check` or `ignored`. `tf-log-miss.sh` refuses a miss without it and prints the four questions; `tf-triage.sh` fills a default; an older record is sorted once with `tf-emit.sh --amend sort `. Session 4's 53 misses are sorted (weak-check 26, unsaid 15, ignored 14); 41 of them closed against the sitting in which the fix landed; four stay open with their outcome named in `docs/TechieFlow-Misses.md`. +- `docs/-Misses.md` and its HTML are rebuilt by `tf-misses-md.sh` from the stream after every write to it, by the emitter itself. This repository, TechieBlog and the MyDiary copy have theirs; every other project gets its file the first time a miss is written after `update-framework.sh`, or by running the script once. +- `tests/mirror/run.sh` (8 checks): mirror parity, OpenCode references, the FR-43 and FR-44 budgets. Self-tests: bugs 48, verify 67, goal 29, doc-check clean, mirror 8. +- `tf-metrics.sh` keys a requirement by project and id in a rollup (the combined first-pass rate is 48%, not the 72% it printed before), reports whose gap and owner reviews, and counts an amended old record as sorted. +- `docs/TechieFlow-Telemetry-Explained.md`: the five numbers with definitions, calculations, combined and named figures, and the owner's stage sentences. The combined figures exclude the OpenCode copies, which carry their originals' history. +- FR-58 to FR-61 added; FR-18, FR-31, FR-32, FR-40 checks reworded. Open script candidates named there: `tf-yolo.sh done complete` refused while rows sit at Implemented or Blocked or the ledger has untested rows (misses 08 of 09-06 and 01 of 09-07); the verdict script mapping a sign-in redirect or an empty list to "not observable, environment" (FR-61); the idea-stage commands still emit no run record (FR-34, FR-60). +- TechieBlog's 87 regression misses of 2026-09-06 are closed as will-not-fix (one empty database, not 87 defects); its readable file says so. + +Open work for this session, in the plan's order: + +1. Split `WorkFlow-Context.md` (344 KB, mostly the six-month incident log) into a briefing of at most 3,000 words (what it is, how it is used, conventions, repo map, open items, maintenance contract) and `docs/CHANGELOG.md` holding the full log untouched. Trim `README.md` (121 KB) to what a new user needs; the rest moves to `docs/`. Both are owner-review surfaces: propose the briefing's outline first. +2. Run `update-framework.sh` on the projects the owner will build next (TechieRag, AstroLyfe, TrStudio), then one library repo (TrBlazeUI), then the rest. Before each: `pgrep -af tf-goal.sh` excluding `bash -c`, so no active supervisor is touched. After each: `tf-doc-check.sh --app --warn` for the report, and `bash .tfcore/utils/tf-misses-md.sh` so the readable file exists. Projects not refreshed since Sitting 4c still name `author-brd` in their own `routing.yaml`; the updater prints the warning. +3. Decide with the owner whether the Codex adapter is removed now (D-14, FR-42) or after the reset; it is frozen, not propagated. +4. Close the session: one `framework-reset` run record, mode `session-6`; propose the Done line; refresh the memory file; write the Session 7 restart prompt. + +Method, unchanged: tables for anything the owner rules on, questions numbered after the table with a suggested answer, every script proven by a real run with its output shown, files mirrored to `.claude/commands/TechieFlow/` (`bash tests/mirror/run.sh` proves it), `opencode.jsonc` checked, both harnesses. Plain words. Owner-reviewed documents change only after the owner's yes. Every gap is logged as a miss through `tf-log-miss.sh` with its sort, the maintainer's own included. Every open decision is restated in full at the end of a message as a yes-or-no question. Fable 5.1 only in the reset session; Sonnet for long Claude runs; OpenCode through `tf-goal.sh --harness opencode --model openai/gpt-5.6-terra`. + +Watch-outs paid for in Session 5, on top of 4b's and 4c's: + +- The shell's working directory drifts into a fixture after a test run; every path is absolute, and a `cd` in a compound command is a permission prompt. +- The metrics guard refuses any command line that names `docs/metrics/*.jsonl` together with `python3`, a redirection, `sed -i` or `cp`. A read of the streams goes into a script file under the scratchpad and the file is run. +- An amend record can never be undone: a sort or a why written on the wrong miss stays. Table the batch, get the yes, run it once. +- A `${var:?message}` guard with an apostrophe in the message is a syntax error that stops the whole script; `bash -n` every script before it writes a record. +- The combined figures pool the OpenCode copies with their originals unless the copies are left out of the rollup. diff --git a/docs/TechieFlow-Session-7-Restart-Prompt.md b/docs/TechieFlow-Session-7-Restart-Prompt.md new file mode 100644 index 0000000..62ef1b1 --- /dev/null +++ b/docs/TechieFlow-Session-7-Restart-Prompt.md @@ -0,0 +1,46 @@ +# TechieFlow — Session 7, restart prompt + +| | | +|---|---| +| Purpose | The text the owner pastes into a fresh Claude Code window to start Session 7 of the reset, the last one. Written 2026-09-07 at the close of Session 6. | +| Audience | The owner, and the maintainer session that reads it. | +| Companion | `TechieFlow-Reset-Plan-2026-09-04.md` (Session 7), `AI-First-Playbook-Review-Prompt.md` (the version 1 draft being replaced), `TechieFlow-How-It-Works.md`, `TechieFlow-Document-Schemas.md`, `TechieFlow-Requirements.md`, `docs/CHANGELOG.md`, `TechieFlow-Session-6-Restart-Prompt.md` (the previous session) | + +--- + +## The prompt (paste from here) + +We are on the TechieFlow reset, Session 7, the last one: carry what these sessions taught into the AI-First Playbook review prompt. Branch: dev, everything uncommitted since Session 3; I commit when all sessions are done, agents never run git. Read, in this order: `WorkFlow-Context.md` (it is now 1,961 words and is the whole briefing), docs/TechieFlow-Reset-Plan-2026-09-04.md, docs/TechieFlow-Requirements.md, docs/AI-First-Playbook-Review-Prompt.md, then docs/TechieFlow-Session-7-Restart-Prompt.md in full. The six-month history is in docs/CHANGELOG.md and is not session reading. + +State on 2026-09-07 at the close of Session 6. Sessions 1 to 6 are done; the Reset Plan carries a Done line for each. Session 6 left this on disk, all proven by real runs: + +- `WorkFlow-Context.md` is a briefing of 1,961 words: what the repository is, how the framework is used, the conventions, the repo map, the open items, the maintenance contract. `README.md` is 1,864 words: what it is, how to install it, the flow, the command table for both harnesses, what it produces, where to read more. The history moved to `docs/CHANGELOG.md` unedited; the machine setup, the permission model and the gotchas moved to `docs/TechieFlow-Setup.md`, `docs/TechieFlow-Permissions-And-YOLO.md` and `docs/TechieFlow-FAQ.md`. +- The framework is deployed to **all 23 projects** carrying `.tfcore/`, three passes, every one exit 0, each verified by content rather than by modification time. No project's `opencode.jsonc` holds a dead reference; no project's `routing.yaml` names a removed command. +- Four framework defects found, fixed and proven (misses 05 to 09 of 2026-09-07): FR-47's check had never been written; `tf-log-miss.sh` called a refused record logged; the updater treated dead BMAD-era registrations as project content and so froze one repository with no working agents; and a run record with no `ended` was accepted and could never be costed. +- Five new checks: three in `tests/mirror/run.sh` (the two readable files against their budgets, no removed command named, FR-47 against a per-machine private-name file) and two in `tests/bugs/run.sh`. Self-tests at the close: mirror 12, doc-check 12, bugs 51, verify 67, goal 29. +- FR-62 added; FR-47's check rewritten. The requirements list stands at 62 lines. + +Open work for this session: + +1. Rewrite `docs/AI-First-Playbook-Review-Prompt.md` as version 2, carrying what the TechieFlow sessions actually taught: the keep-as-words / turn-into-a-script / delete table as the method for shrinking any prose file; the schema block format that stopped documents drifting; the four-question miss sort with its `sort` field; the rule that a rule ignored twice becomes a hook or is deleted; the instruction budget per model tier; and the discipline that every script is proven by a real run whose output is shown. +2. Include a plain list of what went wrong during the TechieFlow sessions so the Playbook review does not repeat it. The candidates are in each session's Done line and in `docs/CHANGELOG.md`'s Session 6 entry: rules that named a script nobody wrote, commands that reported success after a refusal, checks that measured the wrong thing, owner-review documents edited before the owner said yes, and the shell working directory drifting into a fixture. +3. Say what is different about a corporate team, because the Playbook is the team edition: review gates, onboarding, shared standards, and the fact that its harness is OpenCode only. +4. Build it in Claude Code and test it only in OpenCode, per the plan's rule for Playbook work. +5. Close the reset: one `framework-reset` run record, mode `session-7`; propose the Done line for Session 7; refresh the memory file; and tell the owner plainly that the seven sessions are complete and what happens next (the distribution pipeline on `main`, then the Playbook's own sessions, then the blog, then building resumes). + +Three decisions are still the owner's and are carried into this session: + +1. The **Codex adapter**: removed now, or after the reset (D-14, FR-42)? It is frozen, not propagated, and every project still carries it. +2. **`WORKFLOW.html`**: regenerated, or dropped? It is 227 KB, last revised 2026-08-28, still teaches three commands removed in Sitting 4c, and `update-framework.sh` force-deploys it into all 23 projects (`MISS-TechieFlow-20260907-10`, open). Session 6 deliberately added no check for it, because a check that fails every day gets ignored. +3. **Which documents count as public-facing** for FR-47. Session 6 scoped the check to the README, the briefing and the templates, and deliberately left the reset's own working documents out, because they name MyDiary and other fixtures on purpose. If the owner wants a wider scope, the names come out of those documents and the check's file list grows. + +Method, unchanged: tables for anything the owner rules on, questions numbered after the table with a suggested answer, every script proven by a real run with its output shown, files mirrored to `.claude/commands/TechieFlow/` (`bash tests/mirror/run.sh` proves it), `opencode.jsonc` checked, both harnesses. Plain words. Owner-reviewed documents change only after the owner's yes. Every gap is logged as a miss through `tf-log-miss.sh` with its sort, the maintainer's own included. Every open decision is restated in full at the end of a message as a yes-or-no question. Fable 5.1 only in the reset session; Sonnet for long Claude runs; OpenCode through `tf-goal.sh --harness opencode --model openai/gpt-5.6-terra`. + +Watch-outs paid for in Session 6, on top of the earlier ones: + +- The metrics guard refuses any command line that names `docs/metrics/*.jsonl` together with `python3`, a redirection, `sed -i` or `cp`. It fired twice this session. A read of the streams goes into a script file under the scratchpad and the file is run. +- A self-test's `check` helper compares a **string** to 0, so a check must pass the exit code, not the command's output: end the substitution with `; echo $?`. A check written without it reports a failure that is not there, which is how ten minutes went on a fix that was already working. +- Grepping for a phrase that wraps across two comment lines finds nothing and looks exactly like a missing fix. Verify a deployed change by a distinctive phrase that sits on one line. +- A script changed mid-session has to be propagated again: this session ran `update-framework.sh` over all 23 projects three times for that reason. Always confirm by content. +- `tf-doc-check.sh` needs `--app` named explicitly in a repository holding two products: given the choice it takes the first checklist in alphabetical order, so TechieRag was reported as TechieDesk. +- Both readable files are owner-review surfaces. Session 6 rewrote them inside an unattended run and put the outline to the owner afterwards, with the originals recoverable; if the owner wants any of the old wording back, it is in `docs/CHANGELOG.md` and in the last commit on `main`. diff --git a/docs/TechieFlow-Setup.html b/docs/TechieFlow-Setup.html new file mode 100644 index 0000000..21d859a --- /dev/null +++ b/docs/TechieFlow-Setup.html @@ -0,0 +1,503 @@ + + + + + +TechieFlow — Machine setup + + + + + +
    +
    +

    TechieFlow — Machine setup

    +
    Rendered 2026-09-07 · source TechieFlow-Setup.md
    + + + + + + + + + + +
    PurposeEverything you install once per machine before the framework can build, run and see your applications: WSL, macOS, the device hosts for mobile heads, how MAUI is built from each host, and what changes on native Windows or Linux.
    AudienceThe owner, and anyone setting up a new machine.
    StatusMoved out of README.md on 2026-09-07 (Session 6 of the reset), unedited. The sections keep their old numbers so older links still make sense.
    CompanionREADME.md (start there), docs/TechieFlow-Permissions-And-YOLO.md, docs/TechieFlow-FAQ.md.
    +

    Do the one section that matches your machine. Nothing here is needed twice.

    +
    +

    0. WSL bootstrap — DO ONCE, EVER#

    +

    Library persona source files and their NuGet deployment paths are documented +in docs/TechieFlow-Library-Persona-Propagation.md.

    +

    Run this once per WSL distro. Installs headless-Chromium system libs + the MAUI bridge.

    +
    +

    On macOS: skip this section — your one-time setup is §0a instead. There is no winrun bridge on a Mac (dotnet and MAUI run natively) and Playwright's Chromium needs no apt libraries.

    +
    +
    sudo apt-get update && sudo apt-get install -y \
    +  libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 \
    +  libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 \
    +  libcairo2 libasound2 libgtk-3-0 libx11-xcb1
    +
    +mkdir -p ~/bin && cat > ~/bin/winrun << 'SH'
    +#!/usr/bin/env bash
    +WINPATH=$(wslpath -w "$PWD")
    +powershell.exe -NoProfile -Command "cd '$WINPATH'; $*"
    +SH
    +chmod +x ~/bin/winrun
    +grep -q 'HOME/bin' ~/.bashrc || echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
    +

    OpenCode in WSL — the primary path (same distro as Claude Code)#

    +

    Since 2026-08-20 OpenCode runs natively inside the same WSL distro as Claude Code (OpenCode's own docs recommend WSL over native Windows; full rationale, probe evidence, and the crash playbook are in docs/OpenCode-Deployment-Guide.md). It gets the entire runtime harness above — winrun, headless Chromium, Appium — for free, with no SSH bridge and no second NuGet config.

    +
    curl -fsSL https://opencode.ai/install | bash
    +grep -q '.opencode/bin' ~/.bashrc || echo 'export PATH="$HOME/.opencode/bin:$PATH"' >> ~/.bashrc
    +opencode auth login     # or copy a portable API-key entry into ~/.local/share/opencode/auth.json
    +

    The PATH line matters: WSL's Windows-interop otherwise resolves opencode to the Windows npm shim (AppData\Roaming\npm\opencode) — the native-Windows Bun build that breaks on large repos. Verify with type -a opencode (the ~/.opencode/bin entry must come first) and opencode --version.

    +

    The framework side needs no manual setup: scaffold-*.sh / update-framework.sh deploy .opencode/plugin/techieflow.js (the guard bridge — the same .tfcore/hooks/ guards Claude Code runs: git ban, PROJECT-STATUS shape, Verified ledger — plus telemetry with real dollar cost into docs/metrics/sessions.jsonl) and a framework-owned .opencode/opencode.jsonc into every app. Check with opencode agent list in the app (the six TechieFlow agents must appear).

    +

    Large repos: the failure historically blamed on Bun is a /mnt/c (9p filesystem) pathology — OpenCode's snapshot walk can take minutes there while the identical repo on WSL-native ext4 (~/) boots in seconds. Typical TechieFlow apps on /mnt/c are fine; genuinely large repos belong on ext4, or see the watcher/snapshot tuning in docs/OpenCode-Deployment-Guide.md §6.

    +

    OpenCode in Docker on Windows — FALLBACK ONLY#

    +
    +

    Since 2026-08-20 this path is a fallback, kept in case the WSL path ever reproduces the native-Windows crash. Use the WSL section above; nothing below is needed for it.

    +
    +

    A Linux container cannot execute cmd.exe. docs/Dockerfile uses the Debian .NET 10 SDK image and deliberately installs no MAUI workloads. Standard .NET apps build and test inside the container. Windows MAUI Blazor Desktop builds use the image's SSH-backed /usr/local/bin/winrun wrapper and run on the Windows host. Mobile, iOS, and Mac Catalyst builds and runtime tests should run natively on a Mac. This bridge is only needed for Windows-host builds. The first command uses Windows Update and can take several minutes, but it should not remain at Operation [Running] indefinitely. Run the following capability, service, and firewall commands separately in an elevated PowerShell window:

    +
    $cap = Get-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    +$cap.State
    +if ($cap.State -ne 'Installed') {
    +    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    +}
    +Start-Service sshd
    +Set-Service -Name sshd -StartupType Automatic
    +if (-not (Get-NetFirewallRule -Name OpenSSH-Server-In-TCP -ErrorAction SilentlyContinue)) {
    +    New-NetFirewallRule -Name OpenSSH-Server-In-TCP -DisplayName "OpenSSH Server (sshd)" -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
    +}
    +

    When the capability reports Installed, open a normal PowerShell window as the Windows account that will run Docker. Copy and paste this key setup as one complete line; it is safe to run again:

    +
    $ssh="$env:USERPROFILE\.ssh"; New-Item -ItemType Directory -Force $ssh | Out-Null; if (-not (Test-Path "$ssh\opencode-docker")) { ssh-keygen -t ed25519 -f "$ssh\opencode-docker" -N "" }; $publicKey=(Get-Content "$ssh\opencode-docker.pub" -Raw).Trim(); $auth="$ssh\authorized_keys"; if (-not (Test-Path $auth)) { Set-Content -Path $auth -Value $publicKey } elseif ((Get-Content $auth) -notcontains $publicKey) { Add-Content -Path $auth -Value $publicKey }
    +

    Verify the bridge before starting Docker. This test disables password fallback. A successful test prints the Windows host's .NET information and never asks for a password:

    +
    ssh -o BatchMode=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -i "$env:USERPROFILE\.ssh\opencode-docker" "$env:USERNAME@localhost" powershell.exe -NoProfile -NonInteractive -Command "dotnet --info"
    +

    If Add-WindowsCapability stays at Operation [Running] for about 10 minutes, press Ctrl+C; the later commands have not run. Check Get-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 and the Microsoft-Windows-DISM/Operational event log, or install OpenSSH Server through Settings > System > Optional features > View features. Retry only after the capability reports Installed.

    +

    Keep Dockerfile and opencode-docker.cmd in %USERPROFILE%\.opencode-docker-config. Build the image once from that folder:

    +
    Set-Location "$env:USERPROFILE\.opencode-docker-config"
    +docker build --pull --no-cache -t my-opencode-dotnet .
    +

    Put that folder on PATH. From any application folder, run opencode-docker.cmd; it uses the existing my-opencode-dotnet image and mounts the host NuGet and SSH directories:

    +
    docker run --rm -it `
    +  -v "${USERPROFILE}\.opencode-docker\nuget:/root/.nuget/NuGet:ro" `
    +  -v "${USERPROFILE}\.ssh:/root/.ssh:ro" `
    +  -v "${PWD}:/workspace" -w /workspace `
    +  -e TF_WINDOWS_SSH_HOST=host.docker.internal `
    +  -e TF_WINDOWS_SSH_USER="$env:USERNAME" `
    +  -e TF_WINDOWS_SSH_KEY=/root/.ssh/opencode-docker `
    +  -e TF_WINDOWS_APP_PATH="C:\path\to\app" `
    +  -e TF_OPENCODE_DOCKER=1 `
    +  my-opencode-dotnet opencode
    +

    Use dotnet build for Linux-compatible projects and winrun "dotnet build -c Release" for the Windows head. The container is not WSL; if the SSH bridge is unavailable, only the Windows head is STATIC-ONLY.

    +

    The SSH directory is intentionally mounted read-only. Docker Desktop can expose the mounted private key with Linux mode 0777, which OpenSSH rejects, and the mounted directory cannot accept a new known_hosts file. The image's winrun wrapper copies the key to writable /tmp/opencode-docker/opencode-docker with mode 0600 and creates its writable host-trust file there. Do not try to repair the mounted file from inside the container.

    +

    If the test reports Permission denied (publickey), do not enter the VPS password or Windows password. Because the generated key has no passphrase, this means public-key authentication was rejected. If whoami /groups | Select-String 'S-1-5-32-544' prints a result, the account is an Administrator and Windows OpenSSH uses %ProgramData%\ssh\administrators_authorized_keys rather than the profile authorized_keys file. Add the same public key there from elevated PowerShell and apply icacls permissions, as shown in WORKFLOW.html.

    +

    NuGet credentials#

    +

    Keep GitHub Packages credentials out of the repository. Native Windows uses %AppData%\NuGet\NuGet.Config; macOS/Linux uses $HOME/.nuget/NuGet/NuGet.Config. Docker uses the separate user-level %USERPROFILE%\.opencode-docker\nuget\NuGet.Config, mounted read-only by opencode-docker.cmd. Do not mount the normal Windows config for private feeds: its password may be DPAPI-encrypted and therefore unusable inside Linux. Create the Docker config with a Linux-readable credential, for example:

    +
    New-Item -ItemType Directory -Force "$env:USERPROFILE\.opencode-docker\nuget" | Out-Null
    +dotnet nuget add source "https://nuget.pkg.github.com/OWNER/index.json" --name github --username GITHUB_USER --password GITHUB_TOKEN --store-password-in-clear-text --configfile "$env:USERPROFILE\.opencode-docker\nuget\NuGet.Config"
    +

    Replace the placeholders with the package owner's values. The token is stored only in the user profile, not the repository. A project nuget.config may provide source mapping but must contain no PAT.

    +

    0a. macOS bootstrap — DO ONCE, EVER#

    +

    Run this once per Mac. The native equivalent of §0: everything the agents need to build, run, and see your apps on macOS. There is no winrun bridge to install — dotnet, Playwright, and Appium all run natively — but the machine still needs its toolchain once.

    +
    # 1. Xcode Command Line Tools — provides git AND python3 (the framework's
    +#    guard-status/guard-verify hooks silently fail open without python3)
    +xcode-select --install
    +
    +# If full Xcode is installed (required for MAUI iOS / Mac Catalyst builds),
    +# accept its license once or python3/git error out with a license prompt:
    +sudo xcodebuild -license accept
    +
    +# 2. Homebrew (skip if `brew --version` already works)
    +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    +
    +# 3. The toolchain: .NET SDK + Node.js (Node powers Playwright and Appium)
    +brew install dotnet-sdk node
    +
    +# 4. MAUI workload — only if any of your apps ships a MAUI head.
    +#    sudo is REQUIRED on macOS: the SDK lives in root-owned /usr/local/share/dotnet,
    +#    so without it this (and any `dotnet workload update` / SDK update) fails with
    +#    "Inadequate permissions. Run the command with elevated privileges."
    +sudo dotnet workload install maui
    +

    Playwright — nothing for you to do. The verifier self-provisions it per project the first time it runs (verify-phase.md §1, also used by every self-smoke): it creates package.json if missing, runs npm install -D @playwright/test + npx playwright install chromium, and writes a minimal playwright.config.ts. The only machine-level prerequisite is Node (step 3 above). The Chromium download is cached once under ~/Library/Caches/ms-playwright and shared by every project, so only the first project ever pays it — and unlike WSL there are no system libraries to install.

    +

    Verify: dotnet --info prints an SDK, node --version answers, and python3 --version answers without an Xcode-license error. The agents handle everything else per project.

    +

    MAUI native-UI testing (Android emulator / iOS Simulator / Mac Catalyst): continue with §0b — on a Mac-native setup every piece of it (Android Studio + emulator, Appium + drivers, the Simulator) runs on this same machine, and all endpoints are http://localhost:4723.

    +

    0b. Device-host bootstrap (MAUI Android / iOS / Mac Catalyst) — DO ONCE PER HOST#

    +

    Only needed for apps that ship a MAUI mobile or Mac desktop head. It lets the verifier (and the smoke / devguide-OBSERVE gates) drive the running native UI and apply the same data-render + visual-truth gates it applies to Blazor — closing the blind spot where a MAUI app passes every gate while its screens overlap, clip, or render blank. The driver is Appium (the native analogue of headless Playwright: same WebDriver protocol, returns a screenshot + an element tree). The WSL side only talks to an HTTP endpoint — no adb, emulator, or Xcode inside WSL. Builds are unchanged (§9 ladder); this is the runtime-observe leg.

    +

    Step 1 — enable Win11 mirrored networking (once) so WSL reaches the Windows-host Appium on plain localhost. In %UserProfile%\.wslconfig:

    +
    [wsl2]
    +networkingMode=mirrored
    +

    then wsl --shutdown and reopen WSL.

    +

    Step 2 — Android, on the Windows host (Android SDK already present):

    +
    sdkmanager "system-images;android-34;google_apis;x86_64"
    +avdmanager create avd -n Pixel_API_34 -k "system-images;android-34;google_apis;x86_64"
    +npm install -g appium
    +appium driver install uiautomator2
    +# session helper (start-android-verify.ps1) boots the emulator + Appium the verifier calls itself
    +

    Step 3 — iOS + Mac Catalyst, on a Mac on the same LAN (also your iOS build host — Xcode + .NET + dotnet workload install maui already there); give it a stable IP:

    +
    npm install -g appium
    +appium driver install xcuitest      # iOS Simulator
    +appium driver install mac2          # Mac Catalyst desktop
    +appium --address 0.0.0.0 --port 4723
    +

    Step 4 — register the endpoints per app in core-config.yaml → runtimeVerification.appium (only the heads that app ships). The verifier auto-discovers them; an absent/unreachable endpoint degrades that head to ⚠ STATIC-ONLY, never a faked pass.

    +

    WSL-on-Windows setup (Android on this PC, Apple on the LAN Mac):

    +
    runtimeVerification:
    +  appium:
    +    android:     { url: http://localhost:4723, avd: Pixel_API_34, launch: 'winrun "powershell -File start-android-verify.ps1"' }
    +    ios:         { url: http://192.168.1.50:4723, simulator: "iPhone 15" }
    +    maccatalyst: { url: http://192.168.1.50:4723 }
    +

    macOS-native setup (everything on this Mac — no winrun, no LAN address):

    +
    runtimeVerification:
    +  appium:
    +    android:     { url: http://localhost:4723, avd: Pixel_API_34 }
    +    ios:         { url: http://localhost:4723, simulator: "iPhone 15" }
    +    maccatalyst: { url: http://localhost:4723 }
    +

    Running Claude Code natively on a Mac? Everything above collapses onto the one machine: do Step 3 (and Step 2's Android pieces if needed) in the Mac's own Terminal, skip Step 1 (mirrored networking) entirely, and use http://localhost:4723 for every head.

    +

    Verify: from WSL, curl http://localhost:4723/status (Android) and curl http://<mac-ip>:4723/status (iOS/Catalyst); on a Mac-native setup it's curl http://localhost:4723/status for everything. Reliable selectors need a stable AutomationId on key controls (a coding standard — see §10).

    +

    11. MAUI builds & runs — from WSL (bridged) or macOS (native)#

    +

    WSL (Windows) — bridge every dotnet call to the Windows side via winrun (§0):

    +
    cd /mnt/c/path/to/maui-project
    +winrun "dotnet build -c Release"
    +winrun "dotnet test"
    +winrun "dotnet build -t:Run -f net9.0-windows10.0.19041.0"
    +

    macOS — no bridge; dotnet runs natively (ladder §A):

    +
    cd /path/to/maui-project
    +dotnet build -c Release
    +dotnet test
    +dotnet build -t:Run -f net9.0-maccatalyst      # desktop head on Mac = Mac Catalyst
    +dotnet build -t:Run -f net9.0-android          # Android head (emulator via Android Studio)
    +

    On macOS the Windows head (net9.0-windows…) can't build — the Mac desktop head is Mac Catalyst, and iOS builds natively too (Xcode required, §16). The winrun lines apply only inside WSL.

    +

    For verifier on a MAUI Windows app: "This is a MAUI Windows app. Build/run/test via winrun. UI automation: FlaUI or Appium-Windows-driver Windows-side, NOT Playwright. Output evidence the same as Blazor projects." On a Mac the equivalent prompt names the Catalyst head and the local mac2 Appium driver instead.

    +

    Mobile & Mac-desktop heads — runtime-observe over Appium#

    +

    The §4a data-render and §4b visual-truth gates reach the MAUI Android / iOS / Mac Catalyst heads through an Appium WebDriver endpoint — the native analogue of Playwright (same screenshot + element-tree evidence, so the gates run unchanged). One-time host setup is §0b; the per-head driver map lives in build-invocation-ladder.md §D. Builds don't change — on WSL, Android still builds via cmd.exe (ladder rung #4) and iOS/Catalyst on the paired Mac; on a Mac-native setup all three build locally with plain dotnet build and the Appium endpoints are all localhost. This is purely how the verifier reaches the running UI after a green build.

    + + + + + + + + +
    HeadWhere it runs (WSL setup)Appium driverWSL reaches it viamacOS-native reaches it via
    MAUI Androidemulator on the Windows host (Android SDK)uiautomator2http://localhost:4723 (mirrored networking); verifier boots emulator + Appium itselfhttp://localhost:4723 — emulator + Appium run on the Mac itself
    MAUI iOSSimulator on a LAN Macxcuitesthttp://<mac-ip>:4723; Mac must be up or head is ⚠ STATIC-ONLYhttp://localhost:4723 — local Simulator (Xcode)
    MAUI Mac Catalystthe same LAN Mac (desktop .app)mac2http://<mac-ip>:4723http://localhost:4723 — the .app runs right here
    MAUI WindowsWindows sideFlaUI / Appium-Windows (unchanged)winrun / cmd.exen/a — this head doesn't exist on a Mac
    +

    Selectors target each control's AutomationId (a coding standard, §10). A head with no registered endpoint in core-config.yaml → runtimeVerification.appium, or an unreachable host, is stamped ⚠ STATIC-ONLY for that head — never a faked Verified.

    +

    Window binding & input discipline (all native heads, especially MAUI Windows): the driver session is bound to the app under test by identity — the PID the agent launched → that process's top-level window handle (Appium Windows appium:appTopLevelWindow / FlaUI Application.Attach(pid)), or the app package/bundle id on mobile — and every interaction is element-scoped via AutomationId inside that bound window, with focus verified before input and handles re-resolved after dialogs. Global keyboard/mouse injection (FlaUI Keyboard.Type, coordinate clicks, SendKeys) is banned: it types into whatever window happens to hold focus — historically, a completely different window than the app. Full rules: verify-phase.md §3b.

    +

    16. Running on macOS / native Windows / Linux#

    +

    TechieFlow was authored on the owner's WSL-on-Windows machine, so §0/§11 and the build-invocation ladder describe that setup. The framework itself is portable — agents, tasks, templates, and /TechieFlow:* slash-commands are plain Markdown and run identically under Claude Code / OpenCode on macOS, native Windows, or native Linux. Only two things are environment-specific: how dotnet is invoked, and the runtime-verification bridges (headless Playwright for Blazor; the §0b Appium endpoints for MAUI Android/iOS/Mac-Catalyst; FlaUI/Appium-Windows for the MAUI Windows head).

    +

    Same everywhere: the scaffold-*.sh / update-framework.sh scripts (bash + rsync + realpath), all slash commands, the day-1 → split → build → verify → handoff flow, every template, and the permission model. On native Windows run the bash scripts from WSL or Git Bash.

    + + + + + + + + + +
    ConcernWSL-on-WindowsmacOSnative Windowsnative Linux
    dotnetladder §B (~/.dotnet/dotnet, cmd.exe, winrun)§A: dotnet build§C: dotnet build§A: dotnet build
    MAUI iOS / Mac CatalystWindows side via cmd.exenative (Xcode + sudo dotnet workload install maui)needs paired Macnot supported
    MAUI AndroidWindows sidenative (Android SDK + JDK)nativenative
    MAUI Windows headWindows sidenot supportednativenot supported
    Native UI verification (Android/iOS/Catalyst)Appium endpoints (§0b): Android on Windows host, iOS/Catalyst on a LAN Maclocal Appium (all native)local Appium (Android+Catalyst); iOS via paired Maclocal Appium (Android only)
    +

    The build ladder (.tfcore/templates/v4custom/build-invocation-ladder.md) auto-detects the host (uname -aDarwin = macOS, …microsoft… = WSL, plain Linux = native Linux, absent = native Windows) and picks §A/§B/§C. On macOS/Windows/Linux there is one rung — dotnet build — and a missing workload is a one-time dotnet workload install maui (on macOS with sudo: the SDK dir /usr/local/share/dotnet is root-owned, and without it workload/SDK updates fail with "Inadequate permissions. Run the command with elevated privileges."), never a project blocker. Running the scaffold scripts needs bash + rsync + realpath (preinstalled on macOS 12.3+ and Linux; on native Windows run them from WSL or Git Bash).

    +

    macOS quick start:

    +
      +
    1. Run the one-time §0a macOS bootstrap — Xcode CLT/license, Homebrew, .NET SDK + Node, Playwright per project; sudo dotnet workload install maui (sudo required on macOS) + Xcode / Android SDK only for MAUI apps. +
    2. +
    3. Scaffold: /path/to/TechieFlow/scaffold-brownfield.sh /path/to/your-app (or scaffold-greenfield.sh). +
    4. +
    5. Start Claude Code in the app folder: /TechieFlow:agents:analyst *day1-brownfield <AppName> — identical to WSL. The winrun/cmd.exe rungs don't apply once uname reports Darwin. +
    6. +
    +

    Moving an existing project (or this framework repo) from Windows/WSL to a Mac:

    +
      +
    1. Can't see .tfcore/, .claude/, .opencode/ in Finder? Finder hides dot-files by default. Press Cmd+Shift+. in any Finder window to toggle them on (the setting sticks), or run defaults write com.apple.finder AppleShowAllFiles -bool true && killall Finder. The Terminal always sees them: ls -la. Nothing is missing just because Finder doesn't show it — check with ls -la first. +
    2. +
    3. Moved an APP repo via git (clone/pull)? Then the framework folders genuinely AREN'T there — every deployed framework copy (.tfcore/, .claude/, .opencode/, /CLAUDE.md, /WORKFLOW.html, /opencode.jsonc) is gitignored by design (they're copies; this repo is the source of truth). Re-deploy them: ls -la the app — if .tfcore/ exists, run /path/to/TechieFlow/update-framework.sh /path/to/app; if it's absent, run /path/to/TechieFlow/scaffold-brownfield.sh /path/to/app (safe on an app with existing docs/code — it uses --ignore-existing and never touches src/, docs/, or tests). Add --dry-run to update-framework.sh to preview. +
    4. +
    5. Per-project gitignored files don't come back from a scaffold. CLAUDE.md, .tfcore/core-config.yaml customizations, and .claude/settings.local.json are per-project work product that git never carried. A plain folder copy from the old machine keeps them; a git clone loses them — copy them over from the Windows machine, or regenerate (CLAUDE.md comes back via day-1 / *refresh-status). +
    6. +
    7. Scripts won't execute (permission denied)? A copy through a Windows filesystem drops the executable bit. Fix once: chmod +x /path/to/TechieFlow/*.sh /path/to/TechieFlow/.tfcore/hooks/*.sh (or run them as bash script.sh). Hooks inside apps are invoked via bash so they don't need it, but the same chmod doesn't hurt. +
    8. +
    9. No path edits needed: since 2026-07-11 the three scripts locate the framework from their own directory (no hardcoded /mnt/c/…), and they run fine on macOS's stock bash/rsync. +
    10. +
    11. Afterwards, restart Claude Code in the app folder so the freshly deployed agent/task definitions and settings.json load. +
    12. +
    +

    native Windows quick start (Claude Code / OpenCode on Windows, not WSL):

    +
      +
    1. Install the .NET SDK (winget / official installer); confirm dotnet --info. +
    2. +
    3. For MAUI: dotnet workload install maui. Windows + Android heads build natively; iOS / Mac Catalyst need a paired Mac build host. +
    4. +
    5. Run the scaffold scripts from WSL or Git Bash, then drive the framework from Claude Code on Windows — the ladder uses §C (dotnet build). +
    6. +
    +

    native Linux quick start: install the .NET SDK; MAUI supports the Android head only (iOS / Mac Catalyst / Windows heads can't build without their toolchains — a genuine platform limit). Scaffold and run exactly as on macOS (ladder §A).

    +

    The framework never requires MAUI — many apps are Blazor-only and build with plain dotnet build everywhere. The full per-platform dotnet detail lives in .tfcore/templates/v4custom/build-invocation-ladder.md.

    +
    +
    +
    + + + + + + diff --git a/docs/TechieFlow-Setup.md b/docs/TechieFlow-Setup.md new file mode 100644 index 0000000..be20921 --- /dev/null +++ b/docs/TechieFlow-Setup.md @@ -0,0 +1,295 @@ +# TechieFlow — Machine setup + +| | | +|---|---| +| Purpose | Everything you install once per machine before the framework can build, run and see your applications: WSL, macOS, the device hosts for mobile heads, how MAUI is built from each host, and what changes on native Windows or Linux. | +| Audience | The owner, and anyone setting up a new machine. | +| Status | Moved out of `README.md` on 2026-09-07 (Session 6 of the reset), unedited. The sections keep their old numbers so older links still make sense. | +| Companion | `README.md` (start there), `docs/TechieFlow-Permissions-And-YOLO.md`, `docs/TechieFlow-FAQ.md`. | + +Do the one section that matches your machine. Nothing here is needed twice. + +--- + +## 0. WSL bootstrap — DO ONCE, EVER + +Library persona source files and their NuGet deployment paths are documented +in [`docs/TechieFlow-Library-Persona-Propagation.md`](docs/TechieFlow-Library-Persona-Propagation.md). + +**Run this once per WSL distro.** Installs headless-Chromium system libs + the MAUI bridge. + +> **On macOS: skip this section — your one-time setup is §0a instead.** There is no `winrun` bridge on a Mac (`dotnet` and MAUI run natively) and Playwright's Chromium needs no apt libraries. + +```bash +sudo apt-get update && sudo apt-get install -y \ + libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 \ + libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 \ + libcairo2 libasound2 libgtk-3-0 libx11-xcb1 + +mkdir -p ~/bin && cat > ~/bin/winrun << 'SH' +#!/usr/bin/env bash +WINPATH=$(wslpath -w "$PWD") +powershell.exe -NoProfile -Command "cd '$WINPATH'; $*" +SH +chmod +x ~/bin/winrun +grep -q 'HOME/bin' ~/.bashrc || echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc +``` + +### OpenCode in WSL — the primary path (same distro as Claude Code) + +Since 2026-08-20 OpenCode runs **natively inside the same WSL distro as Claude Code** (OpenCode's own docs recommend WSL over native Windows; full rationale, probe evidence, and the crash playbook are in `docs/OpenCode-Deployment-Guide.md`). It gets the entire runtime harness above — `winrun`, headless Chromium, Appium — for free, with no SSH bridge and no second NuGet config. + +```bash +curl -fsSL https://opencode.ai/install | bash +grep -q '.opencode/bin' ~/.bashrc || echo 'export PATH="$HOME/.opencode/bin:$PATH"' >> ~/.bashrc +opencode auth login # or copy a portable API-key entry into ~/.local/share/opencode/auth.json +``` + +The PATH line matters: WSL's Windows-interop otherwise resolves `opencode` to the Windows npm shim (`AppData\Roaming\npm\opencode`) — the native-Windows Bun build that breaks on large repos. Verify with `type -a opencode` (the `~/.opencode/bin` entry must come first) and `opencode --version`. + +The framework side needs no manual setup: `scaffold-*.sh` / `update-framework.sh` deploy `.opencode/plugin/techieflow.js` (the guard bridge — the same `.tfcore/hooks/` guards Claude Code runs: git ban, PROJECT-STATUS shape, Verified ledger — plus telemetry with real dollar cost into `docs/metrics/sessions.jsonl`) and a framework-owned `.opencode/opencode.jsonc` into every app. Check with `opencode agent list` in the app (the six TechieFlow agents must appear). + +**Large repos:** the failure historically blamed on Bun is a `/mnt/c` (9p filesystem) pathology — OpenCode's snapshot walk can take minutes there while the identical repo on WSL-native ext4 (`~/`) boots in seconds. Typical TechieFlow apps on `/mnt/c` are fine; genuinely large repos belong on ext4, or see the watcher/snapshot tuning in `docs/OpenCode-Deployment-Guide.md` §6. + +### OpenCode in Docker on Windows — FALLBACK ONLY + +> **Since 2026-08-20 this path is a fallback**, kept in case the WSL path ever reproduces the native-Windows crash. Use the WSL section above; nothing below is needed for it. + +A Linux container cannot execute `cmd.exe`. `docs/Dockerfile` uses the Debian .NET 10 SDK image and deliberately installs no MAUI workloads. Standard .NET apps build and test inside the container. Windows MAUI Blazor Desktop builds use the image's SSH-backed `/usr/local/bin/winrun` wrapper and run on the Windows host. Mobile, iOS, and Mac Catalyst builds and runtime tests should run natively on a Mac. This bridge is only needed for Windows-host builds. The first command uses Windows Update and can take several minutes, but it should not remain at `Operation [Running]` indefinitely. Run the following capability, service, and firewall commands separately in an **elevated PowerShell** window: + +```powershell +$cap = Get-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 +$cap.State +if ($cap.State -ne 'Installed') { + Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 +} +Start-Service sshd +Set-Service -Name sshd -StartupType Automatic +if (-not (Get-NetFirewallRule -Name OpenSSH-Server-In-TCP -ErrorAction SilentlyContinue)) { + New-NetFirewallRule -Name OpenSSH-Server-In-TCP -DisplayName "OpenSSH Server (sshd)" -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 +} +``` + +When the capability reports `Installed`, open a **normal PowerShell** window as the Windows account that will run Docker. Copy and paste this key setup as one complete line; it is safe to run again: + +```powershell +$ssh="$env:USERPROFILE\.ssh"; New-Item -ItemType Directory -Force $ssh | Out-Null; if (-not (Test-Path "$ssh\opencode-docker")) { ssh-keygen -t ed25519 -f "$ssh\opencode-docker" -N "" }; $publicKey=(Get-Content "$ssh\opencode-docker.pub" -Raw).Trim(); $auth="$ssh\authorized_keys"; if (-not (Test-Path $auth)) { Set-Content -Path $auth -Value $publicKey } elseif ((Get-Content $auth) -notcontains $publicKey) { Add-Content -Path $auth -Value $publicKey } +``` + +Verify the bridge before starting Docker. This test disables password fallback. A successful test prints the Windows host's `.NET` information and never asks for a password: + +```powershell +ssh -o BatchMode=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -i "$env:USERPROFILE\.ssh\opencode-docker" "$env:USERNAME@localhost" powershell.exe -NoProfile -NonInteractive -Command "dotnet --info" +``` + +If `Add-WindowsCapability` stays at `Operation [Running]` for about 10 minutes, press `Ctrl+C`; the later commands have not run. Check `Get-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0` and the `Microsoft-Windows-DISM/Operational` event log, or install **OpenSSH Server** through Settings > System > Optional features > View features. Retry only after the capability reports `Installed`. + +Keep `Dockerfile` and `opencode-docker.cmd` in `%USERPROFILE%\.opencode-docker-config`. Build the image once from that folder: + +```powershell +Set-Location "$env:USERPROFILE\.opencode-docker-config" +docker build --pull --no-cache -t my-opencode-dotnet . +``` + +Put that folder on `PATH`. From any application folder, run `opencode-docker.cmd`; it uses the existing `my-opencode-dotnet` image and mounts the host NuGet and SSH directories: + +```powershell +docker run --rm -it ` + -v "${USERPROFILE}\.opencode-docker\nuget:/root/.nuget/NuGet:ro" ` + -v "${USERPROFILE}\.ssh:/root/.ssh:ro" ` + -v "${PWD}:/workspace" -w /workspace ` + -e TF_WINDOWS_SSH_HOST=host.docker.internal ` + -e TF_WINDOWS_SSH_USER="$env:USERNAME" ` + -e TF_WINDOWS_SSH_KEY=/root/.ssh/opencode-docker ` + -e TF_WINDOWS_APP_PATH="C:\path\to\app" ` + -e TF_OPENCODE_DOCKER=1 ` + my-opencode-dotnet opencode +``` + +Use `dotnet build` for Linux-compatible projects and `winrun "dotnet build -c Release"` for the Windows head. The container is not WSL; if the SSH bridge is unavailable, only the Windows head is `STATIC-ONLY`. + +The SSH directory is intentionally mounted read-only. Docker Desktop can expose the mounted private key with Linux mode `0777`, which OpenSSH rejects, and the mounted directory cannot accept a new `known_hosts` file. The image's `winrun` wrapper copies the key to writable `/tmp/opencode-docker/opencode-docker` with mode `0600` and creates its writable host-trust file there. Do not try to repair the mounted file from inside the container. + +If the test reports `Permission denied (publickey)`, do not enter the VPS password or Windows password. Because the generated key has no passphrase, this means public-key authentication was rejected. If `whoami /groups | Select-String 'S-1-5-32-544'` prints a result, the account is an Administrator and Windows OpenSSH uses `%ProgramData%\ssh\administrators_authorized_keys` rather than the profile `authorized_keys` file. Add the same public key there from elevated PowerShell and apply `icacls` permissions, as shown in `WORKFLOW.html`. + +### NuGet credentials + +Keep GitHub Packages credentials out of the repository. Native Windows uses `%AppData%\NuGet\NuGet.Config`; macOS/Linux uses `$HOME/.nuget/NuGet/NuGet.Config`. Docker uses the separate user-level `%USERPROFILE%\.opencode-docker\nuget\NuGet.Config`, mounted read-only by `opencode-docker.cmd`. Do not mount the normal Windows config for private feeds: its password may be DPAPI-encrypted and therefore unusable inside Linux. Create the Docker config with a Linux-readable credential, for example: + +```powershell +New-Item -ItemType Directory -Force "$env:USERPROFILE\.opencode-docker\nuget" | Out-Null +dotnet nuget add source "https://nuget.pkg.github.com/OWNER/index.json" --name github --username GITHUB_USER --password GITHUB_TOKEN --store-password-in-clear-text --configfile "$env:USERPROFILE\.opencode-docker\nuget\NuGet.Config" +``` + +Replace the placeholders with the package owner's values. The token is stored only in the user profile, not the repository. A project `nuget.config` may provide source mapping but must contain no PAT. + +## 0a. macOS bootstrap — DO ONCE, EVER + +**Run this once per Mac.** The native equivalent of §0: everything the agents need to build, run, and *see* your apps on macOS. There is no `winrun` bridge to install — `dotnet`, Playwright, and Appium all run natively — but the machine still needs its toolchain once. + +```bash +# 1. Xcode Command Line Tools — provides git AND python3 (the framework's +# guard-status/guard-verify hooks silently fail open without python3) +xcode-select --install + +# If full Xcode is installed (required for MAUI iOS / Mac Catalyst builds), +# accept its license once or python3/git error out with a license prompt: +sudo xcodebuild -license accept + +# 2. Homebrew (skip if `brew --version` already works) +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + +# 3. The toolchain: .NET SDK + Node.js (Node powers Playwright and Appium) +brew install dotnet-sdk node + +# 4. MAUI workload — only if any of your apps ships a MAUI head. +# sudo is REQUIRED on macOS: the SDK lives in root-owned /usr/local/share/dotnet, +# so without it this (and any `dotnet workload update` / SDK update) fails with +# "Inadequate permissions. Run the command with elevated privileges." +sudo dotnet workload install maui +``` + +**Playwright — nothing for you to do.** The verifier **self-provisions** it per project the first time it runs (`verify-phase.md §1`, also used by every self-smoke): it creates `package.json` if missing, runs `npm install -D @playwright/test` + `npx playwright install chromium`, and writes a minimal `playwright.config.ts`. The only machine-level prerequisite is **Node** (step 3 above). The Chromium download is cached once under `~/Library/Caches/ms-playwright` and shared by every project, so only the first project ever pays it — and unlike WSL there are no system libraries to install. + +**Verify:** `dotnet --info` prints an SDK, `node --version` answers, and `python3 --version` answers *without* an Xcode-license error. The agents handle everything else per project. + +**MAUI native-UI testing** (Android emulator / iOS Simulator / Mac Catalyst): continue with §0b — on a Mac-native setup every piece of it (Android Studio + emulator, Appium + drivers, the Simulator) runs on this same machine, and all endpoints are `http://localhost:4723`. + +## 0b. Device-host bootstrap (MAUI Android / iOS / Mac Catalyst) — DO ONCE PER HOST + +Only needed for apps that ship a MAUI **mobile or Mac desktop** head. It lets the verifier (and the smoke / devguide-OBSERVE gates) **drive the running native UI** and apply the same data-render + visual-truth gates it applies to Blazor — closing the blind spot where a MAUI app passes every gate while its screens overlap, clip, or render blank. The driver is **Appium** (the native analogue of headless Playwright: same WebDriver protocol, returns a screenshot + an element tree). The WSL side only talks to an **HTTP endpoint** — no `adb`, emulator, or Xcode inside WSL. Builds are unchanged (§9 ladder); this is the *runtime-observe* leg. + +**Step 1 — enable Win11 mirrored networking** (once) so WSL reaches the Windows-host Appium on plain `localhost`. In `%UserProfile%\.wslconfig`: + +```ini +[wsl2] +networkingMode=mirrored +``` + +then `wsl --shutdown` and reopen WSL. + +**Step 2 — Android, on the Windows host** (Android SDK already present): + +```powershell +sdkmanager "system-images;android-34;google_apis;x86_64" +avdmanager create avd -n Pixel_API_34 -k "system-images;android-34;google_apis;x86_64" +npm install -g appium +appium driver install uiautomator2 +# session helper (start-android-verify.ps1) boots the emulator + Appium the verifier calls itself +``` + +**Step 3 — iOS + Mac Catalyst, on a Mac on the same LAN** (also your iOS build host — Xcode + .NET + `dotnet workload install maui` already there); give it a stable IP: + +```bash +npm install -g appium +appium driver install xcuitest # iOS Simulator +appium driver install mac2 # Mac Catalyst desktop +appium --address 0.0.0.0 --port 4723 +``` + +**Step 4 — register the endpoints per app** in `core-config.yaml → runtimeVerification.appium` (only the heads that app ships). The verifier auto-discovers them; an absent/unreachable endpoint degrades that head to `⚠ STATIC-ONLY`, never a faked pass. + +**WSL-on-Windows setup (Android on this PC, Apple on the LAN Mac):** + +```yaml +runtimeVerification: + appium: + android: { url: http://localhost:4723, avd: Pixel_API_34, launch: 'winrun "powershell -File start-android-verify.ps1"' } + ios: { url: http://192.168.1.50:4723, simulator: "iPhone 15" } + maccatalyst: { url: http://192.168.1.50:4723 } +``` + +**macOS-native setup (everything on this Mac — no winrun, no LAN address):** + +```yaml +runtimeVerification: + appium: + android: { url: http://localhost:4723, avd: Pixel_API_34 } + ios: { url: http://localhost:4723, simulator: "iPhone 15" } + maccatalyst: { url: http://localhost:4723 } +``` + +**Running Claude Code natively on a Mac?** Everything above collapses onto the one machine: do Step 3 (and Step 2's Android pieces if needed) in the Mac's own Terminal, skip Step 1 (mirrored networking) entirely, and use `http://localhost:4723` for every head. + +**Verify:** from WSL, `curl http://localhost:4723/status` (Android) and `curl http://:4723/status` (iOS/Catalyst); on a Mac-native setup it's `curl http://localhost:4723/status` for everything. Reliable selectors need a stable `AutomationId` on key controls (a coding standard — see §10). + +## 11. MAUI builds & runs — from WSL (bridged) or macOS (native) + +**WSL (Windows) — bridge every dotnet call to the Windows side via `winrun` (§0):** + +```bash +cd /mnt/c/path/to/maui-project +winrun "dotnet build -c Release" +winrun "dotnet test" +winrun "dotnet build -t:Run -f net9.0-windows10.0.19041.0" +``` + +**macOS — no bridge; dotnet runs natively (ladder §A):** + +```bash +cd /path/to/maui-project +dotnet build -c Release +dotnet test +dotnet build -t:Run -f net9.0-maccatalyst # desktop head on Mac = Mac Catalyst +dotnet build -t:Run -f net9.0-android # Android head (emulator via Android Studio) +``` + +On macOS the Windows head (`net9.0-windows…`) can't build — the Mac desktop head is **Mac Catalyst**, and iOS builds natively too (Xcode required, §16). The `winrun` lines apply only inside WSL. + +For verifier on a MAUI **Windows** app: *"This is a MAUI Windows app. Build/run/test via `winrun`. UI automation: FlaUI or Appium-Windows-driver Windows-side, NOT Playwright. Output evidence the same as Blazor projects."* On a Mac the equivalent prompt names the **Catalyst** head and the local `mac2` Appium driver instead. + +### Mobile & Mac-desktop heads — runtime-observe over Appium + +The §4a data-render and §4b visual-truth gates reach the MAUI **Android / iOS / Mac Catalyst** heads through an **Appium** WebDriver endpoint — the native analogue of Playwright (same screenshot + element-tree evidence, so the gates run unchanged). One-time host setup is §0b; the per-head driver map lives in `build-invocation-ladder.md §D`. **Builds don't change** — on WSL, Android still builds via `cmd.exe` (ladder rung #4) and iOS/Catalyst on the paired Mac; on a Mac-native setup all three build locally with plain `dotnet build` and the Appium endpoints are all `localhost`. This is purely how the verifier reaches the *running* UI after a green build. + +| Head | Where it runs (WSL setup) | Appium driver | WSL reaches it via | macOS-native reaches it via | +|------|---------------|---------------|--------------------|------------------------------| +| MAUI Android | emulator on the Windows host (Android SDK) | `uiautomator2` | `http://localhost:4723` (mirrored networking); verifier boots emulator + Appium itself | `http://localhost:4723` — emulator + Appium run on the Mac itself | +| MAUI iOS | Simulator on a LAN Mac | `xcuitest` | `http://:4723`; Mac must be up or head is `⚠ STATIC-ONLY` | `http://localhost:4723` — local Simulator (Xcode) | +| MAUI Mac Catalyst | the same LAN Mac (desktop .app) | `mac2` | `http://:4723` | `http://localhost:4723` — the .app runs right here | +| MAUI Windows | Windows side | FlaUI / Appium-Windows (unchanged) | `winrun` / `cmd.exe` | n/a — this head doesn't exist on a Mac | + +Selectors target each control's `AutomationId` (a coding standard, §10). A head with no registered endpoint in `core-config.yaml → runtimeVerification.appium`, or an unreachable host, is stamped `⚠ STATIC-ONLY` for that head — never a faked `Verified`. + +**Window binding & input discipline (all native heads, especially MAUI Windows):** the driver session is bound to the app under test *by identity* — the PID the agent launched → that process's top-level window handle (Appium Windows `appium:appTopLevelWindow` / FlaUI `Application.Attach(pid)`), or the app package/bundle id on mobile — and every interaction is **element-scoped via `AutomationId` inside that bound window**, with focus verified before input and handles re-resolved after dialogs. Global keyboard/mouse injection (FlaUI `Keyboard.Type`, coordinate clicks, `SendKeys`) is **banned**: it types into whatever window happens to hold focus — historically, a completely different window than the app. Full rules: `verify-phase.md §3b`. + +## 16. Running on macOS / native Windows / Linux + +TechieFlow was authored on the owner's **WSL-on-Windows** machine, so §0/§11 and the build-invocation ladder describe that setup. The framework itself is **portable** — agents, tasks, templates, and `/TechieFlow:*` slash-commands are plain Markdown and run identically under Claude Code / OpenCode on **macOS, native Windows, or native Linux**. Only two things are environment-specific: how `dotnet` is invoked, and the runtime-verification bridges (headless Playwright for Blazor; the §0b Appium endpoints for MAUI Android/iOS/Mac-Catalyst; FlaUI/Appium-Windows for the MAUI Windows head). + +**Same everywhere:** the `scaffold-*.sh` / `update-framework.sh` scripts (bash + `rsync` + `realpath`), all slash commands, the day-1 → split → build → verify → handoff flow, every template, and the permission model. On native Windows run the bash scripts from **WSL or Git Bash**. + +| Concern | WSL-on-Windows | macOS | native Windows | native Linux | +|---|---|---|---|---| +| `dotnet` | ladder §B (`~/.dotnet/dotnet`, `cmd.exe`, `winrun`) | **§A: `dotnet build`** | **§C: `dotnet build`** | **§A: `dotnet build`** | +| MAUI iOS / Mac Catalyst | Windows side via `cmd.exe` | native (Xcode + `sudo dotnet workload install maui`) | needs paired Mac | not supported | +| MAUI Android | Windows side | native (Android SDK + JDK) | native | native | +| MAUI Windows head | Windows side | not supported | native | not supported | +| Native UI verification (Android/iOS/Catalyst) | Appium endpoints (§0b): Android on Windows host, iOS/Catalyst on a LAN Mac | local Appium (all native) | local Appium (Android+Catalyst); iOS via paired Mac | local Appium (Android only) | + +The build ladder (`.tfcore/templates/v4custom/build-invocation-ladder.md`) auto-detects the host (`uname -a` → `Darwin` = macOS, `…microsoft…` = WSL, plain `Linux` = native Linux, absent = native Windows) and picks §A/§B/§C. On macOS/Windows/Linux there is one rung — `dotnet build` — and a missing workload is a one-time `dotnet workload install maui` (on macOS with `sudo`: the SDK dir `/usr/local/share/dotnet` is root-owned, and without it workload/SDK updates fail with *"Inadequate permissions. Run the command with elevated privileges."*), never a project blocker. Running the scaffold scripts needs `bash` + `rsync` + `realpath` (preinstalled on macOS 12.3+ and Linux; on native Windows run them from **WSL or Git Bash**). + +**macOS quick start:** +1. Run the one-time **§0a macOS bootstrap** — Xcode CLT/license, Homebrew, .NET SDK + Node, Playwright per project; `sudo dotnet workload install maui` (sudo required on macOS) + Xcode / Android SDK only for MAUI apps. +2. Scaffold: `/path/to/TechieFlow/scaffold-brownfield.sh /path/to/your-app` (or `scaffold-greenfield.sh`). +3. Start Claude Code in the app folder: `/TechieFlow:agents:analyst *day1-brownfield ` — identical to WSL. The `winrun`/`cmd.exe` rungs don't apply once `uname` reports `Darwin`. + +**Moving an existing project (or this framework repo) from Windows/WSL to a Mac:** +1. **Can't see `.tfcore/`, `.claude/`, `.opencode/` in Finder?** Finder hides dot-files by default. Press **Cmd+Shift+.** in any Finder window to toggle them on (the setting sticks), or run `defaults write com.apple.finder AppleShowAllFiles -bool true && killall Finder`. The Terminal always sees them: `ls -la`. Nothing is missing just because Finder doesn't show it — check with `ls -la` first. +2. **Moved an APP repo via git (clone/pull)?** Then the framework folders genuinely AREN'T there — every deployed framework copy (`.tfcore/`, `.claude/`, `.opencode/`, `/CLAUDE.md`, `/WORKFLOW.html`, `/opencode.jsonc`) is *gitignored by design* (they're copies; this repo is the source of truth). Re-deploy them: `ls -la` the app — if `.tfcore/` exists, run `/path/to/TechieFlow/update-framework.sh /path/to/app`; if it's absent, run `/path/to/TechieFlow/scaffold-brownfield.sh /path/to/app` (safe on an app with existing docs/code — it uses `--ignore-existing` and never touches `src/`, `docs/`, or tests). Add `--dry-run` to `update-framework.sh` to preview. +3. **Per-project gitignored files don't come back from a scaffold.** `CLAUDE.md`, `.tfcore/core-config.yaml` customizations, and `.claude/settings.local.json` are per-project work product that git never carried. A plain *folder copy* from the old machine keeps them; a git clone loses them — copy them over from the Windows machine, or regenerate (`CLAUDE.md` comes back via day-1 / `*refresh-status`). +4. **Scripts won't execute (`permission denied`)?** A copy through a Windows filesystem drops the executable bit. Fix once: `chmod +x /path/to/TechieFlow/*.sh /path/to/TechieFlow/.tfcore/hooks/*.sh` (or run them as `bash script.sh`). Hooks inside apps are invoked via `bash` so they don't need it, but the same `chmod` doesn't hurt. +5. **No path edits needed:** since 2026-07-11 the three scripts locate the framework from their own directory (no hardcoded `/mnt/c/…`), and they run fine on macOS's stock `bash`/`rsync`. +6. **Afterwards, restart Claude Code** in the app folder so the freshly deployed agent/task definitions and `settings.json` load. + +**native Windows quick start (Claude Code / OpenCode on Windows, not WSL):** +1. Install the .NET SDK (winget / official installer); confirm `dotnet --info`. +2. For MAUI: `dotnet workload install maui`. Windows + Android heads build natively; **iOS / Mac Catalyst need a paired Mac build host**. +3. Run the scaffold scripts from **WSL or Git Bash**, then drive the framework from Claude Code on Windows — the ladder uses §C (`dotnet build`). + +**native Linux quick start:** install the .NET SDK; MAUI supports the **Android** head only (iOS / Mac Catalyst / Windows heads can't build without their toolchains — a genuine platform limit). Scaffold and run exactly as on macOS (ladder §A). + +The framework never *requires* MAUI — many apps are Blazor-only and build with plain `dotnet build` everywhere. The full per-platform `dotnet` detail lives in `.tfcore/templates/v4custom/build-invocation-ladder.md`. + +--- + diff --git a/docs/TechieFlow-Sitting-4b-Restart-Prompt.md b/docs/TechieFlow-Sitting-4b-Restart-Prompt.md new file mode 100644 index 0000000..fee61fa --- /dev/null +++ b/docs/TechieFlow-Sitting-4b-Restart-Prompt.md @@ -0,0 +1,49 @@ +# TechieFlow — Sitting 4b, restart prompt for the second window + +| | | +|---|---| +| Purpose | The text the owner pastes into a fresh Claude Code window to continue Sitting 4b without losing what the first window did. Written 2026-09-06 09:06 UTC at the owner's request, when the first window's context had grown too long. | +| Audience | The owner, and the maintainer session that reads it. | +| Companion | `TechieFlow-Reset-Plan-2026-09-04.md`, `TechieFlow-Document-Schemas.md` §7.2, `TechieFlow-Requirements.md` | + +--- + +## The prompt (paste from here) + +We are on the TechieFlow reset, Session 4, Sitting 4b, second window. Branch: dev, everything uncommitted; I commit, agents never run git. Read, in this order: docs/TechieFlow-Reset-Plan-2026-09-04.md, docs/TechieFlow-How-It-Works.md, docs/TechieFlow-Document-Schemas.md, docs/TechieFlow-Requirements.md, then docs/TechieFlow-Sitting-4b-Restart-Prompt.md in full. + +State on 2026-09-06 09:06 UTC. Done and proven in the first window: + +- The Large layout (Schemas §2, §3.11): per-screen documents split by phase, phase 1 keeps the plain names, `appPhase` in core-config, the Phases document, a phase row may carry several id ranges; checker, splitter, facts script, BRD status script and day-1 files script are phase-aware; the self-test has a two-phase fixture. +- Miss 09 fix: `bash .tfcore/utils/tf-phase.sh start ` is step 0 of every task; `guard-db.sh` refuses direct SQL writes from every command and migration runners unless the marker says build-phase or fix-issues; wired in both harnesses and the scaffold and update scripts; the supervisor writes the start marker when its first cycle begins and the first command claims it. +- build-phase 4,412 to 688 words, with `tf-build.sh` (the rung ladder as a script, proven on Xpenser through the Windows bridge), `tf-build-list.sh --prompts` (mode, clusters, one sub-agent prompt per cluster from `build-subagent-prompt.md`), `_yolo-mode.md` 1,543 to 457, How-It-Works §3.10 for unattended runs. +- flow-master persona 3,499 to 542 words; fifteen commands dropped; `*help` table kept; `*deploy-checklist` added. +- devguide 5,274 to 649 with `tf-devguide-list.sh` (proven on Xpenser and TechieRag); the service-library map lives in the DevGuide for the maintainer (Schemas decision M); the UsageGuide library section is "How to call it". +- refresh-status 2,388 to 494 with `tf-status-evidence.sh` (proven on Xpenser). +- Deployment Checklist: template, four checker rules, the `*deploy-checklist` task, registered in both harnesses and the schema. Run for real on TfLens: Sonnet wrote `docs/TfLens-Deployment-Checklist.md` (0 FAIL, the owner deploys from it, the old one is in OldDocs); OpenCode with glm-5.2 wrote the same on the light copy `/mnt/c/1MyCode/TfLens-oc` (0 FAIL). MiMo hung silently on TfLens for 43 minutes (miss 22) and is not yet re-tested on the copy. +- Readability: an acceptance line is at most 30 words (target 20), one behaviour, plain titles, one plain sentence per screen group in the BRD; the checker holds the cap. Architecture checks: a stack row for Q1 to Q8 and Q11; the head project named exactly the app, `.App` refused. Mockups: real links only, no script navigation, no dead menu item, paths resolved from the mockup folder, stylesheets must exist. +- Emitter: an `ended` in the future is replaced with now; `started` comes from the marker; the `review` record kind exists (FR-36) and the first two real `day1-review` records are on MyDiary and its copy. +- Requirements FR-54 to FR-57 added; FR-10, 15, 36, 53, 55 reworded. Misses 10 to 24 of 2026-09-05 logged. The Reset Plan carries the 4a done note. The first window's run record (mode `sitting-4b`) was written 2026-09-06 09:06 UTC. +- MyDiary (`/mnt/c/1MyCode/MyDiary`): Large, 25 screens, two phases, 114 items; stage 1 and stage 2 done on Sonnet and reviewed by the owner. The scratch copy `/mnt/c/1MyCode/MyDiary-oc` holds MiMo's set, also through stage 2 with 0 FAIL. `*build-phase MyDiary` was running in the MyDiary folder through `tf-goal.sh` on Sonnet: 35 of 77 phase-1 rows were Implemented, the verifier had not run, and Sonnet's usage limit hit at 06:57 UTC (reset 09:15 UTC). The owner stopped the supervisor at 09:10 UTC to close the window. Its state is intact in `MyDiary/.tfcore/.session/goal.json` (session id, cycle 4) and its log in `goal.log`. First action of the new window, once the clock passes 09:15 UTC: `bash /mnt/c/3AIGenCode/TechieFlow/.tfcore/utils/tf-goal.sh --resume /mnt/c/1MyCode/MyDiary` as a background shell, then carry on with the list below while it runs. + +Open work for this window, in order: + +1. Resume the MyDiary build as above, let it finish, and read its result honestly: the checker, the run records (mode build and fix), the smoke, the verifier's counts, the rows left below Implemented and why. Then run `*build-phase MyDiary` on the copy through OpenCode with `opencode-go/glm-5.2` (MiMo is on the watch list until item 2 clears it). The goal text is in the appendix. +2. Re-test MiMo on `/mnt/c/1MyCode/TfLens-oc` with the deploy-checklist goal (appendix) to learn whether the hang is the model. Report either way. +3. Supervisor fixes, only once no supervisor is executing the framework's copy of `tf-goal.sh`: a stall watchdog (kill and re-prompt after fifteen silent minutes), stop the child process when the supervisor is killed, and classify a clean early stop (exit 0, no sentinel) as a stop with a thirty-second retry, not a harness error with a doubling backoff (misses 22, 23). +4. Real runs still owed in both harnesses: `*devguide MyDiary` and `*refresh-status MyDiary` once the build is done. `*handoff-phase` and `*productguide` were not on this sitting's list and stay for later. +5. Close the sitting: one `framework-reset` run record, mode `sitting-4b`, for this window; propose the 4b done note for the Reset Plan and wait for my yes; refresh the memory file `reset-sitting-4b-progress.md`. + +Method, unchanged: print each remaining task as a table (block, plain summary, verdict), questions in a numbered list after the table with a suggested answer, never inside a cell; I rule on every row; write the scripts; prove every script and hook with a real run and show me the output; mirror the file to `.claude/commands/TechieFlow/`; confirm `opencode.jsonc` still points at it; run the command for real in both harnesses. Plain words; owner-reviewed documents (the plan, How-It-Works, the Stack documents, the Schemas doc) change only after my yes; every gap found is logged as a miss with one sentence, my own slips included, never as a Claude Code feedback report; every open decision is restated in full at the end of a message, never as a pointer. Model routing stands: Sonnet for every long Claude run, OpenCode through `tf-goal.sh --harness opencode --model `; Fable 5.1 only in this reset session. + +Watch-outs the first window paid for: `cd` into the application folder before running a script that reads its working tree (a script run from the framework folder prints NOTHING); never touch a folder with an active supervisor (it now refuses a second run and a dry run changes nothing); when I ask about "shells", I mean Claude Code's background shell list, so name each one by its launch line and its folder; MyDiary, MyDiary-oc, TfLens and TfLens-oc carry today's framework through `update-framework.sh`, every other project does not. + +## Appendix: the goal texts used in the first window + +### deploy-checklist on TfLens (both harnesses) + + Load the flow-master persona (.tfcore/agents/flow-master.md) and run `*deploy-checklist TfLens docs/claude-code-deployment-brief-v3.2.md`, following .tfcore/tasks/deploy-checklist.md step by step, starting with step 0. The hosting target is the VPS production pipeline the brief describes; one document, docs/TfLens-Deployment-Checklist.md. The Architecture is an older document: if it has no Q9 (hosting) or Q10 (production secrets and pipeline) row in its Stack decisions, take both answers from the brief and add the two rows, citing the brief. Facts about this app come from the brief, the existing (archived) checklist, docker-compose.prod.template.yml, the Dockerfile and appsettings; never invent a secret name or a command. The checker must print 0 FAIL for PROJECT-STATUS.md and the new checklist; the older TfLens documents are reported with `--warn` and left alone. Run the status gate with `"cmd":"deploy-checklist"`, then `bash .tfcore/utils/tf-yolo.sh done complete ""`. Never deploy, never run git. + +### build-phase on MyDiary (Sonnet; reuse for the OpenCode copy with MyDiary-oc) + + Load the flow-master persona (.tfcore/agents/flow-master.md) and run `*build-phase MyDiary`, following .tfcore/tasks/build-phase.md step by step, starting with step 0 (`bash .tfcore/utils/tf-phase.sh start build-phase MyDiary`). This is phase 1 of a Large project (appPhase 1 in .tfcore/core-config.yaml): the work list is docs/MyDiary-Checklist.md, the design is docs/MyDiary-UIDesign.md with docs/mockups/, the stack is the Architecture's Stack decisions (.NET MAUI Blazor Hybrid, SQLite, no server, no AppManager). There is no code yet: create the solution and projects the Architecture's Solution structure names (the primary head is named exactly `MyDiary`), build with `bash .tfcore/utils/tf-build.sh`, and implement every open row through the clusters `bash .tfcore/utils/tf-build-list.sh MyDiary --prompts` prints. Smoke as the smoke policy says, chain the verifier, loop FIX mode on its failures, run the status gate and the run record, and only then `bash .tfcore/utils/tf-yolo.sh done complete ""`. A row that needs something only the owner has goes under Known blockers, not into a question. Never run git. diff --git a/docs/TechieFlow-Sitting-4c-Restart-Prompt.md b/docs/TechieFlow-Sitting-4c-Restart-Prompt.md new file mode 100644 index 0000000..de58ee9 --- /dev/null +++ b/docs/TechieFlow-Sitting-4c-Restart-Prompt.md @@ -0,0 +1,54 @@ +# TechieFlow — Sitting 4c, restart prompt + +| | | +|---|---| +| Purpose | The text the owner pastes into a fresh Claude Code window to start Sitting 4c of the reset. Written 2026-09-06 16:15 UTC at the close of Sitting 4b. | +| Audience | The owner, and the maintainer session that reads it. | +| Companion | `TechieFlow-Reset-Plan-2026-09-04.md` (Session 4, Sitting 4c), `TechieFlow-How-It-Works.md`, `TechieFlow-Document-Schemas.md`, `TechieFlow-Requirements.md`, `TechieFlow-Sitting-4b-Restart-Prompt.md` (the previous sitting) | + +--- + +## The prompt (paste from here) + +We are on the TechieFlow reset, Session 4, Sitting 4c. Branch: dev, everything uncommitted since Session 3; I commit when all sessions are done, agents never run git. Read, in this order: docs/TechieFlow-Reset-Plan-2026-09-04.md, docs/TechieFlow-How-It-Works.md, docs/TechieFlow-Document-Schemas.md, docs/TechieFlow-Requirements.md, then docs/TechieFlow-Sitting-4c-Restart-Prompt.md in full. + +State on 2026-09-06 16:15 UTC. Sessions 1, 2, 3 and Sittings 4a and 4b are done; the Reset Plan carries a Done line for each. Sitting 4b left these on disk, all proven by real runs: + +- The goal supervisor `.tfcore/utils/tf-goal.sh`: reads the Claude result line before guessing (a clean stop is a stop, not a crash), kills a cycle silent for 15 minutes and re-prompts, starts a fresh session after two stalled resumes (`--resume --fresh` by hand), reads the OpenCode log after a silent stall and exits 5 when the provider refused the model, stops its child on Ctrl-C or kill and exits 130. Self-test `bash tests/goal/run.sh`, 29 checks. +- `tf-yolo.sh done` refuses the completion sentinel until PROJECT-STATUS and a run record for the command the phase marker names are newer than the goal start. +- Hooks, eleven in all, listed in How-It-Works §2: new are `guard-build.sh` (a backgrounded build, test or app run is refused while YOLO is on) and the Stop hook checking every checklist written in the session. The OpenCode plugin vets `apply_patch` per file through the same guards, because OpenAI models have no edit or write tool. +- `tf-build.sh` counts Razor, Blazor and XAML errors as code errors. `tf-emit.sh` names the app from `-Checklist.md` only and fills a missing `duration_s`. +- Models: OpenCode runs on `openai/gpt-5.6-terra` (frontier, standard) and `openai/gpt-5.6-luna` (economy) since 2026-09-06; the OpenCode Go monthly limit is reached until about 2026-09-12. Claude Code: Sonnet for long runs, Haiku for cheap ones. The framework default `.tfcore/routing.yaml` and the four test projects say so; `docs/TechieFlow-Routing-Guide.md` was rewritten plainly. +- MyDiary (`/mnt/c/1MyCode/MyDiary`, Sonnet): built, 67 tests pass, the Windows head boots, but every screen is blank at runtime because routed content never mounts in the layout's `@Body` (found by the DevGuide run). Checklist: 7 Verified, 9 Implemented, 60 Needs re-verify, 1 Blocked. The copy `/mnt/c/1MyCode/MyDiary-oc` (OpenCode, gpt-5.6-terra): 84 Implemented, 1 Needs re-verify, 2 N/A, nothing runtime-verified, DevGuide static-only. +- Misses 01 to 16 of 2026-09-06 are logged; the ones that belong to this sitting: 07 (the smoke policy has no path for a native app head and names no evidence file, so a MAUI build wrote no smoke evidence and called 69 untested rows Implemented), 08 (a build agent wrote "complete" where "blocked" was honest), 13 and 15 (a build closed without its own run record). +- Four projects carry today's framework through `update-framework.sh`: MyDiary, MyDiary-oc, TfLens, TfLens-oc. Every other project does not. + +Open work for this sitting, in the plan's order: + +1. Shrink `verify-phase` (11,850 words, FR-26 says at most 4,000). It is where 63 of the 128 recorded misses came from. The seven checks stay as the definition; the setup and the how-to become scripts; the checks that already are scripts (`tf-assets.sh`, `tf-mockup-parity.sh`, `tf-perf.sh`) are wired, not described. It must say what a verify does for a native app head (MAUI, no browser robot): drive the Windows head, or record honestly that the row is not verified, never "static-only Implemented". A dialog is verified on its parent page, never promoted to a route (Schemas §2, miss 24 of 2026-09-04). +2. Shrink `fix-issues`, `triage-issues`, `log-miss`. Triage calls log-miss for every root cause, fix calls it when a fix lands (D-15). One command runs the owner's whole bug sequence in YOLO with a summary per step (D-16, FR-30). Every task honours YOLO; build and verify default to it (D-18). +3. Remove the seven never-used commands from both harnesses and their registrations: create-brd (author-brd), elicit (advanced-elicitation), document-project, index-docs, shard-doc, execute-checklist, kb-mode-interaction. `create-doc` stays. +4. Real runs, both harnesses, on a project in user testing that the owner picks (the plan names AstroLyfe): a real `*verify` and a real `*triage-issues`. Before that project is used, run `update-framework.sh` on it. A real `*fix-issues` on MyDiary for the blank screens is the natural first fix run. +5. Close the sitting: one `framework-reset` run record, mode `sitting-4c`; propose the 4c Done line for the Reset Plan and wait for my yes; refresh the memory file. + +Method, unchanged: print each task as a table (block, plain summary, verdict: keep as words, script, or delete), questions in a numbered list after the table with a suggested answer, never inside a cell; I rule on every row; write the scripts; prove every script and hook with a real run and show me the output; mirror the file to `.claude/commands/TechieFlow/`; confirm `opencode.jsonc` still points at it; run the command for real in both harnesses. Plain words, no jargon; when I say I did not understand, say it again shorter. Owner-reviewed documents (the plan, How-It-Works, the Stack documents, the Schemas doc) change only after my yes. Every gap found is logged as a miss with one sentence, my own slips included. Every open decision is restated in full at the end of a message as a yes-or-no question, never as a pointer. Fable 5.1 only in this reset session; Sonnet for every long Claude run; OpenCode through `tf-goal.sh --harness opencode --model openai/gpt-5.6-terra`. + +Watch-outs paid for in 4b: + +- The shell's working directory persists between commands. Every script edit and every emit uses the absolute framework path `/mnt/c/3AIGenCode/TechieFlow/…`; a relative path once patched a project copy and once put a framework miss into a copy's stream. +- Never touch a folder with an active supervisor. Check with `pgrep -af tf-goal.sh` before an update or an edit there. +- Start a long supervisor detached (`nohup setsid bash …tf-goal.sh … > log 2>&1 < /dev/null & disown`), because Claude Code kills its own background shells when memory runs low. Watch it by polling its `goal.log` every 30 seconds; `tail -F` fails on `/mnt/c` files. +- `--model` on the supervisor changes only the main agent; sub-agents use the project's routing bindings. When a provider is down, change the project's `routing.yaml` tiers and run `tf-routing.sh bind`. +- OpenCode prints only its header when the provider refuses the model; the reason is in `~/.local/share/opencode/log/opencode.log`. The supervisor now reads it. +- Before saying nothing is running, stop every monitor by its id; the maintainer cannot list them, the owner sees `/tasks`. Name each background shell by its launch line and its folder. +- The framework's own hooks fire in the maintainer's session too: a command whose text writes to PROJECT-STATUS or to `docs/metrics/*.jsonl` is refused even inside a heredoc. Put such test steps in a script file and run the file. + +## Appendix: goal texts to reuse + +### fix-issues on MyDiary (Sonnet, Claude Code) + + Load the flow-master persona (.tfcore/agents/flow-master.md) and run `*fix-issues MyDiary docs/MyDiary-DevGuide.md`, following .tfcore/tasks/fix-issues.md step by step, starting with step 0 (`bash .tfcore/utils/tf-phase.sh start fix-issues MyDiary`). The defect is in the DevGuide's Known issues: on the MAUI Windows head every routed page's content fails to mount inside MainLayout's `@Body` (src/MyDiary/Components/Layout/MainLayout.razor), so all 20 phase-1 screens are blank at runtime. Reproduce it by booting the Windows head (bash .tfcore/utils/tf-build.sh run src/MyDiary/MyDiary.csproj -- -f net10.0-windows10.0.19041.0), fix the root cause, re-smoke every screen, re-verify the touched rows, run the status gate and the run record, then `bash .tfcore/utils/tf-yolo.sh done complete ""`. Builds run in the foreground. Never run git. + +### verify on the user-testing project (both harnesses; replace ) + + Load the verifier persona (.tfcore/agents/verifier.md) and run `*verify all `, following .tfcore/tasks/verify-phase.md step by step, starting with step 0 (`bash .tfcore/utils/tf-phase.sh start verify-phase `). Boot the application yourself, apply the seven checks to every row in scope in the fixed order, record the first failing check per row in gates.jsonl, write `Verified` only for a row you observed, state the observation in the Remark, write docs/.last-verify.json, run the status gate and the run record, then `bash .tfcore/utils/tf-yolo.sh done complete ""`. Never run git. diff --git a/docs/TechieFlow-Telemetry-Explained.html b/docs/TechieFlow-Telemetry-Explained.html new file mode 100644 index 0000000..0e37ec8 --- /dev/null +++ b/docs/TechieFlow-Telemetry-Explained.html @@ -0,0 +1,431 @@ + + + + + +TechieFlow — Telemetry Explained + + + + + +
    + +
    +

    TechieFlow — Telemetry Explained

    +
    Rendered 2026-09-07 · source TechieFlow-Telemetry-Explained.md
    + + + + + + + + + + +
    PurposeThe five numbers the framework's report prints, each in plain words: what it means, how it is worked out, one real figure from all the projects together and one from a named project, and the sentence the owner says about it on stage.
    AudienceThe owner, for talks, blog posts and interviews. Anyone who asks "how do you know".
    StatusWritten 2026-09-07 in Session 5 of the reset, from the streams as they stood that day. The owner rewrites any stage sentence they would not say. Figures are re-read from the report before every use; the ones here are a snapshot.
    CompanionTechieFlow-How-It-Works.md §6 (what the streams are), .tfcore/telemetry/SCHEMA.md (every field), TechieFlow-Reset-Plan-2026-09-04.md (Session 5).
    +
    +

    0. Where the numbers come from, and how to answer "how do you know"#

    +

    Every project the framework runs in keeps five files under docs/metrics/. One line is added per event and no line is ever edited: one line per command run, one per requirement graded in a verify, one per miss and one more when it is fixed, one per chat session, one per commit. A script, tf-metrics.sh, reads the files and prints the five numbers below. It refuses to mix things that must not be mixed: figures reconstructed after the fact never pool with figures written at the time, an app never pools with a library, and a miss whose origin is guessed never enters a per-model figure. It prints insufficient data instead of a number when fewer than three records support it.

    +

    The figures in this page were read on 2026-09-07 from nine repositories: TfLens, TechieBlog, TechieRag, TrBlazeUI, Lekhak, AppManager, MyDiary, Xpenser and the framework itself. The OpenCode test copies (TfLens-oc, TechieBlog-oc, MyDiary-oc) are left out, because each carries a copy of its original's history and would count it twice. To re-read them:

    +
    bash .tfcore/telemetry/tf-metrics.sh --rollup <repo> <repo> …      # all together, still segmented
    +bash .tfcore/telemetry/tf-metrics.sh --report <repo>               # one project
    +

    Two facts to say before any number:

    +
      +
    • Tokens, not dollars. Claude Code reports tokens and never a price, so the framework never converts. Dollars appear only on OpenCode runs, where the harness measured them. Every token figure is output tokens, counted from the harness's own transcript inside the run's time window; a run whose window could not be read is left out, never counted as zero. +
    • +
    • The unit is the run, not the feature. "A build phase costs three hours" is a fact the streams hold. "Requirement 14 took two hours" is not, and nothing here will produce it. +
    • +
    +

    Three caveats that belong beside the figures they touch are marked below with ⚠.

    +
    +

    1. First-pass rate#

    +

    What it means. Of all the requirements the verifier has ever graded, the share that passed the first time they were verified. It measures how often the agent gets a requirement right without rework.

    +

    How it is worked out. Every verify writes one line per requirement with an attempt number: 1 the first time that requirement is graded, 2 the next, and so on. A requirement counts as first-pass when its attempt-1 line says Verified. The rate is first-pass requirements divided by requirements graded, per project type, over live records only. A requirement is identified by its project and its id together, because every project has a REQ-UI-001 (this was wrong until 2026-09-07 and printed 72%; the corrected figure is below).

    + + + + + + + + + + +
    WhereRequirements gradedPassed first timeFirst-pass rate
    All apps together42020148%
    TfLens (the metrics dashboard, seven screens)1783620%
    TechieBlog15512581%
    Lekhak542954%
    MyDiary (first project on the reset framework)27726%
    TfLens's first period, when it was a documents-only project11310694%, reported apart
    +

    ⚠ TfLens is the outlier and the reason the reset happened: 178 requirements for a seven-screen app, written before the acceptance line had a fixed shape, so the verifier and the builder read the same line differently.

    +

    On stage. "Across my apps, about half of the requirements passed verification the first time. On TfLens it was one in five; on TechieBlog four in five. The difference was the specification, not the model: TfLens had a hundred and seventy-eight requirements for a seven-screen app, and the acceptance lines had no fixed shape."

    +
    +

    2. Which check caught it#

    +

    What it means. The verifier applies seven checks to every requirement, always in the same order: does it build, does its acceptance test pass, does the screen show real data, does it look right against the mockup, did the stylesheet and scripts load, is it within its speed budget, does the code follow the standards. The first check to fail is written down. Counting those first failures shows which checks do the work. A failure found by a person after every check passed is written as escaped.

    +

    How it is worked out. Every failed grading line names the check that failed. The distribution is the count per check over all failures, per project type, live records only. A check added after the stream started (speed, assets, mockup comparison) is also reported against the records that actually ran it, so its share is not understated.

    + + + + + + + + + + + + +
    CheckAll apps (293 failures)TfLens (96)Lekhak (28)
    acceptance test127 (43%) ⚠128
    escaped, a person found it45 (15%)220
    no check named44 (15%)440
    data present (render)31 (11%)05
    visual, against the mockup27 (9%)138
    build10 (3%)24
    mockup comparison5 (2%)30
    standards100
    +

    The late checks, over every repository: the speed check has run on 8 records and caught nothing; the assets check on 108 and caught nothing; the mockup comparison on 217 and caught 8.

    +

    ⚠ 107 of the 127 acceptance failures come from one TechieBlog verify on 2026-09-06 that ran against an empty local database with the staff accounts behind a must-change-password screen. Those rows should have been graded "not observable, environment"; the rule now exists (FR-61) and the numbers will look different once it is applied.

    +

    On stage. "When a requirement fails, the acceptance test is what catches it, four times in ten. The visual check and the data check together catch one in five. The speed and assets checks have never caught anything yet, and the mockup comparison caught eight failures in two hundred runs. And one failure in seven was caught by nothing: I found it."

    +
    +

    3. Escape rate#

    +

    What it means. Of the requirements that failed at some point, the share whose failure got past every check and was found by a person, in testing or in production. It says how far the automation can be trusted. A second figure sits beside it, from the miss stream: of all misses, the share found by the owner or by production rather than by a check or an agent's own review. The two are computed from different records and are never merged.

    +

    How it is worked out. From the grading stream: requirements with an escaped line divided by requirements with any failed line, per project type. From the miss stream: misses whose found_by is owner or production divided by all misses.

    + + + + + + + + + + +
    WhereEscape rate (grading stream)Misses found by a person (miss stream)
    All apps together21%32%, 108 of 340 misses across the nine repositories
    TfLens33%49%, 42 of 86
    TechieBlog1%0 of 89 (all found by the verify)
    TrBlazeUI (library)100%, 12 of 1245%, 5 of 11
    MyDiary100%, 20 of 2048%, 20 of 42
    The framework itselfno grading stream36%, 39 of 108
    +

    MyDiary's 100% is one event: the first build's screens were all blank at runtime and the owner found it, not the verifier, which had no driver for the Windows head. TrBlazeUI's 100% is the library case: its verify has no screens of its own to check, so every failure came from a consumer.

    +

    On stage. "One failure in five got past every automated check and was found by me. On TfLens, half of the eighty-six recorded misses were found by me, not by the framework. That number is why the verify task was rewritten."

    +
    +

    4. Misses and rework cost#

    +

    What it means. A miss is one thing an agent got wrong: a requirement built wrongly, half built, never specified, or a rule ignored. One defect is one miss however many times it fails. Each miss records what kind it was, which practice let it through, whose gap it was, who found it, and, when it is fixed, what the fix cost in tokens. Together they show where the process leaks and what each leak costs.

    +

    How it is worked out. Every miss is one record; a fix is a second record linked to it, carrying the output tokens of the run that made the fix. The cost is a measurement only when that run fixed exactly one miss (sole); when one run fixed several, the window is divided equally and reported apart as apportioned. A fix with no run record has no cost and is counted as such, never as free.

    + + + + + + + + + + + +
    All nine repositoriesTfLensThe framework itself
    Misses logged340: 244 open, 95 fixed, 1 will not fix86: 37 open, 49 fixed108: 73 open, 34 fixed
    Which practice failed (of those assessed)the check was too weak 48% · the checklist had a hole 27% · an instruction was ignored 15% · the acceptance line was ambiguous 8%41 · 19 · 5 · 1337 · 37 · 25 · 5
    What kindwrong behaviour 30% · regression 26% ⚠ · half built 20% · never specified 14%half built 29, wrong behaviour 24, never specified 19wrong behaviour 53, never specified 26
    Who found ita check 133 · the owner 108 · an agent's review 81 · a library consumer 17owner 42, agent review 26, check 17agent review 51, owner 39, library 11
    Fix cost, measured (one miss per run)108,671 output tokens per miss, 8 fixes171,804, 4 fixestoo few to say, 2 fixes
    Fix cost, apportioned (several per run)34,417 per miss, 115 fixes39,877, 66 fixes17,745, 18 fixes
    Fixes with no cost recorded21515
    +

    ⚠ 87 of the 90 regressions are the TechieBlog empty-database verify of §2; they are one environment problem logged 87 times, and FR-61 is the answer.

    +

    Per model, per agent. Only misses whose origin run is on record enter this: 139 of 340. By model: claude-opus-5 65, claude-sonnet-5 40 (all from one MyDiary build), unknown 31, gpt-5.6-sol 3. By agent: the TrBlazeUI builder 67, general-purpose builders 39, flow-master 31. This is observational. The hard work went to the expensive model on purpose, so a per-model miss rate is not a ranking.

    +

    Whose gap (from 2026-09-07). Every new miss now answers four questions in order: did the app's spec say it, did the framework say it, was there a check that failed to catch it, was it written and ignored. The answer decides the fix: a checklist line, a requirement line plus a check, a fixed check, or a hook. The first fifty-one misses sorted this way are Session 4's own: the reset's own defects (docs/TechieFlow-Misses.md).

    +

    On stage. "Half of my recorded misses happened because the check was too weak, a quarter because the specification had a hole, and one in seven because the agent ignored an instruction it had just read. Fixing one miss on its own cost about a hundred thousand output tokens. When a fix run repaired several at once, about thirty-five thousand each."

    +
    +

    5. Effort per phase#

    +

    What it means. For each command: how many times it ran, how long it took, how many output tokens it used, on which model, and how much of that went to sub-agents. It shows what each stage of the life cycle costs, and lets a cheap model be compared with an expensive one on the same kind of work.

    +

    How it is worked out. Every command writes one run record with its start and end; the emitter reads the harness's own transcript between those two times and counts the tokens, per model, main thread and sub-agents apart. Medians are over the runs that had a readable window. Fan-out is counted only on runs whose window included the sub-agent transcripts.

    + + + + + + + + + + + + +
    CommandRunsMedian timeMedian output tokensShare of all recorded timeShare of all output
    build-phase232 h 51 min457,000 (10 of 23 measured)44%22%
    verify-phase2930 min98,000 (18 of 29)25%8%
    fix-issues4335 min130,000 (30 of 43)15%19%
    amend-docs920 min91,000 (8 of 9)3%9%
    triage-issues723 min127,000 (5 of 7)1%2%
    mockups325 min99,0001%1%
    log-miss212 min 27 s7,900 (14 of 21)under 1%under 1%
    framework-reset (this reset, 8 sittings)85 h 53 min764,000 (8 of 8)7%27%
    +

    Over the nine repositories: 163 runs, 219 hours of recorded time, 25.1 million output tokens. The build phase spent 38% of its output in sub-agents where that was observed. The reset of the framework, 6.7 million output tokens, cost more output than every build phase together, 5.5 million.

    +

    Named projects: TfLens's eight build runs took a median of 2 h 16 min and 457,000 output tokens each, half of everything TfLens ever spent. TechieBlog's twenty-two fix runs took a median of 37 min and 165,000 tokens each, two thirds of its recorded output. Dollars exist only where OpenCode ran: MyDiary's day-1 on mimo-v2.5 cost $0.06, a deploy checklist $0.05, one log-miss on glm-5.3 $0.23.

    +

    On stage. "A build phase is a three-hour, half-million-token run. A verify is half an hour and a hundred thousand tokens. Logging a miss takes two minutes. And rewriting the framework itself, eight sittings, cost more output tokens than all my build phases put together."

    +
    +

    6. What is not in the numbers yet#

    +
      +
    • Owner reviews. Since 2026-09-06 a review of a phase's output is a record: how many corrections the owner gave, what producing the output cost, what the corrections cost. Two exist (MyDiary day-1). The report prints them; the figure is not yet worth a sentence. +
    • +
    • Idea-stage commands (brainstorm, project brief) write no run record yet, so their cost is unknown. +
    • +
    • Runs whose window could not be read (13 of 23 builds) are outside every token figure. The per-run medians are of the measured ones, and the table says how many that is. +
    • +
    • Dollars for Claude Code will never appear; that is a decision, not a gap. +
    • +
    +
    +
    + + + + + + diff --git a/docs/TechieFlow-Telemetry-Explained.md b/docs/TechieFlow-Telemetry-Explained.md new file mode 100644 index 0000000..789bda8 --- /dev/null +++ b/docs/TechieFlow-Telemetry-Explained.md @@ -0,0 +1,155 @@ +# TechieFlow — Telemetry Explained + +| | | +|---|---| +| Purpose | The five numbers the framework's report prints, each in plain words: what it means, how it is worked out, one real figure from all the projects together and one from a named project, and the sentence the owner says about it on stage. | +| Audience | The owner, for talks, blog posts and interviews. Anyone who asks "how do you know". | +| Status | Written 2026-09-07 in Session 5 of the reset, from the streams as they stood that day. The owner rewrites any stage sentence they would not say. Figures are re-read from the report before every use; the ones here are a snapshot. | +| Companion | `TechieFlow-How-It-Works.md` §6 (what the streams are), `.tfcore/telemetry/SCHEMA.md` (every field), `TechieFlow-Reset-Plan-2026-09-04.md` (Session 5). | + +--- + +## 0. Where the numbers come from, and how to answer "how do you know" + +Every project the framework runs in keeps five files under `docs/metrics/`. One line is added per event and no line is ever edited: one line per command run, one per requirement graded in a verify, one per miss and one more when it is fixed, one per chat session, one per commit. A script, `tf-metrics.sh`, reads the files and prints the five numbers below. It refuses to mix things that must not be mixed: figures reconstructed after the fact never pool with figures written at the time, an app never pools with a library, and a miss whose origin is guessed never enters a per-model figure. It prints `insufficient data` instead of a number when fewer than three records support it. + +The figures in this page were read on 2026-09-07 from nine repositories: TfLens, TechieBlog, TechieRag, TrBlazeUI, Lekhak, AppManager, MyDiary, Xpenser and the framework itself. The OpenCode test copies (`TfLens-oc`, `TechieBlog-oc`, `MyDiary-oc`) are left out, because each carries a copy of its original's history and would count it twice. To re-read them: + +``` +bash .tfcore/telemetry/tf-metrics.sh --rollup … # all together, still segmented +bash .tfcore/telemetry/tf-metrics.sh --report # one project +``` + +Two facts to say before any number: + +- **Tokens, not dollars.** Claude Code reports tokens and never a price, so the framework never converts. Dollars appear only on OpenCode runs, where the harness measured them. Every token figure is output tokens, counted from the harness's own transcript inside the run's time window; a run whose window could not be read is left out, never counted as zero. +- **The unit is the run, not the feature.** "A build phase costs three hours" is a fact the streams hold. "Requirement 14 took two hours" is not, and nothing here will produce it. + +Three caveats that belong beside the figures they touch are marked below with ⚠. + +--- + +## 1. First-pass rate + +**What it means.** Of all the requirements the verifier has ever graded, the share that passed the first time they were verified. It measures how often the agent gets a requirement right without rework. + +**How it is worked out.** Every verify writes one line per requirement with an attempt number: 1 the first time that requirement is graded, 2 the next, and so on. A requirement counts as first-pass when its attempt-1 line says `Verified`. The rate is first-pass requirements divided by requirements graded, per project type, over live records only. A requirement is identified by its project and its id together, because every project has a REQ-UI-001 (this was wrong until 2026-09-07 and printed 72%; the corrected figure is below). + +| Where | Requirements graded | Passed first time | First-pass rate | +|---|---|---|---| +| All apps together | 420 | 201 | 48% | +| TfLens (the metrics dashboard, seven screens) | 178 | 36 | 20% | +| TechieBlog | 155 | 125 | 81% | +| Lekhak | 54 | 29 | 54% | +| MyDiary (first project on the reset framework) | 27 | 7 | 26% | +| TfLens's first period, when it was a documents-only project | 113 | 106 | 94%, reported apart | + +⚠ TfLens is the outlier and the reason the reset happened: 178 requirements for a seven-screen app, written before the acceptance line had a fixed shape, so the verifier and the builder read the same line differently. + +**On stage.** "Across my apps, about half of the requirements passed verification the first time. On TfLens it was one in five; on TechieBlog four in five. The difference was the specification, not the model: TfLens had a hundred and seventy-eight requirements for a seven-screen app, and the acceptance lines had no fixed shape." + +--- + +## 2. Which check caught it + +**What it means.** The verifier applies seven checks to every requirement, always in the same order: does it build, does its acceptance test pass, does the screen show real data, does it look right against the mockup, did the stylesheet and scripts load, is it within its speed budget, does the code follow the standards. The first check to fail is written down. Counting those first failures shows which checks do the work. A failure found by a person after every check passed is written as `escaped`. + +**How it is worked out.** Every failed grading line names the check that failed. The distribution is the count per check over all failures, per project type, live records only. A check added after the stream started (speed, assets, mockup comparison) is also reported against the records that actually ran it, so its share is not understated. + +| Check | All apps (293 failures) | TfLens (96) | Lekhak (28) | +|---|---|---|---| +| acceptance test | 127 (43%) ⚠ | 12 | 8 | +| escaped, a person found it | 45 (15%) | 22 | 0 | +| no check named | 44 (15%) | 44 | 0 | +| data present (render) | 31 (11%) | 0 | 5 | +| visual, against the mockup | 27 (9%) | 13 | 8 | +| build | 10 (3%) | 2 | 4 | +| mockup comparison | 5 (2%) | 3 | 0 | +| standards | 1 | 0 | 0 | + +The late checks, over every repository: the speed check has run on 8 records and caught nothing; the assets check on 108 and caught nothing; the mockup comparison on 217 and caught 8. + +⚠ 107 of the 127 acceptance failures come from one TechieBlog verify on 2026-09-06 that ran against an empty local database with the staff accounts behind a must-change-password screen. Those rows should have been graded "not observable, environment"; the rule now exists (FR-61) and the numbers will look different once it is applied. + +**On stage.** "When a requirement fails, the acceptance test is what catches it, four times in ten. The visual check and the data check together catch one in five. The speed and assets checks have never caught anything yet, and the mockup comparison caught eight failures in two hundred runs. And one failure in seven was caught by nothing: I found it." + +--- + +## 3. Escape rate + +**What it means.** Of the requirements that failed at some point, the share whose failure got past every check and was found by a person, in testing or in production. It says how far the automation can be trusted. A second figure sits beside it, from the miss stream: of all misses, the share found by the owner or by production rather than by a check or an agent's own review. The two are computed from different records and are never merged. + +**How it is worked out.** From the grading stream: requirements with an `escaped` line divided by requirements with any failed line, per project type. From the miss stream: misses whose `found_by` is owner or production divided by all misses. + +| Where | Escape rate (grading stream) | Misses found by a person (miss stream) | +|---|---|---| +| All apps together | 21% | 32%, 108 of 340 misses across the nine repositories | +| TfLens | 33% | 49%, 42 of 86 | +| TechieBlog | 1% | 0 of 89 (all found by the verify) | +| TrBlazeUI (library) | 100%, 12 of 12 | 45%, 5 of 11 | +| MyDiary | 100%, 20 of 20 | 48%, 20 of 42 | +| The framework itself | no grading stream | 36%, 39 of 108 | + +MyDiary's 100% is one event: the first build's screens were all blank at runtime and the owner found it, not the verifier, which had no driver for the Windows head. TrBlazeUI's 100% is the library case: its verify has no screens of its own to check, so every failure came from a consumer. + +**On stage.** "One failure in five got past every automated check and was found by me. On TfLens, half of the eighty-six recorded misses were found by me, not by the framework. That number is why the verify task was rewritten." + +--- + +## 4. Misses and rework cost + +**What it means.** A miss is one thing an agent got wrong: a requirement built wrongly, half built, never specified, or a rule ignored. One defect is one miss however many times it fails. Each miss records what kind it was, which practice let it through, whose gap it was, who found it, and, when it is fixed, what the fix cost in tokens. Together they show where the process leaks and what each leak costs. + +**How it is worked out.** Every miss is one record; a fix is a second record linked to it, carrying the output tokens of the run that made the fix. The cost is a measurement only when that run fixed exactly one miss (`sole`); when one run fixed several, the window is divided equally and reported apart as apportioned. A fix with no run record has no cost and is counted as such, never as free. + +| | All nine repositories | TfLens | The framework itself | +|---|---|---|---| +| Misses logged | 340: 244 open, 95 fixed, 1 will not fix | 86: 37 open, 49 fixed | 108: 73 open, 34 fixed | +| Which practice failed (of those assessed) | the check was too weak 48% · the checklist had a hole 27% · an instruction was ignored 15% · the acceptance line was ambiguous 8% | 41 · 19 · 5 · 13 | 37 · 37 · 25 · 5 | +| What kind | wrong behaviour 30% · regression 26% ⚠ · half built 20% · never specified 14% | half built 29, wrong behaviour 24, never specified 19 | wrong behaviour 53, never specified 26 | +| Who found it | a check 133 · the owner 108 · an agent's review 81 · a library consumer 17 | owner 42, agent review 26, check 17 | agent review 51, owner 39, library 11 | +| Fix cost, measured (one miss per run) | 108,671 output tokens per miss, 8 fixes | 171,804, 4 fixes | too few to say, 2 fixes | +| Fix cost, apportioned (several per run) | 34,417 per miss, 115 fixes | 39,877, 66 fixes | 17,745, 18 fixes | +| Fixes with no cost recorded | 21 | 5 | 15 | + +⚠ 87 of the 90 regressions are the TechieBlog empty-database verify of §2; they are one environment problem logged 87 times, and FR-61 is the answer. + +**Per model, per agent.** Only misses whose origin run is on record enter this: 139 of 340. By model: claude-opus-5 65, claude-sonnet-5 40 (all from one MyDiary build), unknown 31, gpt-5.6-sol 3. By agent: the TrBlazeUI builder 67, general-purpose builders 39, flow-master 31. This is observational. The hard work went to the expensive model on purpose, so a per-model miss rate is not a ranking. + +**Whose gap (from 2026-09-07).** Every new miss now answers four questions in order: did the app's spec say it, did the framework say it, was there a check that failed to catch it, was it written and ignored. The answer decides the fix: a checklist line, a requirement line plus a check, a fixed check, or a hook. The first fifty-one misses sorted this way are Session 4's own: the reset's own defects (`docs/TechieFlow-Misses.md`). + +**On stage.** "Half of my recorded misses happened because the check was too weak, a quarter because the specification had a hole, and one in seven because the agent ignored an instruction it had just read. Fixing one miss on its own cost about a hundred thousand output tokens. When a fix run repaired several at once, about thirty-five thousand each." + +--- + +## 5. Effort per phase + +**What it means.** For each command: how many times it ran, how long it took, how many output tokens it used, on which model, and how much of that went to sub-agents. It shows what each stage of the life cycle costs, and lets a cheap model be compared with an expensive one on the same kind of work. + +**How it is worked out.** Every command writes one run record with its start and end; the emitter reads the harness's own transcript between those two times and counts the tokens, per model, main thread and sub-agents apart. Medians are over the runs that had a readable window. Fan-out is counted only on runs whose window included the sub-agent transcripts. + +| Command | Runs | Median time | Median output tokens | Share of all recorded time | Share of all output | +|---|---|---|---|---|---| +| build-phase | 23 | 2 h 51 min | 457,000 (10 of 23 measured) | 44% | 22% | +| verify-phase | 29 | 30 min | 98,000 (18 of 29) | 25% | 8% | +| fix-issues | 43 | 35 min | 130,000 (30 of 43) | 15% | 19% | +| amend-docs | 9 | 20 min | 91,000 (8 of 9) | 3% | 9% | +| triage-issues | 7 | 23 min | 127,000 (5 of 7) | 1% | 2% | +| mockups | 3 | 25 min | 99,000 | 1% | 1% | +| log-miss | 21 | 2 min 27 s | 7,900 (14 of 21) | under 1% | under 1% | +| framework-reset (this reset, 8 sittings) | 8 | 5 h 53 min | 764,000 (8 of 8) | 7% | 27% | + +Over the nine repositories: 163 runs, 219 hours of recorded time, 25.1 million output tokens. The build phase spent 38% of its output in sub-agents where that was observed. The reset of the framework, 6.7 million output tokens, cost more output than every build phase together, 5.5 million. + +Named projects: TfLens's eight build runs took a median of 2 h 16 min and 457,000 output tokens each, half of everything TfLens ever spent. TechieBlog's twenty-two fix runs took a median of 37 min and 165,000 tokens each, two thirds of its recorded output. Dollars exist only where OpenCode ran: MyDiary's day-1 on mimo-v2.5 cost $0.06, a deploy checklist $0.05, one log-miss on glm-5.3 $0.23. + +**On stage.** "A build phase is a three-hour, half-million-token run. A verify is half an hour and a hundred thousand tokens. Logging a miss takes two minutes. And rewriting the framework itself, eight sittings, cost more output tokens than all my build phases put together." + +--- + +## 6. What is not in the numbers yet + +- **Owner reviews.** Since 2026-09-06 a review of a phase's output is a record: how many corrections the owner gave, what producing the output cost, what the corrections cost. Two exist (MyDiary day-1). The report prints them; the figure is not yet worth a sentence. +- **Idea-stage commands** (brainstorm, project brief) write no run record yet, so their cost is unknown. +- **Runs whose window could not be read** (13 of 23 builds) are outside every token figure. The per-run medians are of the measured ones, and the table says how many that is. +- **Dollars for Claude Code** will never appear; that is a decision, not a gap. diff --git a/docs/metrics/README.md b/docs/metrics/README.md index b92dbe0..7e710ed 100644 --- a/docs/metrics/README.md +++ b/docs/metrics/README.md @@ -16,6 +16,9 @@ after the fact. was missed, which phase/agent/model let it through, who found it) and `miss-fix` (closed: the repair run and its token/cost window, linked by `miss_id`). It is what makes "how much did that miss cost to fix" answerable. SCHEMA.md §5.5. +The readable version is `docs/-Misses.md`: one row per miss with the owner's +sentence and whose gap it was, rebuilt from this stream after every miss record +(SCHEMA.md §5.5.10). Read that file; never edit it. Schema, enums, and every known limitation: `.tfcore/telemetry/SCHEMA.md`. Report: `/TechieFlow:agents:flow-master *metrics ` (OpenCode: `/flow-master *metrics `) → `METRICS.md`. diff --git a/docs/metrics/commits.jsonl b/docs/metrics/commits.jsonl index ff3c8fc..d7cbd74 100644 --- a/docs/metrics/commits.jsonl +++ b/docs/metrics/commits.jsonl @@ -36,3 +36,4 @@ {"v":1,"ts":"2026-08-29T08:11:20Z","kind":"commit","app":"TechieFlow","sha":"e73f546","files":2,"insertions":2,"deletions":0,"subject_prefix":null,"branch":"main","project_type":"framework","harness":null} {"v":1,"ts":"2026-08-31T14:03:05Z","kind":"commit","app":"TechieFlow","sha":"7ee76b3","files":27,"insertions":2425,"deletions":936,"subject_prefix":null,"branch":"main","project_type":"framework","harness":null} {"v":1,"ts":"2026-09-04T17:03:13Z","kind":"commit","app":"TechieFlow","sha":"53a4597","files":12,"insertions":2348,"deletions":0,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-04T19:48:27Z","kind":"commit","app":"TechieFlow","sha":"b77c2ce","files":65,"insertions":4179,"deletions":1112,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} diff --git a/docs/metrics/misses.jsonl b/docs/metrics/misses.jsonl index 9f8e724..1b83b88 100644 --- a/docs/metrics/misses.jsonl +++ b/docs/metrics/misses.jsonl @@ -80,3 +80,175 @@ {"kind":"miss","miss_id":"MISS-TechieFlow-20260904-24","app":"TechieFlow","project_type":"framework","harness":"claude-code","req_id":null,"req_class":null,"miss_class":"missed-requirement","artifact":"other","severity":"major","origin_phase":"verify-phase","origin_agent":"verifier","why_missed":"insufficient-verify-method","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-04T16:58:53Z","v":1,"ts":"2026-09-04T19:13:37Z","origin_confidence":"inferred","origin_model":null,"origin_harness":null} {"kind":"miss","miss_id":"MISS-TechieFlow-20260904-25","app":"TechieFlow","project_type":"framework","harness":"claude-code","req_id":null,"req_class":null,"miss_class":"unspecified-gap","artifact":"other","severity":"major","origin_phase":"day1-brownfield","origin_agent":"analyst","why_missed":"missing-checklist-item","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-04T16:58:53Z","v":1,"ts":"2026-09-04T19:13:37Z","origin_confidence":"inferred","origin_model":null,"origin_harness":null} {"kind":"miss","miss_id":"MISS-TechieFlow-20260904-26","app":"TechieFlow","project_type":"framework","harness":"claude-code","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","origin_phase":"refresh-status","origin_agent":"flow-master","why_missed":"insufficient-verify-method","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-04T16:58:53Z","v":1,"ts":"2026-09-04T19:13:37Z","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"minor","origin_phase":null,"origin_agent":"general","why_missed":"instruction-ignored","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","what":"The first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule.","v":1,"ts":"2026-09-05T05:29:23Z","harness":"claude-code","origin_confidence":"unknown","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-01","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":null,"origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","req_id":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"instruction-ignored","found_by":"owner","what":"The first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule.","v":1,"ts":"2026-09-05T05:30:01Z","harness":"claude-code","origin_confidence":"unknown","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-02","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":null,"origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","req_id":"FR-40","miss_class":"partial-implementation","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","found_by":"agent-review","what":"Six task files edited in .tfcore on 2026-08-31 were never copied to the Claude Code mirror, so the two harnesses ran different smoke, metrics, mockup, render and verify rules for five days; no parity check ran.","v":1,"ts":"2026-09-05T05:30:01Z","harness":"claude-code","origin_confidence":"unknown","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-03","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":null,"origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","req_id":"FR-39","miss_class":"partial-implementation","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","found_by":"agent-review","what":"tf-emit.sh appends a miss record that has no miss_id, although miss_id is the join key to its fix record, so a caller that skips --next-miss-id writes an orphan.","v":1,"ts":"2026-09-05T05:30:01Z","harness":"claude-code","origin_confidence":"unknown","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-04","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":null,"origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","req_id":"FR-41","miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"ambiguous-acceptance","found_by":"owner","what":"The YOLO rule and the flow-master run-workflow command say YOLO logs a phase boundary and continues, but the owner's rule is that YOLO runs one command to completion and never crosses an owner review into the next phase; fixed in 4c when YOLO is made uniform.","v":1,"ts":"2026-09-05T06:42:00Z","harness":"claude-code","origin_confidence":"unknown","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-05","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"mockups","origin_agent":"analyst","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","req_id":null,"miss_class":"unspecified-gap","artifact":"uidesign","severity":"major","why_missed":"missing-checklist-item","found_by":"owner","what":"Mockups were repeatedly delivered as unlinked HTML files with no navigation and dead buttons; nothing in the framework required a click-through set where every link and button behaves.","v":1,"ts":"2026-09-05T07:07:14Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-06","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":null,"origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","req_id":"FR-14","miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"insufficient-verify-method","found_by":"agent-review","what":"The checker compared BRD screen names with their qualifier attached, so 'Profile (planned)' and 'Profile' were reported as two different screens during the Xpenser brownfield run; found by reading the run log, fixed the same hour.","v":1,"ts":"2026-09-05T08:28:43Z","harness":"claude-code","origin_confidence":"unknown","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-07","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":null,"origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","req_id":"FR-39","miss_class":"partial-implementation","artifact":"other","severity":"minor","why_missed":"insufficient-verify-method","found_by":"agent-review","what":"The emitter accepted a run record whose build_result was free text ('PASS (API+Web, DbMigration excluded)') instead of pass, fail or not-run, in the Xpenser OpenCode run; it now refuses it.","v":1,"ts":"2026-09-05T09:58:02Z","harness":"claude-code","origin_confidence":"unknown","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-08","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":null,"origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","req_id":"FR-41","miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"instruction-ignored","found_by":"owner","what":"The unattended Xpenser day-1 run was launched with a bare claude -p instead of the goal supervisor tf-goal.sh, so the usage-limit halt was not survived automatically; unattended runs go through the supervisor, and the YOLO rule will say so in 4c.","v":1,"ts":"2026-09-05T09:59:28Z","harness":"claude-code","origin_confidence":"unknown","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-09","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"day1-brownfield","origin_agent":"analyst","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T05:15:20Z","req_id":"FR-21","miss_class":"scope-creep","artifact":"other","severity":"major","why_missed":"instruction-ignored","found_by":"agent-review","what":"The Xpenser day-1 run in Claude Code created a test user and patched two stored procedures in the development database although the task says create no user and day-1 writes documents only; a hook that refuses database writes outside build and fix is the candidate fix.","v":1,"ts":"2026-09-05T10:19:08Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-10","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"amend-docs","origin_agent":"analyst","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-10","miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"ambiguous-acceptance","found_by":"agent-review","what":"amend-docs proposed a phase split whenever a project passed its size cap, so a Small project growing past 50 requirements (Xpenser with family scope) would have been split into phases instead of first becoming Medium; the split belongs only past Medium, and FR-10 and D-3 did not say which cap.","v":1,"ts":"2026-09-05T16:06:34Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-11","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"day1-greenfield","origin_agent":"analyst","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-39","miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","found_by":"agent-review","what":"The MyDiary day-1 run in OpenCode wrote a run record whose ended time (17:05) was 25 minutes after the record was appended (16:40), an invented duration; the emitter accepted it, so it must set ended to now when it lies in the future or before started.","v":1,"ts":"2026-09-05T16:42:22Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-12","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-14","miss_class":"partial-implementation","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","found_by":"agent-review","what":"The checker counted only the bold BRD-N ledger items and ignored the ids in the Non-functional table, so seven MyDiary NFR requirements escaped the cap, the checklist cross-check and the phase range check until the reading of the output found them.","v":1,"ts":"2026-09-05T16:42:22Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-13","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"day1-greenfield","origin_agent":"analyst","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-15","miss_class":"unspecified-gap","artifact":"brd","severity":"major","why_missed":"missing-checklist-item","found_by":"agent-review","what":"The MyDiary BRD from MiMo packs four or five testable behaviours into one acceptance line (slots shown; Enter advances; empty slots dropped; timer starts), so 50 items stand where about 100 belong and the verifier grades a bundle; nothing in the checker sees a bundled then-clause.","v":1,"ts":"2026-09-05T16:42:22Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-14","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":null,"miss_class":"other","artifact":"other","severity":"minor","why_missed":"instruction-ignored","found_by":"owner","what":"The maintainer ended a report with a one-line pointer to three open decisions made two messages earlier; the owner called it dense chatting and could not tell what was being asked, so every open decision is now restated in full.","v":1,"ts":"2026-09-06T04:06:13Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-15","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-53","miss_class":"partial-implementation","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","found_by":"owner","what":"The mockup click-through check passed the MiMo set although its menus navigated by script, its Settings item went nowhere and thirteen screens could not be reached by clicking; the check resolved links by file name and counted a script as a link, so the owner found the set broken by hand.","v":1,"ts":"2026-09-06T04:06:13Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-16","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-14","miss_class":"partial-implementation","artifact":"architecture","severity":"major","why_missed":"insufficient-verify-method","found_by":"owner","what":"The checker only asks whether the Architecture has a Stack decisions table, so the MiMo run named the head MyDiary.App against the .NET answer set and left questions out of the table, and the owner found both by reading; a row per stack question and the head named exactly are the candidate checks.","v":1,"ts":"2026-09-06T04:08:16Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-17","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":null,"origin_agent":"analyst","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-01","miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"missing-checklist-item","found_by":"owner","what":"The analyst brainstorm and project-brief steps proposed project names (src/MyDiary.App) without reading the stack answer set, so the MiMo day-1 copied a banned name from the brief; the brief template asks for repository thoughts and the idea-stage commands have no run record kind, so nothing checked it.","v":1,"ts":"2026-09-06T04:47:31Z","harness":"claude-code","origin_confidence":"unknown","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-18","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-54","miss_class":"unspecified-gap","artifact":"other","severity":"minor","why_missed":"missing-checklist-item","found_by":"agent-review","what":"The Phases table allowed one id range per phase, so an item added to phase 1 after phase 2 existed had no legal id; a row may now carry several ranges and the checker reads them all.","v":1,"ts":"2026-09-06T05:01:05Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-19","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":null,"miss_class":"other","artifact":"other","severity":"minor","why_missed":"instruction-ignored","found_by":"owner","what":"The maintainer compressed a Session 3 decision (a service library's map lives in the UsageGuide) into half a table cell and a decision line, so the owner could not see where it came from or why, and questioned it as invented; a decision is restated with its source and reason, never as a half sentence.","v":1,"ts":"2026-09-06T05:37:35Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-20","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"day1-greenfield","origin_agent":"analyst","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-55","miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"instruction-ignored","found_by":"agent-review","what":"The MiMo stage 2 run on the MyDiary copy ran step 0 last, so its run record says it started at 05:37 and ended at 05:38 after 38 minutes of work, and the review record copied that as the cost to correct; the supervisor now writes the start marker when its first cycle begins and the command claims it.","v":1,"ts":"2026-09-06T05:40:06Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-21","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":null,"miss_class":"scope-creep","artifact":"other","severity":"minor","why_missed":"instruction-ignored","found_by":"agent-review","what":"The maintainer ran the supervisor in dry-run mode against TfLens while a real run was active there, which rewrote the live goal.json and appended a fake cycle line to its log; a dry run must never touch a folder with an active run, and the supervisor should refuse it.","v":1,"ts":"2026-09-06T05:41:08Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-22","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-40","miss_class":"unspecified-gap","artifact":"other","severity":"major","why_missed":"missing-checklist-item","found_by":"agent-review","what":"The OpenCode run of deploy-checklist on TfLens initialised and then produced no output for 43 minutes, and the supervisor has no stall watchdog, so it waited forever; a cycle whose output does not grow for fifteen minutes must be killed and re-prompted, and the OpenCode hang on this repository is unexplained.","v":1,"ts":"2026-09-06T06:25:18Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-23","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"build-phase","origin_agent":"flow-master","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":"FR-41","miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"instruction-ignored","found_by":"agent-review","what":"The MyDiary build agent started the build as a background task and ended its turn with \"Waiting for the build to finish\", which killed the task; the supervisor then labelled the clean early stop a harness error and backed off 120 then 240 seconds instead of re-prompting after 30; an early stop with exit 0 is a stop, not a crash, and a task never waits on a background job.","v":1,"ts":"2026-09-06T06:34:27Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260905-24","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-05T15:20:00Z","req_id":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"other","found_by":"owner","what":"Asked which of the three background shells in Claude Code was doing what, the maintainer listed operating-system processes instead and called the interactive session a third shell; the owner reads the harness shell list, so a running-work report names each background command by its launch line and its folder.","v":1,"ts":"2026-09-06T09:05:54Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","severity":"major","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-01","req_id":null,"why_missed":"insufficient-verify-method","what":"The supervisor's crash pattern api_error also matched the harmless field api_error_status that every clean Claude result line carries, so cycles 5 to 7 of the MyDiary build, each a clean early stop, were called harness errors and backed off 2, 4 and 8 minutes instead of being re-prompted after 30 seconds; the classifier now reads the result line first.","v":1,"ts":"2026-09-06T09:47:04Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","severity":"major","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-02","req_id":null,"why_missed":"missing-checklist-item","what":"Killing the supervisor with TERM ran a trap that only cleared the YOLO flag and did not exit, and bash held the signal until the running sleep ended, so the stopped MyDiary supervisor woke, launched cycle 8 without its flag and left a harness child to be killed by hand; the trap now stops the child, records stopped and exits 130, and every sleep is interruptible.","v":1,"ts":"2026-09-06T09:47:04Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","severity":"major","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-03","req_id":null,"why_missed":"insufficient-verify-method","what":"The build ladder counted only CS, MSB and NU codes as code errors, so a MyDiary build failing on sixteen Razor RZ9991 errors was taken for a wrong rung on every rung and reported NOT-RUN, a host issue and never a project blocker, while the agent carried on as if the code were fine; RZ, BL and XAML codes now count.","v":1,"ts":"2026-09-06T09:47:04Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","severity":"minor","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-04","req_id":null,"why_missed":"missing-checklist-item","what":"The emitter named the app from the one *-Checklist.md in docs, so once TfLens-oc had a Deployment Checklist beside it the run record and the next miss id fell back to the folder name TfLens-oc, and a Large project's phase-2 checklist would do the same; deployment and phase-n checklists are now excluded.","v":1,"ts":"2026-09-06T09:47:04Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","severity":"major","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-05","req_id":"FR-46","why_missed":"instruction-ignored","what":"For the second time after miss 23 the Sonnet build agent started the build as a background job and ended its turn with Waiting for the build, three cycles in a row, and each turn end killed the job; the supervisor's prompt now says builds run in the foreground, and the second occurrence makes this a hook candidate under FR-46.","v":1,"ts":"2026-09-06T09:47:04Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-05","req_id":"FR-46","fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"cost_attribution":"shared:5","v":1,"ts":"2026-09-06T10:03:20Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_scope":"none"} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-06","req_id":"FR-14","severity":"major","why_missed":"insufficient-verify-method","what":"The MyDiary build closed its status gate with three checklist Remarks cells over 60 words (REQ-NFR-006 to 008), because the Stop hook ran the checker on PROJECT-STATUS only and the agent skipped step 7b on the checklist; the hook now checks every checklist written in the session.","v":1,"ts":"2026-09-06T10:29:25Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-07","req_id":"FR-18","severity":"major","why_missed":"missing-checklist-item","what":"The smoke policy names a desktop and a mobile browser width and no evidence file, and FR-18's check reads a smoke log that no task writes, so the MyDiary MAUI build wrote no smoke evidence at all and marked 69 rows Implemented on a code trace against the mockups; the policy needs a native-head path and a named evidence file.","v":1,"ts":"2026-09-06T10:29:25Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-08","req_id":"FR-41","severity":"minor","why_missed":"instruction-ignored","what":"The MyDiary build agent wrote the sentinel as complete with 69 of 77 rows still Implemented, although the yolo rule says blocked when everything left needs the owner, and here the owner must set up Appium or FlaUI before any UI row can be verified; the honest outcome was blocked.","v":1,"ts":"2026-09-06T10:29:25Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-09","req_id":null,"severity":"major","why_missed":"insufficient-verify-method","what":"On the MyDiary copy the glm-5.2 build's first cycle went silent after 15 minutes and both continue cycles (opencode run -c) printed only their header for 15 minutes each, so a resumed OpenCode session can hang forever; the new stall watchdog caught all three, and the supervisor now starts a fresh session after two stalled resumes and accepts --resume --fresh.","v":1,"ts":"2026-09-06T11:12:10Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-10","req_id":"FR-55","severity":"minor","why_missed":"missing-checklist-item","what":"The devguide and refresh-status run records on MyDiary carried no duration_s because the tasks left it out and the emitter only recomputed one that was present; the emitter now derives it from started and ended.","v":1,"ts":"2026-09-06T11:12:10Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-06","req_id":"FR-14","fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"cost_attribution":"shared:5","v":1,"ts":"2026-09-06T11:12:10Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_scope":"none"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-09","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"cost_attribution":"shared:5","v":1,"ts":"2026-09-06T11:41:22Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_scope":"none"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-10","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"cost_attribution":"shared:5","v":1,"ts":"2026-09-06T11:41:22Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_scope":"none"} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-11","req_id":null,"severity":"major","why_missed":"insufficient-verify-method","what":"When the opencode-go provider refuses a model (monthly usage limit reached, resets in six days) opencode run prints only its header and waits, and the refusal appears only in the OpenCode log file, so three fifteen-minute stalls on MyDiary-oc and a one-line probe looked like hangs; the supervisor now reads that log after a silent stall and stops with exit 5 and the provider message.","v":1,"ts":"2026-09-06T12:05:34Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-12","req_id":null,"severity":"minor","why_missed":"instruction-ignored","what":"The maintainer ran a relative-path patch and a miss emit from whatever folder the previous command had left as the working directory, so a supervisor patch landed in the MyDiary copy and a framework miss record landed in TfLens-oc's stream; every script edit and emit now uses the absolute framework path.","v":1,"ts":"2026-09-06T12:05:34Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-11","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"cost_attribution":"shared:5","v":1,"ts":"2026-09-06T12:05:34Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_scope":"none"} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-13","req_id":"FR-20","severity":"major","why_missed":"instruction-ignored","what":"The OpenCode build on the MyDiary copy (gpt-5.6-terra) wrote code across three fresh sessions, built and tested it, then wrote the sentinel blocked with all 87 rows Not Started, the status file untouched since day-1 and no build-phase run record, because nothing mechanical tied the sentinel to the status gate; tf-yolo.sh done now refuses the sentinel until the status file and a run record are newer than the goal start.","v":1,"ts":"2026-09-06T13:39:34Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-14","req_id":"FR-40","severity":"blocker","why_missed":"missing-checklist-item","what":"OpenAI models in OpenCode edit files only through apply_patch, and the OpenCode plugin refused every apply_patch that touched the checklist or the status file and told the agent to use edit or write tools it does not have, so no OpenAI-model run could ever update a row or the status; the plugin now maps an apply_patch onto the same write guards per file instead of refusing it.","v":1,"ts":"2026-09-06T13:39:34Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-13","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"cost_attribution":"shared:5","v":1,"ts":"2026-09-06T13:43:49Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_scope":"none"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-14","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"cost_attribution":"shared:5","v":1,"ts":"2026-09-06T13:43:49Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_scope":"none"} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"agent-review","miss_id":"MISS-TechieFlow-20260906-15","req_id":"FR-20","severity":"major","why_missed":"insufficient-verify-method","what":"The second OpenCode build on the MyDiary copy (gpt-5.6-terra) marked 85 rows Implemented, ran the verifier on 28 rows and wrote the status file, but left no build-phase run record and no gate records, and the new sentinel guard accepted it because any run record since the goal start satisfied it; the guard now requires a run record for the command the phase marker names.","v":1,"ts":"2026-09-06T14:27:25Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","app":"TechieFlow","project_type":"framework","req_class":null,"origin_phase":"framework-reset","origin_agent":"general","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T09:16:00Z","miss_class":"wrong-behaviour","artifact":"other","found_by":"owner","miss_id":"MISS-TechieFlow-20260906-16","req_id":null,"severity":"minor","why_missed":"instruction-ignored","what":"The maintainer reported no monitors running while the watch on the MyDiary devguide run from 10:28 was still alive after five hours, because it stopped only the monitors it remembered instead of listing them; a running-work report is read from the harness list, never from memory (second occurrence after miss 24 of 2026-09-05).","v":1,"ts":"2026-09-06T15:57:07Z","harness":"claude-code","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-17","req_id":null,"req_class":null,"miss_class":"unspecified-gap","artifact":"other","severity":"major","why_missed":"missing-checklist-item","origin_phase":"framework-reset","origin_agent":"general","found_by":"agent-review","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T18:21:28Z","what":"The old verify task described driving MAUI Android, iOS and Mac Catalyst heads over Appium with the same render and visual checks, but no script in the framework ever implemented that drive, so the prose promised a verify that could not happen; the shrunk task now says those heads have no driver yet and their rows are recorded as not verified.","v":1,"ts":"2026-09-06T18:21:28Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-18","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"insufficient-verify-method","origin_phase":"framework-reset","origin_agent":"general","found_by":"agent-review","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T18:21:28Z","what":"The checklist entry parser in tf-build-list.py read past the end of a row's detail entry into the next one when the next entry started with a list dash, so a row with no mockup or BRD id of its own could inherit the next row's; the verify self-test caught the same bug in the new list script, and both now stop at the next entry.","v":1,"ts":"2026-09-06T18:21:28Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-19","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"blocker","why_missed":"missing-checklist-item","origin_phase":"framework-reset","origin_agent":"general","found_by":"agent-review","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T18:28:58Z","what":"The Stop hook and the status gate ran the document checker on every checklist a command wrote and blocked on any FAIL line, so on an existing project whose checklist predates the schemas (TechieBlog: 376 old findings) no verify or build could ever end its turn without repairing rows it must not touch, against the Session 3 decision that old findings warn and are repaired through amend-docs; tf-phase.sh start now records a baseline of the findings present when a command starts and the checker prints those as OLD and blocks only on new ones.","v":1,"ts":"2026-09-06T18:28:58Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-20","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"insufficient-verify-method","origin_phase":"framework-reset","origin_agent":"general","found_by":"gate","found_phase":"verify-phase","found_gate":null,"found_run_id":"2026-09-06T18:44:43Z","what":"The database guard matched the name of the migration tool inside an echo string and refused a verify run's read of two seed SQL files and a select on TechieBlog, because it matched words anywhere in the command text; it now strips echo and printf strings and comment lines before matching, so only a command that runs a migrator or writes through a client is refused.","v":1,"ts":"2026-09-06T18:44:43Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-21","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"framework-reset","origin_agent":"general","found_by":"gate","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T18:58:04Z","what":"The document checker cut a checklist detail entry at the next anchor only when no list dash preceded it, so two consecutive list-item entries were read as one and a row could borrow the next row's BRD id or acceptance line (the third parser with this boundary bug after tf-build-list.py and the new list script, miss 18); the bugs self-test caught it on a row triage added, and the checker now stops at a dash-prefixed anchor too.","v":1,"ts":"2026-09-06T18:58:05Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-22","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"instruction-ignored","origin_phase":"framework-reset","origin_agent":"general","found_by":"gate","found_phase":"verify-phase","found_gate":null,"found_run_id":"2026-09-06T19:03:27Z","what":"The TechieBlog verify (Sonnet) started the browser tests in the background and ended its turn waiting for them, so the supervisor had to re-prompt a second cycle; the build guard knew only dotnet, msbuild and npm verbs, not npx playwright test or the verify scripts, and now refuses those backgrounded too (third occurrence of the backgrounded-run failure after misses 23 of 2026-09-05 and 05 of 2026-09-06).","v":1,"ts":"2026-09-06T19:03:28Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-23","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"framework-reset","origin_agent":"general","found_by":"gate","found_phase":"fix-issues","found_gate":null,"found_run_id":"2026-09-06T19:17:16Z","what":"The verify boot script wrapped the Windows project path in quote characters inside the cmd.exe command, and the WSL bridge handed those quotes to dotnet as part of the path, so the MyDiary Windows head never started and the fix agent spent its first cycle launching it by hand; the self-test could not catch it because the fixture has no Windows head, and the path is now passed bare unless it holds a space.","v":1,"ts":"2026-09-06T19:17:16Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-24","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"instruction-ignored","origin_phase":"framework-reset","origin_agent":"general","found_by":"agent-review","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-06T19:23:48Z","what":"The maintainer ran a process clean-up that filtered by working directory while its own shell had drifted into the MyDiary folder from an earlier cd, and killed its own command (exit 144); the 4b watch-out that the shell's working directory persists between commands was repeated, so every clean-up now runs from a script file that excludes its own shell and every command names absolute paths.","v":1,"ts":"2026-09-06T19:23:48Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-25","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"framework-reset","origin_agent":"general","found_by":"gate","found_phase":"fix-issues","found_gate":null,"found_run_id":"2026-09-06T21:08:17Z","what":"The fix close script passed the verify ledger's raw verdict (RENDER-FAIL) as a miss-fix verdict_after, a field whose vocabulary is the five checklist statuses, so the emitter refused all twenty miss-fix records of the MyDiary fix run and the cost of the fix was not attached to any miss; the self-test had covered only PASS and FAIL, and the script now maps every ledger verdict to a status and writes one run record per start.","v":1,"ts":"2026-09-06T21:08:17Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-26","req_id":null,"req_class":null,"miss_class":"unspecified-gap","artifact":"other","severity":"major","why_missed":"missing-checklist-item","origin_phase":"framework-reset","origin_agent":"general","found_by":"agent-review","found_phase":"verify-phase","found_gate":null,"found_run_id":"2026-09-06T22:42:51Z","what":"The TechieBlog verify (Sonnet, 2026-09-06) wrote 86 rows FAIL on the acceptance check and 87 regression misses because the local database had no published post and the seeded staff accounts sat behind a must-change-password screen, and nothing in the verify task or the smoke policy says what a verify does when the test data the acceptance needs is missing: create it through the application as the test user, pass a password gate through its screen, and record a row as not observable with the environment reason when neither is possible, never as a failure of the code; for Session 5's sort.","v":1,"ts":"2026-09-06T22:42:52Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260906-27","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"config","severity":"blocker","why_missed":"missing-checklist-item","origin_phase":"framework-reset","origin_agent":"general","found_by":"gate","found_phase":"verify-phase","found_gate":null,"found_run_id":"2026-09-06T23:54:23Z","what":"Removing the seven never-used commands left every project's own routing file naming author-brd, and the binding generator wrote an OpenCode file reference to the vanished task, so OpenCode refused to start on TechieBlog-oc; the generator now skips a phase whose task file does not exist and says so, and the stale line was removed from the three projects refreshed today (the other projects hit the warning, not the error, at their next update).","v":1,"ts":"2026-09-06T23:54:23Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-01","req_id":null,"req_class":null,"miss_class":"partial-implementation","artifact":"other","severity":"major","why_missed":"instruction-ignored","origin_phase":"verify-phase","origin_agent":"verifier","found_by":"agent-review","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T00:10:01Z","what":"The OpenCode verify on TechieBlog-oc (gpt-5.6-terra, 2026-09-07) skipped step 4 of the verify task: the old specs died on missing environment variables and no test was written for the 101 rows without one, so the run ended in twelve minutes with 101 rows not tested and only the screens checks graded; the task's step 4 was read and not done, which is the instruction-ignored pattern, and the Claude run on the same project wrote and repaired tests for three hours.","v":1,"ts":"2026-09-07T00:10:01Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-02","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"insufficient-verify-method","origin_phase":"framework-reset","origin_agent":"general","found_by":"gate","found_phase":"triage-issues","found_gate":null,"found_run_id":"2026-09-07T00:31:01Z","what":"The checklist-edit helper shared by the triage and verdict scripts cut a long Remarks cell to sixty words and then added the ellipsis as a word of its own, so three rows the TechieBlog triage wrote carried 61 words and the Stop hook refused the turn until the agent trimmed them; the ellipsis now rides on the last word, and the self-tests did not cover a remark over the limit.","v":1,"ts":"2026-09-07T00:31:01Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-03","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"framework-reset","origin_agent":"general","found_by":"agent-review","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T03:39:47Z","failure_class":"other","what":"The cross-project rollup keyed a requirement by its id alone, so REQ-UI-001 of TfLens and REQ-UI-001 of TechieBlog counted as one requirement and the combined first-pass rate printed 72% where the true figure is 48%; found by re-reading the numbers before the explainer, fixed by keying on project and id.","sort":"weak-check","v":1,"ts":"2026-09-07T04:04:29Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-04","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"framework-reset","origin_agent":"general","found_by":"gate","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T03:39:47Z","failure_class":"other","what":"The readable miss file's unchanged check compared only the header above the Updated line, so an amend that changed a row but no count left the file stale; the bugs self-test caught it before release and the check now compares everything but the date.","sort":"weak-check","v":1,"ts":"2026-09-07T04:04:29Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-01","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:11Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-01","req_id":null,"fix_run_id":"2026-09-05T05:15:20Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:12Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":9478,"tokens_out":1002995,"tokens_cache_read":118517981,"tokens_cache_write":6079009,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:15"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-02","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:12Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-03","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:12Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-03","req_id":null,"fix_run_id":"2026-09-05T05:15:20Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:12Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":9478,"tokens_out":1002995,"tokens_cache_read":118517981,"tokens_cache_write":6079009,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:15"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-04","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:12Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-04","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:12Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-05","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:13Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-05","req_id":null,"fix_run_id":"2026-09-05T05:15:20Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:13Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":9478,"tokens_out":1002995,"tokens_cache_read":118517981,"tokens_cache_write":6079009,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:15"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-06","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:13Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-06","req_id":null,"fix_run_id":"2026-09-05T05:15:20Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:13Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":9478,"tokens_out":1002995,"tokens_cache_read":118517981,"tokens_cache_write":6079009,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:15"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-07","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:13Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-07","req_id":null,"fix_run_id":"2026-09-05T05:15:20Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:14Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":9478,"tokens_out":1002995,"tokens_cache_read":118517981,"tokens_cache_write":6079009,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:15"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-08","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:14Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-08","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:14Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-09","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:14Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-09","req_id":null,"fix_run_id":"2026-09-05T15:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:15Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":13502,"tokens_out":1916501,"tokens_cache_read":197536980,"tokens_cache_write":6866571,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:16"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-10","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:15Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-10","req_id":null,"fix_run_id":"2026-09-05T05:15:20Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:15Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":9478,"tokens_out":1002995,"tokens_cache_read":118517981,"tokens_cache_write":6079009,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:15"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-11","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:15Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-11","req_id":null,"fix_run_id":"2026-09-05T15:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:15Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":13502,"tokens_out":1916501,"tokens_cache_read":197536980,"tokens_cache_write":6866571,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:16"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-12","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:16Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-12","req_id":null,"fix_run_id":"2026-09-05T15:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:16Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":13502,"tokens_out":1916501,"tokens_cache_read":197536980,"tokens_cache_write":6866571,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:16"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-13","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:16Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-13","req_id":null,"fix_run_id":"2026-09-05T15:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:16Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":13502,"tokens_out":1916501,"tokens_cache_read":197536980,"tokens_cache_write":6866571,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:16"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-14","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:16Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-14","req_id":null,"fix_run_id":"2026-09-05T15:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:17Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":13502,"tokens_out":1916501,"tokens_cache_read":197536980,"tokens_cache_write":6866571,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:16"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-15","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:17Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-15","req_id":null,"fix_run_id":"2026-09-05T15:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:17Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":13502,"tokens_out":1916501,"tokens_cache_read":197536980,"tokens_cache_write":6866571,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:16"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-16","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:17Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-16","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:17Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-17","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:18Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-18","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:18Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-18","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:18Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-19","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:18Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-19","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:18Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-20","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:19Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-20","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:19Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-21","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:19Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-21","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:19Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-22","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:19Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-22","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:20Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-23","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:20Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-23","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:20Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260905-24","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:20Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-24","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:20Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-01","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:21Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-01","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:21Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-02","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:21Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-02","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:21Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-03","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:22Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-03","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:22Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-04","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:22Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-04","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:22Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-05","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:22Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-06","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:23Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-07","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:23Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-07","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:23Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-08","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:23Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-09","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:23Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-10","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:24Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-11","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:24Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-12","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:24Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-12","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:24Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-13","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:25Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-14","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:25Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-15","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:25Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-15","req_id":null,"fix_run_id":"2026-09-06T09:16:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:25Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-16","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:25Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-16","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:26Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-17","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:26Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-17","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:26Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-18","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:26Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-18","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:26Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-19","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:27Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-19","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:27Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-20","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:28Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-20","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:28Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-21","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:29Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-21","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:29Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-22","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:30Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-22","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:30Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-23","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:31Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-23","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:31Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-24","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:32Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-24","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:32Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-25","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:32Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-25","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:33Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-26","field":"sort","value":"unsaid","v":1,"ts":"2026-09-07T04:31:33Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260906-27","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:34Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260906-27","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:34Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260907-01","field":"sort","value":"ignored","v":1,"ts":"2026-09-07T04:31:35Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-amend","miss_id":"MISS-TechieFlow-20260907-02","field":"sort","value":"weak-check","v":1,"ts":"2026-09-07T04:31:35Z","project_type":"framework","harness":"claude-code","app":"TechieFlow"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-02","req_id":null,"fix_run_id":"2026-09-06T16:20:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:31:35Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:7"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260905-02","req_id":null,"fix_run_id":"2026-09-07T03:39:47Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T04:33:41Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":6066,"tokens_out":989471,"tokens_cache_read":55112712,"tokens_cache_write":2699460,"cost_usd":null,"tokens_scope":"main","model":"claude-fable-5-1","cost_attribution":"shared:8"} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-05","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"minor","why_missed":"insufficient-verify-method","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:14:47Z","failure_class":"other","what":"FR-47 says a script greps every public document for private project names, but no such script was ever written, so a private project was named in the public README for months.","sort":"weak-check","v":1,"ts":"2026-09-07T07:14:48Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-06","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:14:54Z","failure_class":"other","what":"tf-log-miss.sh printed 'Miss logged' with an id and said the readable file was rewritten after the emitter had refused the record for a bad artifact value and appended nothing.","sort":"weak-check","v":1,"ts":"2026-09-07T07:14:54Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-07","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"missing-checklist-item","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:14:54Z","failure_class":"other","what":"Nothing capped or checked the two files a person reads first, so the briefing reached 344 KB and the README 121 KB and both still named commands the framework had removed.","sort":"unsaid","v":1,"ts":"2026-09-07T07:14:54Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-08","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"config","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:17:39Z","failure_class":"other","what":"The updater kept a project's root opencode.jsonc because its dead BMAD-era registrations looked like project content, so that repo loaded no framework agents in OpenCode at all and only a warning was printed.","sort":"weak-check","v":1,"ts":"2026-09-07T07:17:39Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-09","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:25:54Z","failure_class":"other","what":"A run record with no ended at all was accepted and could never be costed: the guard only replaced an ended that lied, so this session's own record landed with no duration.","sort":"weak-check","v":1,"ts":"2026-09-07T07:25:54Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-05","req_id":null,"fix_run_id":"2026-09-07T06:55:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T07:32:41Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","cost_attribution":"none"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-06","req_id":null,"fix_run_id":"2026-09-07T06:55:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T07:32:41Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","cost_attribution":"none"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-07","req_id":null,"fix_run_id":"2026-09-07T06:55:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T07:32:41Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","cost_attribution":"none"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-08","req_id":null,"fix_run_id":"2026-09-07T06:55:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T07:32:41Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","cost_attribution":"none"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-09","req_id":null,"fix_run_id":"2026-09-07T06:55:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T07:32:41Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","cost_attribution":"none"} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-10","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:35:35Z","failure_class":"other","what":"WORKFLOW.html is force-deployed to every project and still teaches three commands removed in Sitting 4c, because the removed-command check looks at the task files and the harness registrations but never at the human reference.","sort":"weak-check","v":1,"ts":"2026-09-07T07:35:35Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-11","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:50:26Z","failure_class":"other","what":"The Playbook review prompt was drafted from folder word counts taken only at the top level, so 174 files and 196,498 words counted as zero and the prompt nearly shipped the wrong headline finding.","sort":"weak-check","v":1,"ts":"2026-09-07T07:50:26Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-12","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"instruction-ignored","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:53:13Z","failure_class":"other","what":"The D-21 rule that hidden framework folders are invisible to search was written down and still cost 58 percent of a folder's true size when OpenCode measured the Playbook with a shell glob.","sort":"ignored","v":1,"ts":"2026-09-07T07:53:13Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-11","req_id":null,"fix_run_id":"2026-09-07T07:33:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T07:53:37Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":168,"tokens_out":100681,"tokens_cache_read":28046246,"tokens_cache_write":125296,"cost_usd":null,"tokens_scope":"main","model":"claude-opus-5","cost_attribution":"sole"} diff --git a/docs/metrics/runs.jsonl b/docs/metrics/runs.jsonl index 68a2e86..f05c1f6 100644 --- a/docs/metrics/runs.jsonl +++ b/docs/metrics/runs.jsonl @@ -16,3 +16,21 @@ {"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":null,"started":"2026-09-04T08:18:41Z","ended":"2026-09-04T14:43:11Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":9,"build_result":null,"v":1,"ts":"2026-09-04T14:43:13Z","project_type":"framework","harness":"claude-code","model":"claude-fable-5-1","model_tokens_out":{"claude-fable-5-1":569794},"tokens_in":4392,"tokens_out":569794,"tokens_cache_read":30624828,"tokens_cache_write":1279990,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} {"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":null,"started":"2026-09-04T14:43:11Z","ended":"2026-09-04T16:58:53Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":8,"build_result":null,"v":1,"ts":"2026-09-04T16:58:53Z","project_type":"framework","harness":"claude-code","model":"claude-fable-5-1","model_tokens_out":{"claude-fable-5-1":261809},"tokens_in":980,"tokens_out":261809,"tokens_cache_read":18049655,"tokens_cache_write":207943,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} {"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":null,"started":"2026-09-04T16:58:53Z","ended":"2026-09-04T19:15:00Z","reqs_touched":["FR-08","FR-09","FR-14","FR-15","FR-17"],"reqs_count":5,"subagents":["explore","explore","explore"],"files_written":31,"build_result":null,"project_type":"framework","harness":"claude-code","model":"claude-fable-5-1","model_tokens_out":{"claude-fable-5-1":880963},"tokens_in":3098,"tokens_out":880963,"tokens_cache_read":25533202,"tokens_cache_write":2072181,"cost_usd":null,"tokens_scope":"main","subagent_runs":3,"tokens_out_subagents":212748,"v":1,"ts":"2026-09-04T19:14:11Z","attempt":1} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"sitting-4a","started":"2026-09-05T05:15:20Z","ended":"2026-09-05T15:10:43Z","reqs_touched":["FR-03","FR-06","FR-10","FR-11","FR-12","FR-14","FR-15","FR-20","FR-27","FR-34","FR-39","FR-40","FR-41","FR-44","FR-53"],"reqs_count":15,"subagents":[],"files_written":2,"build_result":null,"v":1,"ts":"2026-09-05T15:10:43Z","project_type":"framework","harness":"claude-code","attempt":2,"model":"claude-fable-5-1","models":["claude-fable-5-1",""],"model_tokens_out":{"claude-fable-5-1":1002995,"":0},"tokens_in":9478,"tokens_out":1002995,"tokens_cache_read":118517981,"tokens_cache_write":6079009,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"sitting-4b","started":"2026-09-05T15:20:00Z","ended":"2026-09-06T09:05:56Z","reqs_touched":["FR-10","FR-13","FR-14","FR-15","FR-21","FR-36","FR-39","FR-40","FR-41","FR-43","FR-44","FR-53","FR-54","FR-55","FR-56","FR-57"],"reqs_count":16,"subagents":[],"files_written":82,"build_result":"pass","v":1,"ts":"2026-09-06T09:05:57Z","project_type":"framework","harness":"claude-code","attempt":3,"model":"claude-fable-5-1","models":["claude-fable-5-1",""],"model_tokens_out":{"claude-fable-5-1":1916501,"":0},"tokens_in":13502,"tokens_out":1916501,"tokens_cache_read":197536980,"tokens_cache_write":6866571,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"sitting-4b","started":"2026-09-06T09:16:00Z","ended":"2026-09-06T15:09:09Z","reqs_touched":["FR-14","FR-18","FR-20","FR-40","FR-41","FR-46","FR-55"],"reqs_count":7,"subagents":[],"files_written":16,"build_result":"pass","yolo":false,"v":1,"ts":"2026-09-06T15:09:09Z","duration_s":21189,"project_type":"framework","harness":"claude-code","attempt":4,"model":"claude-fable-5-1","model_tokens_out":{"claude-fable-5-1":646692},"tokens_in":7868,"tokens_out":646692,"tokens_cache_read":89914215,"tokens_cache_write":1382314,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"sitting-4b","started":"2026-09-06T15:09:10Z","ended":"2026-09-06T16:08:11Z","reqs_touched":["FR-43"],"reqs_count":1,"subagents":[],"files_written":6,"build_result":"pass","yolo":false,"v":1,"ts":"2026-09-06T16:08:11Z","duration_s":3541,"project_type":"framework","harness":"claude-code","attempt":2,"model":"claude-fable-5-1","model_tokens_out":{"claude-fable-5-1":36603},"tokens_in":492,"tokens_out":36603,"tokens_cache_read":9051035,"tokens_cache_write":60748,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"sitting-4c","started":"2026-09-06T16:20:00Z","ended":"2026-09-07T00:41:57Z","reqs_touched":["FR-24","FR-26","FR-28","FR-29","FR-30","FR-35","FR-41"],"reqs_count":7,"subagents":[],"files_written":42,"build_result":"pass","yolo":false,"v":1,"ts":"2026-09-07T00:41:57Z","duration_s":30117,"project_type":"framework","harness":"claude-code","attempt":4,"model":"claude-fable-5-1","model_tokens_out":{"claude-fable-5-1":1397892},"tokens_in":14462,"tokens_out":1397892,"tokens_cache_read":228335175,"tokens_cache_write":5263206,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"session-5","started":"2026-09-07T03:39:47Z","ended":"2026-09-07T04:33:40Z","reqs_touched":["FR-31","FR-32","FR-18","FR-40","FR-58","FR-59","FR-60","FR-61"],"reqs_count":8,"subagents":[],"files_written":24,"build_result":"pass","v":1,"ts":"2026-09-07T04:33:40Z","yolo":false,"duration_s":3233,"project_type":"framework","harness":"claude-code","attempt":4,"model":"claude-fable-5-1","model_tokens_out":{"claude-fable-5-1":989471},"tokens_in":6066,"tokens_out":989471,"tokens_cache_read":55112712,"tokens_cache_write":2699460,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:14:34Z","ended":"2026-09-07T07:14:34Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:14:34Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","tokens_scope":"none"} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:14:47Z","ended":"2026-09-07T07:14:47Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:14:48Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":505},"tokens_in":2,"tokens_out":505,"tokens_cache_read":238870,"tokens_cache_write":1377,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:14:54Z","ended":"2026-09-07T07:14:54Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:14:54Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","tokens_scope":"none"} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:14:54Z","ended":"2026-09-07T07:14:54Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:14:54Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","tokens_scope":"none"} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:15:38Z","ended":"2026-09-07T07:15:38Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:15:39Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":301},"tokens_in":2,"tokens_out":301,"tokens_cache_read":246368,"tokens_cache_write":476,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:17:39Z","ended":"2026-09-07T07:17:39Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:17:40Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":711},"tokens_in":2,"tokens_out":711,"tokens_cache_read":256538,"tokens_cache_write":852,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"session-6","started":"2026-09-07T06:55:00Z","reqs_touched":["FR-40","FR-47","FR-62"],"reqs_count":3,"subagents":[],"files_written":34,"build_result":"pass","yolo":false,"v":1,"ts":"2026-09-07T07:25:19Z","project_type":"framework","harness":"claude-code","attempt":5} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:25:54Z","ended":"2026-09-07T07:25:54Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:25:54Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":285},"tokens_in":2,"tokens_out":285,"tokens_cache_read":285723,"tokens_cache_write":667,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:35:35Z","ended":"2026-09-07T07:35:35Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:35:36Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":1024},"tokens_in":2,"tokens_out":1024,"tokens_cache_read":317165,"tokens_cache_write":867,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:50:26Z","ended":"2026-09-07T07:50:26Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:50:26Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":1739},"tokens_in":2,"tokens_out":1739,"tokens_cache_read":347855,"tokens_cache_write":1548,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:53:13Z","ended":"2026-09-07T07:53:13Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:53:13Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":302},"tokens_in":2,"tokens_out":302,"tokens_cache_read":359283,"tokens_cache_write":739,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"session-7","started":"2026-09-07T07:33:00Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":4,"build_result":"pass","yolo":false,"v":1,"ts":"2026-09-07T07:53:37Z","ended":"2026-09-07T07:53:37Z","duration_s":1237,"project_type":"framework","harness":"claude-code","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":100681},"tokens_in":168,"tokens_out":100681,"tokens_cache_read":28046246,"tokens_cache_write":125296,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} diff --git a/docs/metrics/sessions.jsonl b/docs/metrics/sessions.jsonl index 116f983..428bd78 100644 --- a/docs/metrics/sessions.jsonl +++ b/docs/metrics/sessions.jsonl @@ -21,3 +21,8 @@ {"kind":"session","session_id":"002f7357-e35a-48e8-bdba-524a7ce909cf","harness":"claude-code","model":"claude-opus-5","duration_s":8177,"input_tokens":654,"output_tokens":362601,"cache_read_tokens":92926098,"cache_creation_tokens":2519180,"cost_usd":null,"ts":"2026-08-31T10:48:31Z","v":1,"project_type":"framework","app":"TechieFlow"} {"kind":"session","session_id":"19942a83-079c-4bc3-8dc6-9965bb779351","harness":"claude-code","model":"claude-fable-5-1","duration_s":31427,"input_tokens":5532,"output_tokens":837616,"cache_read_tokens":50408084,"cache_creation_tokens":1531387,"cost_usd":null,"ts":"2026-09-04T17:02:28Z","v":1,"project_type":"framework","app":"TechieFlow"} {"kind":"session","session_id":"ab193039-8c85-4a79-bbdd-cd3d088c7150","harness":"claude-code","model":"claude-fable-5-1","duration_s":9648,"input_tokens":3538,"output_tokens":949795,"cache_read_tokens":35008473,"cache_creation_tokens":2143612,"cost_usd":null,"ts":"2026-09-04T19:47:49Z","v":1,"project_type":"framework","app":"TechieFlow"} +{"kind":"session","session_id":"ebd735ef-7032-41e5-8ee0-e3860758df08","harness":"claude-code","model":"claude-sonnet-5","duration_s":5,"input_tokens":2,"output_tokens":4,"cache_read_tokens":18534,"cache_creation_tokens":12937,"cost_usd":null,"ts":"2026-09-05T15:06:14Z","v":1,"project_type":"framework","app":"TechieFlow"} +{"kind":"session","session_id":"0a805e69-7300-47a8-b455-9a91ec42ce6c","harness":"claude-code","model":"claude-fable-5-1","duration_s":38854,"input_tokens":10246,"output_tokens":1047220,"cache_read_tokens":125890540,"cache_creation_tokens":6389421,"cost_usd":null,"ts":"2026-09-05T15:31:02Z","v":1,"project_type":"framework","app":"TechieFlow"} +{"kind":"session","session_id":"c20b0e68-6fee-4954-81c6-20d5153e90fc","harness":"claude-code","model":"claude-fable-5-1","duration_s":27085,"input_tokens":8788,"output_tokens":717197,"cache_read_tokens":107497268,"cache_creation_tokens":1467204,"cost_usd":null,"ts":"2026-09-06T16:47:48Z","v":1,"project_type":"framework","app":"TechieFlow"} +{"kind":"session","session_id":"86a9cc2f-3d6b-4561-84a8-8e5a9953f4d9","harness":"claude-code","model":"claude-fable-5-1","duration_s":39020,"input_tokens":14726,"output_tokens":1429207,"cache_read_tokens":234885351,"cache_creation_tokens":6543401,"cost_usd":null,"ts":"2026-09-07T03:38:49Z","v":1,"project_type":"framework","app":"TechieFlow"} +{"kind":"session","session_id":"3da736d9-fb6a-4287-9555-1152bad36d6d","harness":"claude-code","model":"claude-fable-5-1","duration_s":11692,"input_tokens":6230,"output_tokens":997732,"cache_read_tokens":57286523,"cache_creation_tokens":3516469,"cost_usd":null,"ts":"2026-09-07T06:54:39Z","v":1,"project_type":"framework","app":"TechieFlow"} diff --git a/opencode.jsonc b/opencode.jsonc index b94b818..dac1a9a 100644 --- a/opencode.jsonc +++ b/opencode.jsonc @@ -253,7 +253,7 @@ "sudo *": "ask" } }, - "description": "Madhav — the single super-agent. Orchestrates the full pipeline (*run-workflow, *phase) with parallel subagents, runs the unified build (*build-phase — calls /trblazeui + /techierag as sub-agents), fixes bugs from screenshots (*fix-issues), analyzes + logs human-found bugs without fixing (*triage-issues), records a single missed requirement in seconds (*log-miss), reports development telemetry (*metrics), and handles doc/status utilities (*render-workflow-docs, *generate-html, *handoff-phase, *refresh-status, *devguide) plus any one-off task." + "description": "Madhav — the TechieFlow master: build (*build-phase), fix (*fix-issues), triage (*triage-issues), log a miss (*log-miss), amend documents (*amend-docs), DevGuide and ProductGuide, handoff, refresh-status, metrics, HTML rendering, YOLO. The analyst owns day-1; the verifier owns *verify.", }, "flow-architect": { "prompt": "{file:./.tfcore/agents/architect.md}", @@ -839,19 +839,23 @@ }, "techieflow:tasks:fix-issues": { "template": "{file:./.tfcore/tasks/fix-issues.md}", - "description": "Bug-fix front door: given a folder of screenshots (+ optional description), reproduce with Playwright, triage (layout/data/logic/RAG), fan fixes to the right builder, re-smoke (data+visual) + re-verify, update docs. Driven by /flow-master." + "description": "Fixes the bugs in a folder of screenshots: triages and logs them, builds the fix through the builders in FIX mode, smokes, re-verifies the touched rows, closes the misses with the fix cost (tf-fix-close.sh). Driven by /flow-master." }, "techieflow:tasks:log-miss": { "template": "{file:./.tfcore/tasks/log-miss.md}", - "description": "The 20-second front door for 'you missed this': turns one sentence about something an agent got wrong into a misses.jsonl record (what was missed, which phase/agent/model let it through, who found it) plus the checklist line — demotion with a dated miss Remark, or a new Planned row when nothing owns it. Never boots the app, never reproduces, never edits code. Driven by /flow-master." + "description": "The twenty-second record: one sentence from the owner becomes one miss record and one checklist line through tf-log-miss.sh. Never boots, never fixes. Driven by /flow-master." }, "techieflow:tasks:triage-issues": { "template": "{file:./.tfcore/tasks/triage-issues.md}", - "description": "ANALYZE-ONLY bug front door for human-found (UAT/production) bugs: reproduce with Playwright, triage to the owning REQ, log in the checklist (demote to Needs re-verify / new Planned rows), optional scoped regression re-verify, update PROJECT-STATUS with the *fix-issues pointer. NEVER edits code. Driven by /flow-master." + "description": "Analyse-only front door for bugs a person found: reproduces each on its screen, logs it in the checklist and the telemetry through tf-triage.sh (demote, new, note, close), never edits code. Driven by /flow-master." + }, + "techieflow:tasks:triage-and-fix": { + "template": "{file:./.tfcore/tasks/triage-and-fix.md}", + "description": "The owner's whole bug sequence in YOLO: compare every screen to its mockup, triage the reported bugs, log every root cause with its cost, fix, log every fix with its cost, refresh the metrics, one summary per step. Driven by /flow-master." }, "techieflow:tasks:verify-phase": { "template": "{file:./.tfcore/tasks/verify-phase.md}", - "description": "Autonomous verification of a scope (ui | functional | all | REQ list | legacy phase-N) — filters the one Checklist by REQ prefix; applies the data-render + visual-truth gates; verdicts land in the Checklist Requirements Status table." + "description": "Verifies every checklist row in a scope (ui | functional | all | REQ list) against the running app: boots it, runs the seven checks in order through the tf-verify-* scripts, writes the verdicts into the checklist and the telemetry. YOLO by default. Driven by /verifier." }, "techieflow:tasks:handoff-phase": { "template": "{file:./.tfcore/tasks/handoff-phase.md}", @@ -873,14 +877,14 @@ "template": "{file:./.tfcore/tasks/generate-html.md}", "description": "Render any markdown file(s) or a non-recursive directory to self-contained HTML using the shared shell." }, - "techieflow:tasks:author-brd": { - "template": "{file:./.tfcore/tasks/author-brd.md}", - "description": "Interactively extend docs/{AppName}-BRD.md with confirmed BRD-N requirements (per-item elicitation)." - }, "techieflow:tasks:amend-docs": { "template": "{file:./.tfcore/tasks/amend-docs.md}", "description": "Fold an evolving concept / changed requirements into the EXISTING day-1 docs IN PLACE: surgically amends BRD + Architecture (append-only IDs), ripples to PROJECT-STATUS / BRD §4 / checklists, re-renders HTML. Incremental alternative to re-running day1-*." }, + "techieflow:tasks:deploy-checklist": { + "template": "{file:./.tfcore/tasks/deploy-checklist.md}", + "description": "Deployment Checklist: the steps to put the application on its host, one document per hosting target, written after UAT from the owner's pipeline guidance document and the Stack answers Q9 and Q10 (docs/TechieFlow-Document-Schemas.md §3.10). Never deploys. Driven by /flow-master." + }, "techieflow:tasks:productguide": { "template": "{file:./.tfcore/tasks/productguide.md}", "description": "End-user Product Guide: screenshot-illustrated, task-oriented how-to manual for external users (what each screen is for + how to do things) — the user-facing sibling of the DevGuide, reusing its captured screenshots. Always MD + HTML. On-demand. Driven by /flow-master." @@ -889,10 +893,6 @@ "template": "{file:./.tfcore/tasks/devguide.md}", "description": "Generate/refresh the screen-by-screen Developer Guide: traces every screen/control from Razor page → service → data-access → stored proc/query, per user role, documenting the code AS BUILT. Single doc for small apps, split per role for large ones; --update refreshes only changed screens." }, - "techieflow:tasks:document-project": { - "template": "{file:./.tfcore/tasks/document-project.md}", - "description": "Generate comprehensive documentation for existing projects optimized for AI development agents." - }, "techieflow:tasks:create-doc": { "template": "{file:./.tfcore/tasks/create-doc.md}", "description": "Create a document from a YAML-driven TechieFlow template (interactive, section by section)." @@ -904,26 +904,6 @@ "techieflow:tasks:facilitate-brainstorming-session": { "template": "{file:./.tfcore/tasks/facilitate-brainstorming-session.md}", "description": "Run a structured brainstorming session; results land in docs/brainstorming-session-results.md." - }, - "techieflow:tasks:advanced-elicitation": { - "template": "{file:./.tfcore/tasks/advanced-elicitation.md}", - "description": "Reflective/brainstorming actions for deeper exploration and iterative refinement of drafted content." - }, - "techieflow:tasks:execute-checklist": { - "template": "{file:./.tfcore/tasks/execute-checklist.md}", - "description": "Validate a document or work product against one of the TechieFlow checklists." - }, - "techieflow:tasks:index-docs": { - "template": "{file:./.tfcore/tasks/index-docs.md}", - "description": "Maintain docs/index.md by scanning all documentation files and indexing them with descriptions." - }, - "techieflow:tasks:kb-mode-interaction": { - "template": "{file:./.tfcore/tasks/kb-mode-interaction.md}", - "description": "User-friendly interface to the TechieFlow knowledge base." - }, - "techieflow:tasks:shard-doc": { - "template": "{file:./.tfcore/tasks/shard-doc.md}", - "description": "Split a large document into smaller documents by level-2 sections, preserving content integrity." } } } diff --git a/scaffold-brownfield.sh b/scaffold-brownfield.sh index c1509d2..3aba510 100755 --- a/scaffold-brownfield.sh +++ b/scaffold-brownfield.sh @@ -282,6 +282,18 @@ if [[ ! -f .claude/settings.json ]]; then { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-metrics.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-db.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-build.sh\"" } ] }, @@ -292,6 +304,10 @@ if [[ ! -f .claude/settings.json ]]; then "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-metrics.sh\"" + }, { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify.sh\"" diff --git a/scaffold-greenfield.sh b/scaffold-greenfield.sh index 7303328..8678eab 100755 --- a/scaffold-greenfield.sh +++ b/scaffold-greenfield.sh @@ -262,6 +262,18 @@ if [[ ! -f .claude/settings.json ]]; then { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-metrics.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-db.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-build.sh\"" } ] }, @@ -272,6 +284,10 @@ if [[ ! -f .claude/settings.json ]]; then "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-metrics.sh\"" + }, { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify.sh\"" diff --git a/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Architecture.md b/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Architecture.md index 8d16b1f..0c1fd74 100644 --- a/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Architecture.md +++ b/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Architecture.md @@ -13,13 +13,19 @@ | Q | Topic | Decision | Source | |---|---|---|---| | Q1 | Configuration | appsettings.json | answer set | +| Q2 | Secrets in development | user secrets | answer set | | Q4 | Authentication | AppManager | owner | +| Q5 | Logging | Serilog to file | answer set | +| Q6 | Tests | xUnit, test project from day one | answer set | +| Q7 | Layout and naming | src/ and tests/; the head is `MyDiary` | answer set | +| Q8 | User interface | TrBlazeUI, Blazor Server | answer set | +| Q11 | Standing rules | the answer set's list | answer set | ## 2. Solution structure | Project | Kind | Purpose | |---|---|---| -| `MyDiary` | web app | the head | +| `MyDiary.App` | web app | the head | | `MyDiary.Tests` | test project | tests | ## 3. Component map diff --git a/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-BRD.md b/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-BRD.md index b72edda..7845415 100644 --- a/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-BRD.md +++ b/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-BRD.md @@ -36,7 +36,7 @@ A private journal site. One user writes dated entries and finds them again later - **BRD-1** — Sign in. *Screen:* Login · *Mockup:* [mockup](mockups/login.html) - *Acceptance:* When the writer enters a valid email and password on Login and presses Enter, then the Entries screen opens. - **BRD-2** — Search entries. *Screen:* Entries · *Mockup:* [mockup](mockups/entries.html) - - *Acceptance:* When the writer types `holiday` in the search box on Entries and presses Enter, then only entries containing `holiday` are listed. + - *Acceptance:* When the writer opens Entries, then the list shows every entry newest first with a preview and a thumbnail, the search box filters as the writer types, Enter opens the first result, empty slots are dropped, and a timer can be started. ## 6. Non-functional requirements diff --git a/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Checklist.md b/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Checklist.md index c72c8d8..da3f99e 100644 --- a/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Checklist.md +++ b/tests/.artifacts/doc-check/fx-bad/docs/MyDiary-Checklist.md @@ -15,7 +15,7 @@ Build the journal site described in the BRD. |----|-------------|--------|---|---------|---------| | REQ-UI-001 | Login screen | Not Started | 0% | — | [view](#d-req-ui-001) | | REQ-FN-001 | Search entries | Started | 10% | history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history history | [view](#d-req-fn-001) | -| REQ-NFR-001 | Entries list speed | Not Started | 0% | — | [view](#d-req-nfr-001) | +| REQ-NFR-001 | Entries list speed | Not Started | 0% | perf script not present anywhere in this tree | [view](#d-req-nfr-001) | ## Page: Login (`/login`) @@ -27,7 +27,7 @@ Build the journal site described in the BRD. - **REQ-FN-001** — Search entries. *BRD:* BRD-2 - - *Acceptance:* Given three entries exist, when the writer types `holiday` in the search box on Entries and presses Enter, then only entries containing `holiday` are listed. + - *Acceptance:* Given three entries exist, when the writer types `holiday` on Entries and presses Enter, then only matching entries are listed. ## Non-functional diff --git a/tests/.artifacts/doc-check/fx-bad/docs/mockups/entries.html b/tests/.artifacts/doc-check/fx-bad/docs/mockups/entries.html index 6c70bcf..8d3d318 100644 --- a/tests/.artifacts/doc-check/fx-bad/docs/mockups/entries.html +++ b/tests/.artifacts/doc-check/fx-bad/docs/mockups/entries.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/.artifacts/doc-check/fx-bad/docs/mockups/login.html b/tests/.artifacts/doc-check/fx-bad/docs/mockups/login.html index 6c70bcf..d2e260e 100644 --- a/tests/.artifacts/doc-check/fx-bad/docs/mockups/login.html +++ b/tests/.artifacts/doc-check/fx-bad/docs/mockups/login.html @@ -1 +1 @@ - \ No newline at end of file +Go \ No newline at end of file diff --git a/tests/.artifacts/doc-check/fx-good/docs/MyDiary-Architecture.md b/tests/.artifacts/doc-check/fx-good/docs/MyDiary-Architecture.md index a8d673c..0b0bd71 100644 --- a/tests/.artifacts/doc-check/fx-good/docs/MyDiary-Architecture.md +++ b/tests/.artifacts/doc-check/fx-good/docs/MyDiary-Architecture.md @@ -13,7 +13,14 @@ | Q | Topic | Decision | Source | |---|---|---|---| | Q1 | Configuration | appsettings.json | answer set | +| Q2 | Secrets in development | user secrets | answer set | +| Q3 | Database | PostgreSQL in a container | answer set | | Q4 | Authentication | AppManager | owner | +| Q5 | Logging | Serilog to file | answer set | +| Q6 | Tests | xUnit, test project from day one | answer set | +| Q7 | Layout and naming | src/ and tests/; the head is `MyDiary` | answer set | +| Q8 | User interface | TrBlazeUI, Blazor Server | answer set | +| Q11 | Standing rules | the answer set's list | answer set | ## 2. Solution structure diff --git a/tests/.artifacts/doc-check/fx-good/docs/MyDiary-BRD.md b/tests/.artifacts/doc-check/fx-good/docs/MyDiary-BRD.md index 227aa5d..613a55f 100644 --- a/tests/.artifacts/doc-check/fx-good/docs/MyDiary-BRD.md +++ b/tests/.artifacts/doc-check/fx-good/docs/MyDiary-BRD.md @@ -44,7 +44,7 @@ A private journal site. One user writes dated entries and finds them again later - **BRD-1** — Sign in. *Screen:* Login · *Mockup:* [mockup](mockups/login.html) - *Acceptance:* When the writer enters a valid email and password on Login and presses Enter, then the Entries screen opens. - **BRD-2** — Search entries. *Screen:* Entries · *Mockup:* [mockup](mockups/entries.html) - - *Acceptance:* When the writer types `holiday` in the search box on Entries and presses Enter, then only entries containing `holiday` are listed. + - *Acceptance:* When the writer types `holiday` in the search box on Entries and presses Enter, then only matching entries are listed. ## 6. Non-functional requirements diff --git a/tests/.artifacts/doc-check/fx-good/docs/MyDiary-Checklist.md b/tests/.artifacts/doc-check/fx-good/docs/MyDiary-Checklist.md index 6b6b9e1..4032aea 100644 --- a/tests/.artifacts/doc-check/fx-good/docs/MyDiary-Checklist.md +++ b/tests/.artifacts/doc-check/fx-good/docs/MyDiary-Checklist.md @@ -27,7 +27,7 @@ Build the journal site described in the BRD. - **REQ-FN-001** — Search entries. *BRD:* BRD-2 - - *Acceptance:* Given three entries exist, when the writer types `holiday` in the search box on Entries and presses Enter, then only entries containing `holiday` are listed. + - *Acceptance:* Given three entries exist, when the writer types `holiday` on Entries and presses Enter, then only matching entries are listed. ## Non-functional diff --git a/tests/.artifacts/doc-check/fx-good/docs/mockups/entries.html b/tests/.artifacts/doc-check/fx-good/docs/mockups/entries.html index 6c70bcf..5b71307 100644 --- a/tests/.artifacts/doc-check/fx-good/docs/mockups/entries.html +++ b/tests/.artifacts/doc-check/fx-good/docs/mockups/entries.html @@ -1 +1,3 @@ - \ No newline at end of file + + +
    Holiday
    \ No newline at end of file diff --git a/tests/.artifacts/doc-check/fx-good/docs/mockups/login.html b/tests/.artifacts/doc-check/fx-good/docs/mockups/login.html index 6c70bcf..fcef672 100644 --- a/tests/.artifacts/doc-check/fx-good/docs/mockups/login.html +++ b/tests/.artifacts/doc-check/fx-good/docs/mockups/login.html @@ -1 +1,3 @@ - \ No newline at end of file + +
    +
    \ No newline at end of file diff --git a/tests/bugs/run.sh b/tests/bugs/run.sh new file mode 100644 index 0000000..8800c2f --- /dev/null +++ b/tests/bugs/run.sh @@ -0,0 +1,170 @@ +#!/usr/bin/env bash +# tests/bugs/run.sh — self-test for the bug scripts (Sitting 4c, 2026-09-06): tf-triage.sh, +# tf-log-miss.sh, tf-fix-close.sh and the checklist edits they share (tf-checklist-edit.py). +# Uses the verify fixture (tests/verify/make-fixtures.py). Checks: +# 1. triage demote: Needs re-verify, % capped at 75, a dated UAT remark in the reporter's words +# 2. triage new: the next free id, a Not Started row, a detail entry with the acceptance line and +# BRD-pending under the named section; the checker prints no FAIL for it +# 3. triage note: a remark, no status change +# 4. triage close: one escaped gate record per row, one miss per row with the symptom as `what` +# (regression when the row was Verified; unspecified-gap on brd for a new row), the run record, +# a warning and an instruction-ignored miss when code changed during the triage; a second +# close adds no duplicate +# 5. log-miss: refused without --sort (the four questions printed); the record with the sentence +# and the sort, the row demoted with a ⚠ miss remark, the run record; a repeat is reported not +# re-logged; --new adds a Not Started row; --fixed closes at once +# 5b. the readable file (FR-31, Session 5): docs/FxApp-Misses.md and its HTML exist, one row per +# miss record, the sentence and whose gap in the row, a fixed miss under Fixed; a miss emitted +# without a sort is sorted later with tf-emit.sh --amend and the file follows; triage misses +# carry the default sorts (weak-check for a demoted row, spec for a new row, ignored for the +# code edit) +# 6. fix-close: the fix-issues run record first, then one miss-fix per row with the verifier's +# verdict from the ledger; a row with no open miss is named, not invented +# Telemetry goes to the fixture only (TF_METRICS_ROOT). Run: bash tests/bugs/run.sh +set -u +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; ROOT="$(cd "$HERE/../.." && pwd)" +export TF_FIXTURE_DIR="$ROOT/tests/.artifacts/verify-fixture" +APP="$(python3 "$ROOT/tests/verify/make-fixtures.py")" || { echo "could not build the fixture"; exit 2; } +pass=0; fail=0 +ok() { pass=$((pass+1)); echo "ok $*"; } +bad() { fail=$((fail+1)); echo "FAIL $*"; } +check() { if [[ "$2" == 0 ]]; then ok "$1"; else bad "$1"; fi; } +has() { grep -q -- "$2" <<<"$1"; echo $?; } +cd "$APP" || exit 2 +export TF_METRICS_ROOT="$APP"; unset CLAUDE_PROJECT_DIR; export TF_PROJECT_DIR="$APP" +U=".tfcore/utils"; CL="docs/FxApp-Checklist.md"; M="docs/metrics" +cell() { grep -E "^\| $1 \|" "$CL" | cut -d'|' -f"$2" | sed 's/^ *//; s/ *$//'; } + +# ---- 1-3. triage edits ---------------------------------------------------------------------- +S1="$(bash $U/tf-phase.sh start triage-issues FxApp 2>/dev/null)" +sleep 1 +out="$(bash $U/tf-triage.sh FxApp demote REQ-UI-003 "Save and Cancel sit on top of each other" --kind layout --evidence uat/editor.png 2>&1)"; rc=$? +check "demote runs (exit $rc): $out" "$rc" +check "demote: status Needs re-verify" "$([[ "$(cell REQ-UI-003 4)" == "Needs re-verify" ]]; echo $?)" +check "demote: % capped at 75" "$([[ "$(cell REQ-UI-003 5)" == "75%" ]]; echo $?)" +check "demote: dated UAT remark in the reporter's words" "$(has "$(cell REQ-UI-003 6)" "⚠ UAT bug $(date +%F): Save and Cancel sit on top of each other (evidence: uat/editor.png; kind: layout)")" +out="$(bash $U/tf-triage.sh FxApp new "Export entries" "When the user taps Export on Entries, then a file downloads." --section Entries --evidence uat/export.png 2>&1)"; rc=$? +check "new runs (exit $rc): $out" "$rc" +check "new: next free id REQ-FN-010, Not Started, 0%" "$([[ "$(cell REQ-FN-010 4)" == "Not Started" && "$(cell REQ-FN-010 5)" == "0%" ]]; echo $?)" +check "new: detail entry with BRD-pending under Entries" "$(python3 - <<'PY' +import re,sys +t=open("docs/FxApp-Checklist.md",encoding="utf-8").read() +sec=t.split("## Entries",1)[1].split("\n## ",1)[0] +ok = 'id="d-req-fn-010"' in sec and "(BRD-pending)" in sec and "*Acceptance:* When the user taps Export on Entries" in sec +sys.exit(0 if ok else 1) +PY +echo $?)" +chk="$(bash $U/tf-doc-check.sh --strict "$CL" 2>&1)" +check "checker: no FAIL on the new row (BRD-pending is a WARN)" "$([[ "$(grep -c 'FAIL.*REQ-FN-010' <<<"$chk")" == 0 && "$(grep -c 'WARN.*REQ-FN-010.*BRD-pending' <<<"$chk")" == 1 ]]; echo $?)" +out="$(bash $U/tf-triage.sh FxApp note REQ-UI-001 "could not reproduce: opened Home twice, list shows three entries" 2>&1)"; rc=$? +check "note runs (exit $rc)" "$rc" +check "note: status unchanged, remark appended" "$([[ "$(cell REQ-UI-001 4)" == Implemented ]] && grep -qE "^\| REQ-UI-001 \|.*$(date +%F) triage: could not reproduce" "$CL"; echo $?)" + +# ---- 4. triage close ---------------------------------------------------------------------------- +mkdir -p src && echo "// edited during triage" > src/Oops.cs +out="$(bash $U/tf-triage.sh FxApp close --started "$S1" 2>&1)"; rc=$? +check "close runs (exit $rc)" "$rc" +check "close: 2 rows logged, 2 escaped gate records, 2 misses, code NOT untouched" "$(has "$out" "triage: 2 row(s) logged — 2 gate record(s) (escaped), 2 miss(es), run record written; code untouched: NO")" +check "close: warns about the edited file and logs it" "$(has "$out" "WARNING: .*src/Oops.cs")" +check "gates: REQ-UI-003 escaped, prior Verified" "$(grep '"req_id":"REQ-UI-003"' $M/gates.jsonl | grep -q '"gate":"escaped".*"prior_verdict":"Verified"\|"prior_verdict":"Verified".*"gate":"escaped"'; echo $?)" +check "miss: REQ-UI-003 is a regression found by the owner with the symptom as what" "$(grep '"req_id":"REQ-UI-003"' $M/misses.jsonl | grep -q '"miss_class":"regression"' && grep '"req_id":"REQ-UI-003"' $M/misses.jsonl | grep -q '"found_by":"owner"' && grep '"req_id":"REQ-UI-003"' $M/misses.jsonl | grep -q '"what":"Save and Cancel sit on top of each other"'; echo $?)" +check "miss: REQ-FN-010 is an unspecified-gap on the brd" "$(grep '"req_id":"REQ-FN-010"' $M/misses.jsonl | grep -q '"miss_class":"unspecified-gap"' && grep '"req_id":"REQ-FN-010"' $M/misses.jsonl | grep -q '"artifact":"brd"'; echo $?)" +check "miss: the code edit is instruction-ignored" "$(grep -c '"why_missed":"instruction-ignored"' $M/misses.jsonl | grep -q '^1$'; echo $?)" +check "run record: cmd triage-issues, started from the marker, not-run" "$(grep '"cmd":"triage-issues"' $M/runs.jsonl | grep -q "\"started\":\"$S1\"" && grep '"cmd":"triage-issues"' $M/runs.jsonl | grep -q '"build_result":"not-run"'; echo $?)" +n1="$(grep -c '"kind":"miss"' $M/misses.jsonl)" +rm -f src/Oops.cs +bash $U/tf-triage.sh FxApp close --started "$S1" >/dev/null 2>&1 +n2="$(grep -c '"kind":"miss"' $M/misses.jsonl)" +check "a second close adds no duplicate miss ($n1 → $n2)" "$([[ "$n1" == "$n2" ]]; echo $?)" + +# ---- 5. log-miss -------------------------------------------------------------------------------- +S2="$(bash $U/tf-phase.sh start log-miss FxApp 2>/dev/null)" +out="$(bash $U/tf-log-miss.sh FxApp --what "The entries list ignores the date filter" --req REQ-UI-002 --class wrong-behaviour 2>&1)"; rc=$? +check "log-miss without --sort is refused (exit $rc) and prints the four questions" "$([[ $rc -eq 2 ]] && [[ "$(has "$out" "1. Did the app's spec say it clearly?")" == 0 ]]; echo $?)" +out="$(bash $U/tf-log-miss.sh FxApp --what "The entries list ignores the date filter" --req REQ-UI-002 --class wrong-behaviour --why instruction-ignored --severity minor --sort ignored 2>&1)"; rc=$? +check "log-miss runs (exit $rc)" "$rc" +check "log-miss: report block names the id, the class and whose gap" "$([[ "$(has "$out" "MISS-FxApp-.*wrong-behaviour / src / minor")" == 0 && "$(has "$out" "Whose gap : ignored")" == 0 ]]; echo $?)" +check "log-miss: the sentence and the sort are in the record" "$(grep '"what":"The entries list ignores the date filter"' $M/misses.jsonl | grep -q '"sort":"ignored"'; echo $?)" +check "log-miss: row demoted with a ⚠ miss remark" "$([[ "$(cell REQ-UI-002 4)" == "Needs re-verify" ]] && grep -qE "^\| REQ-UI-002 \|.*⚠ miss $(date +%F): The entries list ignores the date filter" "$CL"; echo $?)" +check "log-miss: run record cmd log-miss" "$(grep -q '"cmd":"log-miss"' $M/runs.jsonl; echo $?)" +n1="$(grep -c '"kind":"miss"' $M/misses.jsonl)" +out="$(bash $U/tf-log-miss.sh FxApp --what "The entries list ignores the date filter again" --req REQ-UI-002 --class wrong-behaviour --sort weak-check 2>&1)" +n2="$(grep -c '"kind":"miss"' $M/misses.jsonl)" +check "log-miss: a repeat is reported, not re-logged ($n1 → $n2), and its sort is not overwritten" "$([[ "$n1" == "$n2" ]] && [[ "$(has "$out" "already logged as MISS-FxApp-")" == 0 && "$(has "$out" "sort not amended")" == 0 ]]; echo $?)" +out="$(bash $U/tf-log-miss.sh FxApp --what "Nothing lets the user export a month" --new "Export a month" --acceptance "When the user taps Export month on Timeline, then a file downloads." --section Entries --sort spec 2>&1)"; rc=$? +check "log-miss --new adds a Not Started row (exit $rc)" "$([[ $rc -eq 0 && "$(cell REQ-FN-011 4)" == "Not Started" ]]; echo $?)" +check "log-miss --new: unspecified-gap on the brd, req_id the new row" "$(grep '"req_id":"REQ-FN-011"' $M/misses.jsonl | grep -q '"miss_class":"unspecified-gap"'; echo $?)" +out="$(bash $U/tf-log-miss.sh FxApp --what "The header logo was missing and got fixed yesterday" --req REQ-UI-001 --class partial-implementation --fixed --sort weak-check 2>&1)"; rc=$? +check "log-miss --fixed closes at once and leaves the row alone (exit $rc)" "$([[ $rc -eq 0 && "$(cell REQ-UI-001 4)" == Implemented ]] && grep -c '"kind":"miss-fix"' $M/misses.jsonl | grep -q '^1$'; echo $?)" + +# ---- 5b. the readable file ---------------------------------------------------------------------- +MD="docs/FxApp-Misses.md" +check "misses file: $MD and its HTML exist" "$([[ -f "$MD" && -f "docs/FxApp-Misses.html" ]]; echo $?)" +nrec="$(grep -c '"kind":"miss"' $M/misses.jsonl)"; nrow="$(grep -c '^| MISS-FxApp-' "$MD")" +check "misses file: one row per miss record ($nrec records, $nrow rows)" "$([[ "$nrec" == "$nrow" && "$nrec" -gt 0 ]]; echo $?)" +check "misses file: the sentence, the row and whose gap are in the row" "$(grep -E '^\| MISS-FxApp-[0-9]+-[0-9]+ \(REQ-UI-002\) \| [0-9-]+ by owner \| said and ignored \| The entries list ignores the date filter \|' "$MD" >/dev/null; echo $?)" +check "misses file: the fixed miss sits under Fixed with its closing command" "$(python3 - <<'PY' +import sys +t = open("docs/FxApp-Misses.md", encoding="utf-8").read() +fixed = t.split("## Fixed", 1)[1] if "## Fixed" in t else "" +sys.exit(0 if "(REQ-UI-001)" in fixed and "by log-miss" in fixed and "header logo" in fixed else 1) +PY +echo $?)" +check "misses file: triage misses carry the default sorts (demote weak-check, new spec, code edit ignored)" "$(grep '"req_id":"REQ-UI-003"' $M/misses.jsonl | grep -q '"sort":"weak-check"' && grep '"req_id":"REQ-FN-010"' $M/misses.jsonl | grep -q '"sort":"spec"' && grep '"why_missed":"instruction-ignored"' $M/misses.jsonl | grep 'triage edited' | grep -q '"sort":"ignored"'; echo $?)" +mid="$(bash $U/tf-emit.sh --next-miss-id)" +printf '{"kind":"miss","miss_id":"%s","req_id":"REQ-NFR-007","req_class":"NFR","miss_class":"wrong-behaviour","artifact":"src","severity":"minor","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","what":"An older record with no sort yet"}' "$mid" | bash $U/tf-emit.sh misses >/dev/null +check "misses file: a record without a sort shows 'not sorted'" "$(grep -E "^\| $mid \(REQ-NFR-007\) \| .* \| not sorted \| An older record with no sort yet \|" "$MD" >/dev/null; echo $?)" +out="$(bash $U/tf-emit.sh --amend "$mid" sort spec 2>&1)" +check "amend: sort completed on the old record ($out)" "$(has "$out" "amended $mid — sort = spec")" +check "misses file: follows the amend (the app's spec)" "$(grep -F "| $mid (REQ-NFR-007) | " "$MD" | grep -F "| the app's spec | An older record with no sort yet |" >/dev/null; echo $?)" +out="$(bash $U/tf-emit.sh --amend "$mid" sort ignored 2>&1)" +check "amend: a second sort is refused, never overwritten" "$(has "$out" "amend refused")" +out="$(bash $U/tf-emit.sh --amend "$mid" sort nonsense 2>&1)" +check "amend: a value outside the four is refused" "$(has "$out" "not in the closed vocabulary")" +rep="$(bash .tfcore/telemetry/tf-metrics.sh --report . 2>&1)" +check "report: whose-gap block with the four values counted" "$([[ "$(has "$rep" "whose gap")" == 0 && "$(has "$rep" "weak-check")" == 0 && "$(has "$rep" "said and ignored")" == 0 ]]; echo $?)" + +# ---- 6. fix-close ------------------------------------------------------------------------------ +S3="$(bash $U/tf-phase.sh start fix-issues FxApp 2>/dev/null)" +python3 - <&1)"; rc=$? +check "fix-close runs (exit $rc): $out" "$rc" +check "fix-close: a ledger RENDER-FAIL becomes verdict_after Needs re-verify (REQ-UI-002)" "$(grep '"kind":"miss-fix"' $M/misses.jsonl | grep '"req_id":"REQ-UI-002"' | grep -q '"verdict_after":"Needs re-verify"'; echo $?)" +n1="$(grep -c '"cmd":"fix-issues"' $M/runs.jsonl)" +out2="$(bash $U/tf-fix-close.sh FxApp --started "$S3" --reqs REQ-UI-003 --build pass 2>&1)" +n2="$(grep -c '"cmd":"fix-issues"' $M/runs.jsonl)" +check "fix-close called twice writes one run record ($n1 → $n2)" "$([[ "$n1" == 1 && "$n2" == 1 ]] && [[ "$(has "$out2" "already exists")" == 0 ]]; echo $?)" +check "fix-close: run record first, cmd fix-issues mode fix with the sub-agent" "$(grep '"cmd":"fix-issues"' $M/runs.jsonl | grep -q '"mode":"fix"' && grep '"cmd":"fix-issues"' $M/runs.jsonl | grep -q '"subagents":\["trblazeui"\]'; echo $?)" +check "fix-close: miss-fix for REQ-UI-003 says Verified" "$(grep '"kind":"miss-fix"' $M/misses.jsonl | grep '"req_id":"REQ-UI-003"' | grep -q '"verdict_after":"Verified"'; echo $?)" +check "fix-close: miss-fix for REQ-FN-010 says FAIL" "$(grep '"kind":"miss-fix"' $M/misses.jsonl | grep '"req_id":"REQ-FN-010"' | grep -q '"verdict_after":"FAIL"'; echo $?)" +check "fix-close: a row with no open miss is named" "$(has "$out" "no open miss on REQ-UI-004")" +check "fix-close: the miss-fix carries the fix run" "$(grep '"kind":"miss-fix"' $M/misses.jsonl | grep '"req_id":"REQ-UI-003"' | grep -q "\"fix_run_id\":\"$S3\""; echo $?)" + +# ---- 7. the emitter and log-miss report honestly (Session 6, 2026-09-07) --------------------- +# 7a. MISS-TechieFlow-20260907-09: a run record with no `ended` was accepted and could never be +# costed. `ended` is when the record is written, so an absent one is filled in. +echo '{"kind":"run","app":"FxApp","cmd":"devguide","started":"2026-09-07T07:00:00Z"}' | bash $U/tf-emit.sh runs >/dev/null 2>&1 +check "emit: a run record with no ended gets one, and a duration" \ + "$(python3 - "$M/runs.jsonl" <<'PY' +import json,sys +r=[json.loads(l) for l in open(sys.argv[1]) if l.strip() and '"devguide"' in l][-1] +raise SystemExit(0 if r.get("ended") and r.get("duration_s") is not None else 1) +PY +echo $?)" +# 7b. MISS-TechieFlow-20260907-06: a record the emitter refused was reported as "Miss logged", +# with an id that existed nowhere. A refusal must say so and append nothing. +before="$(grep -c '"kind":"miss"' $M/misses.jsonl)" +out7="$(bash $U/tf-log-miss.sh FxApp --sort spec --what "a bad artifact value must be reported, not claimed as logged" --artifact framework --found-by owner 2>&1)"; rc7=$? +after="$(grep -c '"kind":"miss"' $M/misses.jsonl)" +check "log-miss: a refused record says NOT recorded, exits non-zero, appends nothing ($before → $after)" \ + "$([[ "$(has "$out7" "Miss NOT recorded")" == 0 ]] && [[ "$rc7" != 0 ]] && [[ "$before" == "$after" ]]; echo $?)" +check "log-miss: the refusal names the value the emitter rejected" "$(has "$out7" "not in the closed vocabulary")" + +echo +echo "bugs self-test: $pass passed, $fail failed (fixture $APP)" +[[ $fail -eq 0 ]] diff --git a/tests/doc-check/__pycache__/make-fixtures.cpython-312.pyc b/tests/doc-check/__pycache__/make-fixtures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..213510bdbc5bde5727dc602de80fcee0e4a56a79 GIT binary patch literal 20981 zcmc(HeQX;?mS>ZqzG(T|@n>w8C|Q;mk+S5E*cwF}S(0tVvaE=bozQlq7P}=;BH6rd z(lR;9tb!aiHtuloE*40#JHW(P;F3WGnEm5`n*|Qoy*(VTi(6nRC<`k@fbs18ahUtd zo>^dk+&}kwRn=s-MA=RznJ6&3yQ;eC)vNdYUgv*p-Rf8Hcj}cN&;REUMfuL7?ck6rf7xcaQK7GG_K-+`=Uik6W=s|7ozgMD%yo!EE->M(9*YGH4c@fX@9HNKy z?eni}#ErI3+i$;9vHyTpno~QJ#{~Le?ZqCt?y5Ncuy*9B^BvWg)>!G}wZN=LYsyzE ziVZWZrZv;=Q)fJ;9n)GqtHLn+0lMP^9d_@X@8wOdAFWkuqcI22sL)mEcT`-jQ|q!vR_s5iowEBe(`i1_&Wa?r(4hU1{a%!P zEuMR|^j@RZ2R;dC=c7%4C7^G&sjT9z`n7@5o6UUu&gUL~jE~>-+~ZsL_#ICjKd4=> z;n6MTP8FUH$R)Xb3YM(wQEzSC`T#WX~22>rEAB9MxX8-_gc6 z2Zpr>r_Sbz6 z#WIWWRQT7l#7J@5_=U*}LvM^vj15i>42|Wz;c(s`R?SR&MvY~xd_7hP#pd+bLcVt9 zPH5IJw0sS2rd2Jw4aPt`l8nLMP6JPMpF(e^V3d46{f!nFLz={yvsArL(Ms zg%(XbTTH_cjbSrp98ZMI8GviC%$%NN>Z}@1u>^*gVZK5aR)`sC-R}|apg+W} z(?sn|*W#IWfUladIeR|56bxR?X2ihAoND59#Y`u|Yyx12B~2rtb7YR;20ObxXAl56 zrkdhW&4}le4q$;58{c#VN;wE7Uc^hxSWIDiOJaki9oZmBRGp%Yn7W>ldrlaObeN(I zFOL~nj$;YX6(hEgrE|Ry*Au|bIuLAdFvGAh3zRLfsg4BQP2si#-Hjxl=2ok?T4G3e zE|W~OVVs^+Ake)uJ98{WA97sD$#4SNQgE%LSj2vs#;wqq| zwT;v_T_6+BByjxGhL$7T27{v}c-hPey3=!X_8`o0oD3&9ka&C+Xa|;v+7kG&XnDEQ zupcz$9Lxy{q$$JzM-06OBIIW$wjyZiba)+ihFCCoerzDr84PkJj$mah9A-gr(A`0K zUVr~zSx}rYnx1w{e#S6qUo57lGioZPWBBzs5oP(-Pkk#p;?k*)w4_ zVx&Sd*;FhOH%OY9>%{O7iP%0v;vT_{52%#a?CDPAqGhdMcJt^CH`#l%}SJad`%axH-N+l&~B(i*ZD`RF$2plzyd7u-M zNt0nTYo-lr1GHFjO4=r*3>NiiVO(%loYO&(q!jHT;c?x{STqp&J_C7UFG7!`%E)wp zI_@`;X(NR#u%w#y`=_R+k~$b7uK8!69b-`588&_%GGb!rMqpwnaEpaPy)1G&b|Vnc z&D-&qZpk(rlzJmTzkypc50UyMV-aQ=aV-InF;(z`<`nGV+%d?!3}=@dWX|Yi-h^W$0X9y$5DjmOU2{1S&pXF+&=dtWuj8`$mU^9&r&m4O&m>Vi+U`44GLPfOC$+V8BjbrokRw5F-Xn z(=h2UiMXZ{FdQ`xE`F$pjoZCzo=SwKAw zQlq_28<{!DZDMt1<)Gn`;;@F*986A7r=Ca-fXm>F)=sUX%tKsT%>V8y7L+tXi{8P)CYsyb|b-ghoo|jxKG4w10@JWLkAHW~P3CJI+9y%K*Hm94gAA>9f#^%-lbhyBio=k&n z$>dG4X(;Ih29%MhL43%j!Q@awRyq#tZBmy`4A>3n3RgIxLr!N?8hRk)_#Ez&CFw%G z9?ll>fo`WZJ6+k&t;6#9I$PUl`DdSS*Ec=kh_G*ouf}l*=V4$j+14iPWpM<<+_%9H z666<2$m5jcE}`CjFA^vLAZZ|FU~)P!V&zSbwCrScGlzpz@stB2MhV)So(Z_l+FH;P zI-RyjTC052{FwdO0|=Vu% zEr0N@kRlGF+md3OKo}YTz8rmcvSmCw#=GDwE)ZW02%SMp1)G(}Q|K!GcoW^)uK&7b z9tId2d_6QV6gqK&%s8kmfGEAECDOUHuu0JlSirP2g>{zLjoWd3@m6b-7J_jS%Esw+ zc*G=;Fx`a_Ih~|~5?MX(gfl7kgd-Qm7kfqLc!XGW1b$sxgrI!NbDe z{=wIEzWv$PkpNvM9Su^UBnVP4ptR=<;9~ew0<_YUbsDGK>@6)qc69>Yy3j;CzzK64 zEhP0e^982DnVA?YhYrhzwIjxX50x51)E`c2A zGqW6#CfNJu3^-$0tZcQHttJxnDFQ8~hq$8yF!Y%?scU0;o|GlYDd~HfWQfv~WRYTo zwEg2e_)?ye#65p+Fa=X6#$)cnS|<0%=-p+9g2D6@qN0V)?QWQYumyUF|9qaceR4X%? zO~azpwQh&A;uis-%w&2_wIHVl)Lf{O3tT(TfiOG;_dz0j4xAzn?@Ufpexw`bMkZut zQ|mfu9oI4hl;2J75gBR%?!k_*Lx&jLS-*f_!|D`dBaWK|%f%DG7@gS&*}m|l5gLOR zZ7>4F-=9#knvRfxrgw8+w_{vKFi5|ENI^H05ppBl6g3DXffHDwLB5j^M?eW*6d9oK zm1CCYdWWFmVJ|3bS1LyA7WC9_WZ|Pb`~{Gj+WM^>($^5kZt3GV96Af3#9xIx4^!|A zEwl#w5ya@}<#MWr+ysut!886(+vnd4Oq#$CJ8MEmEtyvSHw7Eur6TSfr;g-WP{EzK@i^MOL7II+}rgqsMr2w0&mB4le?Md6InGj-5I(HWlgAz<4 z)igBhjFF&70;y=|(gX}$5EUtsb&LXTM#u~HhJy$@$SvW4XA15TZxH}I7{o~o$&jpw zHA74aE*uBe1-((;2Hjv1q*b`;A$=?ePFIm)=BHf&3>jk*84jB?{`F7(tVAYG&p^Ty z;q!*3b0N6`Of6l2R-$N%mq5fLrHg1u;|=aB-D(x%DTh;%$ug^(PR;VLNAbK9B zbUB;i?}bf@U4~;;_a$sCq4UNmMo>;s9sxo1FqiDk4w7I4YM7sTjvf$Kr_ct(7vU#? z@&p{f1(_nFBU3ZL6T-=oS12Kw-EeYJ_iQ}4j&-1RcD+oN=9CL$JgZ_HkCpK(1Mv$F zoRPCr3KURoP*8v?RLw}`gAsY`5i%zfPa&-0MjB+0It`WxKk7Y*5*i>~VlN-y~Rc8LhQ6%|6tRdFG}y(n@=WJ^-;!B5F>E_=Cw z3Ho-5Tb}87)5!9)IPtY4U`*9`LyFU96CJp0g4fYV=md5-V_-2M%s3p-MUbi^`l86N zP=Hhw@v0KbQP_*;RK(Z_@-{!0EK(D`t$3$J-31Z3+wFi&`B2oNGz@D0>gkKv^r4d_q zy~QcCa+e*(rR8L-l_>d@fR&wwv=6u^63dHmfnIq=JsqL}!4?7x7upsse2?lr5bN`7 zw+9&j_iBU-%JAtXU{~23e5_Xj90VSu4fKK;84?K;*YPTwy$bhh@W5pR2EAh zyA3>3=(zQGpC;;oa!izmNJ&dl|9D%V7u}`Z`Fn&p>zZ2PKE%5KWyZumt@n~B8R+T2 zJ$}&G!AIcg#7(h^$YJYOyJ7d1L~4klj7jaQ7hR1`RYtx0UA+@SBFjQO2V6Z)n>kPR zb$K1QB5;2~CF7S~-Z?sU^|itN@la&EZ+s$xru;$IOWdmEdVM1lQ2cgJ-VMDJPj>HD z`}mw2Lu(RvyTeJ@`2c_CsX8d%yy+NwDj+;2OU0>jPC@OP0t-g@?ax4_A1E1W64HAvQ#RKHebllZ2WJ&&b}*O%OgF?l4pKPxyQcyOx_GAx86~dn}FbL@7q<+ zfT=>M|B|xgwaXIU_GPd$@mHzf{~5DaD7RnoeI{qrJU=U~<54oD4+55aT9xMeS+!jN zep-3EdZ~K8!e;Lk_}qd1v*GqA|M6X)l2e+LPQ~&pdOgaUD5XT}f4Wokrn2a1 zs~H(@^AziN>~YdqD0&w2)!EEU=qztd!Kwjuw{0nU4Xfx&tC_iC4IgV2YpgkVDT!i@ znam)JS*+%USJ5|b#8bt(B#JM!xM>w@@tabUdeKMbSFxHqL~S)iA4OP-)f6eQidCkb zDArOw5)@YSq>J8}IjaWfrsO$eUkTf~19U0MFFR;@1dZ;H`v`TAE7tRQCsB4`HsQU0 z0&cCpz|FGqWo`X?i|;JH_oH`ybYJ_t_RyDg+uq-LZ!7NW4z2A!{F5yoZTZWszfpYE zNA5M;t-5>t%Z6?5U%Yqm{o#AVAMW|kT5V{$>-n&ijq`BOkx~`PP>nE1vozpPFl!;nKZJ_pg86aQJssXyWZpYO%!c zYV60atOkJh_n}?={_6kXukPPnpMMQn_**KMEGqfeMOA=^8I}`wWaUx$C0Xbj%GPZD zw(AQmo=dY!0l(3dNtj(Ap-rj_^UY-7Uw}_R^;QUrsL)J)e+1PYF4gn{3T@-R$I%b8?>I=?o9xkT(tLfRUF& zf&ym+M<0Pdl*LdAV+qotIz}P1{9ZcHiN0}^KhJVw8ZkIp)oN>ou zeM&W{{_3{=>nQEKo_4+$zPeqJ#E|lq#X0NR$(`V}O8MOYeSPUk{w2FHoiK86K;idZ z8;uA{otNp!3NorwF-c}+Z{P=&df!!U-X#5)k5}@XzY_;6f9c!u8BO}gE`QZw$CfQp zr<@4%UNpElDox^zCbHB1Gsyi%OTvjBtWt2=H6wczFhWl5QL_M(mc=$k`ZJx7$bs*z%?bh?T)=i{?|=`@(B z7MX*D8q;y>BY(=GiI^F{Z?S4RK5KU3?*F0DB6DF`S=+Mx{hoV0AJ%_-c6Cd8!Pov< zG%MK-oD^DNr7&F*%vq-8OO1bRH-yjSV3MhtR*JG8!MW>~&US7fuXpcbsL)%1n z$b@2^u+5pNrf4EN$h|H&v@VL;&kJHioUq;YaK%K3cq6)q8F@`EPZ~hoa0sa!bxcni zCJ(fh?`ZHT`z?UYfNELQn%@bC++SPdn>REeC@f?3oxBJatC0kDzvyU@NX!2T$wtVG zD+n_n1_P0b@<}T`9Z$qF(9?7*vI|wZizrriUUQ{8WXtrui+g{lc7Z@0>=j{ z2%KRZp&Y*Z?^P_QUKG5sWEN4oMZ~=9-H52!clO0fnY^YtBxWg$x4B&@2bGLKek|?C z6N#i7F!;<*Kt%A?UU|kelKdQbM3~qBW!tC;qM}?7yf}bOKkhh%uqq|)+z0CX#=~BR zO4Uk5EE~*a*S4Nk&p_t_!gIk$u{ybo;~Ng)nU_nBSt^^H#;d6Hw;9|%Hgxu#u1&2_}qaf8~;EbJe>aaKX|Y=wCM*8{~fUempUz#1%LOl^7k$oj*u4S zb`U2wJP_MhX*I?*iRG4mQd!F=JJ;OZHn~z>Lz4ccb)d3;T^*>i^*QT6Pm%KZCY8RN zxjD;mUw7P$pe~YsD#3Y%>q+_TBS>2mJ>#Y%lYXxnP_a1Goys*uwG9IPsBREU3jj8+ z2Ic8+bZ>AXoTZ;-0kBC0Xbc5Q8!JHnCrnfq6_=u$Uz3X6_709|gNi46M-+!rh?wIY zGD1=T8n0&q&x6b(~|;ym2$8t7MOtsdgc_zM+=W{xHzJMOU%jsmj%yPIms@)!%az zEUw?aJPc_K*hu6D;@bY{r){qE8W)lbA(1~+wPKzv>dG8(kpiMN(v-#P70&3{o(}ok(;;5! z?a|`v(qdfB2YNfZY`N3Zfide+OZjGG&q&OnRN9YQ@`<~tIFlZ@v_mKW~Hn8jEIq_kGii|VvJ zbW??o(~7lv>UP{TQpGBK@NDivySa~k=>JMotW~FBZz6|OH#td(pUKIh?|ffmu;{yZ z_3A*e>U`cEP4mfyezi-W~_ewoCYJeXFj8NCEh zB7kUYptD#tF@&G~;m%?W!7@3_+q*p{3^yQ!T{Ius6-oLH=o7%tK{x{qI!TZm(|5*Fa+y8NU z;f<-)OX}*`>4G*_n7{p4QF5LEF9L$?gWhG|noNf+y|Z+G;KR1n+Ge!X)V(+R&g^@M zcM|tceYk(MCa_%f*wl>aIMYnfb)U)h=T;ttw&9mI_xK4TT=vs5@8Vjs7Hm$V=*A5(73pCSXbIaP%wzbC4 zTCiiSz4OVTZS~&e>fZ!vJx8AGQ9S;;&G)N6SN8s@#(($py9>)zYeyP?vhdMDA^0O~ zu+Q618$3GT{pFEkAI%n8a%(NQ-&T2!{?PO2=+Kkes-v%Yma7WB11riwfN<>iC$Bwt z4ey;uc-J~EW8kqXp8s>K{lWD@Xm%|$S(w%zPS3AQ&leKe!ouxB?uRQ2KU|$&S_v)T zrB}TJ-Y1F@22QIyErVY9;vW_IB87=-tH-WCu2($1g74^x!X7s$&B2emJ~{K?Od)u4 zEjYOr3>CsdE5X+aW08NW_BKy&xcJZ-wST2~V6El+6TsA8>U(tP@K3rw>MpcQ z6{fY-L;Ax*$(2LNHDBmc>*sfVdgm91ezEl*yens4TjiU1@d|%&--@#TQA_ZX#DhfP zm{4{@_ae!NQ?Xp`&lL{`_O5_JYUvM5(2P9`8EGx!|^^k#ofb&k?~L z9%a{&hdYn0>^xRzxdfzK_RJGfb}e|9w>|cHcH9v}f24Ws;E}bawzZ>yHP*CtxN$9T zY^|x~i{rshyZ&nNivuryIQEzOzG!Uzc;3qdfx>*Rm`geiV!-Ld8Eb)jnf70-vp%CgT^j}|PZ#-mED{QKuCJL$C zD$C<|8~f>a@rx}EcxUMn_YPNKr;S&t?D-$Hw0*MhVBxRZ|E^)R<;ugBiItX#!Zoch zlUZ%aK5Th=rR8nB_s5 - **REQ-FN-001** — Search entries. *BRD:* BRD-2 - - *Acceptance:* Given three entries exist, when the writer types `holiday` in the search box on Entries and presses Enter, then only entries containing `holiday` are listed. + - *Acceptance:* Given three entries exist, when the writer types `holiday` on Entries and presses Enter, then only matching entries are listed. ## Non-functional @@ -433,7 +440,78 @@ ![Write an entry](screenshots/MyDiary/entries.png) """ + +LOGIN_HTML = """ +
    +
    """ +ENTRIES_HTML = """ + +
    Holiday
    """ + +DC = """# MyDiary — Deployment Checklist + +| | | +|---|---| +| App | MyDiary | +| Hosting target | VPS | +| Pipeline document | docs/deployment-brief.md | +| Date | 2026-09-06 | +| Proven | never | + +## 1. Who does what + +| Step | Done by | +|---|---| +| Build and publish the artefact | pipeline | +| Approve the release | owner | + +## 2. Secrets and settings + +| Name | Where it is set | What breaks without it | +|---|---|---| +| CONNECTION_STRING | host environment variable | the application cannot start | +| APP_KEY | host environment variable | sign-in fails | + +## 3. Before the first deploy + +- [ ] Create the database user — `psql -c "\\du"` lists it + +## 4. Deploy + +- [ ] Publish the artefact to /srv/mydiary — the folder holds today's build +- [ ] Restart the service — `systemctl status mydiary` says active + +## 5. After the deploy + +- [ ] `curl -I https://mydiary.example/health` — HTTP 200 + +## 6. Rollback + +- [ ] Point the symlink at the previous build and restart — the previous version answers + +A rollback does not undo: database migrations. + +## 7. Routine operations + +| Task | Command | +|---|---| +| Restart the service | `systemctl restart mydiary` | + +## 8. Troubleshooting + +| Symptom | Cause | Fix | +|---|---|---| +| 502 from the proxy | service down | restart it | + +## 9. Proven + +| What | Executed for real | When | +|---|---|---| +| Deploy to the VPS | no | — | +""" + FILES = { + "docs/MyDiary-Deployment-Checklist.md": DC, "docs/MyDiary-BRD.md": BRD, "docs/MyDiary-Architecture.md": ARCH, "docs/MyDiary-UIDesign.md": UI, @@ -443,8 +521,8 @@ "docs/MyDiary-UsageGuide.md": UG, "docs/MyDiary-DevGuide.md": DG, "docs/MyDiary-ProductGuide.md": PG, - "docs/mockups/login.html": "", - "docs/mockups/entries.html": "", + "docs/mockups/login.html": LOGIN_HTML, + "docs/mockups/entries.html": ENTRIES_HTML, "docs/screenshots/MyDiary/login.png": "png", "docs/screenshots/MyDiary/entries.png": "png", } @@ -471,13 +549,236 @@ def write_set(root, files): "*Acceptance:* login works.") .replace("| REQ-FN-001 | Search entries | Not Started | 0% | — |", "| REQ-FN-001 | Search entries | Started | 10% | " + " ".join(["history"] * 70) + " |") + .replace("| REQ-NFR-001 | ", "| REQ-NFR-001 | ", 1)) +# a Remarks cell that says "not present" without naming the path tried (FR-27) +bad["docs/MyDiary-Checklist.md"] = re.sub(r"(\| REQ-NFR-001 \|[^|]*\|[^|]*\|[^|]*\|)[^|]*\|", + r"\1 perf script not present anywhere in this tree |", + bad["docs/MyDiary-Checklist.md"], count=1) +bad["docs/MyDiary-Checklist.md"] = (bad["docs/MyDiary-Checklist.md"] + "\n## UAT Bugs\n\n- a bug\n") +# a bundled acceptance line (five behaviours, 41 words) in the broken BRD +bad["docs/MyDiary-BRD.md"] = bad["docs/MyDiary-BRD.md"].replace( + "*Acceptance:* When the writer types `holiday` in the search box on Entries and presses Enter, then only matching entries are listed.", + "*Acceptance:* When the writer opens Entries, then the list shows every entry newest first with a preview and a thumbnail, the search box filters as the writer types, Enter opens the first result, empty slots are dropped, and a timer can be started.") bad["PROJECT-STATUS.md"] = PS.replace("OpenCode:\n```\n/flow-master *build-phase MyDiary\n```\n", "").replace( "Day-1 documents drafted. Nothing built.", " ".join(["narrative"] * 90)) bad["docs/MyDiary-Architecture.md"] = ARCH.replace("```mermaid\n erDiagram", "```mermaid\n flowchart").replace( - "## 6. Decisions log", "## 6. Deployment\n\nVPS.\n\n## 7. Decisions log") + "## 6. Decisions log", "## 6. Deployment\n\nVPS.\n\n## 7. Decisions log").replace( + "| `MyDiary` | web app | the head |", "| `MyDiary.App` | web app | the head |").replace( + "| Q3 | Database | PostgreSQL in a container | answer set |\n", "") bad["docs/MyDiary-UIDesign.md"] = UI.replace("**States:** empty: form blank · loading: button spinner · error: red alert under the form", "") bad["docs/MyDiary-DevGuide.md"] = DG.replace("| `src/MyDiary/Pages/Login.razor.cs:127` | `HandleLogin` | `aLogin.Email` | the email typed in the box |\n", "").replace( "| File and line | Function | Watch | Expected value |\n|---|---|---|---|\n", "") +bad["docs/mockups/login.html"] = "Go" +bad["docs/mockups/entries.html"] = "" +# deployment checklist: a narrative line in Deploy, a secret described twice, a Proven placeholder +bad["docs/MyDiary-Deployment-Checklist.md"] = (DC.replace("| Proven | never |", "| Proven | {date} |") + .replace("- [ ] Restart the service — `systemctl status mydiary` says active", + "Then restart the service and check it is up.\n- [ ] Restart the service — `systemctl status mydiary` says active") + .replace("| Restart the service | `systemctl restart mydiary` |", + "| Restart the service | `systemctl restart mydiary` |\n| CONNECTION_STRING | set it again in the unit file |")) write_set(BAD, bad) -print("fixtures written:", GOOD, BAD) + +# --------------------------------------------------------------------------- +# A Large project (BigApp), two phases, laid out as docs/TechieFlow-Document-Schemas.md §2 says: +# phase 1 keeps the plain names, phase 2 is BigApp-P2-*, BRD and REQ ids run on, one mockup set. +# --------------------------------------------------------------------------- +LARGE = os.path.join(BASE, "fx-large") +LARGE_BAD = os.path.join(BASE, "fx-large-bad") + + +def big(text): + return text.replace("MyDiary", "BigApp") + + +PHASES = """# BigApp — Phases + +| | | +|---|---| +| App | BigApp | +| Kind | app | +| Size | Large | +| Date | 2026-09-05 | + +## Phases + +| Phase | Name | Screens | BRD range | Status | +|---|---|---|---|---| +| 1 | Core | Login, Entries | BRD-1 to BRD-3 | building | +| 2 | Reports | Reports | BRD-4 to BRD-5 | planned | +""" + +BRD1 = big(BRD).replace("| Size | Small |\n", "| Size | Small |\n| Phase | 1 of 2 |\n") +UI1 = big(UI).replace("| Size | Small |\n", "| Size | Small |\n| Phase | 1 of 2 |\n") +CL1 = big(CL).replace("| Size | Small |\n", "| Size | Small |\n| Phase | 1 of 2 |\n") +ARCH_L = big(ARCH).replace("| Size | Small |", "| Size | Large |") + """ +## 7. Module responsibilities + +| Module | Owns | +|---|---| +| Entries | writing and finding entries | +| Reports | monthly summaries | +""" + +BRD2 = """# BigApp — Business Requirements (phase 2) + +| | | +|---|---| +| App | BigApp | +| Kind | app | +| Size | Small | +| Phase | 2 of 2 | +| Stack answer set | dotnet | +| Status | Draft | +| Date | 2026-09-05 | + +## 1. Summary + +Phase 2 adds monthly reports over the entries written in phase 1. + +## 2. Scope + +**In:** +- Monthly report screen. + +**Out:** +- Export. + +## 3. Users and roles + +| Role | Who they are | What they need | +|---|---|---| +| Writer | the owner | see how much was written | + +## 4. Screens and flow + +| Screen | Route | Role | Mockup | Fields | +|---|---|---|---|---| +| Reports | `/reports` | Writer | [mockup](mockups/reports.html) | month, count | + +**Primary journey:** +1. The writer opens Reports from the menu and picks a month. + +## 5. Requirements + +- **BRD-4** — Monthly report. *Screen:* Reports · *Mockup:* [mockup](mockups/reports.html) + - *Acceptance:* When the writer picks a month on Reports, then the screen shows the number of entries written that month. + +## 6. Non-functional requirements + +| Id | Area | Requirement | Measure | +|---|---|---|---| +| BRD-5 | Performance | Reports open quickly | perf-budget: p95 load <= 2000ms @ concurrency 1 | + +## 7. Development status + +**Snapshot as of 2026-09-05.** + +| Screen | Requirements | Verified | Open | Status | +|---|---|---|---|---| +| Reports | 2 | 0 | 2 | Planned | +""" + +UI2 = """# BigApp — UI Design (phase 2) + +| | | +|---|---| +| App | BigApp | +| Kind | app | +| Size | Small | +| Phase | 2 of 2 | +| UI library | TrBlazeUI 2.0 | +| Theme | both | + +## Design system + +Same shell, theme and spacing as phase 1. + +## Screens + +### Screen: Reports (`/reports`) + +**Mockup:** [mockups/reports.html](mockups/reports.html) · **Roles:** Writer · **BRD:** BRD-4 + +| Region | Control | Shows or binds | +|---|---|---| +| Month picker | TrSelect | month | +| Summary | TrCard | count | + +| Field | Type | Required | Validation | +|---|---|---|---| +| Month | select | yes | a past month | + +**Dialogs opened here:** none + +**States:** empty: "No entries that month" · loading: skeleton card · error: alert +""" + +CL2 = """# BigApp — Checklist (phase 2) + +| | | +|---|---| +| App | BigApp | +| Size | Small | +| Phase | 2 of 2 | + +## Goal + +Add monthly reports over phase 1's entries. + +## Requirements Status + +| ID | Requirement | Status | % | Remarks | Details | +|----|-------------|--------|---|---------|---------| +| REQ-UI-002 | Monthly report | Not Started | 0% | — | [view](#d-req-ui-002) | +| REQ-NFR-002 | Reports speed | Not Started | 0% | — | [view](#d-req-nfr-002) | + +## Page: Reports (`/reports`) + + +- **REQ-UI-002** — Monthly report. *BRD:* BRD-4 · *Mockup:* mockups/reports.html + - *Acceptance:* When the writer picks a month on Reports, then the screen shows the number of entries written that month. + +## Non-functional + + +- **REQ-NFR-002** — Reports speed. *BRD:* BRD-5 + - *Acceptance:* When the Reports screen is measured with one user, then p95 load is within budget; perf-budget: p95 load <= 2000ms @ concurrency 1 +""" + +ENTRIES_HTML_L = ENTRIES_HTML.replace('Log out', 'Reports Log out') +REPORTS_HTML = """ +
    12 entries
    """ + +LFILES = { + "docs/BigApp-Phases.md": PHASES, + "docs/BigApp-BRD.md": BRD1, + "docs/BigApp-Architecture.md": ARCH_L, + "docs/BigApp-UIDesign.md": UI1, + "docs/BigApp-Checklist.md": CL1, + "docs/BigApp-P2-BRD.md": BRD2, + "docs/BigApp-P2-UIDesign.md": UI2, + "docs/BigApp-P2-Checklist.md": CL2, + "docs/BigApp-Coding-Standards.md": big(CS), + "PROJECT-STATUS.md": big(PS), + "docs/BigApp-UsageGuide.md": big(UG).replace("| Size | Small |", "| Size | Large |"), + "docs/mockups/login.html": LOGIN_HTML, + "docs/mockups/entries.html": ENTRIES_HTML_L, + "docs/mockups/reports.html": REPORTS_HTML, + ".tfcore/core-config.yaml": "appSize: L\nappKind: app\nappPhase: 2\n", +} +write_set(LARGE, LFILES) + +lbad = dict(LFILES) +# a screen in two phases, a status outside the fixed values, a phase row with no BRD file +lbad["docs/BigApp-Phases.md"] = PHASES.replace("| 1 | Core | Login, Entries | BRD-1 to BRD-3 | building |", + "| 1 | Core | Login, Entries, Reports | BRD-1 to BRD-3 | started |") \ + + "| 3 | Export | Export | BRD-6 to BRD-9 | planned |\n" +# phase 1 BRD without its Phase row although the project has phases +lbad["docs/BigApp-BRD.md"] = BRD1.replace("| Phase | 1 of 2 |\n", "") +# phase 2 BRD: header says phase 1, and it reuses BRD-2 (which is also outside its range) +lbad["docs/BigApp-P2-BRD.md"] = BRD2.replace("| Phase | 2 of 2 |", "| Phase | 1 of 2 |").replace("**BRD-4**", "**BRD-2**").replace("| Reports | 2 | 0 | 2 | Planned |", "| Reports | 2 | 0 | 2 | Planned |") +# phase 2 checklist reuses REQ-UI-001 from phase 1 +lbad["docs/BigApp-P2-Checklist.md"] = CL2.replace("REQ-UI-002", "REQ-UI-001").replace("d-req-ui-002", "d-req-ui-001").replace("*BRD:* BRD-4", "*BRD:* BRD-2") +write_set(LARGE_BAD, lbad) +print("fixtures written:", GOOD, BAD, LARGE, LARGE_BAD) diff --git a/tests/doc-check/run.sh b/tests/doc-check/run.sh index 7f77ebb..e61fab6 100644 --- a/tests/doc-check/run.sh +++ b/tests/doc-check/run.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash # Self-test for .tfcore/utils/tf-doc-check.sh (FR-14): a clean Small-app document set # must pass with no findings; a deliberately broken twin must fail; --warn must exit 0. +# Since Sitting 4b (2026-09-05) also a Large two-phase set (Schemas §2, §3.11): clean set +# passes, broken twin fails on the phase rules, and the phase-aware scripts (tf-split-brd, +# tf-status-facts, tf-brd-status) run on it for real. # bash tests/doc-check/run.sh set -uo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -8,6 +11,7 @@ ROOT="$(cd "$HERE/../.." && pwd)" export TF_FIXTURE_DIR="$ROOT/tests/.artifacts/doc-check" python3 "$HERE/make-fixtures.py" >/dev/null || { echo "could not build fixtures"; exit 2; } CHK="$ROOT/.tfcore/utils/tf-doc-check.sh" +UTILS="$ROOT/.tfcore/utils" fail=0 out="$(bash "$CHK" --root "$TF_FIXTURE_DIR/fx-good" --app MyDiary --quiet)"; rc=$? if [[ $rc -ne 0 || "$out" != *"0 FAIL, 0 WARN"* ]]; then echo "FAIL good set: exit $rc"; echo "$out"; fail=1; else echo "ok good set passes clean"; fi @@ -16,4 +20,46 @@ n="$(echo "$out" | grep -c '^FAIL')" if [[ $rc -ne 1 || $n -lt 12 ]]; then echo "FAIL bad set: exit $rc, $n FAIL lines"; echo "$out"; fail=1; else echo "ok bad set fails ($n findings)"; fi out="$(bash "$CHK" --root "$TF_FIXTURE_DIR/fx-bad" --app MyDiary --quiet --warn)"; rc=$? if [[ $rc -ne 0 ]]; then echo "FAIL --warn should exit 0, got $rc"; fail=1; else echo "ok --warn exits 0"; fi + +# --- Large, two phases --------------------------------------------------------------- +L="$TF_FIXTURE_DIR/fx-large" +out="$(bash "$CHK" --root "$L" --app BigApp --quiet)"; rc=$? +if [[ $rc -ne 0 || "$out" != *"0 FAIL, 0 WARN"* ]]; then echo "FAIL large set: exit $rc"; echo "$out"; fail=1; else echo "ok large set passes clean (two phases)"; fi +out="$(bash "$CHK" --root "$TF_FIXTURE_DIR/fx-large-bad" --app BigApp --quiet)"; rc=$? +n="$(echo "$out" | grep -c '^FAIL')" +want=("in phase 1 and phase 2" "must be planned, building or done" "phase 3 has no BRD" 'needs the header row "Phase | 1 of' "says 1 but the file name says phase 2" "is also in phase 1's BRD" "outside phase 2's range" "REQ-UI-001 is also in phase 1's checklist") +miss=0 +for w in "${want[@]}"; do echo "$out" | grep -q -- "$w" || { echo " missing finding: $w"; miss=1; }; done +if [[ $rc -ne 1 || $miss -ne 0 ]]; then echo "FAIL large bad set: exit $rc, $n FAIL lines"; echo "$out"; fail=1; else echo "ok large bad set fails on the phase rules ($n findings)"; fi + +# the splitter writes phase 2's checklist with numbers running on from phase 1 +rm -f "$L/docs/BigApp-P2-Checklist.md" +out="$(cd "$L" && bash "$UTILS/tf-split-brd.sh" BigApp --phase 2 2>&1)"; rc=$? +if [[ $rc -ne 0 || ! -f "$L/docs/BigApp-P2-Checklist.md" ]] || ! grep -q "REQ-UI-002" "$L/docs/BigApp-P2-Checklist.md" || grep -q "REQ-UI-001" "$L/docs/BigApp-P2-Checklist.md" || ! grep -q "| Phase | 2 of 2 |" "$L/docs/BigApp-P2-Checklist.md"; then + echo "FAIL tf-split-brd --phase 2: exit $rc"; echo "$out"; fail=1 +else echo "ok tf-split-brd --phase 2 writes BigApp-P2-Checklist.md, ids run on (REQ-UI-002)"; fi +# a raw split leaves a TODO acceptance line on every item the BRD gave none (the NFR row here); +# the checker must refuse exactly those and nothing else +out="$(bash "$CHK" --root "$L" --app BigApp --quiet)"; rc=$? +todo="$(grep -c 'TODO' "$L/docs/BigApp-P2-Checklist.md")" +nf="$(echo "$out" | grep -c '^FAIL')"; na="$(echo "$out" | grep -c 'acceptance line does not read')" +if [[ $rc -ne 1 || $nf -ne $todo || $na -ne $nf ]]; then echo "FAIL large set after split: exit $rc, $nf FAIL for $todo TODO"; echo "$out"; fail=1; else echo "ok after the split the checker refuses only the $todo TODO acceptance line(s)"; fi +# --all-phases rewrites both from the Phases table +out="$(cd "$L" && bash "$UTILS/tf-split-brd.sh" BigApp --all-phases --force 2>&1)"; rc=$? +if [[ $rc -ne 0 ]] || ! grep -q "REQ-UI-001" "$L/docs/BigApp-Checklist.md" || ! grep -q "REQ-UI-002" "$L/docs/BigApp-P2-Checklist.md"; then + echo "FAIL tf-split-brd --all-phases: exit $rc"; echo "$out"; fail=1 +else echo "ok tf-split-brd --all-phases writes both checklists"; fi +# the facts script reads the phase from core-config (appPhase: 2) and names it +out="$(cd "$L" && bash "$UTILS/tf-status-facts.sh" BigApp "build-phase" 2>&1)"; rc=$? +if [[ $rc -ne 0 || "$out" != *"Phase 2 of 2 (Reports)"* || "$out" != *"docs/BigApp-P2-Checklist.md"* ]]; then echo "FAIL tf-status-facts on phase 2: exit $rc"; echo "$out"; fail=1; else echo "ok tf-status-facts reads appPhase 2 and names the phase"; fi +out="$(cd "$L" && bash "$UTILS/tf-status-facts.sh" BigApp "build-phase" --phase 1 2>&1)"; rc=$? +if [[ $rc -ne 0 || "$out" != *"Phase 1 of 2 (Core)"* || "$out" != *"docs/BigApp-Checklist.md"* ]]; then echo "FAIL tf-status-facts --phase 1: exit $rc"; echo "$out"; fail=1; else echo "ok tf-status-facts --phase 1 reads the plain names"; fi +# the BRD status table is written into the phase's BRD +out="$(cd "$L" && bash "$UTILS/tf-brd-status.sh" BigApp --no-render 2>&1)"; rc=$? +if [[ $rc -ne 0 || "$out" != *"docs/BigApp-P2-BRD.md Development status updated"* ]]; then echo "FAIL tf-brd-status on phase 2: exit $rc"; echo "$out"; fail=1; else echo "ok tf-brd-status writes phase 2's BRD"; fi +# --size L writes the Phases skeleton and --phase sets appPhase (the script reads templates from cwd) +rm -f "$L/docs/BigApp-Phases.md" +mkdir -p "$L/.tfcore/templates" && cp -r "$ROOT/.tfcore/templates/v4custom" "$L/.tfcore/templates/" +out="$(cd "$L" && bash "$UTILS/tf-day1-files.sh" BigApp --size L --phase 1 2>&1)"; rc=$? +if [[ $rc -ne 0 || ! -f "$L/docs/BigApp-Phases.md" ]] || ! grep -q "^appPhase: 1" "$L/.tfcore/core-config.yaml"; then echo "FAIL tf-day1-files --size L --phase 1: exit $rc"; echo "$out"; fail=1; else echo "ok tf-day1-files --size L writes the Phases skeleton, --phase sets appPhase"; fi exit $fail diff --git a/tests/goal/run.sh b/tests/goal/run.sh new file mode 100644 index 0000000..13548a0 --- /dev/null +++ b/tests/goal/run.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env bash +# tests/goal/run.sh — self-test for the goal supervisor (.tfcore/utils/tf-goal.sh). +# Builds a throw-away app folder, replaces the harness with TF_GOAL_FAKE_CMD, and proves: +# 1. classification: a clean Claude stop is IDLE (not a crash), a rejected rate limit is +# LIMIT at the event's reset epoch plus the buffer, a non-zero exit is CRASH, plain +# OpenCode text is IDLE +# 2. a cycle whose output stops growing is killed after the stall clock and re-prompted +# 3. TERM to the supervisor stops its harness child and exits 130 within seconds +# 4. a clean early stop is re-prompted after --idle-retry-sec, never backed off +# 5. two stalled resumes start a fresh session; --resume --fresh does the same by hand +# 6. a silent OpenCode cycle whose log says the provider refused the model exits 5 +# 7. tf-yolo.sh done refuses the sentinel until the status file and a run record are newer +# than the goal start +# Run: bash tests/goal/run.sh (about 60 seconds; exit 0 = every check passed) +set -u +HERE="$(cd "$(dirname "$0")" && pwd)"; ROOT="$(cd "$HERE/../.." && pwd)" +GOAL_SH="$ROOT/.tfcore/utils/tf-goal.sh" +WORK="$(mktemp -d "${TMPDIR:-/tmp}/tf-goal-test.XXXXXX")" +APP="$WORK/fakeapp"; mkdir -p "$APP/.tfcore/utils" "$APP/docs" +cp "$ROOT/.tfcore/utils/tf-yolo.sh" "$APP/.tfcore/utils/" +trap 'rm -rf "$WORK"' EXIT +pass=0; fail=0 +ok() { pass=$((pass+1)); echo "ok $*"; } +bad() { fail=$((fail+1)); echo "FAIL $*"; } +check() { # description, condition-result + if [[ "$2" == 0 ]]; then ok "$1"; else bad "$1"; fi +} +classify() { TF_GOAL_CLASSIFY="$1" TF_GOAL_CLASSIFY_RC="${2:-0}" bash "$GOAL_SH" "$APP" x | tr '\t' ' '; } + +# ---- 1. classification ------------------------------------------------------------- +cat > "$WORK/clean.out" <<'EOF' +{"type":"system","subtype":"init","session_id":"abc12345-0000-0000-0000-000000000000","model":"claude-sonnet-5"} +{"type":"assistant","message":{"content":[{"type":"text","text":"Waiting for the build to complete."}]},"session_id":"abc12345-0000-0000-0000-000000000000"} +{"type":"result","subtype":"success","is_error":false,"num_turns":9,"api_error_status":null,"result":"Waiting for the build to complete.","session_id":"abc12345-0000-0000-0000-000000000000"} +EOF +r="$(classify "$WORK/clean.out" 0)"; check "clean Claude stop is IDLE ($r)" "$([[ "$r" == IDLE* ]]; echo $?)" + +reset=$(( $(date +%s) + 3600 )) +cat > "$WORK/limit.out" < "$WORK/oc.out" +r="$(classify "$WORK/oc.out" 0)"; check "plain OpenCode text is IDLE ($r)" "$([[ "$r" == IDLE* ]]; echo $?)" + +# ---- 2. stall → kill → re-prompt → sentinel on the next cycle ---------------------------- +rm -f "$APP/.tfcore/.session/"* 2>/dev/null +FAKE='cd "$PWD"; if [[ -f cycle1.done ]]; then bash .tfcore/utils/tf-yolo.sh done complete "second cycle"; else touch cycle1.done; echo started; sleep 120; fi' +t0=$(date +%s) +TF_GOAL_FAKE_CMD="$FAKE" TF_GOAL_STALL_SEC=4 TF_GOAL_STALL_TICK=1 \ + bash "$GOAL_SH" --idle-retry-sec 1 --max-cycles 3 "$APP" "stall test" >"$WORK/stall.log" 2>&1; rc=$? +took=$(( $(date +%s) - t0 )) +check "stalled cycle is killed and the run completes on cycle 2 (exit $rc, ${took}s)" "$([[ $rc -eq 0 && $took -lt 40 ]]; echo $?)" +check "log names the stall" "$(grep -q 'STALL: no output for' "$WORK/stall.log"; echo $?)" +check "stall is re-prompted, not backed off" "$(grep -q 'stalled 15m) — re-prompting in 1s' "$WORK/stall.log"; echo $?)" +check "no fake harness left running" "$(pgrep -x sleep -a | grep -q ' 120$'; [[ $? -ne 0 ]]; echo $?)" + +# ---- 3. TERM stops the supervisor AND its child --------------------------------------- +rm -f "$APP/.tfcore/.session/"* "$APP/cycle1.done" 2>/dev/null +TF_GOAL_FAKE_CMD='echo up; sleep 300' TF_GOAL_STALL_TICK=1 \ + bash "$GOAL_SH" --idle-retry-sec 1 "$APP" "kill test" >"$WORK/kill.log" 2>&1 & +sup=$! +sleep 3 +kill -TERM "$sup" +t0=$(date +%s); wait "$sup"; rc=$?; took=$(( $(date +%s) - t0 )) +check "TERM exits 130 within seconds (exit $rc, ${took}s)" "$([[ $rc -eq 130 && $took -lt 15 ]]; echo $?)" +sleep 1 +check "the harness child is gone" "$(pgrep -x sleep -a | grep -q ' 300$'; [[ $? -ne 0 ]]; echo $?)" +check "goal.json says stopped" "$(grep -q '"last_reason": "stopped"' "$APP/.tfcore/.session/goal.json"; echo $?)" +check "YOLO flag cleared" "$([[ ! -f "$APP/.tfcore/.session/yolo.json" ]]; echo $?)" + +# ---- 4. clean early stop → re-prompt after idle-retry, never a backoff -------------------- +rm -f "$APP/.tfcore/.session/"* "$APP/cycle1.done" 2>/dev/null +FAKE='if [[ -f cycle1.done ]]; then bash .tfcore/utils/tf-yolo.sh done complete "done"; else touch cycle1.done; cat '"$WORK/clean.out"'; fi' +t0=$(date +%s) +TF_GOAL_FAKE_CMD="$FAKE" TF_GOAL_STALL_TICK=1 bash "$GOAL_SH" --idle-retry-sec 1 --max-cycles 3 "$APP" "idle test" >"$WORK/idle.log" 2>&1; rc=$? +took=$(( $(date +%s) - t0 )) +check "clean stop completes on cycle 2 (exit $rc, ${took}s)" "$([[ $rc -eq 0 && $took -lt 20 ]]; echo $?)" +check "log says clean stop, re-prompting in 1s" "$(grep -q 'clean stop: result is_error=false, 9 turn(s)) — re-prompting in 1s' "$WORK/idle.log"; echo $?)" +check "no harness/API error line" "$(grep -q 'harness/API error' "$WORK/idle.log"; [[ $? -ne 0 ]]; echo $?)" + +# ---- 5. two stalled resumes → a fresh session; and --resume --fresh ---------------------- +rm -f "$APP/.tfcore/.session/"* "$APP/cycle1.done" "$APP/n" 2>/dev/null +# cycle 1: clean stop (so cycle 2+ are resumes); cycles 2,3: hang; cycle 4: sentinel +FAKE='n=$(( $(cat n 2>/dev/null || echo 0) + 1 )); echo $n > n; case $n in 1) cat '"$WORK/clean.out"';; 2|3) echo hang; sleep 120;; *) bash .tfcore/utils/tf-yolo.sh done complete "fresh worked";; esac' +t0=$(date +%s) +TF_GOAL_FAKE_CMD="$FAKE" TF_GOAL_STALL_SEC=3 TF_GOAL_STALL_TICK=1 bash "$GOAL_SH" --idle-retry-sec 1 --max-cycles 6 "$APP" "fresh test" >"$WORK/fresh.log" 2>&1; rc=$? +took=$(( $(date +%s) - t0 )) +check "run completes on cycle 4 after two stalled resumes (exit $rc, ${took}s)" "$([[ $rc -eq 0 && $took -lt 40 ]]; echo $?)" +check "log announces the fresh session" "$(grep -q 'two stalled resumes in a row' "$WORK/fresh.log"; echo $?)" +check "cycle 4 was launched as a first (fresh) cycle" "$(grep -q 'cycle 4 (first)' "$WORK/fresh.log"; echo $?)" +check "cycle 3 was still a resume" "$(grep -q 'cycle 3 (resume)' "$WORK/fresh.log"; echo $?)" +check "no stray fake harness" "$(pgrep -x sleep -a | grep -q ' 120$'; [[ $? -ne 0 ]]; echo $?)" +# --resume --fresh on a stopped run: the state says cycle 4 and done; make it look stopped +python3 - "$APP/.tfcore/.session/goal.json" <<'PY2' +import json,sys; p=sys.argv[1]; d=json.load(open(p)); d["last_reason"]="stopped"; d["session_id"]="oldsession"; json.dump(d,open(p,"w")) +PY2 +rm -f "$APP/.tfcore/.session/goal-done.json" "$APP/n"; echo 3 > "$APP/n" # next fake call is n=4 → sentinel +TF_GOAL_FAKE_CMD="$FAKE" TF_GOAL_STALL_TICK=1 bash "$GOAL_SH" --resume --fresh "$APP" >"$WORK/fresh2.log" 2>&1; rc=$? +check "--resume --fresh completes (exit $rc)" "$([[ $rc -eq 0 ]]; echo $?)" +check "--resume --fresh logs the fresh session and launches (first)" "$(grep -q 'in a FRESH session' "$WORK/fresh2.log" && grep -q 'cycle 5 (first)' "$WORK/fresh2.log"; echo $?)" + +# ---- 6. OpenCode says nothing, its log says "monthly usage limit" → exit 5 --------------- +rm -f "$APP/.tfcore/.session/"* "$APP/n" 2>/dev/null +FAKELOG="$WORK/opencode.log"; : > "$FAKELOG" +FAKE='echo "> build · glm-5.2"; sleep 2; printf "timestamp=%s level=ERROR run=x message=\"stream error\" providerID=opencode-go modelID=glm-5.2 error.error=\"AI_APICallError: Monthly usage limit reached. Resets in 6 days.\"\n" "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" >> '"$FAKELOG"'; sleep 120' +t0=$(date +%s) +TF_GOAL_FAKE_CMD="$FAKE" TF_GOAL_OPENCODE_LOG="$FAKELOG" TF_GOAL_STALL_SEC=4 TF_GOAL_STALL_TICK=1 \ + bash "$GOAL_SH" --harness opencode --idle-retry-sec 1 --max-cycles 3 "$APP" "limit test" >"$WORK/plimit.log" 2>&1; rc=$? +took=$(( $(date +%s) - t0 )) +check "silent provider limit stops the supervisor with exit 5 (exit $rc, ${took}s)" "$([[ $rc -eq 5 && $took -lt 30 ]]; echo $?)" +check "log carries the provider's own words" "$(grep -q 'Monthly usage limit reached' "$WORK/plimit.log"; echo $?)" +check "goal.json says provider-limit" "$(grep -q '"last_reason": "provider-limit"' "$APP/.tfcore/.session/goal.json"; echo $?)" +check "no stray fake harness" "$(pgrep -x sleep -a | grep -q ' 120$'; [[ $? -ne 0 ]]; echo $?)" + +# ---- 7. `done` is refused while the status gate has not run -------------------------------- +rm -f "$APP/.tfcore/.session/"* "$APP/n" 2>/dev/null; mkdir -p "$APP/docs/metrics" +PS="$APP/PROJECT-STATUS.md"; printf '# status\n' > "$PS"; touch -d '2026-01-01' "$PS"; : > "$APP/docs/metrics/runs.jsonl" +FAKE='bash .tfcore/utils/tf-yolo.sh done blocked "too early"; if [[ ! -f .tfcore/.session/goal-done.json ]]; then echo refused-as-expected; touch PROJECT-STATUS.md; printf "{\"cmd\":\"build-phase\",\"claimed\":true}" > .tfcore/.session/phase.json; printf "{\"kind\":\"run\",\"cmd\":\"verify-phase\",\"ts\":\"%s\"}\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> docs/metrics/runs.jsonl; bash .tfcore/utils/tf-yolo.sh done blocked "only a verify record"; [[ -f .tfcore/.session/goal-done.json ]] || echo refused-again-as-expected; printf "{\"kind\":\"run\",\"cmd\":\"build-phase\",\"ts\":\"%s\"}\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> docs/metrics/runs.jsonl; bash .tfcore/utils/tf-yolo.sh done complete "after the gate"; fi' +TF_GOAL_FAKE_CMD="$FAKE" TF_GOAL_STALL_TICK=1 bash "$GOAL_SH" --idle-retry-sec 1 --max-cycles 2 "$APP" "done test" >"$WORK/done.log" 2>&1; rc=$? +check "done is refused before the gate and accepted after it (exit $rc)" "$([[ $rc -eq 0 ]] && grep -q 'refused-as-expected' "$APP/.tfcore/.session/goal.log" && grep -q 'GOAL-DONE refused' "$APP/.tfcore/.session/goal.log"; echo $?)" +check "done is refused again when only another command's run record exists" "$(grep -q 'refused-again-as-expected' "$APP/.tfcore/.session/goal.log" && grep -q 'no run record for build-phase' "$APP/.tfcore/.session/goal.log"; echo $?)" +check "the accepted sentinel is the complete one" "$(grep -q '"outcome":"complete"' "$APP/.tfcore/.session/goal-done.json"; echo $?)" + +echo "tests/goal: $pass passed, $fail failed" +[[ $fail -eq 0 ]] diff --git a/tests/mirror/run.sh b/tests/mirror/run.sh new file mode 100644 index 0000000..86cc965 --- /dev/null +++ b/tests/mirror/run.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +# tests/mirror/run.sh — harness parity (FR-40; Session 5, 2026-09-07, from MISS-TechieFlow-20260905-02: +# six task files edited in .tfcore were not copied to the Claude Code mirror for five days and no +# check ran). Checks: +# 1. every persona and task under .tfcore/{agents,tasks}/ is byte-identical in .claude/commands/TechieFlow/ +# 2. the mirror holds nothing .tfcore no longer has (a removed command must be gone from both) +# 3. every .tfcore/tasks/*.md and .tfcore/agents/*.md is referenced from opencode.jsonc, +# and every {file:./.tfcore/...} reference in opencode.jsonc resolves to a file +# 4. every task file is under the FR-43 frontier budget of 7,000 words, and the shared rule files +# together stay under 3,000 (FR-44); every persona under 1,500 +# Run: bash tests/mirror/run.sh (a second's work; the distribution pipeline runs it too) +set -u +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; ROOT="$(cd "$HERE/../.." && pwd)" +pass=0; fail=0 +ok() { pass=$((pass+1)); echo "ok $*"; } +bad() { fail=$((fail+1)); echo "FAIL $*"; } +M="$ROOT/.claude/commands/TechieFlow" + +# 1. byte-identical mirror +for kind in agents tasks; do + n=0; d=0 + for f in "$ROOT/.tfcore/$kind"/*.md; do + b="$(basename "$f")"; n=$((n+1)) + if [[ ! -f "$M/$kind/$b" ]]; then bad "$kind/$b is missing from the Claude Code mirror"; d=$((d+1)) + elif ! cmp -s "$f" "$M/$kind/$b"; then bad "$kind/$b differs from the Claude Code mirror (cp -p .tfcore/$kind/$b .claude/commands/TechieFlow/$kind/$b)"; d=$((d+1)); fi + done + [[ $d -eq 0 ]] && ok "$n $kind file(s) byte-identical in the Claude Code mirror" +done + +# 2. nothing stale in the mirror +stale=0 +for kind in agents tasks; do + for f in "$M/$kind"/*.md; do + [[ -f "$f" ]] || continue + b="$(basename "$f")" + [[ -f "$ROOT/.tfcore/$kind/$b" ]] || { bad "mirror holds $kind/$b, which .tfcore no longer has (remove it)"; stale=$((stale+1)); } + done +done +[[ $stale -eq 0 ]] && ok "the mirror holds nothing .tfcore no longer has" + +# 3. opencode.jsonc references +OC="$ROOT/opencode.jsonc" +if [[ -f "$OC" ]]; then + miss=0 + for f in "$ROOT/.tfcore/tasks"/*.md "$ROOT/.tfcore/agents"/*.md; do + rel="${f#$ROOT/}" + [[ "$(basename "$f")" == _* ]] && continue # a shared rule file is read by the tasks that name it, not registered as a command + grep -qF "{file:./$rel}" "$OC" || { bad "opencode.jsonc does not reference $rel"; miss=$((miss+1)); } + done + [[ $miss -eq 0 ]] && ok "every command task and persona is referenced from opencode.jsonc" + broken=0 + while IFS= read -r ref; do + [[ -f "$ROOT/$ref" ]] || { bad "opencode.jsonc references $ref, which does not exist"; broken=$((broken+1)); } + done < <(grep -o '{file:\./[^}]*}' "$OC" | sed 's/{file:\.\///; s/}$//' | sort -u) + [[ $broken -eq 0 ]] && ok "every {file:} reference in opencode.jsonc resolves" +else + bad "opencode.jsonc not found" +fi + +# 4. instruction budgets +over=0 +for f in "$ROOT/.tfcore/tasks"/*.md; do + w=$(wc -w < "$f"); [[ $w -le 7000 ]] || { bad "$(basename "$f") is $w words, over the 7,000-word frontier budget (FR-43)"; over=$((over+1)); } +done +[[ $over -eq 0 ]] && ok "every task file is under 7,000 words (FR-43)" +shared=$(cat "$ROOT/.tfcore/tasks"/_*.md | wc -w) +[[ $shared -le 3000 ]] && ok "shared rule files total $shared words (FR-44: under 3,000)" || bad "shared rule files total $shared words, over 3,000 (FR-44)" +overp=0 +for f in "$ROOT/.tfcore/agents"/*.md; do + w=$(wc -w < "$f"); [[ $w -le 1500 ]] || { bad "$(basename "$f") is $w words, over the 1,500-word persona cap (FR-44)"; overp=$((overp+1)); } +done +[[ $overp -eq 0 ]] && ok "every persona is under 1,500 words (FR-44)" + +# 5. the two readable surfaces (Session 6, 2026-09-07) +# The briefing and the README are what a person reads first. They grew to 344 KB and 121 KB, and +# nothing counted them or noticed when they named a command the framework had removed. +brief="$ROOT/WorkFlow-Context.md"; readme="$ROOT/README.md" +if [[ -f $brief ]]; then + w=$(wc -w < "$brief") + [[ $w -le 3000 ]] && ok "WorkFlow-Context.md is $w words (budget 3,000)" \ + || bad "WorkFlow-Context.md is $w words, over the 3,000-word budget; the log belongs in docs/CHANGELOG.md" +else bad "WorkFlow-Context.md not found"; fi +if [[ -f $readme ]]; then + w=$(wc -w < "$readme") + [[ $w -le 4000 ]] && ok "README.md is $w words (budget 4,000)" \ + || bad "README.md is $w words, over the 4,000-word budget; move the detail into docs/" +else bad "README.md not found"; fi + +# 5b. no readable surface names a command the framework removed in Sitting 4c +gone_hits=0 +for f in "$brief" "$readme"; do + [[ -f $f ]] || continue + while read -r cmd; do + if grep -qi -- "$cmd" "$f"; then + bad "$(basename "$f") names '$cmd', a command removed in Sitting 4c"; gone_hits=$((gone_hits+1)) + fi + done <<< "author-brd +create-brd +advanced-elicitation +document-project +index-docs +shard-doc +execute-checklist +kb-mode-interaction" +done +[[ $gone_hits -eq 0 ]] && ok "no readable surface names a removed command" + +# 5c. FR-47 — a public-facing document names no private project. +# The names themselves are never written into this repository: they are read from a per-machine +# file, ~/.techieflow/private-names.txt (one name per line, '#' comments). Without it the check +# says so and passes, because a clone on another machine cannot know the owner's private repos. +priv_file="${TF_PRIVATE_NAMES:-$HOME/.techieflow/private-names.txt}" +if [[ -f $priv_file ]]; then + leaks=0 + mapfile -t priv < <(grep -v '^\s*#' "$priv_file" | grep -v '^\s*$' | tr -d '\r') + while IFS= read -r f; do + for n in "${priv[@]}"; do + grep -q -w -- "$n" "$f" 2>/dev/null && { bad "FR-47: $(realpath --relative-to="$ROOT" "$f") names a private project"; leaks=$((leaks+1)); } + done + done < <(printf '%s\n' "$readme" "$brief"; find "$ROOT/.tfcore/templates" -name '*.md' -o -name '*.yaml' 2>/dev/null) + [[ $leaks -eq 0 ]] && ok "FR-47: the README, the briefing and the templates name no private project (${#priv[@]} names checked)" +else + ok "FR-47: skipped, no private-name list at $priv_file" +fi + +echo +echo "mirror self-test: $pass passed, $fail failed" +[[ $fail -eq 0 ]] diff --git a/tests/verify/__pycache__/make-fixtures.cpython-312.pyc b/tests/verify/__pycache__/make-fixtures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f58ee13ceb8b35cdf60e5786be07d9af0eb426f GIT binary patch literal 13812 zcmdU0Yj6}-cJ6s;q(Kh^7-9KAGXhJ(ni;);ku+;SfFFQuBy21jYg^MjnwDm|$K5?h zGe|+IO$uCbl|{u>$hGTQ_D|L>oZ9_q=Z~HI@>Z&L{d-8Q9#r&MRttbObxiuppH-71V3=FVT^xUoj5QS?n8;5gm!4#}sI z_A0Gad1NJd)JcstPkH)+pwBtA*QToQT!(`D;1#!gTx+kPJk5B2s({mOZ4S3hvx9r* zeK*H&x7ipAPpJ+x)Y&vRXqWb%qs;xGTAB?I-8{-D4 z%Fp{zsM@wks`B&hqN-lpF77DNsbi{=?l-U(#7D%PHl3vwBQ}lggw@oGyQG~ZYu1a8 zip^z^$Hd3WAWw)-mO*xld>Leq=r2IT7I=VWj(Y=Ma;$NzX>1o;eXrE`b57j%flGW! zYC@}3+J)b?4?KmL?>x@-LA}_17p42frwij4z78=^1__FxGDxS`RR-x6d&(faVqXCw z_KOF^gW@4EEDnf+zo?q(vG-TudBbXgOZfaZ;8nq;$>R|5_<>W}B}TFsRlxhsPR3bt z!Am;0jI;eG!ZgClk+hr;b>4_e_@}8e0bxeS@F`85lA;E0NV=hiE=Zaj%Y?MF64c{7 zdy^%d7bXQ+(Ty-K@VX%wa+LQAsg$3ek&QU7DiS}bs-jPiYLcXMFo2Xy85zD9q5@k2 zvt(t`g7bnRvcYNK&Lkw!f)8MsXk3a;CuH5|;LkocEa~#3(&0ODW|+-6s|%CTiL@+A zysR*WMkHOARfX^2^|+9dbf8e8aa9A(q#B)0rwGEoln^pAnmiddcs(UWc|}NKz(j_J zGWfc@OLyig2yx0OU zdDcj4QhV^VS>*33bV8Irqu1}*{axV0D_8Ift1++5*H4J&V3t( zCa-Y@1Jch_@hA&A<;i#F8wO!uf9CQAGw(F4bOU%Q1kU@cQXwgtbK2oXbbRbA7^aaA zfOAfyHtvr-wiD^&=3R5HsS3%^oMWodrn&GGtLzo*rvnKc{VlAP;aa#ZBm^@~2lpE1 zz~2lv>w1lwakP6+kACFHRZFult#Nfa=a|WQ(nc(Bu$`bvR25;L-#dKmH94oM=iDj5 zi09l>s;uN(hhKUrSDh55B~jM&oEN`jKsmRXl9ZeWhGj^(s)TN6a;n{(b7@i{=QgBS zBj-rv95J2pDIR~mf1+fs5kgz_dN7sIND=IZWTMOXS>V2|_Py(U+xu?q+qLhFEVryy zKe=%DYj@S<7ZzW5&w0&z)qAbsYQuHM>Q?`Uqqp4MU)R)K5tg^{mJhpb zSJx1RCHJ*0SGOz=uWsFQv)cci3w__wfIIZ_ySwto|m#GuBy{|uEvlOQc^Os zOm>$wB}DTAQB{e_lfjITOk`j7!S+u>uwnlA?BP_3UPt6CUPq|;f~<(}Lni#CxS*q> z%STA%s2=w5wnG~?GAVS?z?@rDqfnSNDEs>fRm@zBsfrPZ2}wDT3G0HQ2Xyj%14%)f zl$CJjJRg??c!7&4K_r_9cXg*`2PTB*^rWW3KZIMlyL$$rYC_e*EwNZ^o=?I@7Do4{ zX6JcBm`F$$CsYlHz+?jc5A@m%^ot!v-0BZ^rDl0uO~@kOa_G>Z0ejtEfDaO~iL_y; zg%z*>Pc+SE>vULNh<<`mUC%bKSBOcP4aSXRLa2d@@D1*P5J)CG!6Jj!FL;a$LNMfU z1x=TXAwSy={>Y#q8wn}$teTVtLuPw0AuH27?0U%09JU^pB*V|gH7N#&wLWIT?aL|n zSS>n4aUwXS0~j))XpmqdzCqFg=wMRjMZpjPv@c|F$Zx_hC*BodHh?)@kwHbc5E&G# zIdYvTP?!N?Am0(IRWu=F@)t4%9by{yWw$W%Fx8UXhMfnSlY$9# zvaK~X+uA;mZDa#10h$){X9yFUwxGNQHfBVdHj@ha&6B`1%ywm|=q+votP6TZl0+NB z(5M+177W@0arQDw+roScQ9t13SWC=?{iMAQwdpBAxqGn;+(BA_N%b&2!;-Nj}6eeJ6X+s(?)Ks_&!v52B z>}1+;0JXi95~#RL>KJW zBxF6PrpSCFC&_3BL)M!O9TqfrcN-Wo@qla-du9JkIz$$c_sAv6thlS&-R-WNI}Q`d zxhc@5-B8CSLShMj4;cQe=y$(x_os!wF2pU{Bum%3SJOQOojv5R2FH$vLm( zz0?1LTyrDBu{XNK>5uC6S)>pnOj-s-j-s{|CQ9PM{lbD$78VL}slxFCt~*y{y7255 z4r0t3^-93R5ivp6y1 zQCSV>AbX)HFQ?_`G;ec)4ixGhWIhYj41=vjtXr(1b!aFXVv>>FRV2DX0g)v5DdI}0 zk>~4+Opv2Emhx#Z3zMAVTMDbbgxqchhkG2`(^|AhoX4oVbT27(0STRUnE(}3P&;@< z<*CXUKt(ez+6-#1=59GQD@9RUf#fEs?&!@8dCAR(+*PbP8<$D=d zCsZNf^Bp%!mgdGgBfXWDHDpH$h^Gb8jN|8qK~Yg3-po!k_OUJ$O{PI9_Ov0O0JNU@ zSSEDl*voJ-fzD2n0<$MNU86Ehr|?$Yj(q5RE}5c&kz$}sJI5dBqtzx*#y0SLT|D2` zA_g?+tw35PWbMpU^2oa}=9qc-H|sEX-w3|$GJHLR&vXK2Y#@eTp_-A*sO#$NB=XdR z$fsZzXqHphQ--k@WAY9mg_1VjH$q`=0pIb{kU$^dL(+*#H}qK8au1F+rVyGkwEdWW zln$%>gdlE&NQSMyfbI10GeCa;=uWDWB%lXmR*7jeuN=`qU|`mRT0YajhR_`>pku;2 z1Z=~ano2QLr$dMFZYnfw@M;WK6DacnR-{zM-yy<7@r+3E`2svDRCK^AwoE$j2oZk& z{=9K^?cdM0!BGcbwd?PIpyij>4Rt&p;P)SnMx~TN2f^@u{?$0HFW@+tzv8Jft+NF> z2x#_@d(7~(<;W465{%ImHK&52(r{5k zxET**9d}T4vs0PA{=4bxx9P(MEHdQ#lVys}aw0{9u+j|)&-O3yn*!V($(>3BrZ=@Z zE89eO37O0rcH3lPU)zY;nPnX_kWA-hW(N0@u*nJ$Xe-`Y!eUFp9aP3FIr5OOz^=sD z^_~pRj9eaG1=$=s=UePCUs##Dp1Zm0vAKhLDRraku~(Es-H$t}YeW20g(0o5nHFQF zL4tI%RDu|b!Zh6+;@Xd%R?2CeLo2h>dpA41HaqZdB|dx|ZtU#m!L*fv_a-Hn77v!} zC5nuY1q_{STw;k`pqk8_R+T^u1!+nbD8`rhB+H$a@MO7Wn<@BOa?$tPA(YnRo>(s; z2}NW+CL^KLBwXttjg$mb#ZmXiwJ`HV6*jF&&A|%P9JHx{t1kHYb!igTad{99Zg*yo zy|MbdbzwY_Mkq6ad`i+{0rMryrw;Y;h7p_K2Z#8+&d#LHKZ8o25>0Cwt_Cx>XDSn= z839UYH-m{yI--LWRMw?iQH?n2VwM=KH{^AXmE01VSHwZOw#Ide;KF61k81Y@e~~4U z9lSJ)K7*gx5-Ew8%68G*|)X6UQ$k=m8?6N^H{vK zy*+5C&m#3Ddp$ zX{bu}Ti-?8+|s$uANH%${tiE#ro?gc{vEJ0sF&kP9UTc|1PKg#{i46f1^UBX9e!P) zV;TB-J3IU{k}&NL$B_O=9e(qp0Dl;8)*RQ6IIvfoamTwmJAvchC6ZXkg}_J3g zcK`i8+8vfA(v$bF=l^rv!iFtoVYeG(cI`gQ-hca+u-gMlDw;Cwy^;+a?CtR1dkf#g znjePa`>A(>9D@y$VM9wlgunWcnDrN_*Y=T;NM4$Ja@arXpKq_qdev#uo7b7&DhUbm z)hCjvo9!BWnj>0GV%Tgp6p)&LEiCJc&KbG^3kE5bEKCaQ{eYq+%T=S~l3`%i(UB2+IZ0n$%gHsD!{NB&b~#QuzOCjQ zzCUtxf8>1s?5(*xvp944(&D8h?b^)MnQL=b=dK_5^@(4d`1Omwdhxf zc5%UZyQ=!~T z&3Dze-1Aor-*8UPYtAd~+glozBCA{W+p0xRdnzB)Jl<+<^%&WT^1 z6IZ18%9OHtPQCej>dL7r$KO5m_Nit6FI(Sl{bk4d9oO{_hgX}sKHqVx_V~($ODh*< zzq)Yg%L|v*IPS3XsPj8|JnOt;KG@qS^X-%qLryzirpJi$RVRdd*m;f~Gmh7s|GnlR z`qrvAN9}_1@|MLdSB@_4y~XV&$kv6b%MFVSV1~X)ti4*hJbL}mhm)&Y`#*7h=Ka+B zS?#B_pO1XezH;`B)r03(l5gGOwA)<64>)Ujb!+&OBcGl4^u%W;KRx-y_Alg>*TmI9 zX(gSx#by7AbF4TYU*o*H-T%pX9iFwV1ul-fd*baA@1A`7gSZUmMlY5GFK7ti?nJe7E3eFYh z4nUepAUoGM&!d2NKt;W$+4&7u<;Xa{bU(5%d}ZI#jytuSr|L@GlDPEBa^FhTo|S#) zSKf@SoEM=K$sv$#CY&*P2+m1*Ogd-iv3J(_UmWMjIM?b3-<^84{G-bteD}W)_swfu z^AlLN8HD$xw#M~62}mCx3cjcXOS{*&CO<$;0QE9R z*Ba;Q1q5G}uKBpFmt1R3Zs##Kez!kEzuSh{?N!{S;=Z4?e$!Cx xXkUCDtnY=u!1$J>;idkSs^;b4>%H&4aLe7c)`$^zc5sfWh3H@SvHome +

    FxApp

    +

    Today

    +
    • Morning walk
    • Lunch with Ana
    • Read chapter 4
    + + +
    +""") +w("site/app.js", "console.log('fx');") +w("site/entries.html", """ +Entries +

    FxApp

    +

    Entries

    +
    DateTitle
    +3 entries +
    +""") +w("site/broken.html", """ +Editor + +

    FxApp

    +

    Editor

    +
    +
    +""") +w("site/unstyled.html", """ +Settings +

    FxApp

    +

    Settings

    +
    +""") + +for name, title, body in [ + ("home", "Home", '

    FxApp

    '), + ("entries", "Entries", '

    FxApp

    DateTitle
    n entries
    '), + ("editor", "Editor", '

    FxApp

    '), + ("settings", "Settings", '

    FxApp

    '), +]: + w(f"docs/mockups/{name}.html", f'{title}{body}\n') +w("docs/mockups/site.css", CSS) + +w("docs/FxApp-UIDesign.md", """ +# FxApp — UIDesign + +| | | +|---|---| +| App | FxApp | +| Kind | app | +| Size | S | + +## Design system + +Plain. + +## Screens + +### Screen: Home (`/`) +Mockup: docs/mockups/home.html + +### Screen: Entries (`/entries.html`) +Mockup: docs/mockups/entries.html + +### Screen: Editor (`/broken.html`) +Mockup: docs/mockups/editor.html + +### Screen: Settings (`/unstyled.html`) +Mockup: docs/mockups/settings.html +""") +w("docs/FxApp-BRD.md", """ +# FxApp — BRD + +## Screens and flow + +| Screen | Route | Role | Mockup | Fields | +|---|---|---|---|---| +| Home | / | User | docs/mockups/home.html | — | +| Quick settings | on / | User | docs/mockups/home.html | theme | +| Entries | /entries.html | User | docs/mockups/entries.html | — | +""") +w("docs/FxApp-UsageGuide.md", """ +# FxApp — UsageGuide + +## Test users + +| # | User | Password source | Role | Exists | +|---|---|---|---|---| +| 1 | tester | none, no sign-in | User | yes | + +## Execution guide + +Static. +""") +w("docs/FxApp-Checklist.md", """ +# FxApp — Checklist + +## Goal + +Fixture. + +## Requirements Status + +| ID | Requirement | Status | % | Remarks | Details | +|---|---|---|---|---|---| +| REQ-UI-001 | Home lists today's entries | Implemented | 75% | built | [d](#d-req-ui-001) | +| REQ-UI-002 | Entries table | Implemented | 75% | built | [d](#d-req-ui-002) | +| REQ-UI-003 | Editor buttons | Verified | 100% | old pass | [d](#d-req-ui-003) | +| REQ-UI-004 | Settings page | Implemented | 75% | built | [d](#d-req-ui-004) | +| REQ-FN-005 | Save a quick setting | Implemented | 75% | built | [d](#d-req-fn-005) | +| REQ-FN-006 | Count badge | Implemented | 75% | built | [d](#d-req-fn-006) | +| REQ-NFR-007 | Logs | Implemented | 75% | built | [d](#d-req-nfr-007) | +| REQ-NFR-008 | Home speed | Implemented | 75% | built | [d](#d-req-nfr-008) | +| REQ-FN-009 | Dropped | N/A | 0% | out of scope | [d](#d-req-fn-009) | + +## Home + +- **REQ-UI-001** (BRD-1) Home lists today's entries. Mockup: docs/mockups/home.html + - *Acceptance:* When the user opens Home, then the entry list on Home shows today's entries. +- **REQ-FN-005** (BRD-2) Save a quick setting. + - *Acceptance:* When the user taps Save on Quick settings, then the theme is stored. +- **REQ-FN-006** (BRD-3) Count badge. + - *Acceptance:* When the user opens the list on Entries, then the count equals the rows. + +## Entries + +- **REQ-UI-002** (BRD-4) Entries table. Mockup: docs/mockups/entries.html + - *Acceptance:* When the user opens the list on Entries, then every entry is a row with its date and title. + +## Editor + +- **REQ-UI-003** (BRD-5) Editor buttons. Mockup: docs/mockups/editor.html + - *Acceptance:* When the user types text on Editor, then Save and Cancel sit side by side. + +## Settings + +- **REQ-UI-004** (BRD-6) Settings page. Mockup: docs/mockups/settings.html + - *Acceptance:* When the user picks a theme on Settings, then the page shows it styled. + +## Non-functional + +- **REQ-NFR-007** (BRD-7) Logs. + - *Acceptance:* When the app runs, then the log file records each screen opened. +- **REQ-NFR-008** (BRD-8) Home speed. + - *Acceptance:* When a user opens Home, then it answers within the budget. perf-budget: p95 ttfb <= 500ms @ concurrency 1 +- **REQ-FN-009** (BRD-9) Dropped. + - *Acceptance:* When nothing, then nothing. +""") + +w("tests/verify/verify.spec.js", """ +const { test, expect } = require('@playwright/test'); +const BASE = process.env.BASE_URL || 'http://localhost:5117'; +test('REQ-UI-001 home lists entries', async ({ page }) => { + await page.goto(BASE + '/'); + await expect(page.locator('[data-testid="entry-list"] li')).toHaveCount(3); +}); +test('REQ-UI-002 entries table has rows', async ({ page }) => { + await page.goto(BASE + '/entries.html'); + await expect(page.locator('[data-testid="entries-table"]')).toBeVisible(); +}); +test('REQ-UI-003 editor buttons present', async ({ page }) => { + await page.goto(BASE + '/broken.html'); + await expect(page.locator('[data-testid="save"]')).toBeVisible(); +}); +test('REQ-UI-004 settings page opens', async ({ page }) => { + await page.goto(BASE + '/unstyled.html'); + await expect(page.locator('[data-testid="theme"]')).toBeVisible(); +}); +test('REQ-FN-005 quick setting is stored', async ({ page }) => { + await page.goto(BASE + '/'); + await expect(page.locator('[data-testid="stored-theme"]')).toBeVisible({ timeout: 1500 }); +}); +""") + +# a perf measurement over budget for REQ-NFR-008 +w("tests/.artifacts/verify/perf/REQ-NFR-008.json", """ +{"status":"ok","build_config":"Release","levels":[{"concurrency":1,"samples":40,"weak":false,"errors":0,"error_rate":0,"non_200":[],"redirect_rate":0, + "ttfb_ms":{"p50":320,"p95":900,"max":1300},"load_ms":{"p50":400,"p95":1100,"max":1500}}]} +""") +for name, body in { + "ok.json": '{"status":"ok","build_config":"Release","levels":[{"concurrency":50,"samples":60,"weak":false,"errors":0,"error_rate":0,"non_200":[],"redirect_rate":0,"ttfb_ms":{"p50":100,"p95":410,"max":600},"load_ms":{"p50":200,"p95":500,"max":700}}]}', + "marginal.json": '{"status":"ok","build_config":"Release","levels":[{"concurrency":50,"samples":60,"weak":false,"errors":0,"error_rate":0,"non_200":[],"redirect_rate":0,"ttfb_ms":{"p50":100,"p95":600,"max":900},"load_ms":{"p50":200,"p95":500,"max":700}}]}', + "debug.json": '{"status":"ok","build_config":"Debug","levels":[{"concurrency":50,"samples":60,"weak":false,"errors":0,"error_rate":0,"non_200":[],"redirect_rate":0,"ttfb_ms":{"p50":100,"p95":300,"max":400},"load_ms":{"p50":200,"p95":500,"max":700}}]}', + "shed.json": '{"status":"ok","build_config":"Release","levels":[{"concurrency":50,"samples":30,"weak":false,"errors":20,"error_rate":0.4,"non_200":[],"redirect_rate":0,"ttfb_ms":{"p50":100,"p95":300,"max":400},"load_ms":{"p50":200,"p95":500,"max":700}}]}', + "weak.json": '{"status":"ok","build_config":"Release","levels":[{"concurrency":50,"samples":4,"weak":true,"errors":0,"error_rate":0,"non_200":[],"redirect_rate":0,"ttfb_ms":{"p50":100,"p95":300,"max":400},"load_ms":{"p50":200,"p95":500,"max":700}}]}', + "redirected.json": '{"status":"redirected","base":"x"}', +}.items(): + w(f"perf-cases/{name}", body + "\n") + +print(APP) diff --git a/tests/verify/run.sh b/tests/verify/run.sh new file mode 100644 index 0000000..d915058 --- /dev/null +++ b/tests/verify/run.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env bash +# tests/verify/run.sh — self-test for the verify scripts (Sitting 4c, 2026-09-06). +# Builds a throw-away project (tests/verify/make-fixtures.py) with a static four-screen "app": +# one good screen, one whose table has no rows, one whose buttons overlap, one whose stylesheet +# is missing; a checklist of nine rows; anchored mockups; a spec with one failing test; a perf +# measurement over its budget. Then runs the whole verify chain on it and checks: +# 1. tf-verify-list.sh rows in scope, the N/A row skipped, a dialog row mapped to its page +# 2. tf-verify-env.sh installs the browser tooling into the fixture and reports READY +# 3. tf-verify-boot.sh serves the static app, reports BOOTED, stops it, the port is free +# 4. tf-verify-screens.sh render EMPTY on the empty table, visual FAIL on the overlap and the +# unstyled page, OK on the good screen, a screenshot per screen and width +# 5. tf-verify-tests.sh REQ-FN-005 FAIL, the four UI rows PASS, from the Playwright JSON +# 6. tf-assets.sh the missing stylesheet fails the assets check +# 7. tf-mockup-parity.sh runs and writes its JSON +# 8. tf-perf-grade.sh OK, MARGINAL, FAIL, load-shed FAIL, UNMEASURED on Debug, weak, auth wall +# 9. tf-verify-verdict.sh one verdict per row in check order, the ledger with every row, the +# checklist cells rewritten, NOT-* rows untouched +# 10. tf-verify-emit.sh gate records, one miss per failing row, no duplicate on a second run, +# one run record with cmd verify-phase and the yolo flag +# 11. guard-verify.sh refuses a hand-written Verified the ledger does not list as PASS, +# allows one it does, refuses everything when no ledger exists +# Telemetry goes to the fixture's docs/metrics only (TF_METRICS_ROOT), never to this repository. +# Run: bash tests/verify/run.sh (about two minutes the first time, for the browser install) +set -u +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; ROOT="$(cd "$HERE/../.." && pwd)" +export TF_FIXTURE_DIR="$ROOT/tests/.artifacts/verify-fixture" +APP="$(python3 "$HERE/make-fixtures.py")" || { echo "could not build the fixture"; exit 2; } +PORT=5117 +pass=0; fail=0 +ok() { pass=$((pass+1)); echo "ok $*"; } +bad() { fail=$((fail+1)); echo "FAIL $*"; } +check() { if [[ "$2" == 0 ]]; then ok "$1"; else bad "$1"; fi; } +has() { grep -q -- "$2" <<<"$1"; echo $?; } + +cd "$APP" || exit 2 +export TF_METRICS_ROOT="$APP"; unset CLAUDE_PROJECT_DIR; export TF_PROJECT_DIR="$APP" +U=".tfcore/utils" +trap 'bash $U/tf-verify-boot.sh stop >/dev/null 2>&1' EXIT + +# ---- 1. the list ------------------------------------------------------------------------- +out="$(bash $U/tf-verify-list.sh FxApp all 2>&1)"; rc=$? +check "list runs (exit $rc)" "$rc" +check "list grades 8 rows and skips the N/A row" "$(has "$out" "Rows to grade: 8 (.*1 N/A skipped")" +check "list maps the Quick settings dialog row to Home" "$(has "$out" "REQ-FN-005 .* Home / — dialog Quick settings")" +check "list finds the perf budget on REQ-NFR-008" "$(has "$out" "REQ-NFR-008 .*perf-budget: p95 ttfb <= 500ms")" +check "list names four screens to drive" "$(has "$out" "Screens to drive (4 of 4")" +n="$(python3 -c "import json;print(len(json.load(open('tests/.artifacts/verify/list.json'))['rows']))")" +check "list.json carries the 8 rows" "$([[ "$n" == 8 ]]; echo $?)" +out="$(bash $U/tf-verify-list.sh FxApp ui 2>&1)"; check "scope ui keeps only the four UI rows" "$(has "$out" "Rows to grade: 4 ")" +out="$(bash $U/tf-verify-list.sh FxApp REQ-UI-001,REQ-FN-006 2>&1)"; check "scope by id list keeps two rows" "$(has "$out" "Rows to grade: 2 ")" +bash $U/tf-verify-list.sh FxApp all >/dev/null 2>&1 + +# ---- 2. the environment -------------------------------------------------------------------- +out="$(bash $U/tf-verify-env.sh 2>&1)"; rc=$? +check "env reports READY (exit $rc)" "$rc" +check "env pinned playwright.config.ts under tests/.artifacts" "$(grep -q "tests/.artifacts/test-results" playwright.config.ts; echo $?)" +check "env added the .gitignore lines" "$(grep -q "tests/.artifacts/" .gitignore; echo $?)" +out="$(bash $U/tf-verify-env.sh --check 2>&1)"; check "env --check is READY on the second pass" "$(has "$out" "^READY")" + +# ---- 3. boot --------------------------------------------------------------------------- +out="$(bash $U/tf-verify-boot.sh start --static site --port $PORT 2>&1)"; rc=$? +check "boot serves the static app (exit $rc): ${out%% log=*}" "$rc" +check "boot state says mode=base" "$(has "$(cat tests/.artifacts/verify/boot.json)" '"mode": "base"')" +BASE="http://localhost:$PORT" +out="$(bash $U/tf-verify-boot.sh start --static site --port $PORT 2>&1)"; rc=$? +check "boot refuses a port already in use (exit $rc)" "$([[ $rc -eq 2 ]]; echo $?)" +check "a refused second start leaves the first boot's state alone" "$(has "$(cat tests/.artifacts/verify/boot.json)" '"mode": "base"')" + +# ---- 4. screens -------------------------------------------------------------------------- +out="$(bash $U/tf-verify-screens.sh --list tests/.artifacts/verify/list.json --base "$BASE" 2>&1)"; rc=$? +check "screens exits 5 with failures (exit $rc)" "$([[ $rc -eq 5 ]]; echo $?)" +check "Home renders and looks right" "$(has "$out" "^OK Home (/) — render OK, visual OK, 5 anchors")" +check "Entries: header-only table is render EMPTY (zero-rows)" "$(has "$out" "^FAIL Entries .*render EMPTY.*has a header and no rows")" +check "Editor: overlapping buttons are visual FAIL" "$(has "$out" "^FAIL Editor .*visual FAIL.*save overlaps cancel")" +check "Settings: missing stylesheet is visual FAIL (unstyled)" "$(has "$out" "^FAIL Settings .*visual FAIL.*no stylesheet")" +shots="$(ls tests/.artifacts/verify/screens/*.png 2>/dev/null | wc -l)" +check "a screenshot per screen and width (8): $shots" "$([[ "$shots" == 8 ]]; echo $?)" +out2="$(bash $U/tf-verify-screens.sh --screen Home=/ --base "$BASE" --mockups docs/mockups --json-out tests/.artifacts/verify/smoke.json 2>&1)"; rc=$? +check "a smoke on one named screen passes (exit $rc)" "$rc" + +# ---- 5. tests ---------------------------------------------------------------------------- +out="$(bash $U/tf-verify-tests.sh --base "$BASE" --no-unit 2>&1)"; rc=$? +check "tests run the spec (exit $rc)" "$rc" +check "tests: 4 PASS, 1 FAIL" "$(has "$out" "rows with a test: 5 — 4 PASS, 1 FAIL")" +check "tests: REQ-FN-005 is the failure" "$(has "$out" "FAIL REQ-FN-005")" + +# ---- 6. assets and 7. parity ---------------------------------------------------------------- +bash $U/tf-assets.sh --base "$BASE" --paths "/,/entries.html,/broken.html,/unstyled.html" --json-out tests/.artifacts/verify/assets.json >/dev/null 2>&1; rc=$? +check "assets exits 5 on the missing stylesheet (exit $rc)" "$([[ $rc -eq 5 ]]; echo $?)" +bash $U/tf-mockup-parity.sh --base "$BASE" --screen Home=/ --screen Entries=/entries.html --screen Editor=/broken.html --screen Settings=/unstyled.html --json-out tests/.artifacts/verify/parity.json >/dev/null 2>&1; rc=$? +check "parity runs and writes its JSON (exit $rc)" "$([[ -s tests/.artifacts/verify/parity.json ]]; echo $?)" + +# ---- 8. perf grading ------------------------------------------------------------------------- +g() { bash $U/tf-perf-grade.sh --budget "p95 ttfb <= 500ms @ concurrency 50" --json "perf-cases/$1" 2>&1; } +check "perf OK" "$(has "$(g ok.json)" "^PERF-OK")" +check "perf MARGINAL within a quarter over" "$(has "$(g marginal.json)" "^PERF-MARGINAL")" +check "perf FAIL on load shed (timeout)" "$(has "$(g shed.json)" "^PERF-FAIL reason=.*failed at concurrency")" +check "perf UNMEASURED on a Debug build" "$(has "$(g debug.json)" "^PERF-UNMEASURED reason=build is Debug")" +check "perf UNMEASURED on a weak sample" "$(has "$(g weak.json)" "^PERF-UNMEASURED reason=weak sample")" +check "perf UNMEASURED on an auth wall" "$(has "$(g redirected.json)" "^PERF-UNMEASURED reason=auth wall")" +check "perf FAIL slow" "$(has "$(bash $U/tf-perf-grade.sh --budget "p95 ttfb <= 500ms" --json tests/.artifacts/verify/perf/REQ-NFR-008.json)" "^PERF-FAIL reason=p95 ttfb 900 ms vs budget 500 ms")" + +# ---- 9. verdicts ----------------------------------------------------------------------------- +STARTED="$(bash $U/tf-phase.sh start verify-phase FxApp 2>/dev/null)" +out="$(bash $U/tf-verify-verdict.sh FxApp --apply --started "$STARTED" 2>&1)"; rc=$? +check "verdict runs (exit $rc)" "$rc" +v() { python3 -c "import json;print(json.load(open('docs/.last-verify.json'))['rows'].get('$1'))"; } +check "REQ-UI-001 PASS" "$([[ "$(v REQ-UI-001)" == PASS ]]; echo $?)" +check "REQ-UI-002 RENDER-FAIL" "$([[ "$(v REQ-UI-002)" == RENDER-FAIL ]]; echo $?)" +check "REQ-UI-003 VISUAL-FAIL (prior Verified)" "$([[ "$(v REQ-UI-003)" == VISUAL-FAIL ]]; echo $?)" +check "REQ-UI-004 ASSET-FAIL before visual" "$([[ "$(v REQ-UI-004)" == ASSET-FAIL ]]; echo $?)" +check "REQ-FN-005 FAIL (acceptance)" "$([[ "$(v REQ-FN-005)" == FAIL ]]; echo $?)" +check "REQ-FN-006 NOT-TESTED, screen failed elsewhere so RENDER-FAIL" "$([[ "$(v REQ-FN-006)" == RENDER-FAIL ]]; echo $?)" +check "REQ-NFR-007 NOT-OBSERVABLE" "$([[ "$(v REQ-NFR-007)" == NOT-OBSERVABLE ]]; echo $?)" +check "REQ-NFR-008 PERF-FAIL" "$([[ "$(v REQ-NFR-008)" == PERF-FAIL ]]; echo $?)" +check "ledger dated today with the checks that ran" "$(has "$(cat docs/.last-verify.json)" "\"date\": \"$(date +%F)\"")" +cell() { grep -E "^\| $1 \|" docs/FxApp-Checklist.md | cut -d'|' -f4 | tr -d ' '; } +check "checklist: REQ-UI-001 written Verified" "$([[ "$(cell REQ-UI-001)" == Verified ]]; echo $?)" +check "checklist: REQ-UI-003 demoted to Needs re-verify" "$([[ "$(cell REQ-UI-003)" == Needsre-verify ]]; echo $?)" +check "checklist: REQ-FN-005 written FAIL" "$([[ "$(cell REQ-FN-005)" == FAIL ]]; echo $?)" +check "checklist: REQ-NFR-007 untouched (Implemented)" "$([[ "$(cell REQ-NFR-007)" == Implemented ]]; echo $?)" +check "checklist: REQ-FN-009 N/A untouched" "$([[ "$(cell REQ-FN-009)" == N/A ]]; echo $?)" +check "checklist: remark names the evidence" "$(grep -E "^\| REQ-UI-002 \|" docs/FxApp-Checklist.md | grep -q "verify: ⚠ render — table entries-table has a header and no rows on Entries @1280 (tests/.artifacts/verify/screens/entries-1280.png)"; echo $?)" +check "REQ-NFR-007 remark says not observable" "$(grep -E "^\| REQ-NFR-007 \|" docs/FxApp-Checklist.md | grep -q "not observable"; echo $?)" + +# ---- 10. telemetry ---------------------------------------------------------------------------- +out="$(bash $U/tf-verify-emit.sh FxApp --started "$STARTED" 2>&1)"; rc=$? +check "emit runs (exit $rc): $out" "$rc" +gates="$(grep -c '"kind":"gate"' docs/metrics/gates.jsonl 2>/dev/null)"; misses="$(grep -c '"kind":"miss"' docs/metrics/misses.jsonl 2>/dev/null)"; runs="$(grep -c '"cmd":"verify-phase"' docs/metrics/runs.jsonl 2>/dev/null)" +check "7 gate records (every graded row; NOT-OBSERVABLE none): $gates" "$([[ "$gates" == 7 ]]; echo $?)" +check "6 misses (one per failing row): $misses" "$([[ "$misses" == 6 ]]; echo $?)" +check "one verify-phase run record with yolo false: $runs" "$([[ "$runs" == 1 ]] && grep -q '"yolo":false' docs/metrics/runs.jsonl; echo $?)" +check "the run record's started is the phase marker's" "$(grep -q "\"started\":\"$STARTED\"" docs/metrics/runs.jsonl; echo $?)" +check "REQ-UI-003 miss is a regression (prior Verified)" "$(grep '"req_id":"REQ-UI-003"' docs/metrics/misses.jsonl | grep -q '"miss_class":"regression"'; echo $?)" +check "REQ-UI-004 gate is assets with missing-asset" "$(grep '"req_id":"REQ-UI-004"' docs/metrics/gates.jsonl | grep -q '"gate":"assets".*"failure_class":"missing-asset"\|"failure_class":"missing-asset".*"gate":"assets"'; echo $?)" +bash $U/tf-verify-emit.sh FxApp --started "$STARTED" >/dev/null 2>&1 +misses2="$(grep -c '"kind":"miss"' docs/metrics/misses.jsonl)" +check "a second emit adds no duplicate miss: $misses2" "$([[ "$misses2" == 6 ]]; echo $?)" + +# ---- 11. the hook ------------------------------------------------------------------------------- +HOOK=".tfcore/hooks/guard-verify.sh"; CL="$APP/docs/FxApp-Checklist.md" +hook() { printf '{"tool_name":"Edit","tool_input":{"file_path":"%s","old_string":"%s","new_string":"%s"}}' "$CL" "$1" "$2" | bash "$HOOK" 2>&1; echo "rc=$?"; } +r="$(hook '| REQ-FN-005 | Save a quick setting | FAIL |' '| REQ-FN-005 | Save a quick setting | Verified |')" +check "hook refuses Verified on a row the ledger lists as FAIL" "$(has "$r" "rc=2")" +check "hook names the row and its ledger verdict" "$(has "$r" "REQ-FN-005: FAIL, not PASS")" +r="$(hook '| REQ-UI-001 | Home lists today'"'"'s entries | Implemented |' '| REQ-UI-001 | Home lists today'"'"'s entries | Verified |')" +check "hook allows Verified on a row the ledger lists as PASS" "$(has "$r" "rc=0")" +r="$(hook '| REQ-UI-002 | Entries table | Verified |' '| REQ-UI-002 | Entries table | Needs re-verify |')" +check "hook allows a demotion" "$(has "$r" "rc=0")" +mv docs/.last-verify.json docs/.last-verify.json.off +r="$(hook '| REQ-UI-001 | x | Implemented |' '| REQ-UI-001 | x | Verified |')" +check "hook refuses when no ledger exists" "$(has "$r" "rc=2")" +mv docs/.last-verify.json.off docs/.last-verify.json +r="$(printf '{"tool_name":"Bash","tool_input":{"command":"echo hi"}}' | bash "$HOOK" 2>&1; echo "rc=$?")" +check "hook ignores other tools" "$(has "$r" "rc=0")" + +# ---- 3b. stop ------------------------------------------------------------------------------------- +out="$(bash $U/tf-verify-boot.sh stop 2>&1)"; rc=$? +sleep 1 +check "boot stop frees the port (exit $rc)" "$(curl -s -o /dev/null -m 2 "$BASE/" && echo 1 || echo 0)" + +echo +echo "verify self-test: $pass passed, $fail failed (fixture $APP)" +[[ $fail -eq 0 ]] diff --git a/update-framework.sh b/update-framework.sh index eda649e..67bb236 100755 --- a/update-framework.sh +++ b/update-framework.sh @@ -479,6 +479,18 @@ CANONICAL_SETTINGS='{ { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-metrics.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-db.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-build.sh\"" } ] }, @@ -489,6 +501,10 @@ CANONICAL_SETTINGS='{ "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-metrics.sh\"" + }, { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify.sh\"" From a255b5b5addb7b6b7506183c886e1633a809b320 Mon Sep 17 00:00:00 2001 From: S Ravi Kumar Date: Mon, 7 Sep 2026 15:12:14 +0530 Subject: [PATCH 3/3] Removed Codex and fixed the Pipeline issue --- .agents/skills/techieflow-amend-docs/SKILL.md | 13 - .agents/skills/techieflow-author-brd/SKILL.md | 13 - .agents/skills/techieflow-build/SKILL.md | 13 - .../techieflow-day1-brownfield/SKILL.md | 13 - .../techieflow-day1-greenfield/SKILL.md | 13 - .agents/skills/techieflow-devguide/SKILL.md | 13 - .agents/skills/techieflow-fix-issues/SKILL.md | 13 - .../skills/techieflow-generate-html/SKILL.md | 13 - .agents/skills/techieflow-handoff/SKILL.md | 13 - .agents/skills/techieflow-log-miss/SKILL.md | 13 - .../skills/techieflow-metrics-report/SKILL.md | 13 - .agents/skills/techieflow-mockups/SKILL.md | 13 - .../skills/techieflow-productguide/SKILL.md | 13 - .../skills/techieflow-refresh-status/SKILL.md | 13 - .../techieflow-render-workflow-docs/SKILL.md | 13 - .agents/skills/techieflow-split-brd/SKILL.md | 13 - .../skills/techieflow-triage-issues/SKILL.md | 13 - .agents/skills/techieflow-verify/SKILL.md | 13 - .agents/skills/techieflow-yolo/SKILL.md | 6 - .../TechieFlow/tasks/metrics-report.md | 2 +- .codex/agents/analyst.toml | 3 - .codex/agents/architect.toml | 3 - .codex/agents/flow-master.toml | 3 - .codex/agents/techierag.toml | 3 - .codex/agents/tf-builder.toml | 3 - .codex/agents/tf-explorer.toml | 4 - .codex/agents/tf-test-writer.toml | 3 - .codex/agents/trblazeui.toml | 3 - .codex/agents/verifier.toml | 3 - .codex/config.toml | 46 - .codex/hooks.json | 63 - .codex/rules/techieflow.rules | 8 - .gitignore | 23 + .tfcore/hooks/block-git.sh | 2 +- .tfcore/hooks/codex-adapter.py | 248 -- .tfcore/hooks/guard-artifacts.sh | 4 +- .tfcore/hooks/sweep-artifacts.sh | 4 +- .tfcore/routing.yaml | 7 +- .tfcore/tasks/metrics-report.md | 2 +- .tfcore/telemetry/SCHEMA.md | 10 +- .tfcore/telemetry/tf-metrics.sh | 6 +- .../templates/v4custom/app-agents-md-tmpl.md | 7 +- .../templates/v4custom/app-claude-md-tmpl.md | 2 +- .../v4custom/metrics-report-template.md | 4 +- .tfcore/user-guide.md | 23 - .tfcore/utils/tf-codex-bind.py | 196 -- .tfcore/utils/tf-codex-telemetry.py | 60 - .tfcore/utils/tf-devguide-list.py | 2 +- .tfcore/utils/tf-emit.sh | 9 +- .tfcore/utils/tf-goal.sh | 48 +- .tfcore/utils/tf-harness.sh | 13 +- .tfcore/utils/tf-mockups-locate.py | 2 +- .tfcore/utils/tf-routing-bind.sh | 4 - .tfcore/utils/tf-routing.sh | 13 +- CodexChanges.md | 489 ---- DECISIONS.md | 2 + README.md | 2 +- WORKFLOW.html | 1986 ----------------- WorkFlow-Context.md | 17 +- docs/Adapter-Design.md | 2 + docs/CHANGELOG.html | 19 + docs/CHANGELOG.md | 25 + docs/Capability-Matrix.md | 2 + docs/Coupling-Points.md | 2 + docs/Miss-Telemetry-AI-First-Playbook.md | 2 + docs/Miss-Telemetry-TechieFlow.md | 2 + ...TechieFlow-Distribution-Pipeline-Prompt.md | 2 + docs/TechieFlow-FAQ.md | 2 +- docs/TechieFlow-How-It-Works.md | 2 +- docs/TechieFlow-Installation.md | 6 +- .../TechieFlow-Library-Persona-Propagation.md | 2 + docs/TechieFlow-Misses.html | 16 +- docs/TechieFlow-Misses.md | 12 +- docs/TechieFlow-Permissions-And-YOLO.md | 6 +- docs/TechieFlow-Release-Guide.md | 2 +- docs/TechieFlow-Requirements.md | 3 +- docs/TechieFlow-Reset-Plan-2026-09-04.md | 4 + docs/TechieFlow-Routing-Guide.md | 2 + docs/TechieFlow-Session-7-Restart-Prompt.md | 8 +- docs/TechieFlow-Setup.md | 4 +- docs/TechieFlow-Telemetry-Guide.md | 2 + docs/Telemetry-Hooks.md | 2 + docs/metrics/README.md | 19 +- docs/metrics/commits.jsonl | 12 + docs/metrics/misses.jsonl | 6 + docs/metrics/runs.jsonl | 4 + package.json | 4 - scaffold-brownfield.sh | 29 +- scaffold-greenfield.sh | 28 +- scripts/install.mjs | 98 +- scripts/test-install.mjs | 46 +- scripts/validate.mjs | 8 +- tests/mirror/run.sh | 31 + update-framework.sh | 65 +- 94 files changed, 368 insertions(+), 3683 deletions(-) delete mode 100644 .agents/skills/techieflow-amend-docs/SKILL.md delete mode 100644 .agents/skills/techieflow-author-brd/SKILL.md delete mode 100644 .agents/skills/techieflow-build/SKILL.md delete mode 100644 .agents/skills/techieflow-day1-brownfield/SKILL.md delete mode 100644 .agents/skills/techieflow-day1-greenfield/SKILL.md delete mode 100644 .agents/skills/techieflow-devguide/SKILL.md delete mode 100644 .agents/skills/techieflow-fix-issues/SKILL.md delete mode 100644 .agents/skills/techieflow-generate-html/SKILL.md delete mode 100644 .agents/skills/techieflow-handoff/SKILL.md delete mode 100644 .agents/skills/techieflow-log-miss/SKILL.md delete mode 100644 .agents/skills/techieflow-metrics-report/SKILL.md delete mode 100644 .agents/skills/techieflow-mockups/SKILL.md delete mode 100644 .agents/skills/techieflow-productguide/SKILL.md delete mode 100644 .agents/skills/techieflow-refresh-status/SKILL.md delete mode 100644 .agents/skills/techieflow-render-workflow-docs/SKILL.md delete mode 100644 .agents/skills/techieflow-split-brd/SKILL.md delete mode 100644 .agents/skills/techieflow-triage-issues/SKILL.md delete mode 100644 .agents/skills/techieflow-verify/SKILL.md delete mode 100644 .agents/skills/techieflow-yolo/SKILL.md delete mode 100644 .codex/agents/analyst.toml delete mode 100644 .codex/agents/architect.toml delete mode 100644 .codex/agents/flow-master.toml delete mode 100644 .codex/agents/techierag.toml delete mode 100644 .codex/agents/tf-builder.toml delete mode 100644 .codex/agents/tf-explorer.toml delete mode 100644 .codex/agents/tf-test-writer.toml delete mode 100644 .codex/agents/trblazeui.toml delete mode 100644 .codex/agents/verifier.toml delete mode 100644 .codex/config.toml delete mode 100644 .codex/hooks.json delete mode 100644 .codex/rules/techieflow.rules delete mode 100644 .tfcore/hooks/codex-adapter.py delete mode 100644 .tfcore/utils/tf-codex-bind.py delete mode 100644 .tfcore/utils/tf-codex-telemetry.py delete mode 100644 CodexChanges.md delete mode 100644 WORKFLOW.html diff --git a/.agents/skills/techieflow-amend-docs/SKILL.md b/.agents/skills/techieflow-amend-docs/SKILL.md deleted file mode 100644 index 47491ef..0000000 --- a/.agents/skills/techieflow-amend-docs/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-amend-docs -description: Amend existing requirements and design artifacts in place. Use when a TechieFlow project needs its `amend-docs` workflow. ---- - -# techieflow-amend-docs - -1. Read `.tfcore/tasks/amend-docs.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `analyst` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-author-brd/SKILL.md b/.agents/skills/techieflow-author-brd/SKILL.md deleted file mode 100644 index e332e55..0000000 --- a/.agents/skills/techieflow-author-brd/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-author-brd -description: Interactively author or extend numbered business requirements. Use when a TechieFlow project needs its `author-brd` workflow. ---- - -# techieflow-author-brd - -1. Read `.tfcore/tasks/author-brd.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `analyst` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-build/SKILL.md b/.agents/skills/techieflow-build/SKILL.md deleted file mode 100644 index 1f9fe23..0000000 --- a/.agents/skills/techieflow-build/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-build -description: Implement the open checklist and chain smoke and verification. Use when a TechieFlow project needs its `build-phase` workflow. ---- - -# techieflow-build - -1. Read `.tfcore/tasks/build-phase.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-day1-brownfield/SKILL.md b/.agents/skills/techieflow-day1-brownfield/SKILL.md deleted file mode 100644 index a7c9ecb..0000000 --- a/.agents/skills/techieflow-day1-brownfield/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-day1-brownfield -description: Reverse-document and initialize an existing application. Use when a TechieFlow project needs its `day1-brownfield` workflow. ---- - -# techieflow-day1-brownfield - -1. Read `.tfcore/tasks/day1-brownfield.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `analyst` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-day1-greenfield/SKILL.md b/.agents/skills/techieflow-day1-greenfield/SKILL.md deleted file mode 100644 index 3be074c..0000000 --- a/.agents/skills/techieflow-day1-greenfield/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-day1-greenfield -description: Initialize planning artifacts and mockups for a new application. Use when a TechieFlow project needs its `day1-greenfield` workflow. ---- - -# techieflow-day1-greenfield - -1. Read `.tfcore/tasks/day1-greenfield.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `analyst` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-devguide/SKILL.md b/.agents/skills/techieflow-devguide/SKILL.md deleted file mode 100644 index e2e089a..0000000 --- a/.agents/skills/techieflow-devguide/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-devguide -description: Create the developer-facing screen and component guide. Use when a TechieFlow project needs its `devguide` workflow. ---- - -# techieflow-devguide - -1. Read `.tfcore/tasks/devguide.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-fix-issues/SKILL.md b/.agents/skills/techieflow-fix-issues/SKILL.md deleted file mode 100644 index 59e4b3a..0000000 --- a/.agents/skills/techieflow-fix-issues/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-fix-issues -description: Fix reported or verifier-discovered application defects. Use when a TechieFlow project needs its `fix-issues` workflow. ---- - -# techieflow-fix-issues - -1. Read `.tfcore/tasks/fix-issues.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-generate-html/SKILL.md b/.agents/skills/techieflow-generate-html/SKILL.md deleted file mode 100644 index 366390d..0000000 --- a/.agents/skills/techieflow-generate-html/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-generate-html -description: Render an arbitrary human-readable Markdown document to HTML. Use when a TechieFlow project needs its `generate-html` workflow. ---- - -# techieflow-generate-html - -1. Read `.tfcore/tasks/generate-html.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-handoff/SKILL.md b/.agents/skills/techieflow-handoff/SKILL.md deleted file mode 100644 index ab62208..0000000 --- a/.agents/skills/techieflow-handoff/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-handoff -description: Complete handoff documents, status, and rendered artifacts. Use when a TechieFlow project needs its `handoff-phase` workflow. ---- - -# techieflow-handoff - -1. Read `.tfcore/tasks/handoff-phase.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-log-miss/SKILL.md b/.agents/skills/techieflow-log-miss/SKILL.md deleted file mode 100644 index 609b495..0000000 --- a/.agents/skills/techieflow-log-miss/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-log-miss -description: Record one missed requirement as telemetry and a checklist line. Use when a TechieFlow project needs its `log-miss` workflow. ---- - -# techieflow-log-miss - -1. Read `.tfcore/tasks/log-miss.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-metrics-report/SKILL.md b/.agents/skills/techieflow-metrics-report/SKILL.md deleted file mode 100644 index 871bf82..0000000 --- a/.agents/skills/techieflow-metrics-report/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-metrics-report -description: Report TechieFlow development telemetry without changing code. Use when a TechieFlow project needs its `metrics-report` workflow. ---- - -# techieflow-metrics-report - -1. Read `.tfcore/tasks/metrics-report.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-mockups/SKILL.md b/.agents/skills/techieflow-mockups/SKILL.md deleted file mode 100644 index 67f6e10..0000000 --- a/.agents/skills/techieflow-mockups/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-mockups -description: Create or update the greenfield UI design and HTML mockups. Use when a TechieFlow project needs its `mockups` workflow. ---- - -# techieflow-mockups - -1. Read `.tfcore/tasks/mockups.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `analyst` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-productguide/SKILL.md b/.agents/skills/techieflow-productguide/SKILL.md deleted file mode 100644 index 4646126..0000000 --- a/.agents/skills/techieflow-productguide/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-productguide -description: Create the user-facing guide from the running application. Use when a TechieFlow project needs its `productguide` workflow. ---- - -# techieflow-productguide - -1. Read `.tfcore/tasks/productguide.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-refresh-status/SKILL.md b/.agents/skills/techieflow-refresh-status/SKILL.md deleted file mode 100644 index bfa6a9f..0000000 --- a/.agents/skills/techieflow-refresh-status/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-refresh-status -description: Recover truthful project status after an interrupted run. Use when a TechieFlow project needs its `refresh-status` workflow. ---- - -# techieflow-refresh-status - -1. Read `.tfcore/tasks/refresh-status.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-render-workflow-docs/SKILL.md b/.agents/skills/techieflow-render-workflow-docs/SKILL.md deleted file mode 100644 index f804735..0000000 --- a/.agents/skills/techieflow-render-workflow-docs/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-render-workflow-docs -description: Render the canonical BRD, Architecture, and status HTML files. Use when a TechieFlow project needs its `render-workflow-docs` workflow. ---- - -# techieflow-render-workflow-docs - -1. Read `.tfcore/tasks/render-workflow-docs.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-split-brd/SKILL.md b/.agents/skills/techieflow-split-brd/SKILL.md deleted file mode 100644 index 42e5321..0000000 --- a/.agents/skills/techieflow-split-brd/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-split-brd -description: Convert a BRD into the single implementation checklist. Use when a TechieFlow project needs its `split-brd` workflow. ---- - -# techieflow-split-brd - -1. Read `.tfcore/tasks/split-brd.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `analyst` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-triage-issues/SKILL.md b/.agents/skills/techieflow-triage-issues/SKILL.md deleted file mode 100644 index d0be12a..0000000 --- a/.agents/skills/techieflow-triage-issues/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-triage-issues -description: Analyze and document human-found bugs without fixing source. Use when a TechieFlow project needs its `triage-issues` workflow. ---- - -# techieflow-triage-issues - -1. Read `.tfcore/tasks/triage-issues.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `flow_master` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-verify/SKILL.md b/.agents/skills/techieflow-verify/SKILL.md deleted file mode 100644 index e601b66..0000000 --- a/.agents/skills/techieflow-verify/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: techieflow-verify -description: Independently verify requirements against runtime evidence. Use when a TechieFlow project needs its `verify-phase` workflow. ---- - -# techieflow-verify - -1. Read `.tfcore/tasks/verify-phase.md` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `verifier` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. diff --git a/.agents/skills/techieflow-yolo/SKILL.md b/.agents/skills/techieflow-yolo/SKILL.md deleted file mode 100644 index 0249104..0000000 --- a/.agents/skills/techieflow-yolo/SKILL.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -name: techieflow-yolo -description: Enable, disable, or inspect TechieFlow YOLO mode for an explicitly requested unattended workflow. ---- - -Read `.tfcore/tasks/_yolo-mode.md` completely. Run `bash .tfcore/utils/tf-yolo.sh on|off` as requested. YOLO removes elicitation pauses but does not broaden the user's task, allow git/gh, bypass the Codex workspace sandbox, or waive genuine external blockers. For a supervised long-running goal, use `.tfcore/utils/tf-goal.sh --harness codex`. diff --git a/.claude/commands/TechieFlow/tasks/metrics-report.md b/.claude/commands/TechieFlow/tasks/metrics-report.md index e9c018b..4c7b02a 100644 --- a/.claude/commands/TechieFlow/tasks/metrics-report.md +++ b/.claude/commands/TechieFlow/tasks/metrics-report.md @@ -72,7 +72,7 @@ If you find yourself wanting to write "overall, across all projects, …" — ** ### 3. Honesty rules for every number on the page - **Fewer than 3 supporting records → print `insufficient data (n=…)`, never a number.** A 100% first-pass rate from one REQ is noise wearing a suit. -- **Never estimate, interpolate, or infer a metric that was not measured.** A missing number is reported as missing. `cost_usd` is `null` on every Claude Code and Codex record (no cost source exists, and inventing one would be an estimate presented as a measurement) — report **tokens**, name the harness, and say why dollars are absent. **Do not multiply tokens by a rate card.** Real dollars appear only on OpenCode records and are never pooled with the others. +- **Never estimate, interpolate, or infer a metric that was not measured.** A missing number is reported as missing. `cost_usd` is `null` on every Claude Code record (no cost source exists, and inventing one would be an estimate presented as a measurement) — report **tokens**, name the harness, and say why dollars are absent. **Do not multiply tokens by a rate card.** Real dollars appear only on OpenCode records and are never pooled with the others. - **Measured cost and apportioned cost are two columns, never one number.** A fix run that repaired three misses has one token window; dividing it three ways is arithmetic. Print `cost_attribution:"sole"` figures as the headline and `shared:` figures beside them, labelled as apportioned. `none` records are counted and costed at nothing. - **Never invent a metric that has no stream behind it.** No cycle-time-per-feature: the unit of work in this framework is the run, not the ticket, and that is deliberate. - Records with `project_type_inferred: true` are **unclassified** — give them their own row labelled as such. Do not silently fold them into `app`. diff --git a/.codex/agents/analyst.toml b/.codex/agents/analyst.toml deleted file mode 100644 index 444393e..0000000 --- a/.codex/agents/analyst.toml +++ /dev/null @@ -1,3 +0,0 @@ -name = "analyst" -description = "TechieFlow analyst specialist." -developer_instructions = "Read `.tfcore/agents/analyst.md` completely before acting and follow its persona, core principles, command routing, and dependency rules. Skip only its activation greeting/help/halt ritual because this is a spawned Codex role. Treat `.tfcore/tasks/*.md` as executable workflows. Never run git or gh. Preserve TechieFlow's smoke, verifier, artifact-location, local-only runtime, checklist, and PROJECT-STATUS invariants. Load only dependencies needed for the assigned task." diff --git a/.codex/agents/architect.toml b/.codex/agents/architect.toml deleted file mode 100644 index d6c0de8..0000000 --- a/.codex/agents/architect.toml +++ /dev/null @@ -1,3 +0,0 @@ -name = "architect" -description = "TechieFlow architect specialist." -developer_instructions = "Read `.tfcore/agents/architect.md` completely before acting and follow its persona, core principles, command routing, and dependency rules. Skip only its activation greeting/help/halt ritual because this is a spawned Codex role. Treat `.tfcore/tasks/*.md` as executable workflows. Never run git or gh. Preserve TechieFlow's smoke, verifier, artifact-location, local-only runtime, checklist, and PROJECT-STATUS invariants. Load only dependencies needed for the assigned task." diff --git a/.codex/agents/flow-master.toml b/.codex/agents/flow-master.toml deleted file mode 100644 index 0a0e051..0000000 --- a/.codex/agents/flow-master.toml +++ /dev/null @@ -1,3 +0,0 @@ -name = "flow_master" -description = "TechieFlow flow-master specialist." -developer_instructions = "Read `.tfcore/agents/flow-master.md` completely before acting and follow its persona, core principles, command routing, and dependency rules. Skip only its activation greeting/help/halt ritual because this is a spawned Codex role. Treat `.tfcore/tasks/*.md` as executable workflows. Never run git or gh. Preserve TechieFlow's smoke, verifier, artifact-location, local-only runtime, checklist, and PROJECT-STATUS invariants. Load only dependencies needed for the assigned task." diff --git a/.codex/agents/techierag.toml b/.codex/agents/techierag.toml deleted file mode 100644 index 3602631..0000000 --- a/.codex/agents/techierag.toml +++ /dev/null @@ -1,3 +0,0 @@ -name = "techierag" -description = "TechieFlow techierag role." -developer_instructions = "Read the NuGet-deployed TechieRag persona and adopt its implementation rules. Resolve it in this order and use the FIRST that exists: `.claude/commands/techierag.md` (current deploy target), `.claude/techierag.md` (legacy deploy target), `.opencode/command/techierag.md`, `.techierag/TechieRag-AI-Reference.md` (packaged service reference). Only if NONE exists, report that the TechieRag persona is not deployed (`dotnet build` the app to unpack the package) and stop. Never run git or gh." diff --git a/.codex/agents/tf-builder.toml b/.codex/agents/tf-builder.toml deleted file mode 100644 index fe28fcf..0000000 --- a/.codex/agents/tf-builder.toml +++ /dev/null @@ -1,3 +0,0 @@ -name = "tf_builder" -description = "TechieFlow tf-builder role." -developer_instructions = "Implement one assigned FN/NFR requirement cluster. Read `.tfcore/tasks/_smoke-test-policy.md`. Never run git or gh. Follow project coding standards, smoke the changed behavior, update only the assigned checklist rows, and return structured evidence to the parent." diff --git a/.codex/agents/tf-explorer.toml b/.codex/agents/tf-explorer.toml deleted file mode 100644 index 99d30c0..0000000 --- a/.codex/agents/tf-explorer.toml +++ /dev/null @@ -1,4 +0,0 @@ -name = "tf_explorer" -description = "TechieFlow tf-explorer role." -developer_instructions = "Perform a focused read-only codebase scan. Never edit files and never run git or gh. Return concise evidence with file paths and symbols." -sandbox_mode = "read-only" diff --git a/.codex/agents/tf-test-writer.toml b/.codex/agents/tf-test-writer.toml deleted file mode 100644 index 29e9e6c..0000000 --- a/.codex/agents/tf-test-writer.toml +++ /dev/null @@ -1,3 +0,0 @@ -name = "tf_test_writer" -description = "TechieFlow tf-test-writer role." -developer_instructions = "Write black-box verification tests for one assigned requirement cluster. Never edit application source and never run git or gh. Follow `.tfcore/tasks/verify-phase.md` and return tests added, tests refreshed, and anything unobservable." diff --git a/.codex/agents/trblazeui.toml b/.codex/agents/trblazeui.toml deleted file mode 100644 index a18d6dd..0000000 --- a/.codex/agents/trblazeui.toml +++ /dev/null @@ -1,3 +0,0 @@ -name = "trblazeui" -description = "TechieFlow trblazeui role." -developer_instructions = "Read the NuGet-deployed TrBlazeUI persona and adopt its implementation rules. Resolve it in this order and use the FIRST that exists: `.claude/commands/trblazeui.md` (current deploy target), `.claude/trblazeui.md` (legacy deploy target), `.opencode/command/trblazeui.md`, `.trblazeui/TrBlazeUI-AI-Reference.md` (packaged component reference). Only if NONE exists, report that the TrBlazeUI persona is not deployed (`dotnet build` the app to unpack the package) and stop. Never run git or gh." diff --git a/.codex/agents/verifier.toml b/.codex/agents/verifier.toml deleted file mode 100644 index 1072e20..0000000 --- a/.codex/agents/verifier.toml +++ /dev/null @@ -1,3 +0,0 @@ -name = "verifier" -description = "TechieFlow verifier specialist." -developer_instructions = "Read `.tfcore/agents/verifier.md` completely before acting and follow its persona, core principles, command routing, and dependency rules. Skip only its activation greeting/help/halt ritual because this is a spawned Codex role. Treat `.tfcore/tasks/*.md` as executable workflows. Never run git or gh. Preserve TechieFlow's smoke, verifier, artifact-location, local-only runtime, checklist, and PROJECT-STATUS invariants. Load only dependencies needed for the assigned task." diff --git a/.codex/config.toml b/.codex/config.toml deleted file mode 100644 index d622f1d..0000000 --- a/.codex/config.toml +++ /dev/null @@ -1,46 +0,0 @@ -# TechieFlow Codex adapter. Project-local config loads only after the repository -# is trusted. User authentication, providers, profiles, notifications and OTel -# remain in ~/.codex/config.toml and are never managed by TechieFlow. - -sandbox_mode = "workspace-write" -approval_policy = "on-request" - -[agents] -enabled = true -max_concurrent_threads_per_session = 8 - -[agents.flow_master] -description = "TechieFlow orchestrator and workflow router." -config_file = "agents/flow-master.toml" - -[agents.analyst] -description = "Business analysis, discovery, BRDs, and mockups." -config_file = "agents/analyst.toml" - -[agents.architect] -description = "Architecture and technical design specialist." -config_file = "agents/architect.toml" - -[agents.verifier] -description = "Independent requirement verifier and evidence grader." -config_file = "agents/verifier.toml" - -[agents.tf_builder] -description = "Implementation worker for one FN/NFR requirement cluster." -config_file = "agents/tf-builder.toml" - -[agents.tf_test_writer] -description = "Black-box verification-test writer for one requirement cluster." -config_file = "agents/tf-test-writer.toml" - -[agents.tf_explorer] -description = "Read-only codebase explorer for focused evidence gathering." -config_file = "agents/tf-explorer.toml" - -[agents.trblazeui] -description = "TrBlazeUI implementation specialist; available when its persona is deployed." -config_file = "agents/trblazeui.toml" - -[agents.techierag] -description = "TechieRag implementation specialist; available when its persona is deployed." -config_file = "agents/techierag.toml" diff --git a/.codex/hooks.json b/.codex/hooks.json deleted file mode 100644 index 5928f83..0000000 --- a/.codex/hooks.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "description": "TechieFlow policy guards and session telemetry for Codex.", - "hooks": { - "PreToolUse": [ - { - "matcher": "Bash|Edit|Write|apply_patch", - "hooks": [ - { - "type": "command", - "command": "python3 -c 'import pathlib,runpy,sys; root=next(p for p in (pathlib.Path.cwd(), *pathlib.Path.cwd().parents) if (p / \".tfcore\").is_dir()); sys.argv=[\"codex-adapter.py\", sys.argv[1]]; runpy.run_path(str(root / \".tfcore/hooks/codex-adapter.py\"), run_name=\"__main__\")' pre-tool", - "timeout": 15, - "statusMessage": "Checking TechieFlow policy" - } - ] - } - ], - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "python3 -c 'import pathlib,runpy,sys; root=next(p for p in (pathlib.Path.cwd(), *pathlib.Path.cwd().parents) if (p / \".tfcore\").is_dir()); sys.argv=[\"codex-adapter.py\", sys.argv[1]]; runpy.run_path(str(root / \".tfcore/hooks/codex-adapter.py\"), run_name=\"__main__\")' session-start", - "timeout": 10 - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "python3 -c 'import pathlib,runpy,sys; root=next(p for p in (pathlib.Path.cwd(), *pathlib.Path.cwd().parents) if (p / \".tfcore\").is_dir()); sys.argv=[\"codex-adapter.py\", sys.argv[1]]; runpy.run_path(str(root / \".tfcore/hooks/codex-adapter.py\"), run_name=\"__main__\")' session-start", - "timeout": 10 - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "python3 -c 'import pathlib,runpy,sys; root=next(p for p in (pathlib.Path.cwd(), *pathlib.Path.cwd().parents) if (p / \".tfcore\").is_dir()); sys.argv=[\"codex-adapter.py\", sys.argv[1]]; runpy.run_path(str(root / \".tfcore/hooks/codex-adapter.py\"), run_name=\"__main__\")' stop", - "timeout": 10, - "statusMessage": "Checking PROJECT-STATUS.html is current" - } - ] - } - ], - "SessionEnd": [ - { - "hooks": [ - { - "type": "command", - "command": "python3 -c 'import pathlib,runpy,sys; root=next(p for p in (pathlib.Path.cwd(), *pathlib.Path.cwd().parents) if (p / \".tfcore\").is_dir()); sys.argv=[\"codex-adapter.py\", sys.argv[1]]; runpy.run_path(str(root / \".tfcore/hooks/codex-adapter.py\"), run_name=\"__main__\")' session-end", - "timeout": 3 - } - ] - } - ] - } -} diff --git a/.codex/rules/techieflow.rules b/.codex/rules/techieflow.rules deleted file mode 100644 index 968ac32..0000000 --- a/.codex/rules/techieflow.rules +++ /dev/null @@ -1,8 +0,0 @@ -# TechieFlow's owner performs every version-control operation manually. -prefix_rule( - pattern = [["git", "gh"]], - decision = "forbidden", - justification = "TechieFlow agents never run git or gh; inspect working-tree files and framework artifacts instead.", - match = ["git status", "git add .", "gh pr view 1", "gh issue list"], -) - diff --git a/.gitignore b/.gitignore index 1051fb2..f63434d 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,26 @@ tmpclaude-* .DS_Store Thumbs.db desktop.ini + +# TechieFlow framework — deployed copies, never commit (managed by scaffold/update-framework.sh) +.tfcore/ +.claude/ +.opencode/ +.codex/ +.agents/skills/ +/CLAUDE.md +/WORKFLOW.html +/opencode.jsonc +/.tf-scaffold-note.txt + +# TechieFlow agent artifacts — machine-generated test harness & logs, never commit (managed by scaffold/update-framework.sh) +node_modules/ +/package.json +/package-lock.json +test-results/ +test-results-*/ +/scripts-*/ +playwright-report/ +.verify/ +logs/ +/docs/.last-verify.json diff --git a/.tfcore/hooks/block-git.sh b/.tfcore/hooks/block-git.sh index 677d218..ebd0dfa 100755 --- a/.tfcore/hooks/block-git.sh +++ b/.tfcore/hooks/block-git.sh @@ -64,7 +64,7 @@ MSG block_read_msg() { if [[ "$STRICT_GIT" == "1" ]]; then cat >&2 <<'MSG' -BLOCKED by TechieFlow Codex policy: agents do not run any git or gh command, including read-only status/log/diff/blame, in any mode. +BLOCKED by TechieFlow policy: agents do not run any git or gh command, including read-only status/log/diff/blame, in any mode. Use the checklist Requirements Status table, working-tree files, filesystem metadata, and fresh build/test evidence instead. The owner performs version-control operations manually. MSG return diff --git a/.tfcore/hooks/codex-adapter.py b/.tfcore/hooks/codex-adapter.py deleted file mode 100644 index 8c7a4e4..0000000 --- a/.tfcore/hooks/codex-adapter.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python3 -"""Translate Codex hooks to TechieFlow's existing guard and telemetry contracts. - -Modes (see .codex/hooks.json): - pre-tool Bash -> block-git.sh + guard-artifacts.sh - Edit/Write/apply_patch -> guard-status.sh + guard-verify.sh - stop guard-status-html.sh (PROJECT-STATUS.html must not be stale) - session-start session pointer + sweep-artifacts.sh (expired run material) - session-start write the .tfcore/.session/codex.json pointer - session-end pointer + session telemetry -""" - -from __future__ import annotations - -import json -import os -import pathlib -import re -import subprocess -import sys -from datetime import datetime, timezone - - -def read_event() -> dict: - try: - value = json.load(sys.stdin) - return value if isinstance(value, dict) else {} - except Exception: - return {} - - -def project_root(event: dict) -> pathlib.Path: - start = pathlib.Path(str(event.get("cwd") or os.getcwd())).resolve() - for candidate in (start, *start.parents): - if (candidate / ".tfcore").is_dir(): - return candidate - return start - - -def environment(root: pathlib.Path, event: dict) -> dict[str, str]: - env = os.environ.copy() - env.update( - TF_HARNESS="codex", - TF_PROJECT_DIR=str(root), - CLAUDE_PROJECT_DIR=str(root), - TF_SESSION_ID=str(event.get("session_id") or ""), - ) - if (root / ".tfcore" / ".session" / "yolo.json").exists(): - env["TF_YOLO"] = "1" - return env - - -def deny(reason: str) -> None: - print(json.dumps({ - "hookSpecificOutput": { - "hookEventName": "PreToolUse", - "permissionDecision": "deny", - "permissionDecisionReason": reason, - } - })) - - -def run_guard(root: pathlib.Path, event: dict, script: str, payload: dict) -> str | None: - path = root / ".tfcore" / "hooks" / script - if not path.exists(): - return None - try: - env = environment(root, event) - if script == "block-git.sh": - # Codex also has an all-git exec-policy rule. Keep the hook's - # compound-command fallback equally strict in YOLO mode. - env["TF_STRICT_GIT"] = "1" - result = subprocess.run( - ["bash", str(path)], input=json.dumps(payload), text=True, - capture_output=True, timeout=10, env=env, check=False, - ) - except Exception as exc: - # Policy checks fail closed. Telemetry is handled separately and fails open. - return f"TechieFlow could not execute {script}: {exc}" - if result.returncode == 2: - return result.stderr.strip() or f"Blocked by TechieFlow guard {script}" - if result.returncode not in (0,): - return f"TechieFlow guard {script} failed unexpectedly (exit {result.returncode})." - return None - - -def pre_tool(event: dict) -> None: - root = project_root(event) - tool = str(event.get("tool_name") or "") - raw = event.get("tool_input") - args = raw if isinstance(raw, dict) else {} - scripts: list[str] = [] - if tool == "Bash": - payload = {"tool_name": "Bash", "tool_input": {"command": str(args.get("command") or "")}} - scripts = ["block-git.sh", "guard-artifacts.sh"] - elif tool == "apply_patch": - patch = str(args.get("command") or args.get("patch") or "") - # Run the existing guards once per file using actual paths and separated - # added/removed lines. This avoids treating context or removed Verified - # cells as newly introduced content. - headers = list(re.finditer(r"(?m)^\*\*\* (Update|Add|Delete) File: (.+)$", patch)) - for index, header in enumerate(headers): - end = headers[index + 1].start() if index + 1 < len(headers) else len(patch) - section = patch[header.end():end] - file_path = header.group(2).strip() - base_name = pathlib.PurePosixPath(file_path.replace("\\", "/")).name.lower() - if header.group(1) == "Delete" and ( - base_name == "project-status.md" or base_name.endswith("-checklist.md") - ): - deny( - "TechieFlow protected documents cannot be deleted: " - f"{file_path}. Update their canonical fixed shape instead." - ) - return - added = [] - removed = [] - for line in section.splitlines(): - if line.startswith("+") and not line.startswith("+++"): - added.append(line[1:]) - elif line.startswith("-") and not line.startswith("---"): - removed.append(line[1:]) - payload = { - "hook_event_name": "PreToolUse", "cwd": str(root), - "session_id": event.get("session_id"), "tool_name": "Edit", - "tool_input": {"file_path": file_path, - "old_string": "\n".join(removed), - "new_string": "\n".join(added)}, - } - for script in ("guard-status.sh", "guard-verify.sh"): - reason = run_guard(root, event, script, payload) - if reason: - deny(reason) - return - return - elif tool in ("Edit", "Write"): - payload = {"tool_name": tool, "tool_input": args} - scripts = ["guard-status.sh", "guard-verify.sh"] - else: - return - payload.update(hook_event_name="PreToolUse", cwd=str(root), session_id=event.get("session_id")) - for script in scripts: - reason = run_guard(root, event, script, payload) - if reason: - deny(reason) - return - - -def stop(event: dict) -> None: - """Stop hook: refuse to end the turn while PROJECT-STATUS.html is stale. - - Same contract as the Claude Code Stop hook (_status-update-gate.md §8): - guard-status-html.sh exits 2 when the .html is older than the .md or - missing; `stop_hook_active` is passed through so a turn that genuinely - cannot render still terminates instead of looping. - """ - root = project_root(event) - payload = { - "hook_event_name": "Stop", "cwd": str(root), - "session_id": event.get("session_id"), - "stop_hook_active": bool(event.get("stop_hook_active")), - } - reason = run_guard(root, event, "guard-status-html.sh", payload) - if reason: - print(json.dumps({"decision": "block", "reason": reason})) - - -def write_pointer(root: pathlib.Path, event: dict) -> None: - session_id = str(event.get("session_id") or "") - if not session_id: - return - target = root / ".tfcore" / ".session" / "codex.json" - try: - target.parent.mkdir(parents=True, exist_ok=True) - target.write_text(json.dumps({ - "session_id": session_id, - "transcript_path": event.get("transcript_path"), - "model": event.get("model"), - "ts": datetime.now(timezone.utc).isoformat(), - }) + "\n", encoding="utf-8") - except Exception: - pass - - -def sweep_artifacts(root: pathlib.Path, event: dict) -> None: - """SessionStart analogue of the Claude Code sweep-artifacts.sh hook: delete - run material under tests/.artifacts/ and .verify/ older than the retention - window and any banned repo-root legacy dir. No veto — never raises.""" - script = root / ".tfcore" / "hooks" / "sweep-artifacts.sh" - if not script.exists(): - return - try: - res = subprocess.run(["bash", str(script)], input=json.dumps(event), text=True, - capture_output=True, timeout=30, env=environment(root, event), check=False) - summary = (res.stdout or "").strip() - if summary: - print(summary) # SessionStart stdout is surfaced into the session - except Exception: - pass - - -def session_end(root: pathlib.Path, event: dict) -> None: - # Transcript format is explicitly not a stable Codex hook interface. Record - # honest session metadata; codex exec --json telemetry is enriched separately. - emit = root / ".tfcore" / "utils" / "tf-emit.sh" - if not emit.exists() or not event.get("session_id"): - return - record = { - "kind": "session", - "session_id": event.get("session_id"), - "model": event.get("model"), - "duration_s": None, - "input_tokens": None, - "output_tokens": None, - "cache_read_tokens": None, - "cache_creation_tokens": None, - "cost_usd": None, - "children_sessions": None, - "harness": "codex", - } - try: - subprocess.run(["bash", str(emit), "sessions"], input=json.dumps(record), text=True, - capture_output=True, timeout=2, env=environment(root, event), check=False) - except Exception: - pass - - -def main() -> int: - event = read_event() - root = project_root(event) - mode = sys.argv[1] if len(sys.argv) > 1 else "" - if mode == "pre-tool": - pre_tool(event) - elif mode == "stop": - stop(event) - elif mode == "session-start": - write_pointer(root, event) - # .codex/hooks.json runs session-start for UserPromptSubmit too; sweep - # only on the real session start so every prompt does not walk the tree. - if str(event.get("hook_event_name") or "").lower() != "userpromptsubmit": - sweep_artifacts(root, event) - elif mode == "session-end": - write_pointer(root, event) - session_end(root, event) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.tfcore/hooks/guard-artifacts.sh b/.tfcore/hooks/guard-artifacts.sh index 5564952..f368358 100644 --- a/.tfcore/hooks/guard-artifacts.sh +++ b/.tfcore/hooks/guard-artifacts.sh @@ -13,8 +13,8 @@ # and overriding the config's pinned outputDir. This hook makes the rule # MECHANICAL, the same way block-git.sh made the git ban mechanical. # -# Wired in .claude/settings.json → hooks.PreToolUse (matcher "Bash"); Codex via -# .tfcore/hooks/codex-adapter.py pre-tool; OpenCode via .opencode/plugin/techieflow.js. +# Wired in .claude/settings.json → hooks.PreToolUse (matcher "Bash"); +# OpenCode via .opencode/plugin/techieflow.js. # Exit 2 + stderr = block the call and feed the message back to the agent. # # What it blocks: diff --git a/.tfcore/hooks/sweep-artifacts.sh b/.tfcore/hooks/sweep-artifacts.sh index ab63139..8b16b34 100644 --- a/.tfcore/hooks/sweep-artifacts.sh +++ b/.tfcore/hooks/sweep-artifacts.sh @@ -2,7 +2,7 @@ # TechieFlow hook — automatic sweep of expired run material (added 2026-08-26). # # Wired as a SessionStart hook in .claude/settings.json (Claude Code), as -# `codex-adapter.py session-start` (Codex) and on the first root +# on the first root # `session.created` in .opencode/plugin/techieflow.js (OpenCode). # # WHY: verify-phase.md §1 pins every run artifact under tests/.artifacts/ and @@ -60,7 +60,7 @@ root = os.path.realpath(root) DRY = os.environ.get("TF_SWEEP_DRY_RUN") == "1" # Throttle: at most one sweep per hour per project, whichever harness fires it -# (Codex re-fires session-start on every prompt; a walk of a big tree on every +# (a harness may re-fire session-start on every prompt; a walk of a big tree on every # turn is waste). TF_SWEEP_FORCE=1 bypasses. Dry runs never touch the stamp. stamp = os.path.join(root, ".tfcore", ".session", "sweep.stamp") if not DRY and os.environ.get("TF_SWEEP_FORCE") != "1": diff --git a/.tfcore/routing.yaml b/.tfcore/routing.yaml index 3c628e6..247b755 100644 --- a/.tfcore/routing.yaml +++ b/.tfcore/routing.yaml @@ -7,7 +7,7 @@ # A phase declares a TIER, never a model. The tiers map resolves a tier to a # per-harness model id (Claude Code aliases: opus|sonnet|haiku|inherit|, # pinnable via ANTHROPIC_DEFAULT_*_MODEL; OpenCode ids: provider/model, list -# with `opencode models`; Codex model slugs: list with `codex debug models`). +# with `opencode models`). # Routing is declared at the harness boundary and # OBSERVED by telemetry (runs.jsonl model/tier fields) — it is never enforced # mid-turn, because neither harness can switch a running turn's model. @@ -47,15 +47,12 @@ tiers: frontier: claude: sonnet opencode: openai/gpt-5.6-terra - codex: gpt-5.6 standard: claude: sonnet opencode: openai/gpt-5.6-terra - codex: gpt-5.6-terra economy: claude: haiku opencode: openai/gpt-5.6-luna - codex: gpt-5.6-luna # --------------------------------------------------------------------------- # Phase → tier. One line each; values: frontier | standard | economy | inherit. @@ -93,7 +90,7 @@ subagents: tf-explorer: economy # --------------------------------------------------------------------------- -# Tier → reasoning effort (used by Claude Code and Codex; ignored by OpenCode). +# Tier → reasoning effort (used by Claude Code; ignored by OpenCode). # --------------------------------------------------------------------------- effort: frontier: high diff --git a/.tfcore/tasks/metrics-report.md b/.tfcore/tasks/metrics-report.md index e9c018b..4c7b02a 100644 --- a/.tfcore/tasks/metrics-report.md +++ b/.tfcore/tasks/metrics-report.md @@ -72,7 +72,7 @@ If you find yourself wanting to write "overall, across all projects, …" — ** ### 3. Honesty rules for every number on the page - **Fewer than 3 supporting records → print `insufficient data (n=…)`, never a number.** A 100% first-pass rate from one REQ is noise wearing a suit. -- **Never estimate, interpolate, or infer a metric that was not measured.** A missing number is reported as missing. `cost_usd` is `null` on every Claude Code and Codex record (no cost source exists, and inventing one would be an estimate presented as a measurement) — report **tokens**, name the harness, and say why dollars are absent. **Do not multiply tokens by a rate card.** Real dollars appear only on OpenCode records and are never pooled with the others. +- **Never estimate, interpolate, or infer a metric that was not measured.** A missing number is reported as missing. `cost_usd` is `null` on every Claude Code record (no cost source exists, and inventing one would be an estimate presented as a measurement) — report **tokens**, name the harness, and say why dollars are absent. **Do not multiply tokens by a rate card.** Real dollars appear only on OpenCode records and are never pooled with the others. - **Measured cost and apportioned cost are two columns, never one number.** A fix run that repaired three misses has one token window; dividing it three ways is arithmetic. Print `cost_attribution:"sole"` figures as the headline and `shared:` figures beside them, labelled as apportioned. `none` records are counted and costed at nothing. - **Never invent a metric that has no stream behind it.** No cycle-time-per-feature: the unit of work in this framework is the run, not the ticket, and that is deliberate. - Records with `project_type_inferred: true` are **unclassified** — give them their own row labelled as such. Do not silently fold them into `app`. diff --git a/.tfcore/telemetry/SCHEMA.md b/.tfcore/telemetry/SCHEMA.md index a03c224..e52e59e 100644 --- a/.tfcore/telemetry/SCHEMA.md +++ b/.tfcore/telemetry/SCHEMA.md @@ -39,7 +39,7 @@ Questions 1–3 are answered by `gates.jsonl`, which remains **the primary strea | `project_type_inferred` | bool | Present **only when `true`** — `metrics.project_type` was absent from `core-config.yaml` and `app` was assumed. Reports must label these records **unclassified**, never silently pool them. | | `backfilled` | bool | Present **only when `true`** — the record was reconstructed after the fact, not written at the moment of the event. Written exclusively by `tf-metrics.sh --backfill-*`. | | `inferred` | string[] | Present only on backfilled records. Names the fields that were **guessed rather than read**. | -| `harness` | string \| null | `claude-code` \| `opencode` \| `codex` \| `null`. **Detected by `tf-emit.sh`, never declared by a task** — see below. | +| `harness` | string \| null | `claude-code` \| `opencode` \| `null`. **Detected by `tf-emit.sh`, never declared by a task** — see below. `codex` is retired (2026-09-07): nothing writes it, and records that carry it stay valid and readable. | ### `project_type` — what it is and why it exists @@ -70,11 +70,11 @@ So `install-metrics.sh` now re-examines **`docs` alone**, on a later refresh, an ### `harness` — detected, never declared -The framework runs under **three harnesses**: Claude Code (`.claude/commands/TechieFlow/`), OpenCode (agents/tasks loaded from `opencode.jsonc`), and Codex (`.agents/skills/` plus `.codex/agents/`). The task content is identical across harnesses. A task template therefore **cannot know which one is executing it** — an agent copying a literal from the markdown would stamp whichever harness the example happened to name, and every per-harness comparison would be quietly wrong. +The framework runs under **two harnesses**: Claude Code (`.claude/commands/TechieFlow/`) and OpenCode (agents/tasks loaded from `opencode.jsonc`). A third, Codex, was retired on 2026-09-07; records written before then may carry `harness: "codex"` and stay valid. The task content is identical across harnesses. A task template therefore **cannot know which one is executing it** — an agent copying a literal from the markdown would stamp whichever harness the example happened to name, and every per-harness comparison would be quietly wrong. So `tf-emit.sh` detects it and injects it. **Never write `harness` into an emit template.** Detection order: -1. the adapter-owned `TF_HARNESS`, then harness environment variables (`CLAUDECODE`, `CLAUDE_CODE_*` → `claude-code`; any `OPENCODE*` → `opencode`; Codex thread/session markers → `codex`); +1. the adapter-owned `TF_HARNESS`, then harness environment variables (`CLAUDECODE`, `CLAUDE_CODE_*` → `claude-code`; any `OPENCODE*` → `opencode`); 2. the parent process chain, bounded to 12 levels (OpenCode sets no `OPENCODE_*` variables, so the process name is the only honest signal); 3. **`null`** if neither resolves. @@ -121,7 +121,7 @@ So `tf-emit.sh` detects it and injects it. **Never write `harness` into an emit | `routed` | bool | `model == tier_model`. Present only when both are known. Routing is observed, never enforced — `routed:false` is drift made visible, not an error. | | `tokens_in`, `tokens_out`, `tokens_cache_read`, `tokens_cache_write` | int | Σ over assistant messages whose timestamp ∈ [`started`, `ended`]. Requires the record to carry `started` + `ended` and the session pointer (`.tfcore/.session/.json` — written by the `session-pointer.sh` hook on Claude, by the plugin on OpenCode). | | `cost_usd` | number \| null | Σ real per-message cost from `opencode.db` on OpenCode; **always `null` on Claude Code** (the transcript has no cost and a rate-card estimate would be an estimate presented as a measurement). | -| `tokens_scope` | string | `tree` = the full session tree — OpenCode: pointer session + descendant sessions; Claude: pointer transcript + the subagent transcripts beside it (`//subagents/agent-*.jsonl`, a deterministic path verified 2026-08-20 via a `SubagentStop` payload's `agent_transcript_path`). `main` = Claude main thread only (no subagents dir existed). `conversation` = an exact persisted Codex rollout counter bounded by documented conversation events; measured session data, not an estimate. `none` = window could not be computed (no pointer / unreadable store / empty window) — **tokens are never estimated**. | +| `tokens_scope` | string | `tree` = the full session tree — OpenCode: pointer session + descendant sessions; Claude: pointer transcript + the subagent transcripts beside it (`//subagents/agent-*.jsonl`, a deterministic path verified 2026-08-20 via a `SubagentStop` payload's `agent_transcript_path`). `main` = Claude main thread only (no subagents dir existed). `conversation` = an exact persisted measured session data, not an estimate. `none` = window could not be computed (no pointer / unreadable store / empty window) — **tokens are never estimated**. | | `attempt` | int | **`runs` only, added 2026-08-21.** `1 +` the number of prior non-backfilled `run` records with the same `cmd` whose `reqs_touched` intersects this record's. Stamped only when the record carries a non-empty `reqs_touched`; absent on backfilled records and on REQ-less runs (`metrics-report`, renders). Distinct from the gate-level `attempt` in §3.1 (per REQ per verify). This is the counter `routing.yaml` `escalation:` reads **at launch** — `bash .tfcore/utils/tf-emit.sh --next-run-attempt ...` prints the value the next record would get. Advisory: telemetry records, it never switches a model (DECISIONS.md 2026-08-21). | @@ -422,7 +422,7 @@ The agent's job on a `miss` record is therefore small and honest: name what was | `verdict_after` | string | `Verified` \| `Needs re-verify` \| `FAIL` \| `deferred` \| `wont-fix` | | `reopened` | bool | `true` when this miss had already closed `Verified` and a later escape re-opened it. | | `cost_attribution` | string | **Derived by `tf-emit.sh`** from the fix run's `reqs_touched`: `sole` \| `shared:` \| `none` — §5.5.3. | -| `tokens_in`, `tokens_out`, `tokens_cache_read`, `tokens_cache_write`, `cost_usd`, `tokens_scope`, `model` | — | **Injected by `tf-emit.sh`** from the `fix_run_id` window, by exactly the §2.5 mechanism. Never written by an agent. `cost_usd` stays `null` on Claude Code and Codex, per §4. | +| `tokens_in`, `tokens_out`, `tokens_cache_read`, `tokens_cache_write`, `cost_usd`, `tokens_scope`, `model` | — | **Injected by `tf-emit.sh`** from the `fix_run_id` window, by exactly the §2.5 mechanism. Never written by an agent. `cost_usd` stays `null` on Claude Code, per §4. | ### 5.5.3 `cost_attribution` — the field the money number stands on diff --git a/.tfcore/telemetry/tf-metrics.sh b/.tfcore/telemetry/tf-metrics.sh index 56c1d38..b0b669b 100644 --- a/.tfcore/telemetry/tf-metrics.sh +++ b/.tfcore/telemetry/tf-metrics.sh @@ -356,7 +356,7 @@ def analyse_misses(misses): sole_tokens, sole_priced_n = tok(sole) shared_priced = [f for f in shared if f.get("tokens_out") is not None] - # Dollars exist ONLY where a harness measured them. Claude Code and Codex carry + # Dollars exist ONLY where a harness measured them. Claude Code carries # cost_usd:null permanently (SCHEMA.md §4) and are never priced from a rate card # here — a pooled sum over mixed harnesses would silently under-report. paid = [f for f in sole if f.get("cost_usd") is not None] @@ -487,7 +487,7 @@ def analyse_phases(runs): confident fan-out figures largely composed of runs that could not have seen a subagent. Tree-scope only, with the exclusion printed. - 3. DOLLARS. Never pooled across harness (SCHEMA.md §4). Claude and Codex + 3. DOLLARS. Never pooled across harness (SCHEMA.md §4). Claude carry cost_usd:null permanently; a sum over mixed records under-reports silently. Reported per harness or not at all. @@ -1373,7 +1373,7 @@ def print_misses(m, W): else: print(" USD per miss : no measured dollars (%d priced records)" % m["cost_usd_records"]) - print(" Claude Code and Codex carry cost_usd:null permanently and are NEVER") + print(" Claude Code carries cost_usd:null permanently and is NEVER") print(" priced from a rate card here (SCHEMA.md §4). Real dollars come from") print(" OpenCode runs; token counts are the honest figure everywhere else.") print(" shared (apportioned): %d fix records — equal division, NOT a measurement" diff --git a/.tfcore/templates/v4custom/app-agents-md-tmpl.md b/.tfcore/templates/v4custom/app-agents-md-tmpl.md index ce6a224..d042784 100644 --- a/.tfcore/templates/v4custom/app-agents-md-tmpl.md +++ b/.tfcore/templates/v4custom/app-agents-md-tmpl.md @@ -1,7 +1,7 @@ # {AppName} — project session memory (all harnesses) @@ -13,9 +13,9 @@ ALWAYS read and follow: ## Hard rules (non-negotiable — the harness enforces #1) -1. **Git is manual — agents NEVER run `git` or `gh`.** Not to commit, and not to read (`status`/`log`/`diff`/`grep`/`blame`). All harnesses enforce this mechanically: Claude Code via `.claude/settings.json` and `block-git.sh`; OpenCode via `opencode.jsonc` plus its plugin bridge; Codex via `.codex/rules/techieflow.rules` plus `.codex/hooks.json`. A blocked git call is the policy working, not an obstacle to route around. Evidence for status updates / "what changed" = the checklist Requirements Status table + the working-tree files (+ mtimes) + a fresh `dotnet build` (`.tfcore/tasks/_status-update-gate.md`). The OWNER commits, in a separate terminal. +1. **Git is manual — agents NEVER run `git` or `gh`.** Not to commit, and not to read (`status`/`log`/`diff`/`grep`/`blame`). All harnesses enforce this mechanically: Claude Code via `.claude/settings.json` and `block-git.sh`; OpenCode via `opencode.jsonc` plus its plugin bridge. A blocked git call is the policy working, not an obstacle to route around. Evidence for status updates / "what changed" = the checklist Requirements Status table + the working-tree files (+ mtimes) + a fresh `dotnet build` (`.tfcore/tasks/_status-update-gate.md`). The OWNER commits, in a separate terminal. 2. **Run the app yourself — the test harness is fully set up.** Headless Playwright + Chromium live in WSL; the Windows/MAUI dotnet bridge is rung #4 of the build ladder; MAUI Android/iOS/Mac Catalyst are driven over the Appium bridge (`core-config.yaml → runtimeVerification.appium`). NEVER ask the owner to boot the app, run a build, or execute a command — "can't run on Linux/WSL", "it targets Windows", "it's MAUI", "Playwright needs a GUI", "the dependent service is down" are BANNED excuses (`.tfcore/tasks/_smoke-test-policy.md`). Asking the owner is the LAST resort, only after the build ladder + `verify-phase §3a` escalation genuinely fail — and even then you still run the test yourself once they reply. -3. **The framework tree is invisible to search — a search that finds nothing is NOT proof a file is missing.** `.tfcore/`, `.claude/`, `.codex/`, `.opencode/` and `.agents/skills/` are hidden dot-directories AND gitignored here (deliberate — deployed framework copies are never committed). File search skips hidden paths *and* honours `.gitignore`, so it takes **both** (`rg -uu`; `--hidden` alone is not enough), and `git grep`/`git ls-files` see nothing at all. Confirm a framework file by **reading its literal path** — every one has a single canonical location, and whatever needs it names that path. Never write "not present in this tree" into a verdict, a checklist Remarks cell, a BRD row, or a blocker without having tried the path and failed: that false negative propagates into the docs and the next agent treats it as fact. If a framework file really is absent, the repo needs `update-framework.sh ` run once on this machine — report that instead of reimplementing what it does. (`.tfcore/tasks/_status-update-gate.md` §"The framework tree is INVISIBLE to search".) +3. **The framework tree is invisible to search — a search that finds nothing is NOT proof a file is missing.** `.tfcore/`, `.claude/`, `.opencode/` and `.agents/skills/` are hidden dot-directories AND gitignored here (deliberate — deployed framework copies are never committed). File search skips hidden paths *and* honours `.gitignore`, so it takes **both** (`rg -uu`; `--hidden` alone is not enough), and `git grep`/`git ls-files` see nothing at all. Confirm a framework file by **reading its literal path** — every one has a single canonical location, and whatever needs it names that path. Never write "not present in this tree" into a verdict, a checklist Remarks cell, a BRD row, or a blocker without having tried the path and failed: that false negative propagates into the docs and the next agent treats it as fact. If a framework file really is absent, the repo needs `update-framework.sh ` run once on this machine — report that instead of reimplementing what it does. (`.tfcore/tasks/_status-update-gate.md` §"The framework tree is INVISIBLE to search".) 4. **Native-head automation binds to the app's own window.** Drive a MAUI head only through a session attached to the app under test (Windows: launched PID → its top-level window handle; Android/iOS/Catalyst: the app's package/bundle id), interact element-by-element via `AutomationId`, and NEVER inject global keyboard/mouse input — it lands in whatever window happens to have focus, not the app (`verify-phase.md §3b`). ## Project basics @@ -41,7 +41,6 @@ never silently worked around. ## Slash-command syntax (READ ME if a `/agent *command` invocation fails) -**Codex:** use repository skills such as `$techieflow-build`, `$techieflow-verify`, and `$techieflow-refresh-status`, or ask for the skill by name in plain language. Skills load the canonical `.tfcore/tasks/*.md` workflow. When a task calls for fan-out, delegate to the registered `tf_builder`, `tf_test_writer`, `tf_explorer`, `trblazeui`, or `techierag` Codex role and wait for its result. Defining a role alone does not authorize delegation; the user request or applicable skill must call for it. Project config and hooks require repository trust; review changed hooks with `/hooks`. **Claude Code** registers TechieFlow-native agents under the path-derived namespace `TechieFlow:agents:`. The short `/` form does NOT always resolve. When in doubt use the full form: diff --git a/.tfcore/templates/v4custom/app-claude-md-tmpl.md b/.tfcore/templates/v4custom/app-claude-md-tmpl.md index c46e901..234fc36 100644 --- a/.tfcore/templates/v4custom/app-claude-md-tmpl.md +++ b/.tfcore/templates/v4custom/app-claude-md-tmpl.md @@ -26,7 +26,7 @@ | Confirm a **framework** file exists | Glob/Grep for its name — **returns nothing even when it is there** | **Read** the literal path (`.tfcore/…`) — see below | | Loop over a list of files | `for f in ...; do ...; done` | Multiple parallel Read/Edit calls in one assistant turn | -**Glob and Grep cannot see the framework tree.** `.tfcore/`, `.claude/`, `.codex/`, `.opencode/` and `.agents/skills/` are hidden dot-directories *and* are gitignored in this repo (deliberately — the deployed framework copies are never committed). Grep is ripgrep-backed: it skips hidden paths AND honours `.gitignore`, so it needs **both** flags — `rg --hidden --no-ignore`, i.e. `rg -uu`. `--hidden` alone is not enough. Glob, `git grep` and `git ls-files` are blind for the same reasons. +**Glob and Grep cannot see the framework tree.** `.tfcore/`, `.claude/` and `.opencode/` are hidden dot-directories *and* are gitignored in this repo (deliberately — the deployed framework copies are never committed). Grep is ripgrep-backed: it skips hidden paths AND honours `.gitignore`, so it needs **both** flags — `rg --hidden --no-ignore`, i.e. `rg -uu`. `--hidden` alone is not enough. Glob, `git grep` and `git ls-files` are blind for the same reasons. So **a search returning nothing is not evidence that a framework file is missing.** Confirm framework files by **Read**ing the literal path (every one has exactly one canonical location, and whatever needs it names that path). Never report one as absent without having tried the path first — that false negative gets written into the checklist and the BRD, where the next agent inherits it as fact. If a framework file really is gone, the repo needs `update-framework.sh ` run once on this machine; say that rather than working around it. Full rule: `.tfcore/tasks/_status-update-gate.md` §"The framework tree is INVISIBLE to search". diff --git a/.tfcore/templates/v4custom/metrics-report-template.md b/.tfcore/templates/v4custom/metrics-report-template.md index a80492a..6dafdc3 100644 --- a/.tfcore/templates/v4custom/metrics-report-template.md +++ b/.tfcore/templates/v4custom/metrics-report-template.md @@ -99,7 +99,7 @@ pooled deliberately.* | Tokens per `Verified` REQ | {n} | | Commit cadence | {n} commits/active day over {n} days | -**Cost in USD is not reported here.** Claude Code transcripts and Codex usage +**Cost in USD is not reported here.** Claude Code transcripts and usage payloads carry token counts but no per-message dollar cost, and this framework runs on subscriptions where marginal per-token cost is not the real unit. Multiplying 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 | Unattributable (`none` — no usable token window) | {n} | — | **Dollars.** {Either: "$X per miss — MEASURED, from {n} OpenCode records." Or: -"No measured dollars. Claude Code and Codex carry `cost_usd: null` permanently — +"No measured dollars. Claude Code carries `cost_usd: null` permanently — no cost source exists on either, and pricing tokens from a rate card here would be an estimate presented as a measurement. Tokens are the honest figure."} diff --git a/.tfcore/user-guide.md b/.tfcore/user-guide.md index 0c254b1..05963b0 100644 --- a/.tfcore/user-guide.md +++ b/.tfcore/user-guide.md @@ -235,29 +235,6 @@ TechieFlow integrates with OpenCode via a project-level `opencode.jsonc`/`openco } ``` -### Codex (CLI & Web) - -TechieFlow's full build/runtime workflow is supported in local Codex CLI, IDE, -and desktop sessions. The scaffold/update scripts install `.codex/` project -config, custom agents, hooks and rules plus reusable workflows under -`.agents/skills/`. Trust the repository and review `/hooks` after installation -or a hook update. - -Codex loads the root `AGENTS.md`. Invoke `$techieflow-build`, -`$techieflow-verify`, `$techieflow-refresh-status`, or name the skill in plain -language. Literal Claude/OpenCode `*command` and slash names are vocabulary -aliases, not Codex command registrations. - -For an unattended local run: - -```bash -bash .tfcore/utils/tf-goal.sh --harness codex . "" -``` - -Codex cloud cannot automatically reach local `winrun`, Appium hosts, NuGet -credentials, or already-running services. It is static-only unless equivalent -infrastructure is explicitly provisioned. - ## Special Agents There are two TechieFlow agents — in the future they'll be consolidated into a single Flow-Master. diff --git a/.tfcore/utils/tf-codex-bind.py b/.tfcore/utils/tf-codex-bind.py deleted file mode 100644 index 5e3c5b0..0000000 --- a/.tfcore/utils/tf-codex-bind.py +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env python3 -"""Generate TechieFlow's Codex agents and thin workflow skills.""" - -from __future__ import annotations - -import argparse -import pathlib -import re - - -PHASES = { - "day1-brownfield": ("analyst", "Reverse-document and initialize an existing application."), - "day1-greenfield": ("analyst", "Initialize planning artifacts and mockups for a new application."), - "amend-docs": ("analyst", "Amend existing requirements and design artifacts in place."), - "author-brd": ("analyst", "Interactively author or extend numbered business requirements."), - "mockups": ("analyst", "Create or update the greenfield UI design and HTML mockups."), - "split-brd": ("analyst", "Convert a BRD into the single implementation checklist."), - "build-phase": ("flow_master", "Implement the open checklist and chain smoke and verification."), - "verify-phase": ("verifier", "Independently verify requirements against runtime evidence."), - "fix-issues": ("flow_master", "Fix reported or verifier-discovered application defects."), - "triage-issues": ("flow_master", "Analyze and document human-found bugs without fixing source."), - "log-miss": ("flow_master", "Record one missed requirement as telemetry and a checklist line."), - "devguide": ("flow_master", "Create the developer-facing screen and component guide."), - "productguide": ("flow_master", "Create the user-facing guide from the running application."), - "handoff-phase": ("flow_master", "Complete handoff documents, status, and rendered artifacts."), - "refresh-status": ("flow_master", "Recover truthful project status after an interrupted run."), - "generate-html": ("flow_master", "Render an arbitrary human-readable Markdown document to HTML."), - "render-workflow-docs": ("flow_master", "Render the canonical BRD, Architecture, and status HTML files."), - "metrics-report": ("flow_master", "Report TechieFlow development telemetry without changing code."), -} - -PERSONAS = { - "flow-master": "flow-master.md", - "analyst": "analyst.md", - "architect": "architect.md", - "verifier": "verifier.md", -} - -WORKERS = { - "tf-builder": """Implement one assigned FN/NFR requirement cluster. Read `.tfcore/tasks/_smoke-test-policy.md`. Never run git or gh. Follow project coding standards, smoke the changed behavior, update only the assigned checklist rows, and return structured evidence to the parent.""", - "tf-test-writer": """Write black-box verification tests for one assigned requirement cluster. Never edit application source and never run git or gh. Follow `.tfcore/tasks/verify-phase.md` and return tests added, tests refreshed, and anything unobservable.""", - "tf-explorer": """Perform a focused read-only codebase scan. Never edit files and never run git or gh. Return concise evidence with file paths and symbols.""", - "trblazeui": """Read the NuGet-deployed TrBlazeUI persona and adopt its implementation rules. Resolve it in this order and use the FIRST that exists: `.claude/commands/trblazeui.md` (current deploy target), `.claude/trblazeui.md` (legacy deploy target), `.opencode/command/trblazeui.md`, `.trblazeui/TrBlazeUI-AI-Reference.md` (packaged component reference). Only if NONE exists, report that the TrBlazeUI persona is not deployed (`dotnet build` the app to unpack the package) and stop. Never run git or gh.""", - "techierag": """Read the NuGet-deployed TechieRag persona and adopt its implementation rules. Resolve it in this order and use the FIRST that exists: `.claude/commands/techierag.md` (current deploy target), `.claude/techierag.md` (legacy deploy target), `.opencode/command/techierag.md`, `.techierag/TechieRag-AI-Reference.md` (packaged service reference). Only if NONE exists, report that the TechieRag persona is not deployed (`dotnet build` the app to unpack the package) and stop. Never run git or gh.""", -} - -# Library-persona workers are COMPATIBILITY wrappers: the library NuGet packages own -# the native `.codex/agents/.toml` (TrBlazeUI.Components >= 2.0.3 deploys it on -# `dotnet build`; TechieRag is expected to follow the same contract). The package's -# MSBuild target deploys only when the file is absent OR the ownership marker -# `./.codex-agent-package-owned` exists; otherwise it preserves what it finds as -# consumer-owned. So the framework must (a) never overwrite a file that is not its own -# wrapper, and (b) write the marker alongside its wrapper so the package is allowed to -# replace it. The wrapper then self-retires per repo on the first post-2.0.3 build. -COMPAT = { - "trblazeui": ("TrBlazeUI.Components", ".trblazeui"), - "techierag": ("TechieRag", ".techierag"), -} -MARKER = ".codex-agent-package-owned" - - -def own_wrapper(path: pathlib.Path, name: str) -> bool: - """True when `path` is absent or holds the framework's own compat wrapper.""" - if not path.exists(): - return True - return f'description = "TechieFlow {name} role."' in path.read_text(encoding="utf-8") - - -def routing(path: pathlib.Path) -> dict: - cfg = {"enabled": False, "tiers": {}, "phases": {}, "subagents": {}, "effort": {}} - section = tier = None - if not path.exists(): - return cfg - for line in path.read_text(encoding="utf-8").splitlines(): - if not line.strip() or line.lstrip().startswith("#"): - continue - if not line.startswith(" "): - key, _, value = line.partition(":") - section, tier = key.strip(), None - if section == "enabled": - cfg["enabled"] = value.strip() == "true" - elif section == "tiers": - if re.match(r"^ [a-z-]+:\s*$", line): - tier = line.strip()[:-1] - cfg["tiers"][tier] = {} - elif tier and line.startswith(" "): - key, _, value = line.strip().partition(":") - cfg["tiers"][tier][key] = value.strip() - elif section in ("phases", "subagents", "effort"): - key, _, value = line.strip().partition(":") - cfg[section][key] = value.strip() - return cfg - - -def quoted(value: str) -> str: - return '"' + value.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n") + '"' - - -def write(path: pathlib.Path, content: str) -> None: - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(content.rstrip() + "\n", encoding="utf-8", newline="\n") - - -def persona_instructions(root: pathlib.Path, source_name: str) -> str: - source = root / ".tfcore" / "agents" / source_name - return f"""Read `{source.relative_to(root)}` completely before acting and follow its persona, core principles, command routing, and dependency rules. Skip only its activation greeting/help/halt ritual because this is a spawned Codex role. Treat `.tfcore/tasks/*.md` as executable workflows. Never run git or gh. Preserve TechieFlow's smoke, verifier, artifact-location, local-only runtime, checklist, and PROJECT-STATUS invariants. Load only dependencies needed for the assigned task.""" - - -def agent_file(name: str, description: str, instructions: str, model: str | None, - effort: str | None, read_only: bool = False) -> str: - lines = [f"name = {quoted(name)}", f"description = {quoted(description)}", - f"developer_instructions = {quoted(instructions)}"] - if model: - lines.append(f"model = {quoted(model)}") - if effort: - lines.append(f"model_reasoning_effort = {quoted(effort)}") - if read_only: - lines.append('sandbox_mode = "read-only"') - return "\n".join(lines) - - -def main() -> int: - parser = argparse.ArgumentParser() - parser.add_argument("root", nargs="?", default=".") - args = parser.parse_args() - root = pathlib.Path(args.root).resolve() - if not (root / ".tfcore").is_dir(): - parser.error(f"not a TechieFlow repository: {root}") - cfg = routing(root / ".tfcore" / "routing.yaml") - - persona_tier = {"flow-master": "build-phase", "analyst": "day1-greenfield", - "architect": "day1-greenfield", "verifier": "verify-phase"} - for name, source in PERSONAS.items(): - tier = cfg["phases"].get(persona_tier[name], "inherit") - model = cfg["tiers"].get(tier, {}).get("codex") if cfg["enabled"] and tier != "inherit" else None - effort = cfg["effort"].get(tier) if model else None - write(root / ".codex" / "agents" / f"{name}.toml", agent_file( - name.replace("-", "_"), f"TechieFlow {name} specialist.", - persona_instructions(root, source), model, effort)) - - kept = [] - for name, instructions in WORKERS.items(): - tier = cfg["subagents"].get(name, "inherit") - model = cfg["tiers"].get(tier, {}).get("codex") if cfg["enabled"] and tier != "inherit" else None - effort = cfg["effort"].get(tier) if model else None - target = root / ".codex" / "agents" / f"{name}.toml" - if name in COMPAT: - package, lib_dir = COMPAT[name] - if not own_wrapper(target, name): - kept.append(name) # package- or consumer-owned agent: never overwrite - continue - marker = root / lib_dir / MARKER - if not marker.exists(): - write(marker, package) - ignore = root / lib_dir / ".gitignore" # same `*` the package writes on build - if not ignore.exists(): - write(ignore, "*") - write(target, agent_file( - name.replace("-", "_"), f"TechieFlow {name} role.", instructions, - model, effort, read_only=name == "tf-explorer")) - - for phase, (owner, description) in PHASES.items(): - skill_name = f"techieflow-{phase.removesuffix('-phase')}" - task = f".tfcore/tasks/{phase}.md" - body = f"""--- -name: {skill_name} -description: {description} Use when a TechieFlow project needs its `{phase}` workflow. ---- - -# {skill_name} - -1. Read `{task}` completely and follow it as the canonical executable workflow. -2. Read `.tfcore/core-config.yaml` and only the task dependencies required for this run. -3. Operate as the `{owner}` role. Delegate only where the task explicitly calls for independent subagents, using the registered Codex roles and waiting for their results. -4. Preserve interactive elicitation unless `.tfcore/.session/yolo.json` exists or the user explicitly requested YOLO/goal mode. -5. Never run `git` or `gh`. Run required builds, smoke tests, runtime observations, verifier gates, document updates, and telemetry yourself. -6. Treat old `*...` and harness slash-command text in the canonical task as vocabulary aliases; execute the named task or delegate to the named Codex role directly. -""" - write(root / ".agents" / "skills" / skill_name / "SKILL.md", body) - - yolo = """--- -name: techieflow-yolo -description: Enable, disable, or inspect TechieFlow YOLO mode for an explicitly requested unattended workflow. ---- - -Read `.tfcore/tasks/_yolo-mode.md` completely. Run `bash .tfcore/utils/tf-yolo.sh on|off` as requested. YOLO removes elicitation pauses but does not broaden the user's task, allow git/gh, bypass the Codex workspace sandbox, or waive genuine external blockers. For a supervised long-running goal, use `.tfcore/utils/tf-goal.sh --harness codex`. -""" - write(root / ".agents" / "skills" / "techieflow-yolo" / "SKILL.md", yolo) - print(f"tf-codex-bind: generated {len(PERSONAS) + len(WORKERS) - len(kept)} agents and {len(PHASES) + 1} skills") - for name in kept: - print(f"tf-codex-bind: kept library-owned .codex/agents/{name}.toml (compat wrapper retired)") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.tfcore/utils/tf-codex-telemetry.py b/.tfcore/utils/tf-codex-telemetry.py deleted file mode 100644 index d129b20..0000000 --- a/.tfcore/utils/tf-codex-telemetry.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -"""Emit authoritative Codex exec JSONL usage into TechieFlow sessions telemetry.""" - -from __future__ import annotations - -import json -import os -import pathlib -import subprocess -import sys - - -def main() -> int: - if len(sys.argv) != 3: - print("usage: tf-codex-telemetry.py ", file=sys.stderr) - return 2 - root = pathlib.Path(sys.argv[1]).resolve() - source = pathlib.Path(sys.argv[2]) - thread_id = None - model = None - totals = {"input_tokens": 0, "output_tokens": 0, "cache_read_tokens": 0, - "reasoning_output_tokens": 0} - try: - for line in source.read_text(encoding="utf-8", errors="replace").splitlines(): - try: - event = json.loads(line) - except Exception: - continue - if event.get("type") == "thread.started": - thread_id = event.get("thread_id") or thread_id - if event.get("model"): - model = event.get("model") - if event.get("type") == "turn.completed" and isinstance(event.get("usage"), dict): - usage = event["usage"] - totals["input_tokens"] += int(usage.get("input_tokens") or 0) - totals["output_tokens"] += int(usage.get("output_tokens") or 0) - totals["cache_read_tokens"] += int(usage.get("cached_input_tokens") or 0) - totals["reasoning_output_tokens"] += int(usage.get("reasoning_output_tokens") or 0) - except Exception: - return 0 - if not thread_id or not any(totals.values()): - return 0 - record = { - "kind": "session", "session_id": thread_id, "model": model, - "duration_s": None, **totals, "cache_creation_tokens": None, - "cost_usd": None, "children_sessions": None, - } - emit = root / ".tfcore" / "utils" / "tf-emit.sh" - env = os.environ.copy() - env.update(TF_HARNESS="codex", TF_PROJECT_DIR=str(root), CLAUDE_PROJECT_DIR=str(root)) - try: - subprocess.run(["bash", str(emit), "sessions"], input=json.dumps(record), text=True, - timeout=10, env=env, check=False) - except Exception: - pass - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.tfcore/utils/tf-devguide-list.py b/.tfcore/utils/tf-devguide-list.py index ee2b0dc..e19d4d6 100644 --- a/.tfcore/utils/tf-devguide-list.py +++ b/.tfcore/utils/tf-devguide-list.py @@ -47,7 +47,7 @@ def cfg(key, default): return default -PRUNE = {"bin", "obj", "node_modules", ".git", ".artifacts", "OldDocs", "dist", ".tfcore", ".claude", ".opencode", ".codex", "packages", "TestResults"} +PRUNE = {"bin", "obj", "node_modules", ".git", ".artifacts", "OldDocs", "dist", ".tfcore", ".claude", ".opencode", "packages", "TestResults"} def walk(root, exts, skip_samples=False): diff --git a/.tfcore/utils/tf-emit.sh b/.tfcore/utils/tf-emit.sh index 5179345..d27e92e 100644 --- a/.tfcore/utils/tf-emit.sh +++ b/.tfcore/utils/tf-emit.sh @@ -495,11 +495,8 @@ def _detect_harness(): # the OTHER harness inherits that harness's markers (e.g. OpenCode started # from a Claude Code bash still carries CLAUDE_PROJECT_DIR). tf = os.environ.get("TF_HARNESS") - if tf in ("claude-code", "opencode", "codex"): + if tf in ("claude-code", "opencode"): return tf - for k in ("CODEX_THREAD_ID", "CODEX_SESSION_ID"): - if os.environ.get(k): - return "codex" for k in ("CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT", "CLAUDE_CODE_SESSION_ID", "CLAUDE_PROJECT_DIR"): if os.environ.get(k): @@ -527,8 +524,6 @@ def _detect_harness(): name, ppid = os.path.basename(parts[0]), int(parts[1]) if name and "opencode" in name.lower(): return "opencode" - if name and "codex" in name.lower(): - return "codex" if name and "claude" in name.lower(): return "claude-code" pid = ppid @@ -865,7 +860,7 @@ def enrich_run(rec): tier = ROUTING["phases"].get(cmd) if tier and tier != "inherit": rec["tier"] = tier - key = {"claude-code": "claude", "opencode": "opencode", "codex": "codex"}.get(HARNESS) + key = {"claude-code": "claude", "opencode": "opencode"}.get(HARNESS) tm = ROUTING["tiers"].get(tier, {}).get(key) if key else None if tm: rec["tier_model"] = tm diff --git a/.tfcore/utils/tf-goal.sh b/.tfcore/utils/tf-goal.sh index e44abe3..f25e11b 100644 --- a/.tfcore/utils/tf-goal.sh +++ b/.tfcore/utils/tf-goal.sh @@ -10,8 +10,8 @@ # bash .tfcore/utils/tf-goal.sh [options] @goal.md # # Options -# --harness claude|opencode|codex default: claude -# --model claude: --model; opencode: -m; codex: -m +# --harness claude|opencode default: claude +# --model claude: --model; opencode: -m # --buffer-min minutes added after a stated limit-reset time (default 15) # --probe-min limit hit but NO reset time parseable → fire a one-turn probe every n # minutes until the API answers again, then resume (default 15) @@ -52,13 +52,8 @@ # claude -p "" --permission-mode bypassPermissions --output-format stream-json --verbose # resume: claude -p --resume "" (fallback: --continue) # opencode run --auto "" resume: opencode run --auto -c "" -# codex exec --json --sandbox workspace-write -c approval_policy="never" "" -# NB: `--ask-for-approval` is NOT a `codex exec` flag (verified against -# codex-cli 0.149.1: "error: unexpected argument '--ask-for-approval'", # exit 2). The approval policy is set as a config override instead. -# resume: codex exec resume "" --json # Override command lines with TF_GOAL_CLAUDE_FLAGS / TF_GOAL_OPENCODE_FLAGS / -# TF_GOAL_CODEX_FLAGS. set -u @@ -92,7 +87,7 @@ if [[ -z "$APP_DIR" || ( -z "$GOAL_ARG" && $RESUME -eq 0 ) ]]; then fi APP_DIR="$(cd "$APP_DIR" 2>/dev/null && pwd)" || { echo "no such dir: $1" >&2; exit 2; } [[ -d "$APP_DIR/.tfcore" ]] || { echo "$APP_DIR has no .tfcore/ — scaffold it first" >&2; exit 2; } -case "$HARNESS" in claude|opencode|codex) ;; *) echo "--harness must be claude|opencode|codex" >&2; exit 2 ;; esac +case "$HARNESS" in claude|opencode) ;; *) echo "--harness must be claude|opencode" >&2; exit 2 ;; esac command -v python3 >/dev/null 2>&1 || { echo "python3 is required" >&2; exit 2; } STATE_DIR="$APP_DIR/.tfcore/.session"; mkdir -p "$STATE_DIR" @@ -156,9 +151,8 @@ fi # ---------------------------------------------------------------- prompts # PREAMBLE IS HARNESS-NEUTRAL. Every claim in it must hold for claude AND opencode -# AND codex, because all three are sent this text verbatim. Anything true of only -# one harness goes in that harness's block below (see the `codex` note after -# FIRST_PROMPT) — never in here. The 2026-08-28 Codex adapter review put Codex's +# because both are sent this text verbatim. Anything true of only +# one harness goes in that harness's block below — never in here. The 2026-08-28 review put its # strict no-git policy into this shared text, which then told Claude and OpenCode # goal runs to avoid read-only git that their own hook allows; nothing written down # had said the preamble was shared, so this comment is that rule. @@ -183,12 +177,6 @@ FIRST_PROMPT="$PREAMBLE THE GOAL: $GOAL" -if [[ "$HARNESS" == codex ]]; then - FIRST_PROMPT="$FIRST_PROMPT - -CODEX POLICY NOTE: \`.codex/rules/techieflow.rules\` forbids every git/gh command even in YOLO mode, read-only diagnostics included. Use working-tree files and framework artifacts; do not attempt read-only git." -fi - # ---------------------------------------------------------------- harness command harness_cmd() { # $1 = first|resume ; prints the argv via NUL-separated echo local kind="$1" prompt @@ -212,19 +200,12 @@ harness_cmd() { # $1 = first|resume ; prints the argv via NUL-separated echo fi CMD+=("$prompt") else - if [[ "$kind" == resume && -n "$SESSION_ID" ]]; then - CMD=(codex exec resume "$SESSION_ID" "$prompt" --json) - else - CMD=(codex exec --json --sandbox workspace-write -c approval_policy="never") - [[ -n "$MODEL" ]] && CMD+=(-m "$MODEL") - local tier effort - tier="$(bash "$APP_DIR/.tfcore/utils/tf-harness.sh" tier build-phase)" - effort="$(bash "$APP_DIR/.tfcore/utils/tf-harness.sh" effort "$tier")" - [[ -n "$effort" ]] && CMD+=(-c "model_reasoning_effort=\"$effort\"") - # shellcheck disable=SC2206 - [[ -n "${TF_GOAL_CODEX_FLAGS:-}" ]] && CMD+=($TF_GOAL_CODEX_FLAGS) - CMD+=("$prompt") + CMD=(opencode run --auto) + [[ -n "$MODEL" ]] && CMD+=(-m "$MODEL") + if [[ "$kind" == resume ]]; then + if [[ -n "$SESSION_ID" ]]; then CMD+=(-s "$SESSION_ID"); else CMD+=(-c); fi fi + CMD+=("$prompt") fi } @@ -347,7 +328,7 @@ out("IDLE", "no sentinel") PY } -extract_session_id() { # from a cycle's output file (Claude/OpenCode/Codex JSONL) +extract_session_id() { # from a cycle's output file (Claude/OpenCode JSONL) python3 - "$1" <<'PY' 2>/dev/null import sys, json, re sid = "" @@ -375,10 +356,8 @@ probe_until_clear() { n=$(( n + 1 )) if [[ "$HARNESS" == claude ]]; then ( cd "$APP_DIR" && claude -p --max-turns 1 --output-format text "Reply with the single word OK." ) > "$pout" 2>&1; prc=$? - elif [[ "$HARNESS" == opencode ]]; then - ( cd "$APP_DIR" && opencode run --auto "Reply with the single word OK." ) > "$pout" 2>&1; prc=$? else - ( cd "$APP_DIR" && codex exec --json --sandbox read-only -c approval_policy="never" "Reply with the single word OK." ) > "$pout" 2>&1; prc=$? + ( cd "$APP_DIR" && opencode run --auto "Reply with the single word OK." ) > "$pout" 2>&1; prc=$? fi if [[ $prc -eq 0 ]] && ! grep -qiE 'usage limit|hit your limit|rate[ _-]?limit|limit (has been )?(reached|exceeded)|too many requests|\b429\b|overloaded|weekly limit|resets? (at|in)\b' "$pout"; then log "probe #$n OK"; return 0 @@ -520,9 +499,6 @@ while :; do [[ $CYCLE -eq 1 && -f "$APP_DIR/.tfcore/utils/tf-phase.sh" ]] && bash "$APP_DIR/.tfcore/utils/tf-phase.sh" goal "$(basename "$APP_DIR")" >/dev/null 2>&1 || true run_cycle SID="$(extract_session_id "$OUT")"; [[ -n "$SID" ]] && { SESSION_ID="$SID"; state_set session_id "$SID"; } - if [[ "$HARNESS" == codex ]]; then - python3 "$APP_DIR/.tfcore/utils/tf-codex-telemetry.py" "$APP_DIR" "$OUT" || true - fi KIND=resume if [[ -f "$DONE" ]]; then continue; fi diff --git a/.tfcore/utils/tf-harness.sh b/.tfcore/utils/tf-harness.sh index b38a7c5..8040ab0 100644 --- a/.tfcore/utils/tf-harness.sh +++ b/.tfcore/utils/tf-harness.sh @@ -6,7 +6,7 @@ # scripts call this instead of hard-coding either harness's dialect. # # USAGE -# tf-harness.sh detect -> claude-code | opencode | codex | unknown +# tf-harness.sh detect -> claude-code | opencode | unknown # tf-harness.sh root -> project root path # tf-harness.sh enabled -> true | false (routing.yaml flag) # tf-harness.sh tier -> frontier | standard | economy | inherit @@ -42,8 +42,7 @@ _root() { # --- detect ---------------------------------------------------------------- _detect() { - case "${TF_HARNESS:-}" in claude-code|opencode|codex) printf '%s' "$TF_HARNESS"; return ;; esac - if [[ -n "${CODEX_THREAD_ID:-}${CODEX_SESSION_ID:-}" ]]; then printf 'codex'; return; fi + case "${TF_HARNESS:-}" in claude-code|opencode) printf '%s' "$TF_HARNESS"; return ;; esac if [[ -n "${CLAUDECODE:-}${CLAUDE_CODE_ENTRYPOINT:-}${CLAUDE_CODE_SESSION_ID:-}${CLAUDE_PROJECT_DIR:-}" ]]; then printf 'claude-code'; return fi @@ -58,7 +57,6 @@ _detect() { ppid="$(printf '%s' "${stat##*) }" | awk '{print $2}')" case "$name" in *opencode*) printf 'opencode'; return ;; - *codex*) printf 'codex'; return ;; *claude*) printf 'claude-code'; return ;; esac pid="$ppid" @@ -92,7 +90,6 @@ _model() { [[ -f "$f" ]] || { printf 'inherit'; return; } local key="claude" [[ "$harness" == "opencode" ]] && key="opencode" - [[ "$harness" == "codex" ]] && key="codex" awk -v tier="$tier" -v key="$key" ' /^tiers:/ { in_tiers=1; next } /^[a-z]+:/ { in_tiers=0 } @@ -135,13 +132,11 @@ case "$CMD" in H="$(_detect)" case "$KIND" in task) - if [[ "$H" == "codex" ]]; then printf 'Use the $techieflow-%s skill with arguments: %s\n' "${NAME%-phase}" "$ARGS" - elif [[ "$H" == "opencode" ]]; then printf '/techieflow:tasks:%s %s\n' "$NAME" "$ARGS" + if [[ "$H" == "opencode" ]]; then printf '/techieflow:tasks:%s %s\n' "$NAME" "$ARGS" else printf '/TechieFlow:tasks:%s %s\n' "$NAME" "$ARGS"; fi ;; agent) - if [[ "$H" == "codex" ]]; then printf 'Delegate to the `%s` Codex subagent with: %s\n' "$NAME" "$ARGS" - elif [[ "$H" == "opencode" ]]; then printf 'opencode run --agent %s "%s" (TUI: Tab to %s, then type: %s)\n' "$NAME" "$ARGS" "$NAME" "$ARGS" + if [[ "$H" == "opencode" ]]; then printf 'opencode run --agent %s "%s" (TUI: Tab to %s, then type: %s)\n' "$NAME" "$ARGS" "$NAME" "$ARGS" else printf '/TechieFlow:agents:%s %s\n' "$NAME" "$ARGS"; fi ;; *) echo "usage: tf-harness.sh invoke [args]" >&2; exit 2 ;; diff --git a/.tfcore/utils/tf-mockups-locate.py b/.tfcore/utils/tf-mockups-locate.py index e341d7e..e9d0ca5 100644 --- a/.tfcore/utils/tf-mockups-locate.py +++ b/.tfcore/utils/tf-mockups-locate.py @@ -17,7 +17,7 @@ import sys NAMES = {"mockup", "mockups", "wireframe", "wireframes", "designs", "screens", "ui-mockups", "mock-ups"} -SKIP_DIRS = {".tfcore", ".claude", ".opencode", ".codex", ".agents", ".git", "node_modules", "bin", "obj", +SKIP_DIRS = {".tfcore", ".claude", ".opencode", ".git", "node_modules", "bin", "obj", "OldDocs", "tests", "wwwroot"} EXT = (".html", ".htm", ".png", ".jpg", ".jpeg", ".svg") diff --git a/.tfcore/utils/tf-routing-bind.sh b/.tfcore/utils/tf-routing-bind.sh index 0c9f6e4..17b545e 100644 --- a/.tfcore/utils/tf-routing-bind.sh +++ b/.tfcore/utils/tf-routing-bind.sh @@ -16,9 +16,6 @@ # model-only agent entries merge, command # entries must be complete → they carry # template+description+model) -# Codex: -# .codex/agents/*.toml + .agents/skills/techieflow-*/SKILL.md, generated -# by tf-codex-bind.py. Subagent model/effort is pinned when routing is on; # skills remain thin loaders and do not claim to switch the main thread. # # When `enabled: false` (the default) every generated artifact is REMOVED, using @@ -221,5 +218,4 @@ with open(manifest_path, "w", encoding="utf-8", newline="\n") as fh: print("tf-routing-bind: routing enabled — %d artifact(s) generated (%d stale removed)" % (len(generated), removed)) PY -python3 "$SELF_DIR/tf-codex-bind.py" "$ROOT" || echo "tf-routing-bind: warning: Codex bindings were not refreshed" >&2 exit 0 diff --git a/.tfcore/utils/tf-routing.sh b/.tfcore/utils/tf-routing.sh index 75074da..8f0a8f7 100644 --- a/.tfcore/utils/tf-routing.sh +++ b/.tfcore/utils/tf-routing.sh @@ -8,7 +8,7 @@ # bash .tfcore/utils/tf-routing.sh set-model # e.g. set-model economy opencode opencode-go/deepseek-v4-flash # e.g. set-model frontier claude opus -# e.g. set-model standard codex gpt-5.6-terra +# e.g. set-model standard opencode openai/gpt-5.6-terra # bash .tfcore/utils/tf-routing.sh bind re-generate bindings after editing routing.yaml by hand # bash .tfcore/utils/tf-routing.sh set-escalation # e.g. set-escalation fix-issues 2 frontier @@ -90,12 +90,12 @@ PY set-model) TIER="${2:-}"; HARNESS="${3:-}"; MODEL="${4:-}" case "$TIER" in frontier|standard|economy) ;; *) - echo "usage: tf-routing.sh set-model " >&2; exit 2 ;; + echo "usage: tf-routing.sh set-model " >&2; exit 2 ;; esac - case "$HARNESS" in claude|opencode|codex) ;; *) - echo "harness must be claude, opencode, or codex" >&2; exit 2 ;; + case "$HARNESS" in claude|opencode) ;; *) + echo "harness must be claude or opencode" >&2; exit 2 ;; esac - [[ -n "$MODEL" ]] || { echo "usage: tf-routing.sh set-model " >&2; exit 2; } + [[ -n "$MODEL" ]] || { echo "usage: tf-routing.sh set-model " >&2; exit 2; } python3 - "$RY" "$TIER" "$HARNESS" "$MODEL" <<'PY' import sys p, tier, harness, model = sys.argv[1:5] @@ -199,7 +199,7 @@ print() print("Tier models:") for t in ("frontier", "standard", "economy"): m = cfg["tiers"].get(t, {}) - print(" %-9s claude: %-10s opencode: %-36s codex: %s" % (t, m.get("claude", "-"), m.get("opencode", "-"), m.get("codex", "-"))) + print(" %-9s claude: %-10s opencode: %s" % (t, m.get("claude", "-"), m.get("opencode", "-"))) print() print("Phases by tier:") for t in ("frontier", "standard", "economy", "inherit"): @@ -226,7 +226,6 @@ if cfg["enabled"]: print("Invoke routed phases as:") print(" OpenCode: /techieflow:tasks: (same commands as always — model now pinned)") print(" Claude Code: /tf: (new wrappers; old commands still work, unrouted)") - print(" Codex: $techieflow- (main-thread model inherited; delegated roles are pinned)") print() print("WHERE YOU SEE IT — opening the TUI looks UNCHANGED on purpose: your normal chat") print("(the default 'build' agent) stays on YOUR selected model. Routing becomes visible when:") diff --git a/CodexChanges.md b/CodexChanges.md deleted file mode 100644 index 63351d3..0000000 --- a/CodexChanges.md +++ /dev/null @@ -1,489 +0,0 @@ -# TechieFlow changes required for Codex - -**Assessment date:** 2026-08-24 -**Scope:** the repository's canonical `.tfcore/` framework, its Claude Code and OpenCode adapters, the three scaffold/update scripts, routing, telemetry, unattended goal mode, library personas, and the current official Codex feature set. - -**Implementation status (reviewed 2026-08-28):** the repository changes described here are implemented. A follow-up audit against `codex-cli 0.150.1` and the current official Codex manual corrected nested-directory hook launch, protected-file deletion handling, and the unattended approval example below. The public command and behavior references are synchronized in `README.md`, `WORKFLOW.html`, `.tfcore/user-guide.md`, and `docs/TechieFlow-Routing-Guide.md`; the durable session record is in `WorkFlow-Context.md`. The acceptance checklist below remains a release-validation checklist for exercising the adapter in real applications, not a list of missing source changes. - -## 1. Executive conclusion - -TechieFlow can run on Codex, including its specialist agents, build/verify fan-out, mechanical write guards, MCP-backed tools, model tiers, and non-interactive runs. The workflow content in `.tfcore/` does not need to be rewritten wholesale. - -It does need a third harness adapter. Copying either the Claude or OpenCode integration unchanged will not work: - -- Codex uses root/nested `AGENTS.md` for durable repository instructions. -- Reusable, repository-shared commands should be Codex skills under `.agents/skills/`; deprecated custom prompts are user-local and are the wrong distribution mechanism. -- Specialist subagents are project TOML files under `.codex/agents/`. -- Project settings and lifecycle hooks belong in `.codex/config.toml` and/or `.codex/hooks.json` and run only after the project and hook definitions are trusted. -- Scripted execution uses `codex exec`, with `--json` for event/usage capture and `codex exec resume` for continuation. - -The recommended implementation is therefore: - -```text -.tfcore/ canonical workflow content (keep) - | - +-- Claude adapter existing .claude/ - +-- OpenCode adapter existing opencode.jsonc + .opencode/ - +-- Codex adapter new .codex/ + .agents/skills/ + AGENTS.md -``` - -## 2. What was reviewed - -The assessment started with `WorkFlow-Context.md` and then inspected the repository-owned code and documentation, excluding generated/vendor contents under `.opencode/node_modules/`. In particular: - -- canonical agents, tasks, templates, checklists, data, hooks, telemetry and utilities under `.tfcore/`; -- `.claude/commands/TechieFlow/`, `.claude/settings.json`, and the library command shims; -- `opencode.jsonc`, `.opencode/command/`, and `.opencode/plugin/techieflow.js`; -- `scaffold-brownfield.sh`, `scaffold-greenfield.sh`, and `update-framework.sh`; -- `tf-harness.sh`, `tf-routing.sh`, `tf-routing-bind.sh`, `tf-goal.sh`, `tf-yolo.sh`, and `tf-emit.sh`; -- the capability, coupling, adapter, routing, telemetry, deployment, and decision documents. - -Current Codex behavior was checked against the official Codex manual fetched on 2026-08-24. Relevant official pages are [AGENTS.md](https://learn.chatgpt.com/docs/agent-configuration/agents-md), [skills](https://learn.chatgpt.com/docs/build-skills), [subagents](https://learn.chatgpt.com/docs/agent-configuration/subagents), [hooks](https://learn.chatgpt.com/docs/hooks), [configuration](https://learn.chatgpt.com/docs/config-file/config-reference), [MCP](https://learn.chatgpt.com/docs/extend/mcp), and [non-interactive mode](https://learn.chatgpt.com/docs/non-interactive-mode). - -## 3. Capability mapping - -| TechieFlow concern | Claude/OpenCode implementation now | Codex implementation | -|---|---|---| -| Always-loaded operating rules | `CLAUDE.md`, OpenCode `instructions`, root `AGENTS.md` | Root `AGENTS.md`; nested overrides only where needed | -| User-invocable workflow commands | Claude command mirrors; OpenCode config commands | Repository skills in `.agents/skills//SKILL.md` | -| Specialist personas | Claude command/agent files; OpenCode configured agents | `.codex/agents/*.toml` custom agents | -| Builder/test/explorer fan-out | Task/Agent tools | Native Codex subagent tools; explicitly enabled by skill/`AGENTS.md` instructions | -| Per-subagent model and effort | Claude/OpenCode agent config | `model` and `model_reasoning_effort` in custom-agent TOML | -| Per-phase model | generated command binding | launcher-selected `codex exec -m ... -c model_reasoning_effort=...`, or phase-specific custom agent | -| Git/status/verify enforcement | Claude `PreToolUse`; OpenCode JS bridge | Native Codex `PreToolUse` hooks plus Codex exec-policy rules | -| Session lifecycle | Claude hooks; OpenCode plugin events | Native `SessionStart`, `SessionEnd`, `SubagentStart`, `SubagentStop`, `Stop` hooks | -| Token telemetry | transcript parser / OpenCode events | `codex exec --json` `turn.completed.usage`; optional session rollout/OTel adapter for interactive sessions | -| Unattended runs | `tf-goal.sh` drives `claude -p` or `opencode run` | Extend it to drive `codex exec` and `codex exec resume` | -| External tools | harness-specific MCP config | `[mcp_servers.*]` in Codex config or a distributable Codex plugin | -| Distribution | scaffolded hidden harness folders | Scaffold `.codex/`, `.agents/skills/`, and Codex additions to `AGENTS.md` | - -## 4. Required repository changes - -### 4.1 Add a repository-scoped Codex configuration - -Create `.codex/config.toml`. It should contain only repository-safe settings; authentication, provider definitions, profiles, notifications, and OTel export belong in the user's `~/.codex/config.toml` because Codex ignores those keys in project-local config. - -The project config should: - -- enable multi-agent support; -- set an appropriate maximum concurrent thread count for TechieFlow fan-outs; -- register each custom role by description and config file; -- configure `workspace-write` as the normal sandbox, with only the app repository and genuinely required external directories writable; -- use `on-request` for ordinary interactive sessions; unattended launches should override this with an explicit non-interactive policy; -- configure project hooks, preferably by referencing `.codex/hooks.json` rather than duplicating hook definitions; -- contain MCP entries only when they are portable and non-secret. - -Illustrative shape (model IDs are deliberately not hard-coded here; generate them from `routing.yaml`): - -```toml -[agents] -enabled = true -max_concurrent_threads_per_session = 8 - -[agents.flow_master] -description = "TechieFlow orchestrator and workflow router." -config_file = "agents/flow-master.toml" - -[agents.analyst] -description = "Business analysis, discovery, BRDs, and mockups." -config_file = "agents/analyst.toml" - -[agents.architect] -description = "Architecture and technical design specialist." -config_file = "agents/architect.toml" - -[agents.verifier] -description = "Independent requirement verifier and evidence grader." -config_file = "agents/verifier.toml" -``` - -Do not put `approval_policy = "never"` into the shared project default. That would silently reject approval requests during normal interactive use. Pass it only in a controlled unattended invocation after the sandbox and rules are correct. - -### 4.2 Convert the four canonical personas to Codex custom agents - -Generate these project files: - -```text -.codex/agents/flow-master.toml -.codex/agents/analyst.toml -.codex/agents/architect.toml -.codex/agents/verifier.toml -``` - -Also generate the routed implementation roles already emitted for Claude/OpenCode when routing is enabled: - -```text -.codex/agents/tf-builder.toml -.codex/agents/tf-test-writer.toml -.codex/agents/tf-explorer.toml -.codex/agents/trblazeui.toml # only when the library persona exists -.codex/agents/techierag.toml # only when the library persona exists -``` - -Each file requires `name`, `description`, and `developer_instructions`. Convert the YAML persona content in `.tfcore/agents/*.md` into plain developer instructions; do not ask the Codex subagent to “activate” itself, greet, print help, or halt. Those activation rituals were designed for command-driven primary personas and are counterproductive in a spawned Codex worker. - -Preserve these behavioral requirements in the converted instructions: - -- dependency paths resolve under `.tfcore/`; -- task files are executable workflow specifications; -- the git/gh prohibition; -- verifier independence and evidence requirements; -- triage is analyze-only; -- smoke/build/verify must be executed by the agent; -- checklist and PROJECT-STATUS update gates; -- library feedback and runtime-observation rules. - -Codex custom agents may set `model`, `model_reasoning_effort`, `sandbox_mode`, MCP servers, and skill configuration. Use `read-only` for `tf-explorer` and other genuinely read-only roles. Do not give `verifier` read-only mode because it must update verdict artifacts. - -### 4.3 Convert tasks into repository skills - -Create one skill directory for every user-facing TechieFlow workflow. At minimum: - -```text -.agents/skills/techieflow-day1-brownfield/SKILL.md -.agents/skills/techieflow-day1-greenfield/SKILL.md -.agents/skills/techieflow-amend-docs/SKILL.md -.agents/skills/techieflow-author-brd/SKILL.md -.agents/skills/techieflow-mockups/SKILL.md -.agents/skills/techieflow-split-brd/SKILL.md -.agents/skills/techieflow-build/SKILL.md -.agents/skills/techieflow-verify/SKILL.md -.agents/skills/techieflow-fix-issues/SKILL.md -.agents/skills/techieflow-triage-issues/SKILL.md -.agents/skills/techieflow-devguide/SKILL.md -.agents/skills/techieflow-productguide/SKILL.md -.agents/skills/techieflow-handoff/SKILL.md -.agents/skills/techieflow-refresh-status/SKILL.md -.agents/skills/techieflow-generate-html/SKILL.md -.agents/skills/techieflow-metrics/SKILL.md -.agents/skills/techieflow-yolo/SKILL.md -``` - -Keep `.tfcore/tasks/*.md` canonical. A generated `SKILL.md` should be a thin loader that: - -1. declares a precise name and trigger description; -2. reads the complete corresponding `.tfcore/tasks/.md` when invoked; -3. reads only the dependencies named by that task; -4. states which custom agent should own or assist with the work; -5. preserves interactive elicitation unless YOLO mode is active; -6. translates old invocation prose (`*verify`, `/TechieFlow:...`, `/flow-...`) into the Codex skill name or natural-language delegation. - -Do not use `~/.codex/prompts` as the main port. Codex custom prompts are deprecated, user-local, top-level-only, and unsuitable for a framework deployed into multiple application repositories. - -The framework can keep documenting the familiar `*command` vocabulary as a conceptual alias, but Codex will not register those literal star commands. User-facing Codex examples should use either `$techieflow-verify ...` (explicit skill invocation where supported) or plain language such as “Use the techieflow-verify skill for AppName, scope all.” - -### 4.4 Make `AGENTS.md` the Codex operating contract - -The day-1 tasks already create a harness-neutral root `AGENTS.md`; expand its template (`.tfcore/templates/v4custom/app-agents-md-tmpl.md`) so it is sufficient for Codex without loading `CLAUDE.md`. - -Add a concise “TechieFlow under Codex” section that tells Codex to: - -- read `.tfcore/core-config.yaml` and the relevant skill/task, not the whole framework, before a workflow; -- use `.agents/skills/techieflow-*` for framework phases; -- delegate only when the user or the applicable skill/`AGENTS.md` explicitly requests subagents (this matches current Codex delegation behavior); -- use the registered `tf-builder`, `tf-test-writer`, `tf-explorer`, `trblazeui`, and `techierag` roles where the task specifies them; -- never run git or gh; -- obey the artifact-location, local-only verification, status, and verified-verdict rules; -- treat `.tfcore/` as canonical and `.codex/`/`.agents/skills/` as generated adapter output. - -Keep `AGENTS.md` short enough to remain always-loaded. Detailed process belongs in skills and `.tfcore/tasks`, using Codex's progressive disclosure. - -### 4.5 Port mechanical guards to native Codex hooks - -Create `.codex/hooks.json` with these mappings: - -| Codex event/matcher | Existing script or new adapter | Purpose | -|---|---|---| -| `PreToolUse`, `Bash` | `.tfcore/hooks/block-git.sh` + `guard-artifacts.sh` through a payload adapter | Block all git/gh access and destructive forms; block repo-root `test-results*` / `scripts-*` artifact dirs (2026-08-25) | -| `PreToolUse`, `Edit|Write|apply_patch` | `guard-status.sh` and `guard-verify.sh` through a payload adapter | Mechanically validate status/checklist writes | -| `Stop` | `guard-status-html.sh` through the adapter (`codex-adapter.py stop`) | Refuse to end the turn while `PROJECT-STATUS.html` is older than `PROJECT-STATUS.md` or missing (2026-08-25); honours `stop_hook_active` | -| `SessionStart` and `UserPromptSubmit` | Codex-aware `session-pointer.sh` | Maintain a Codex session pointer | -| `SessionStart` | `sweep-artifacts.sh` through the adapter (`codex-adapter.py session-start`, skipped on the UserPromptSubmit re-fire; the script itself throttles to once/hour) | Delete run material under `tests/.artifacts/` and `.verify/` older than the retention window (default 7d) and banned repo-root legacy dirs (2026-08-26) | -| `SessionEnd` | Codex-aware telemetry adapter | Emit session telemetry when reliable usage is available | -| `SubagentStart`/`SubagentStop` | optional telemetry adapter | Track child sessions without confusing them with the main session | - -Codex hook payloads are similar in spirit but must not be assumed byte-identical to Claude payloads. Add `.tfcore/hooks/codex-adapter.sh` (or a small Python program) that converts Codex JSON into the existing Claude-shaped contract. Set at least: - -```text -TF_HARNESS=codex -TF_PROJECT_DIR= -CLAUDE_PROJECT_DIR= # compatibility only for existing scripts -TF_SESSION_ID= -``` - -Important implementation details: - -- Codex treats unified exec as `Bash` and lets `apply_patch` match `apply_patch`, `Edit`, or `Write`. -- Therefore, unlike the current OpenCode plugin, Codex can mechanically inspect and block an `apply_patch` that changes PROJECT-STATUS or a checklist. Do not ban `apply_patch` outright; normalize its patch payload and run the guards. -- Multiple matching hooks launch concurrently. If guard ordering matters, put the status and verify checks behind one adapter command rather than two independent handlers. -- Project hooks are skipped until the repository and the exact hook definitions are trusted. The scaffold/update output must tell the owner to trust the repo and review `/hooks` after installation or any hook change. -- Hooks should continue to fail open for telemetry, but policy guards should fail closed on malformed protected-file writes. The current universal fail-open posture is too weak for a new adapter that can validate the payload natively. - -### 4.6 Add Codex exec-policy rules - -Hooks are defense in depth, not the only permission layer. Create project rules under the Codex rules location used by the installed client (for example `.codex/rules/techieflow.rules`) to reject git and gh commands before execution. - -The rules must cover command segments, not merely raw-string prefixes, and should distinguish: - -- always forbidden: every `git` command and state-changing `gh` command required by the framework's “GIT IS MANUAL” rule; -- harmless diagnostics that TechieFlow still deliberately forbids for agent consistency; -- destructive filesystem commands, which remain approval-gated outside YOLO; -- normal build/test/runtime commands, which should be allowed inside the workspace sandbox. - -Verify the exact rule syntax against the installed Codex version during implementation; do not mechanically translate Claude `Bash(pattern*)` or OpenCode wildcard syntax into Codex rules. - -### 4.7 Extend harness detection and invocation - -Update `.tfcore/utils/tf-harness.sh`: - -- detection result set: `claude-code | opencode | codex | unknown`; -- recognize `TF_HARNESS=codex` first; -- recognize Codex-specific process/environment/session evidence only as fallback; -- add `model codex` using a new `models..codex` key; -- map `invoke task ` to the relevant Codex skill instruction; -- map `invoke agent ` to “delegate to the `` Codex subagent” rather than inventing a slash command; -- return `.tfcore/.session/codex.json` from `session`. - -Update every whitelist currently accepting only `claude-code` or `opencode`, notably `tf-emit.sh`, `tf-yolo.sh`, `tf-routing.sh`, and `tf-goal.sh`. - -### 4.8 Extend model routing - -Add a Codex model column to `.tfcore/routing.yaml`: - -```yaml -models: - frontier: - claude: - opencode: - codex: - standard: - claude: - opencode: - codex: - economy: - claude: - opencode: - codex: -``` - -Replace the comment “Claude-only knob” above `effort` with a per-harness mapping or explicitly state that Codex also supports reasoning effort. Codex custom agents use `model_reasoning_effort`. - -Extend `tf-routing-bind.sh` to generate: - -- model/effort fields in `.codex/agents/*.toml` for routed subagents; -- a machine-readable phase launcher map for `tf-goal.sh`/a new `tf-codex.sh`; -- optionally phase-specific orchestrator agents when a phase must force a model in interactive use. - -Do not claim that a repository skill itself forces the current main thread onto a different model. Codex supports model selection for spawned custom agents and the CLI invocation; use those boundaries. For a phase started in an already-running interactive main thread, routing is advisory unless the phase is delegated to a configured role. - -Update `tf-routing.sh` to accept `codex` and validate model IDs without assuming OpenCode's `provider/model` syntax. - -### 4.9 Extend telemetry - -Update the telemetry schema and emitters to accept `harness: codex`. - -For headless/goal runs, use the reliable path: - -```bash -codex exec --json "" -``` - -Parse: - -- `thread.started.thread_id` for the session pointer; -- `turn.completed.usage.input_tokens`; -- `turn.completed.usage.cached_input_tokens`; -- `turn.completed.usage.output_tokens`; -- `turn.completed.usage.reasoning_output_tokens` where the schema is extended to retain it. - -Do not synthesize `cost_usd` from account credits. Leave it null unless the selected authentication/provider supplies an authoritative cost signal. - -For interactive CLI/IDE sessions, choose one explicit strategy and document its fidelity: - -1. parse Codex session rollout JSONL at `SessionEnd`, if the current hook payload exposes a stable path; or -2. configure OTel in the user's global config and ingest its events outside the repository; or -3. record session metadata without token totals and mark the fields null. - -Project-local config cannot set OTel routing, so scaffolding must never overwrite a user's telemetry destination. `tf-emit.sh` should add `_window_codex()` only after the session storage shape is verified against the installed Codex version. - -### 4.10 Extend unattended goal mode - -Update `tf-goal.sh` to accept `--harness codex`. - -Initial cycle: - -```bash -codex exec --json --sandbox workspace-write \ - -c 'approval_policy="never"' \ - -m "" \ - -c 'model_reasoning_effort=""' \ - "" -``` - -Resume cycle: - -```bash -codex exec resume "" --json -``` - -Confirm the installed CLI's option ordering while implementing. Capture the thread ID from JSON instead of scraping prose. Preserve the existing maximum-cycle, idle retry, state-file, and crash-resume controls. - -Codex `--full-auto` is deprecated; use explicit sandbox and approval flags. `danger-full-access` must not be the default. The existing YOLO promise should mean “no elicitation pauses and no avoidable approval prompts inside the configured workspace,” not “unrestricted machine access.” - -Rate-limit recovery needs a Codex-specific parser. If the JSON event does not contain an authoritative reset time, use bounded backoff; do not infer a precise reset window from human-readable text. - -### 4.11 Port library personas - -The NuGet libraries currently deploy `.claude/.md` and `.opencode/command/.md`. Add a Codex payload at the library source of truth, ideally: - -```text -.codex/agents/trblazeui.toml -.codex/agents/techierag.toml -``` - -or install the persona as a repository skill plus a generated custom-agent TOML. Update the NuGet `.targets` files documented in `docs/TechieFlow-Library-Persona-Propagation.md` to deploy the Codex files into consuming apps. - -The framework updater must preserve NuGet-owned Codex persona files exactly as it preserves the Claude/OpenCode library files. Do not generate a Codex role that references a missing persona file, because a missing project config dependency can prevent the role from loading. - -### 4.12 Update scaffold and update scripts - -Modify all three scripts to deploy and report: - -```text -.codex/config.toml -.codex/hooks.json -.codex/agents/*.toml -.codex/rules/techieflow.rules -.agents/skills/techieflow-*/SKILL.md -``` - -Required behavior: - -- `.tfcore/` remains canonical and force-refreshed as today. -- Framework-owned Codex adapter files are refreshed; per-project Codex additions need a documented preservation/merge policy. -- User-global `~/.codex/*` is never written by a project scaffold. -- The script warns that the owner must trust the repository and review changed hooks. -- `--dry-run` shows Codex additions and removals. -- routing-enabled output runs the Codex binding generator; routing-disabled output removes only generated routing fields/files, not user-owned Codex config. -- legacy migrations never delete an existing `.codex/` directory wholesale. -- generated `.gitignore` blocks include `.codex/` and `.agents/skills/` only if the existing product decision remains to keep all deployed framework copies untracked. If teams should share Codex support through Git, reverse that policy deliberately; repository skills and project config are designed to be committed. - -That last choice deserves an explicit decision. Ignoring `.agents/skills/` means every clone needs the scaffold/update step before Codex sees TechieFlow. Committing the adapter makes Codex support available immediately to collaborators but changes the current “framework copies are deployment artifacts” model. - -### 4.13 Update documentation and maintenance contracts - -Update at least: - -- `README.md` and `WORKFLOW.html`: bootstrap, project structure, command examples, permissions, YOLO/goal mode, routing, telemetry, FAQ, and cheat sheet; -- `.tfcore/user-guide.md`: replace the currently incomplete “Codex (CLI & Web)” text with the actual skill/agent/config workflow; -- `docs/Capability-Matrix.md`: add a Codex column; -- `docs/Coupling-Points.md`: classify Codex breaks/degrades/cosmetic differences; -- `docs/Adapter-Design.md`: make the harness boundary three-way; -- `docs/TechieFlow-Routing-Guide.md`: Codex model/effort and interactive-routing caveat; -- telemetry guides/schema: Codex session and usage provenance; -- `WorkFlow-Context.md`: add the implementation entry and update the repo map/maintenance contract; -- `DECISIONS.md`: record the skills-vs-prompts choice, trust behavior, telemetry fidelity, and whether generated Codex files are tracked. - -Search and neutralize two-harness assumptions such as “both harnesses,” “Claude Code or OpenCode,” and enum checks containing only `claude-code|opencode`. - -## 5. Features that Codex cannot support exactly - -These are exact-parity gaps, not necessarily blockers to the framework. - -### 5.1 Literal TechieFlow `*commands` and the existing slash-command namespace - -Codex does not natively register the framework's `*verify`, `/TechieFlow:tasks:verify-phase`, or OpenCode `/techieflow:tasks:*` vocabulary from this repository. Skills provide equivalent explicit/implicit invocation, but the text typed by the user and command-menu presentation differ. - -**Impact:** cosmetic and documentation-level after skills are added. - -### 5.2 A repository skill cannot reliably force the main thread's model for one phase - -Codex can set model/effort on custom subagents and at `codex exec` launch. A skill is reusable instruction content, not a guaranteed turn-scoped model switch for the already-running main agent. - -**Impact:** per-phase routing is exact in scripted runs or delegated phase agents; it is advisory for an in-place interactive main-thread phase. - -### 5.3 Automatic use of subagents without an explicit enabling instruction - -Current Codex releases delegate when the user asks or when an applicable `AGENTS.md`/skill requests it. TechieFlow therefore must place delegation instructions in the build/verify skills. It cannot assume that merely defining agents causes automatic fan-out. - -**Impact:** fully addressable by the generated skills, but not implicit from agent registration alone. - -### 5.4 Trust-free activation of repository hooks and config - -Codex intentionally ignores project `.codex/` config in untrusted projects and requires review/trust of non-managed hook definitions. A scaffold cannot silently activate new or changed policy hooks for a user. - -**Impact:** unavoidable one-time/manual trust checkpoint per repo or changed hook hash. Enterprise managed configuration can remove this checkpoint only through administrator policy. - -### 5.5 OpenCode-style in-process plugin event parity - -Codex does not load `.opencode/plugin/techieflow.js`, and command hooks are process-based rather than an arbitrary OpenCode JavaScript event plugin. Codex hooks cover the important enforcement lifecycle, but there is no reason to expect every OpenCode event object (`message.updated` cost/tokens, `session.idle`, mutable `permission.ask`, `shell.env`) to have an identical Codex callback. - -**Impact:** guards port cleanly; telemetry, YOLO auto-approval, and environment injection need Codex-specific implementations. - -### 5.6 Guaranteed interactive token and cost telemetry identical to OpenCode - -`codex exec --json` exposes authoritative per-turn token usage. Interactive token capture depends on hook/session-log or OTel details and may not expose authoritative monetary cost. ChatGPT credits are not interchangeable with API dollar cost. - -**Impact:** headless run telemetry can be complete for tokens; interactive session and cost fields may be null and must be labeled honestly. - -### 5.7 Unlimited unattended operation through account limits or machine prompts - -No harness can guarantee uninterrupted multi-day execution across account exhaustion, OS credential prompts, reboots, unavailable mobile/device hosts, or administrator policy. Codex can resume threads and a supervisor can retry, but it cannot bypass limits or policy. - -**Impact:** retain bounded retries, durable state, explicit blocked states, and truthful `STATIC-ONLY` degradation. - -### 5.8 Identical behavior across every Codex surface - -Local CLI/IDE/app runs can access the local repository, hooks, local tools, and device bridges according to their sandbox. Codex cloud runs in a hosted environment and cannot automatically reach the owner's WSL-to-Windows `winrun`, LAN Mac Appium host, local NuGet credentials, or already-running services. - -**Impact:** TechieFlow's full MAUI/runtime verification path remains a local Codex workflow. Cloud runs should be documented as planning, review, or static verification unless equivalent infrastructure is explicitly provisioned. - -## 6. Features that remain supported without framework redesign - -The following are already harness-neutral or need only the adapter wiring above: - -- BRD, Architecture, Checklist, PROJECT-STATUS, DevGuide, ProductGuide, UsageGuide, mockup, and HTML artifact formats; -- stable BRD/REQ identifiers and the one-checklist model; -- build → self-smoke → verifier → status/HTML → telemetry sequencing; -- Playwright, Appium, FlaUI, `winrun`, .NET, shell utilities, and performance harnesses, subject to the same host prerequisites; -- local-only verification and `STATIC-ONLY` truthfulness; -- artifact confinement under `tests/.artifacts/`; -- telemetry JSONL streams and provenance separation; -- MCP servers and authenticated external connectors; -- multiple specialist subagents with distinct models, reasoning effort, sandbox modes, skills, and MCP access; -- non-interactive execution, JSONL events, resumable threads, and structured output; -- mechanical pre-tool enforcement for Bash and protected-file edits. - -## 7. Recommended implementation order - -1. Add `codex` to the harness enums and telemetry schema without changing behavior for Claude/OpenCode. -2. Generate root `AGENTS.md` additions and the minimal workflow skills. -3. Generate the four primary custom agents and routed builder/test/explorer roles. -4. Add Codex hooks plus the payload adapter; test git, PROJECT-STATUS, Checklist, and `apply_patch` denial cases. -5. Extend routing and verify model/effort selection in a spawned agent and `codex exec`. -6. Extend `tf-goal.sh` and JSON telemetry using a short disposable goal. -7. Add NuGet library persona deployment. -8. Update all scaffold/update scripts and verify dry-run, first install, repeat install, preserved user config, routing on/off, and hook-change trust messaging. -9. Update the public docs, capability matrix, context, and decisions. -10. Run one brownfield day-1, one routed build fan-out, one full verify, one triage-only run, one interrupted/resumed goal, and one metrics report under Codex before declaring parity. - -## 8. Acceptance checklist for a Codex adapter - -- [ ] A fresh scaffold is recognized by Codex after the owner trusts the repo. -- [ ] Codex discovers every TechieFlow skill without loading every task body into context. -- [ ] The four primary personas and five routed worker roles appear as custom agents. -- [ ] A build skill explicitly fans independent work to the intended roles and waits for results. -- [ ] A verifier remains independent and is the only path that introduces `Verified` verdicts. -- [ ] Direct Bash, compound Bash, and indirect attempts to run `git` or `gh` are blocked. -- [ ] `apply_patch`, edit, and write operations on protected docs all pass through guards. -- [ ] Normal source edits and build/test commands still work inside the workspace sandbox. -- [ ] Routing off inherits the launch model; routing on selects the declared Codex model/effort at the supported boundary. -- [ ] `tf-emit.sh` records `harness: codex` and never fabricates token or cost values. -- [ ] `tf-goal.sh --harness codex` captures a thread ID, resumes it, and survives a killed supervisor process. -- [ ] Scaffold/update is idempotent and preserves project/user-owned Codex settings. -- [ ] Claude Code and OpenCode byte/content parity requirements continue to pass unchanged. -- [ ] README, WORKFLOW, user guide, routing guide, telemetry guide, context, and decisions agree on the supported Codex behavior and limitations. - -## 9. Bottom line - -There is no fundamental Codex blocker for TechieFlow's core lifecycle. The highest-risk work is not the agent prompts; it is the mechanical edge of the adapter: protected-file hook payload normalization, permission rules, interactive telemetry fidelity, model-routing boundaries, and safe merging of generated `.codex/config.toml` with project-owned configuration. Implement and test those as first-class code rather than documenting them as assumptions. diff --git a/DECISIONS.md b/DECISIONS.md index 5789042..ac151de 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1,5 +1,7 @@ # TechieFlow — Decisions +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + ## 2026-08-24 — Codex is the third additive harness Canonical tasks/personas remain in `.tfcore/`. Codex consumes thin repository diff --git a/README.md b/README.md index 3fdb9f1..9fc89e4 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ The scaffolders never touch a file that already exists. The updater force-overwr | Overwritten (the framework wins) | Preserved (your work, never touched) | |---|---| -| `.tfcore/{tasks,templates,agents,standards,utils,hooks,telemetry}/`, `.claude/commands/TechieFlow/`, `.claude/settings.json`, `.opencode/`, `WORKFLOW.html` | `docs/`, `src/`, `tests/`, `PROJECT-STATUS.md`, `CLAUDE.md`, `.editorconfig`, `.tfcore/core-config.yaml`, `.tfcore/routing.yaml`, the root `opencode.jsonc`, the NuGet-deployed library personas | +| `.tfcore/{tasks,templates,agents,standards,utils,hooks,telemetry}/`, `.claude/commands/TechieFlow/`, `.claude/settings.json`, `.opencode/` | `docs/`, `src/`, `tests/`, `PROJECT-STATUS.md`, `CLAUDE.md`, `.editorconfig`, `.tfcore/core-config.yaml`, `.tfcore/routing.yaml`, the root `opencode.jsonc`, the NuGet-deployed library personas | Everything the framework drops into a project is a copy, so the scripts also keep the project's `.gitignore` ignoring those copies, and a second block for machine-generated test material. They never run git themselves: if a framework file was committed before the ignore entry existed, you untrack it yourself once. diff --git a/WORKFLOW.html b/WORKFLOW.html deleted file mode 100644 index 56731d6..0000000 --- a/WORKFLOW.html +++ /dev/null @@ -1,1986 +0,0 @@ - - - - - -TechieFlow: Solo-Dev Delivery Workflow — Claude Code + OpenCode + Codex - - - -
    - - -
    - -

    TechieFlow: Solo-Dev Delivery Workflow

    -
    Compressed TechieFlow process for one-person teams. Two flows (brownfield + greenfield), human-readable BRD + Architecture with Mermaid, project-specific coding standards every agent follows, automated verification via verifier, your own library agents (trblazeui, techierag) baked in, zero-friction permissions.
    - -
    - Root /Volumes/MacD/MyCode/TechieFlow (macOS) · /mnt/c/3AIGenCode/TechieFlow (WSL)  ·  - Harness Claude Code + OpenCode + Codex  ·  - Stack .NET / Blazor / MAUI / TrBlazeUI / TechieRag -
    - - - - -

    Process at a glance — flowcharts quick reference#

    -

    The whole process on one screen. Each box is the exact command to run (agent + *task); the deep sections (§7, §8) explain each step. Loops mean "repeat until the gate passes".

    - -
    A · Greenfield — new app from scratch
    -
    -flowchart TD
    -  A["scaffold-greenfield.sh ."] --> B["/analyst *day1-greenfield"]
    -  B --> M["/analyst *mockups (auto-run at day-1)"]
    -  M --> C{"Approve BRD + Architecture + mockups"}
    -  C -->|"approved"| D["/analyst *split-brd  (creates the one Checklist)"]
    -  D --> E["/flow-master *build-phase  (calls trblazeui + techierag)"]
    -  E --> F["/verifier *verify all  (data + visual gates)"]
    -  F --> G{"All REQ Verified?"}
    -  G -->|"bugs found"| X["/flow-master *fix-issues {App} {screenshots-folder}"]
    -  X --> F
    -  G -->|"UAT/prod bugs — log, don't fix yet"| T["/flow-master *triage-issues {App} {evidence}"]
    -  T --> X
    -  G -->|"yes"| H["/flow-master *handoff-phase"]
    -
    - -
    B · Brownfield — existing app
    -
    -flowchart TD
    -  A["scaffold-brownfield.sh ."] --> B["/analyst *day1-brownfield  (reverse-doc + DevGuide)"]
    -  B --> C{"Was a dev plan migrated into the Checklist?"}
    -  C -->|"no"| D["/analyst *split-brd  (creates the Checklist)"]
    -  C -->|"yes"| E["/flow-master *build-phase"]
    -  D --> E
    -  E --> F["/verifier *verify all"]
    -  F --> G{"All REQ Verified?"}
    -  G -->|"bugs found"| X["/flow-master *fix-issues {App} {screenshots-folder}"]
    -  X --> F
    -  G -->|"UAT/prod bugs — log, don't fix yet"| T["/flow-master *triage-issues {App} {evidence}"]
    -  T --> X
    -  G -->|"yes"| H["/flow-master *handoff-phase"]
    -
    - -
    C · Which command next? — the Build to Verify to Handoff ladder
    -

    Pick the next command from the weakest open REQ in the Checklist. When in doubt, build.

    -
    -flowchart TD
    -  S{"Weakest open REQ in the Checklist?"}
    -  S -->|"any REQ unbuilt (Planned / In Progress / PARTIAL), or built but not testable yet"| B["/flow-master *build-phase {App}"]
    -  S -->|"all built and testable, some not yet Verified"| V["/verifier *verify all {App}"]
    -  S -->|"all REQ Verified"| H["/flow-master *handoff-phase {App}"]
    -
    - -
    D · Recovering a cold / interrupted project
    -
    -flowchart LR
    -  A["Session died mid-phase — PROJECT-STATUS is stale"] --> B["/flow-master *refresh-status {App}"]
    -  B --> C["Rebuilds status from Checklist + files on disk + a fresh build"]
    -  C --> D["Prints the exact next command to resume"]
    -
    - -
    E · Library project (TrBlazeUI / TechieRag) — docs and DevGuide
    -

    A NuGet library is a first-class project: same doc set as an app, DevGuide driven by its demo/sample app.

    -
    -flowchart TD
    -  A["Library repo (TrBlazeUI / TechieRag)"] --> B["/analyst *day1-brownfield  (docs + PROJECT-STATUS)"]
    -  B --> C["/analyst *split-brd  (the Checklist)"]
    -  C --> D["/flow-master *devguide --update"]
    -  D --> E["UI-component library: component-by-component"]
    -  D --> G["Service / SDK library: service-by-service"]
    -
    - - -

    0. WSL bootstrap — DO ONCE, EVER#

    - -

    Run this once per WSL distro. Installs headless-Chromium system libs + the MAUI bridge.

    - -
    On macOS: skip this section — your one-time setup is §0a instead. There is no winrun bridge on a Mac (dotnet and MAUI run natively) and Playwright's Chromium needs no apt libraries.
    - -
    sudo apt-get update && sudo apt-get install -y \
    -  libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 \
    -  libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 \
    -  libcairo2 libasound2 libgtk-3-0 libx11-xcb1
    -
    -mkdir -p ~/bin && cat > ~/bin/winrun << 'SH'
    -#!/usr/bin/env bash
    -WINPATH=$(wslpath -w "$PWD")
    -powershell.exe -NoProfile -Command "cd '$WINPATH'; $*"
    -SH
    -chmod +x ~/bin/winrun
    -grep -q 'HOME/bin' ~/.bashrc || echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
    - -
    Verify: open a new WSL terminal and run which winrun/home/<you>/bin/winrun.
    - -

    OpenCode in WSL — the primary path (since 2026-08-20)

    -

    OpenCode runs natively inside the same WSL distro as Claude Code — OpenCode's own docs recommend WSL over native Windows, and inside WSL it gets the entire runtime harness above (winrun, headless Chromium, Appium) for free. Full rationale, probe evidence, and the crash playbook: docs/OpenCode-Deployment-Guide.md.

    -
    curl -fsSL https://opencode.ai/install | bash
    -grep -q '.opencode/bin' ~/.bashrc || echo 'export PATH="$HOME/.opencode/bin:$PATH"' >> ~/.bashrc
    -opencode auth login
    -
    The PATH line matters: WSL's Windows-interop otherwise resolves opencode to the Windows npm shim (AppData\Roaming\npm\opencode) — the native-Windows Bun build that breaks on large repos. Verify with type -a opencode (the ~/.opencode/bin entry must come first).
    -

    The framework side is automatic: the scaffold/update scripts deploy .opencode/plugin/techieflow.js — the guard bridge that runs the same .tfcore/hooks/ guards Claude Code runs (git ban, PROJECT-STATUS shape, Verified ledger) plus session telemetry with real dollar cost — and a framework-owned .opencode/opencode.jsonc into every app. Check with opencode agent list (the six TechieFlow agents must appear).

    -
    Large repos: the failure historically blamed on Bun is a /mnt/c (9p filesystem) pathology — OpenCode's snapshot walk can take minutes there, while the identical repo on WSL-native ext4 boots in seconds. Typical apps on /mnt/c are fine; genuinely large repos belong on ext4 (or see the watcher tuning in the deployment guide).
    - -
    Everything below is the FALLBACK path (Docker), kept in case WSL ever reproduces the native-Windows crash — skip it on the WSL path. OpenCode in Docker on Windows is different from WSL: a Linux container cannot execute Windows cmd.exe or see the Windows .NET workloads. Do not install a fake cmd.exe. The supplied docs/Dockerfile uses the .NET 10 SDK and deliberately installs no MAUI workloads. Build standard .NET apps in the container; build Windows MAUI Blazor Desktop heads through its SSH-backed winrun bridge to the Windows host. Build and test mobile, iOS, and Mac Catalyst heads natively on a Mac.
    - -

    Containerized OpenCode — one-time Windows host bridge (fallback only)

    -

    This bridge is only required when the container must build or run a Windows-host project (for example, a Windows MAUI head). Blazor/Linux-compatible projects do not need it. The first command uses Windows Update and can take several minutes, but it should not remain at Operation [Running] indefinitely.

    -

    Run each command separately in an elevated PowerShell window. Do not paste the whole block while the first command is still running:

    -
    $cap = Get-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    -$cap.State
    -
    -if ($cap.State -ne 'Installed') {
    -    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    -}
    -
    -Get-Service sshd -ErrorAction SilentlyContinue
    -Start-Service sshd
    -Set-Service -Name sshd -StartupType Automatic
    -
    -if (-not (Get-NetFirewallRule -Name OpenSSH-Server-In-TCP -ErrorAction SilentlyContinue)) {
    -    New-NetFirewallRule -Name OpenSSH-Server-In-TCP -DisplayName "OpenSSH Server (sshd)" -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
    -}
    -

    If Add-WindowsCapability is still running after about 10 minutes, press Ctrl+C. It has not reached the SSH commands. Check the servicing state and Windows Update source, then retry:

    -
    Get-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    -Get-WinEvent -LogName 'Microsoft-Windows-DISM/Operational' -MaxEvents 20 | Format-Table TimeCreated,Id,LevelDisplayName,Message -Wrap
    -Get-Service wuauserv,bits,TrustedInstaller | Format-Table Name,Status,StartType
    -# Optional: use Settings > System > Optional features > View features and install
    -# "OpenSSH Server" if the capability command cannot obtain its payload.
    -

    When the capability reports Installed, open a normal PowerShell window as the same non-administrator Windows user that will run the launcher. Copy and paste this as one complete line. It is safe to run again:

    -
    $ssh="$env:USERPROFILE\.ssh"; New-Item -ItemType Directory -Force $ssh | Out-Null; if (-not (Test-Path "$ssh\opencode-docker")) { ssh-keygen -t ed25519 -f "$ssh\opencode-docker" -N "" }; $publicKey=(Get-Content "$ssh\opencode-docker.pub" -Raw).Trim(); $auth="$ssh\authorized_keys"; if (-not (Test-Path $auth)) { Set-Content -Path $auth -Value $publicKey } elseif ((Get-Content $auth) -notcontains $publicKey) { Add-Content -Path $auth -Value $publicKey }
    -

    Verify the bridge before starting Docker. This test deliberately disables password fallback. A successful test prints the Windows host's dotnet --info output and never asks for a password:

    -
    ssh -o BatchMode=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -i "$env:USERPROFILE\.ssh\opencode-docker" "$env:USERNAME@localhost" powershell.exe -NoProfile -NonInteractive -Command "dotnet --info"
    -

    If the test reports Permission denied (publickey), do not enter your VPS password or Windows password. Public-key authentication was not accepted. Check whether the account is an Administrator:

    -
    whoami /groups | Select-String 'S-1-5-32-544'
    -

    If that prints a result, Windows OpenSSH reads administrator keys from C:\ProgramData\ssh\administrators_authorized_keys instead of the profile file. In an elevated PowerShell window, install the same public key there and apply the required permissions:

    -
    $auth="$env:ProgramData\ssh\administrators_authorized_keys"; $publicKey=(Get-Content "$env:USERPROFILE\.ssh\opencode-docker.pub" -Raw).Trim(); New-Item -ItemType Directory -Force (Split-Path $auth) | Out-Null; if (-not (Test-Path $auth)) { Set-Content -Path $auth -Value $publicKey } elseif ((Get-Content $auth) -notcontains $publicKey) { Add-Content -Path $auth -Value $publicKey }; icacls $auth /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"
    -

    Retry the SSH command after that. If the account is not an Administrator, keep using the profile authorized_keys file and inspect the SSH log instead of entering a password:

    -
    Get-WinEvent -LogName 'OpenSSH/Operational' -MaxEvents 20 | Format-Table TimeCreated,Id,Message -Wrap
    -

    Keep Dockerfile and opencode-docker.cmd in %USERPROFILE%\\.opencode-docker-config. Build the image once from that folder, then put that folder on PATH so the command is available in every application PowerShell:

    -
    Set-Location "$env:USERPROFILE\.opencode-docker-config"
    -docker build --pull --no-cache -t my-opencode-dotnet .
    -# Only if an app actually targets Tizen:
    -# docker build --pull --no-cache --build-arg INSTALL_TIZEN=true -t my-opencode-dotnet .
    -

    From the application folder, run opencode-docker.cmd. It uses the existing my-opencode-dotnet image, gives each app its own OpenCode data directory, mounts NuGet and SSH credentials read-only, and passes the Windows app path to the bridge:

    -
    opencode-docker.cmd
    -

    The equivalent expanded container command is:

    -
    docker run --rm -it `
    -  -v "${USERPROFILE}\.opencode-docker\nuget:/root/.nuget/NuGet:ro" `
    -  -v "${USERPROFILE}\.ssh:/root/.ssh:ro" `
    -  -v "${PWD}:/workspace" -w /workspace `
    -  -e TF_WINDOWS_SSH_HOST=host.docker.internal `
    -  -e TF_WINDOWS_SSH_USER="$env:USERNAME" `
    -  -e TF_WINDOWS_SSH_KEY=/root/.ssh/opencode-docker `
    -  -e TF_WINDOWS_APP_PATH="C:\\path\\to\\app" `
    -  -e TF_OPENCODE_DOCKER=1 `
    -  my-opencode-dotnet opencode
    -

    Inside the container use dotnet build and dotnet test for standard .NET projects. Use winrun "dotnet --info" as the bridge probe, then winrun "dotnet build -c Release" for the Windows MAUI Blazor Desktop head. TF_WINDOWS_APP_PATH is the Windows path to the same app mounted as /workspace. Mobile, iOS, and Mac Catalyst builds and runtime UI tests are outside this Windows container setup and should run on a Mac.

    -

    The SSH directory is intentionally mounted read-only. Docker Desktop may expose the mounted key with Linux mode 0777, which OpenSSH rejects, and the mounted directory cannot accept a new known_hosts file. The image's winrun wrapper copies the key to writable /tmp/opencode-docker/ with mode 0600 and creates its writable host-trust file there. Do not try to chmod the mounted key from inside the container.

    - - -

    0a. macOS bootstrap — DO ONCE, EVER#

    - -

    Run this once per Mac. The native equivalent of §0: everything the agents need to build, run, and see your apps on macOS. There is no winrun bridge to install — dotnet, Playwright, and Appium all run natively — but the machine still needs its toolchain once.

    - -
    # 1. Xcode Command Line Tools — provides git AND python3 (the framework's
    -#    guard-status/guard-verify hooks silently fail open without python3)
    -xcode-select --install
    -
    -# If full Xcode is installed (required for MAUI iOS / Mac Catalyst builds),
    -# accept its license once or python3/git error out with a license prompt:
    -sudo xcodebuild -license accept
    -
    -# 2. Homebrew (skip if `brew --version` already works)
    -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    -
    -# 3. The toolchain: .NET SDK + Node.js (Node powers Playwright and Appium)
    -brew install dotnet-sdk node
    -
    -# 4. MAUI workload — only if any of your apps ships a MAUI head.
    -#    sudo is REQUIRED on macOS: the SDK lives in root-owned /usr/local/share/dotnet,
    -#    so without it this (and any `dotnet workload update` / SDK update) fails with
    -#    "Inadequate permissions. Run the command with elevated privileges."
    -sudo dotnet workload install maui
    - -
    Playwright — nothing for you to do. The verifier self-provisions it per project the first time it runs (verify-phase.md §1, also used by every self-smoke): it creates package.json if missing, runs npm install -D @playwright/test + npx playwright install chromium, and writes a minimal playwright.config.ts. The only machine-level prerequisite is Node (step 3 above). The Chromium download is cached once under ~/Library/Caches/ms-playwright and shared by every project, so only the first project ever pays it — and unlike WSL there are no system libraries to install.
    - -
    Verify: dotnet --info prints an SDK, node --version answers, and python3 --version answers without an Xcode-license error. The agents handle everything else per project.
    - -
    MAUI native-UI testing (Android emulator / iOS Simulator / Mac Catalyst): continue with §0b — on a Mac-native setup every piece of it (Android Studio + emulator, Appium + drivers, the Simulator) runs on this same machine, and all endpoints are http://localhost:4723.
    - - -

    0b. Device-host bootstrap (MAUI Android / iOS / Mac Catalyst) — DO ONCE PER HOST#

    - -

    What this is, in plain words

    - -

    The AI does its own testing of every app it builds. For a website app it can already "see" the screens — it opens the site in an invisible browser, looks at each page, and checks that everything shows up and looks right. But when an app runs on a phone, tablet, or as a Mac desktop program, the AI has no window into it: it can confirm the app compiles, but it can't see what the running app looks like. That is the gap this setup closes — without it, the AI might report "all good" while the real phone screen has buttons on top of each other, text cut off, or empty lists.

    - -
    The analogy: think of the AI as a quality inspector. For websites it already has a screen to watch. This one-time setup gives it a video feed and a remote control for a phone (and a Mac) so it can tap around the real app, take screenshots, and catch a broken-looking screen — the same way it already does for websites. You set up the camera and remote once; after that the AI uses them by itself every time it tests.
    - -
    Do you even need this? Only if the app runs on phones/tablets (Android or iPhone/iPad) or as a Mac desktop program. If your app is a normal website (Blazor), you can skip this whole section — website testing already works out of the box. You can also set up just the parts you need: only Android, or only Apple, or both.
    - -

    A few words you'll see

    - - - - - - - - - -
    WordWhat it means here
    AppiumThe free tool that acts as the "remote control" — it lets the AI tap buttons and read the screen of a phone or Mac app.
    Emulator / SimulatorA pretend phone that runs on your computer in a window, so you don't need a physical phone plugged in. "Emulator" = Android; "Simulator" = iPhone/iPad.
    AVD"Android Virtual Device" — one specific pretend Android phone you've created (e.g. a Pixel running Android 14).
    SDKThe official toolkit from Google (Android) or Apple (iPhone) for building and running their apps.
    WSLThe Linux environment inside your Windows PC where the AI (Claude Code) actually runs.
    Endpoint / URLAn address like http://localhost:4723 that the AI uses to reach the remote control. localhost means "this same computer".
    Terminal / PowerShellA text window where you type commands. On Windows it's "PowerShell" or "Terminal"; on Mac it's the "Terminal" app; inside WSL it's your Ubuntu window.
    - -

    What you'll need first

    -
      -
    • For Android testing: a Windows PC, plus Android Studio (the free Google app — the easiest way to get the Android toolkit, a pretend phone, and the emulator all at once) and Node.js (free, from nodejs.org — it provides the npm command used to install Appium).
    • -
    • For iPhone / iPad / Mac-desktop testing: a Mac computer on the same Wi-Fi/network as your PC, with Xcode (free from the Mac App Store) and Node.js. This is the same Mac used to build Apple apps.
    • -
    • Permission to install software (you may be asked for your password), and an internet connection for the downloads.
    • -
    - -
    How the pieces fit: the pretend phone and its remote control run where that platform lives — Android on your Windows PC, iPhone/iPad and Mac-desktop on the Mac. The AI just needs the address to reach each one; it never needs the phone tools installed in its own Linux environment. Builds are unaffected — this only adds the AI's ability to watch the running app.
    - -
    Running Claude Code natively on a Mac? Then everything in this section collapses onto the one machine: Android Studio + the emulator + Appium run locally, and the "paired Mac" for iPhone/iPad and Mac-desktop testing is this same machine. Do Step 3 (and Step 2's Android pieces if the app has an Android head) in the Mac's own Terminal, skip the WSL mirrored-networking step entirely, and use http://localhost:4723 for every head in core-config.yaml — no fixed LAN address needed.
    - -

    Step 1 — let the AI reach the Android remote control (Windows only, once)

    -

    By default the AI's Linux environment and the rest of Windows don't share the same network address. Turning on "mirrored networking" makes them share localhost, so the AI can reach the Android remote control with a simple address. Create or edit the file .wslconfig in your user folder (its full path is %UserProfile%\.wslconfig — paste that into the File Explorer address bar) and put these two lines in it:

    -
    [wsl2]
    -networkingMode=mirrored
    -

    Save it, then open PowerShell (click Start, type "PowerShell", press Enter) and run wsl --shutdown. Close and reopen your WSL window. (Skip this step if you're only setting up Apple testing.)

    - -

    Step 2 — set up the Android pretend-phone + remote control (on the Windows PC)

    -

    Easiest way (clicking): install Android Studio, open its Device Manager, and click "Create device" to make a pretend phone (e.g. a Pixel with Android 14). Android Studio installs the Android toolkit for you. Then install the remote control by opening PowerShell and running the two appium lines below.

    -

    Command way (does the same thing): in PowerShell:

    -
    # create a pretend Android phone (an "AVD")
    -sdkmanager "system-images;android-34;google_apis;x86_64"
    -avdmanager create avd -n Pixel_API_34 -k "system-images;android-34;google_apis;x86_64"
    -
    -# install the remote control (Appium) and its Android adapter
    -npm install -g appium
    -appium driver install uiautomator2
    -

    Finally, save a tiny helper file named start-android-verify.ps1 (the AI runs this itself to switch the phone + remote control on when it tests). It contains:

    -
    Start-Process emulator -ArgumentList "-avd Pixel_API_34 -no-snapshot -no-boot-anim"
    -Start-Process appium   -ArgumentList "--address 0.0.0.0 --port 4723"
    -

    If you used a different phone name in Android Studio, use that name instead of Pixel_API_34 here and in the settings file in Step 4.

    - -

    Step 3 — set up Apple (iPhone/iPad + Mac desktop) testing (on the Mac)

    -

    On the Mac (which must be on the same network as your PC), open the Terminal app and install the remote control plus the Apple adapters:

    -
    npm install -g appium
    -appium driver install xcuitest      # for the iPhone/iPad simulator
    -appium driver install mac2          # for the Mac desktop app
    -appium --address 0.0.0.0 --port 4723   # start the remote control (leave this running while testing)
    -

    Give the Mac a fixed network address so the AI always finds it (in your Wi-Fi router, reserve an IP for the Mac — search "DHCP reservation" for your router model). Note that address down; you'll put it in Step 4. (Skip this step if you're only setting up Android.)

    - -

    Step 4 — tell the app where the remote controls are

    -

    Each app keeps its settings in a file called core-config.yaml. Add a runtimeVerification.appium section listing only the platforms this app runs on. The AI reads this automatically; if a platform isn't listed (or can't be reached), the AI simply marks that platform's screens "not visually checked" rather than pretending they passed.

    -

    WSL-on-Windows setup (Android on this PC, Apple on the LAN Mac):

    -
    runtimeVerification:
    -  appium:
    -    android:     { url: http://localhost:4723, avd: Pixel_API_34, launch: 'winrun "powershell -File start-android-verify.ps1"' }
    -    ios:         { url: http://192.168.1.50:4723, simulator: "iPhone 15" }
    -    maccatalyst: { url: http://192.168.1.50:4723 }
    -

    macOS-native setup (everything on this Mac — no winrun, no LAN address):

    -
    runtimeVerification:
    -  appium:
    -    android:     { url: http://localhost:4723, avd: Pixel_API_34 }
    -    ios:         { url: http://localhost:4723, simulator: "iPhone 15" }
    -    maccatalyst: { url: http://localhost:4723 }
    -

    Use the block for the machine the AI runs on. On WSL: replace 192.168.1.50 with your Mac's actual address from Step 3 and keep localhost for Android (that's "this PC"). Remove any platform the app doesn't have.

    - -
    Check it works: in your WSL window, type curl http://localhost:4723/status (Android) and curl http://<your-mac-address>:4723/status (Apple); on a Mac-native setup it's curl http://localhost:4723/status for everything. If you see something containing "ready":true, the remote control is reachable — you're done. From now on the AI turns the pretend phone on, takes screenshots, and checks each screen by itself; you don't run anything.
    - -
    If something doesn't work: -
      -
    • "command not found" for sdkmanager/avdmanager/emulator: these come with Android Studio but may not be on your command path — easiest fix is to create the pretend phone by clicking inside Android Studio's Device Manager instead.
    • -
    • "command not found" for npm or appium: install Node.js first (from nodejs.org), then re-run the npm install -g appium line.
    • -
    • The curl check fails for Android: make sure Step 1 (mirrored networking) was done and you reopened WSL, and that the emulator + Appium are actually running (run start-android-verify.ps1 once to start them).
    • -
    • The curl check fails for Apple: confirm the Mac is on and on the same Wi-Fi, that appium is still running in its Terminal window, and that you used the Mac's correct address.
    • -
    • The AI says a screen is "STATIC-ONLY": that just means it couldn't reach that platform's remote control this time (e.g. the Mac was off) — it's being honest, not failing. Bring the device host up and ask it to verify again.
    • -
    - -

    One more thing for whoever writes the app's screens: each important button, list, and value should be given a stable name (an AutomationId) so the AI can find it reliably — this is part of the coding standards (see §10). The AI handles this when it builds; you don't need to.

    - - -

    1. Overview & principles#

    - -
    -
    -

    Compress, don't expand

    -

    No story-by-story TechieFlow. One BRD + one Architecture + one Coding-Standards doc (all human-readable) feed the unified build-phase — which reads ONE AI-only doc (the app Checklist) and calls the library agents (/trblazeui, /techierag) as sub-agents.

    -
    -
    -

    Verify, don't trust

    -

    The build-phase self-smokes (data + visual) and then chains verifier. Headless Playwright + dotnet test, with a data-render gate AND a visual-truth gate. Verdicts written into the checklist's Requirements Status table.

    -
    -
    -

    Standards enforced from day 1

    -

    Every project has docs/<APP>-Coding-Standards.md. Every implementation agent prompt references it. CLAUDE.md at project root pins it for auto-load.

    -
    -
    - -
    - Core idea: Requirements get IDs (REQ-UI-*, REQ-FN-*, REQ-RAG-*, REQ-NFR-*) in ONE checklist. Implementing agents reference IDs in commits and follow <APP>-Coding-Standards.md. verifier maps IDs → test evidence (data + visual gates), written into the checklist's single Requirements Status table. Nothing is "done" until every row in that table is green. -
    - - -

    2. Pain points → solutions#

    - -
    -

    1. Agents miss requirements; verify-fix loop is exhausting

    -

    verifier mandatory + ID-driven; chain in same prompt

    -
    -
    -

    2. WSL has no GUI browser; Playwright MCP eats context

    -

    → Headless Playwright CLI from §0 bootstrap

    -
    -
    -

    3. Can't build/run MAUI from WSL

    -

    winrun WSL→Windows bridge (§9)

    -
    -
    -

    4. Hard to scan markdown on cold re-entry

    -

    <APP>-BRD.html + <APP>-Architecture.html + PROJECT-STATUS.html with Mermaid (§6 + §11)

    -
    -
    -

    5. npx techieflow install grabs v6, breaks customizations

    -

    scaffold-brownfield.sh / scaffold-greenfield.sh copy your v4 setup (§3)

    -
    -
    -

    6. Claude Code prompts every Bash; *yolo doesn't help; a VM goal run waits days on delete prompts, git-read blocks and usage limits

    -

    → Pre-built .claude/settings.json (§12)

    -
    -
    -

    7. Generated code uses inconsistent style across projects

    -

    <APP>-Coding-Standards.md per project, referenced in every impl prompt; CLAUDE.md pin

    -
    -
    -

    8. Agents excuse themselves from smoke-testing ("can't run on Linux / it targets Windows / it's MAUI / Playwright needs a GUI")

    -

    _smoke-test-policy.md: those are banned excuses — headless Playwright + the Windows/MAUI bridge are already set up (§0). Every change is self-smoked by the agent before the verifier; the user is asked to boot only after the build ladder genuinely fails. "It runs" means controls render their data, not just HTTP 200.

    -
    -
    -

    9. Smoke/verify runs pollute the DB with random throwaway test users

    -

    _smoke-test-policy.md: use the canonical test accounts in <APP>-UsageGuide.md (or look them up in the DB via the connection string); never auto-create — ask the owner to confirm, then record the account in the UsageGuide.

    -
    -
    -

    10. The data + visual gates only reached Blazor + the MAUI Windows head — Android/iOS/Mac-desktop screens were build-only (never run/observed)

    -

    Appium runtime bridge (§0b): the verifier drives MAUI Android (emulator on the Windows host), iOS (Simulator on a LAN Mac), and Mac Catalyst (same Mac) over an HTTP WebDriver endpoint that returns the same screenshot + element tree, so the §4a/§4b gates run unchanged. Endpoints live in core-config.yaml → runtimeVerification.appium; an unreachable host degrades that head to ⚠ STATIC-ONLY, never a faked pass.

    -
    - - -

    3. Scaffolding a new project — copy, don't npm-install#

    -
    Codex adapter. Both scaffolders and the updater deploy .codex/ (config, custom agents, hooks and rules) and .agents/skills/ (TechieFlow workflows). Trust the repository and review /hooks after installation. Invoke $techieflow-build, $techieflow-verify, or another generated skill. Use tf-goal.sh --harness codex for unattended local work; full winrun/Appium verification requires a local Codex environment.
    - -

    You have a customized v4 setup. npx techieflow install would fetch v6 and lose your customizations. Use the scaffold script:

    - -

    Three scripts: two scaffolders (one per flow) plus an updater for projects scaffolded earlier. All are idempotent (scaffolders use rsync --ignore-existing — existing files preserved on re-run; the updater force-refreshes framework files including .claude/settings.json, see below).

    - -

    Brownfield (existing app) — scaffold-brownfield.sh

    -

    WSL (Windows):

    -
    cd /path/to/existing-app
    -/mnt/c/3AIGenCode/TechieFlow/scaffold-brownfield.sh .
    -

    macOS:

    -
    cd /path/to/existing-app
    -/Volumes/MacD/MyCode/TechieFlow/scaffold-brownfield.sh .
    - -

    Adds .tfcore/, .claude/commands/, .opencode/, .codex/, .agents/skills/, WORKFLOW.html, opencode.jsonc, and .claude/settings.json. Does NOT touch existing src/, tests/, or other docs/ contents. Warns (non-blocking) if no .csproj/.sln found within 4 levels. Refuses if the target directory doesn't exist (use greenfield script for that). (No .opencode/command/TechieFlow/ mirror is deployed — OpenCode loads agents/tasks from opencode.jsonc {file:./.tfcore/...} references instead.)

    - -

    Greenfield (new app) — scaffold-greenfield.sh

    -

    WSL (Windows):

    -
    mkdir /path/to/my-new-app && cd /path/to/my-new-app
    -git init
    -/mnt/c/3AIGenCode/TechieFlow/scaffold-greenfield.sh .
    -

    macOS:

    -
    mkdir /path/to/my-new-app && cd /path/to/my-new-app
    -git init
    -/Volumes/MacD/MyCode/TechieFlow/scaffold-greenfield.sh .
    -

    Then, on either machine:

    -
    dotnet new sln -n MyNewApp
    -dotnet new blazor -n MyNewApp.Web -o src/MyNewApp.Web
    -dotnet sln add src/MyNewApp.Web
    -dotnet add src/MyNewApp.Web package TrBlazeUI    # if UI involved
    -dotnet add src/MyNewApp.Web package TechieRag    # if AI/RAG involved
    -dotnet build                                      # deploys library agent files
    - -

    Same framework drop as brownfield, plus creates empty src/, tests/playwright/, tests/unit/ folders ready for use.

    - -
    - What neither script creates: per-project doc files (BRD, Architecture, Coding Standards, the Checklist, etc.). Those are produced by /analyst on day 1 of the workflow (§7 step 1). Both scripts also exclude the library-deployed agent files (trblazeui.md, techierag.md) — those land via dotnet build after you add the NuGet packages. -
    - -

    Updating an already-scaffolded project — update-framework.sh

    - -

    When the reference framework repo (WSL: /mnt/c/3AIGenCode/TechieFlow · macOS: /Volumes/MacD/MyCode/TechieFlow) evolves (new tasks, updated templates, agent fixes), pull those changes into an existing project with the updater. Unlike the scaffolders (--ignore-existing: never touch a file that's already there), the updater force-overwrites framework files and preserves everything that contains your work product. The scripts self-locate — invoke whichever machine's copy you're on and it uses itself as the source.

    - -

    WSL (Windows):

    -
    # Preview what would change (recommended first):
    -/mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/your-app --dry-run
    -
    -# Apply:
    -/mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/your-app
    -
    -# Or run from inside the project (defaults to $PWD):
    -cd /path/to/your-app
    -/mnt/c/3AIGenCode/TechieFlow/update-framework.sh
    -

    macOS:

    -
    # Preview what would change (recommended first):
    -/Volumes/MacD/MyCode/TechieFlow/update-framework.sh /path/to/your-app --dry-run
    -
    -# Apply:
    -/Volumes/MacD/MyCode/TechieFlow/update-framework.sh /path/to/your-app
    -
    -# Or run from inside the project (defaults to $PWD):
    -cd /path/to/your-app
    -/Volumes/MacD/MyCode/TechieFlow/update-framework.sh
    - -

    Note: the script is NOT on your PATH — always invoke it with the full path shown above (a bare update-framework.sh gives "command not found"). Optional alias — WSL: echo "alias update-framework.sh='/mnt/c/3AIGenCode/TechieFlow/update-framework.sh'" >> ~/.bashrc · macOS (zsh): echo "alias update-framework.sh='/Volumes/MacD/MyCode/TechieFlow/update-framework.sh'" >> ~/.zshrc.

    - - - - - - - -
    Force-overwritten (framework — reference repo wins)Preserved (your work product — never touched)
    .tfcore/{tasks,templates,agents,checklists,data,utils,workflows,agent-teams}/
    - .claude/commands/TechieFlow/ subtree
    - .claude/commands/*.md top-level commands (generate-html etc.)
    - .opencode/command/*.md top-level commands (generate-html etc.)
    - WORKFLOW.html
    - .claude/settings.json (refreshed to canonical config by default; old file → settings.json.bak; --keep-permissions to skip; settings.local.json never touched)
    docs/, src/, tests/
    - PROJECT-STATUS.md, CLAUDE.md, .editorconfig
    - .tfcore/core-config.yaml
    - opencode.jsonc
    - .claude/{trblazeui,techierag}.md + .opencode/command/{trblazeui,techierag}.md (NuGet-deployed)
    - -

    The scaffolders and the updater also ensure the project's .gitignore ignores the deployed framework copies (.tfcore/, .claude/, .opencode/, .codex/, .agents/skills/, /CLAUDE.md, /WORKFLOW.html, /opencode.jsonc, /.tf-scaffold-note.txt) — everything the framework drops into an app is a copy (source of truth: the reference repo, or the NuGet package for the library personas) and must never be committed in the app repo. The step is append-only and idempotent: your own .gitignore entries are respected, nothing is removed. Note git never untracks a file just because it became ignored — if a framework file was committed before the entry existed, run git rm -r --cached <path> once yourself (git is manual in TechieFlow; agents never run it).

    - -

    They also manage a second block — agent test-harness & log artifacts (node_modules/, /package.json, /package-lock.json, tests/.artifacts/, test-results/, test-results-*/, /scripts-*/, playwright-report/, .verify/, logs/, /docs/.last-verify.json, .DS_Store). These are machine-generated by the verifier's npm/Playwright self-provisioning (verify-phase §1) and the standing Serilog default, and are fully regenerable — you should never have to triage them at commit time. verify-phase §1 also self-heals the block whenever it provisions, so even a project scaffolded before this block existed gets it on its next verify. playwright.config.ts deliberately stays tracked — committed test suites depend on it. Same caveat as above: already-tracked artifacts need a one-time git rm -r --cached <path> from you.

    - -

    Where test artifacts live — tests/.artifacts/, never the repo root (rule added 2026-08-10). Screenshots, traces, videos and Playwright/Appium output are failure evidence with a session lifetime, not work product: they exist so a verify run can cite a path in a checklist Remark, and Playwright wipes the directory at the start of the next run. verify-phase §1 now pins outputDir: './tests/.artifacts/test-results' in playwright.config.ts — creating the config if absent and editing it if it lacks the setting — and bans the two habits that used to litter app repos: a repo-root test-results/, and per-cluster siblings like test-results-cluster-a/ from a fan-out passing --output test-results-<slug>. Those siblings were the real damage: test-results/ does not match test-results-cluster-a/, so they were never ignored, they surfaced untracked at every commit, and they accumulated one directory per run (TechieBlog reached fourteen). If a parallel run genuinely needs isolation, the only permitted form is a subfolder — --output tests/.artifacts/<slug>. The bare test-results/ and the test-results-*/ glob stay in the ignore block purely to cover legacy trees; a compliant run writes neither, and verify-phase §1 deletes any it finds at the repo root on its next pass. Deliberately tracked screenshots — the DevGuide's reviewed set under docs/screenshots/<APP>/ — are untouched by all of this.

    - -

    The rule covers the harness too, not just its output (extended 2026-08-10). The first version of this rule pinned where the tools write and said nothing about where the agent puts the scripts it writes — so the next fan-out stopped creating test-results-cluster-a/ and created scripts-cluster-b/ instead: same defect, different noun, same four untracked folders in your commit view. A throwaway smoke/verify/load/cleanup script an agent authors for a run is scratch, and it now lands in tests/.artifacts/harness/, named per cluster inside that folder. Two things follow. Your project's own scripts/ — release, publish, dev-setup scripts you track and own — is off limits to agents in both directions: nothing writes a run harness into it, and no sweep ever deletes it (the ignore entry is /scripts-*/, which requires the hyphen and the root anchor, so plain scripts/ can never match). And a harness that does import { chromium } from 'playwright' drives the library, not the test runner — it never loads playwright.config.ts, so outputDir does not apply and nothing wipes what it writes; such a script must place its own captures under tests/.artifacts/ and must never hardcode an absolute path. The general lesson, worth stating because the first fix missed it: a location rule has to name the class of thing (everything this run generates that is not a deliverable), not the specific filenames a tool happened to default to.

    - -
    - After every update: restart Claude Code in that project. New/changed tasks and agents under .claude/commands/TechieFlow/ are only registered as skills at session start — without a restart, a freshly-synced command like /TechieFlow:tasks:generate-html won't resolve. -
    - - -

    4. File-naming convention — <APP> prefix#

    - -

    Every per-project document filename starts with the application name. Examples from the user's existing projects: AppManager-Coding-Standards.md, AstroLyfe-Coding-Standards.md. Same convention applies to every doc:

    - - - - - - - - - - - - -
    PatternExample for app "AppManager"Example for app "AstroLyfe"
    <APP>-BRD.md / .htmlAppManager-BRD.mdAstroLyfe-BRD.md
    <APP>-Architecture.md / .htmlAppManager-Architecture.mdAstroLyfe-Architecture.md
    <APP>-Coding-Standards.mdAppManager-Coding-Standards.mdAstroLyfe-Coding-Standards.md
    <APP>-Checklist.md (ONE per app — all REQ-UI/FN/RAG/NFR-*)AppManager-Checklist.mdAstroLyfe-Checklist.md
    <APP>-UIDesign.md (greenfield mockups spec)AppManager-UIDesign.mdAstroLyfe-UIDesign.md
    <APP>-<Library>-Feedback.md (one per library)AppManager-TrBlazeUI-Feedback.mdAstroLyfe-TechieRag-Feedback.md
    <APP>-UsageGuide.mdAppManager-UsageGuide.mdAstroLyfe-UsageGuide.md
    <APP>-DevGuide.md (developer code-map)AppManager-DevGuide.mdAstroLyfe-DevGuide.md
    <APP>-ProductGuide.md (end-user how-to manual)AppManager-ProductGuide.mdAstroLyfe-ProductGuide.md
    - -

    Throughout this document, <APP> is a placeholder. When you paste a prompt to an agent, substitute it with your actual application name (no spaces; use PascalCase to match the user's two existing samples).

    - -

    Files that stay generic (not <APP>-prefixed):

    -
      -
    • PROJECT-STATUS.md / .html — one per repo, project-name is in its content
    • -
    • CLAUDE.md — Claude Code's auto-loaded session memory; one per repo
    • -
    • WORKFLOW.html — this file; identical across projects
    • -
    - - -

    5. Project structure#

    - -
    your-app/                              ← e.g. AppManager/
    -├── PROJECT-STATUS.md                  ← /analyst (day 1)
    -├── PROJECT-STATUS.html                ← /flow-master after each phase
    -├── CLAUDE.md                          ← /analyst (day 1) — pins coding standards
    -├── WORKFLOW.html                      ← from scaffold
    -│
    -├── .tfcore/                        ← from scaffold (your customized v4)
    -├── .claude/
    -│   ├── commands/TechieFlow/...              ← from scaffold
    -│   ├── settings.json                  ← from scaffold (yolo-except-git-writes)
    -│   ├── trblazeui.md                   ← from `dotnet build` (TrBlazeUI NuGet)
    -│   └── techierag.md                   ← from `dotnet build` (TechieRag NuGet)
    -├── .opencode/command/
    -│   ├── generate-html.md                ← from scaffold
    -│   ├── trblazeui.md                   ← from dotnet build
    -│   └── techierag.md                   ← from dotnet build
    -├── .trblazeui/TrBlazeUI-AI-Reference.md   ← from dotnet build
    -├── .techierag/TechieRag-AI-Reference.md   ← from dotnet build
    -│
    -├── .editorconfig                      ← /analyst (day 1) — machine-checkable subset of coding standards
    -│
    -├── docs/
    -│   ├── <APP>-BRD.md                ← /analyst — humans + AI
    -│   ├── <APP>-BRD.html              ← /flow-master renders BRD.md → humans
    -│   ├── <APP>-Architecture.md       ← /analyst — humans + AI (both flows!)
    -│   ├── <APP>-Architecture.html     ← /flow-master renders Architecture.md → humans
    -│   ├── <APP>-Coding-Standards.md   ← /analyst (day 1) — ALL agents follow this
    -│   ├── <APP>-Checklist.md          ← /analyst (*split-brd) — ONE checklist, all REQ-UI/FN/RAG/NFR-* — AI
    -│   ├── <APP>-UIDesign.md           ← /analyst (*mockups, greenfield) — per-screen spec + component map — humans
    -│   ├── <APP>-UIDesign.html         ← rendered for humans
    -│   ├── mockups/                    ← /analyst (*mockups, greenfield) — rendered *.html screens (TrBlazeUI-styled)
    -│   ├── screenshots/<APP>/          ← /flow-master (*devguide OBSERVE — any built app) — per-screen *.png (DevGuide + Product Guide source)
    -│   ├── <APP>-TrBlazeUI-Feedback.md ← agents log TrBlazeUI issues — one file PER library
    -│   ├── <APP>-TechieRag-Feedback.md ← agents log TechieRag issues — each goes to its team
    -│   ├── <APP>-UsageGuide.md  ← /flow-master — humans
    -│   ├── <APP>-UsageGuide.html       ← /flow-master — rendered for humans
    -│   ├── <APP>-DevGuide.md           ← /flow-master (*devguide) — humans (small app: single doc)
    -│   ├── <APP>-DevGuide.html         ← /flow-master — rendered for humans
    -│   ├── devguides/                  ← large app: split per-role DevGuide (index + <APP>-DevGuide-<Role>.md/.html)
    -│   ├── <APP>-ProductGuide.md       ← /flow-master (*productguide) — END USERS / external (small app: single doc)
    -│   ├── <APP>-ProductGuide.html     ← /flow-master — rendered for end users
    -│   └── productguides/              ← large app: split per-role Product Guide (index + <APP>-ProductGuide-<Role>.md/.html)
    -│
    -├── tests/{playwright,unit}/  …
    -└── src/  …
    - - -

    6. Doc artifacts & audiences#

    - -

    Agent-facing authoring notes never render. The doc templates carry drafting-agent instructions (the "Depth mandate" / "Mermaid mandate" notes) as HTML comments, so they are invisible in both the generated .md and the rendered HTML — the human reader must never see them. Docs generated from pre-2026-07 templates may still show them as visible blockquotes; the next HTML render strips them automatically (html-render-shell.md §6b).

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FileAudienceFormatCreated byPurpose
    docs/<APP>-BRD.mdH AIMD + Mermaid/analystBusiness Requirements — the WHY.
    docs/<APP>-BRD.htmlHSelf-contained HTML/flow-masterBrowseable BRD for stakeholders / future-you.
    docs/<APP>-Architecture.mdH AIMD + Mermaid/analyst (both flows)Brownfield: current architecture + planned deltas. Greenfield: target architecture.
    docs/<APP>-Architecture.htmlHSelf-contained HTML/flow-masterBrowseable architecture diagrams.
    docs/<APP>-Coding-Standards.mdH AIMarkdown/analyst (day 1)Every implementation agent follows this. Pinned by CLAUDE.md.
    docs/<APP>-Checklist.mdAIMarkdown w/ REQ-UI-*, REQ-FN-*, REQ-RAG-*, REQ-NFR-*/analyst (*split-brd)The ONE checklist — every REQ class in a single Requirements Status table. Input for *build-phase (which routes REQ-UI-* to /trblazeui and REQ-RAG-* to /techierag as sub-agents). Scopes (*verify ui|functional|all) filter this one table by REQ prefix. Agent-only working doc — never rendered to HTML.
    docs/<APP>-UIDesign.md + .html
    docs/mockups/*.html
    HMD + HTML/analyst (*mockups, greenfield)Per-screen UI Design Spec with a region → TrBlazeUI control component map + rendered TrBlazeUI-styled mockups. Approved with the BRD + Architecture before build; what /trblazeui builds from; the verifier's visual baseline (§4b).
    PROJECT-STATUS.md + .htmlH AIMD + HTML/analyst then /flow-masterSingle source of "where am I".
    CLAUDE.mdAIMarkdown/analyst (day 1)Auto-loaded by Claude Code; points to coding standards, BRD, Architecture.
    .editorconfigAI (toolchain)EditorConfig/analyst (day 1)Machine-checkable subset of coding standards (file-scoped namespace, async suffix, no-underscore field naming rule).
    Requirements Status table (inside the one checklist)AI HMD tablebuild agents + /verifierPer-REQ status/%/remarks — single source of truth; REQ ID → PASS/FAIL/Blocked evidence.
    docs/<APP>-TrBlazeUI-Feedback.md
    docs/<APP>-TechieRag-Feedback.md
    HMarkdownAll implementing agentsIssues to ship back to each library's team — one file per library (separate codebases, separate teams; each file is handed to its owning team, who use this same framework to fix them).
    docs/<APP>-UsageGuide.md + .htmlHMD + HTML/flow-masterFinal handoff: install, run, test, smoke checklist.
    docs/<APP>-DevGuide.md + .html
    (large apps: split per role into docs/devguides/<APP>-DevGuide-{Role}.md + index)
    HMD + HTML/flow-master (*devguide)Developer reference: every screen → control → service method → stored procedure, grouped by user role. Used to trace bugs and verify AI-generated code. Auto-generated at handoff; re-runnable anytime. See §6.
    docs/<APP>-ProductGuide.md + .html
    (large apps: split per role into docs/productguides/<APP>-ProductGuide-{Role}.md + index)
    H (end users / external)MD + HTML/flow-master (*productguide)End-user how-to manual: what each screen is for and how to do each task, illustrated with the screenshots captured for the DevGuide. The user-facing sibling of the DevGuide (same screens, different audience). On-demand; --update refreshes changed screens; always emits MD + HTML. See §6.
    .tfcore/TOKEN-GUIDE.mdHMarkdownframework (ships with scaffold)Token-efficiency guide — where AI tokens go and how to keep usage low. See §14.
    - -
    - Mermaid in HTML. /flow-master renders BRD.html and Architecture.html as self-contained HTML with <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script> and mermaid.initialize({startOnLoad:true, theme:'dark'}). ```mermaid fences in the MD become SVG diagrams in the HTML. -
    - - -

    7. Workflows#

    - -
    - No paste-and-substitute friction: every workflow step below is wrapped as a proper TechieFlow task that takes {AppName} as an argument. Run /analyst *day1-brownfield TrTools and the analyst executes the full multi-step task with the right file paths and templates already wired up. The agent will ask for the app name only if you forget to pass it. -

    - The task files live in .tfcore/tasks/: day1-brownfield.md, day1-greenfield.md, mockups.md, render-workflow-docs.md, split-brd.md, build-phase.md (the ONE unified build), verify-phase.md, fix-issues.md, triage-issues.md, devguide.md, handoff-phase.md. Open any of them to see exactly what the agent will do. -
    - -
    - If *<command> says "I don't have that command": Claude Code cached the old agent persona. Two things to check: -
      -
    1. Re-run the scaffold for this project: scaffold-brownfield.sh . or scaffold-greenfield.sh .. It includes a force-sync step that copies agent files from .tfcore/agents/ to .claude/commands/TechieFlow/agents/ (the path Claude Code actually loads).
    2. -
    3. Restart Claude Code in this project so it re-scans skills. The slash-command registry only refreshes at startup.
    4. -
    - Rule of thumb: edit agent files only in .tfcore/agents/; the scaffold force-mirrors them to .claude/commands/TechieFlow/agents/. Never edit the harness copy directly — it'll be overwritten next scaffold. -
    - -
    - - -
    - - -
    - -
    - Prereq: ran scaffold-brownfield.sh in the project root. -
    Typical agent invocations: 4–6 (day-1, split-brd, build-phase ×1–2, handoff). Manual checkpoints: 2 (BRD/Architecture/Standards + DevGuide-screenshot review, final UAT). -
    - -
    - -
    -
    1
    -
    -

    Reverse-doc + create all six day-1 deliverables

    -

    Produces, in ONE bulk pass (no per-section/per-requirement confirmation): <APP>-Architecture.md, <APP>-BRD.md (feature catalog + BRD ledger), <APP>-Coding-Standards.md, .editorconfig, PROJECT-STATUS.md, CLAUDE.md, the <APP>-UsageGuide.md, and — because brownfield already has built code — the screen-by-screen <APP>-DevGuide.md (§7.6) — plus the HTML render of every doc (no separate render step). Updates core-config.yaml with the app-specific doc paths.

    -

    /analyst *day1-brownfield TrTools

    -

    Substitute TrTools with your app name. Asks at most twice: app name (if omitted) and which existing docs to harvest (all = default / a selection / extra paths / drafting instructions). It never asks merge-vs-new: deliverables are always written fresh at canonical names and pre-existing/superseded docs are archived to docs/OldDocs/. If a dev/phase plan exists, it's migrated into the one <APP>-Checklist.md in the same run (completed phases pre-marked Done — skip step 3 below). Brownfield-only: because the repo already has built code, it also generates the screen-by-screen DevGuide (§7.6 — the as-built page → control → service → data-access → proc map). The DevGuide's OBSERVE pass boots the app, captures a screenshot of every screen to docs/screenshots/<APP>/, embeds them, and presents an owner visual-review gate ("here is how each screen renders — what needs to change?") — the brownfield counterpart to greenfield mockups. (Commonly STATIC-ONLY at day-1 until the stack is up; found defects land in the one checklist.) Greenfield day-1 has no code, so instead it produces UI mockups (§7.10). Task: .tfcore/tasks/day1-brownfield.md.

    -
    -
    - -
    - -
    -
    2
    -
    -

    Review the rendered HTMLs — manual checkpoint (≈15 min)

    -

    No command — day-1 already rendered everything. Open <APP>-BRD.html (business intent right?), <APP>-Architecture.html (matches what you want?), PROJECT-STATUS.html (next command correct?), skim Coding Standards. Cheapest catch-mistakes point. Edit the .md sources directly for anything wrong, then re-render just those files: /generate-html @docs/<APP>-BRD.md (see §6).

    -
    -
    - -
    - -
    -
    3
    -
    -

    Split BRD into the one Checklist

    -

    /analyst *split-brd TrTools

    -

    Every BRD-N maps to one or more REQ-UI-*/REQ-FN-*/REQ-RAG-*/REQ-NFR-* in the single docs/<APP>-Checklist.md (one Requirements Status table), each back-linked to its source BRD. Each REQ-UI-* cites the mockup screen it realizes (greenfield). Task: .tfcore/tasks/split-brd.md.

    -

    Manual checkpoint: read the checklist, adjust if needed.

    -
    -
    - -
    - -
    -
    4
    -
    -

    Build phase — ONE unified build (chains self-smoke + verifier)

    -

    /flow-master *build-phase TrTools

    -

    There is no longer a separate UI / RAG / functional build. *build-phase reads the one checklist, clusters ALL open REQs, and fans them out — calling /trblazeui (REQ-UI-*, building from the approved mockups) and /techierag (REQ-RAG-*) as sub-agents, and building REQ-FN-* / REQ-NFR-* itself. It then self-smokes (data + visual) and auto-chains /verifier. You never invoke /trblazeui or /techierag directly — flow-master orchestrates them. Task: .tfcore/tasks/build-phase.md.

    -
    -
    - -
    - -
    -
    5
    -
    -

    Verify loop (until green)

    -

    *build-phase self-chains /verifier, which writes verdicts into the one checklist's Requirements Status table and applies BOTH gates — the data-render gate (§4a: every control renders its data) AND the visual-truth gate (§4b: no overlap, every control in-viewport and non-zero-size at desktop + mobile, screenshot inspected, diffed against the mockup when one exists). A REQ is Verified only if acceptance passes AND data renders AND the screen looks right.

    - -

    The performance gate (§4c, added 2026-08-10) — opt-in by declaring a number. Render and visual say nothing about speed: a page that shows every control perfectly and takes nine seconds passed every gate the framework had. §4c closes that, and it is deliberately the narrowest of the four. It grades a REQ only if that REQ's acceptance criteria carry a machine-read budget line:

    -
    perf-budget: p95 load <= 2000ms @ concurrency 1
    -perf-budget: p95 ttfb <= 500ms  @ concurrency 50
    -

    No line, no gate — and that is the correct outcome, not a gap to fill. A threshold you never agreed to would produce failures you never asked for, and the first false failure is the moment gate verdicts stop being believed. Measurement is the shipped harness .tfcore/utils/tf-perf.sh (TTFB and full-load p50/p95/max at graded concurrency, warm-up discarded, per-path breakdown so you can see which page is slow).

    - - - - - - - -
    Measured vs budgetVerdictEffect
    ≤ budgetPERF-OKgate passes
    > budget, ≤ budget × 1.25PERF-MARGINALnever blocks Verified — a dated remark, so drift is visible before it becomes a failure
    > budget × 1.25PERF-FAILNeeds re-verify
    -

    The gate refuses to fail a REQ on a Debug build, on fewer than 20 samples behind the p95, when errors occurred during the run, or on a visibly contended host — each becomes PERF-UNMEASURED with the reason stated, because a wrong perf failure costs more than a missing one. MAUI native heads are not perf-gated (no HTTP surface; app-launch and frame timing are a different discipline with different tooling). Write budgets in the BRD §11 Performance NFR; *split-brd copies the line verbatim into the REQ, and *metrics reports the gate's catch rate against how many records actually ran it, never against the whole distribution (SCHEMA.md §3.5).

    -

    If Vidur reports misses, just re-run /flow-master *build-phase TrTools — it detects FIX mode and fans out repair subagents (layout/visual fixes route back to /trblazeui). Or re-verify a scope directly with /TechieFlow:agents:verifier *verify ui|functional|all (it filters the one table by REQ prefix). Loop until green; Blocked (library-gap) items pass through. Vidur also runs the standards-compliance grep checks from <APP>-Coding-Standards.md §"Enforcement".

    -
    -
    - -
    - -
    -
    6
    -
    -

    Handoff: usage doc + dev guide + status + library-feedback consolidation + HTML refresh

    -

    /flow-master *handoff-phase TrTools

    -

    Produces UsageGuide doc (test users + test plan + setup), runs *devguide (§3a — generates the screen-by-screen Developer Guide documenting the code as-built, capturing a screenshot of every screen), sets PROJECT-STATUS phase to Handoff, re-renders human-facing HTMLs (BRD, Architecture, UIDesign, UsageGuide, DevGuide, PROJECT-STATUS — not the checklist), consolidates each per-library feedback file with summary counts. It also points owners at *productguide <APP> (§6) — the optional end-user how-to manual, built from the same screens + screenshots the DevGuide just captured. Task: .tfcore/tasks/handoff-phase.md.

    -

    Manual checkpoint: 15-min UAT against the smoke checklist. Then hand each <APP>-<Library>-Feedback.md to its team / file as GitHub issues (§9.2). Found a bug after shipping? Drop screenshots into a folder and run *fix-issues (§7.11) — or *triage-issues (§7.12) to analyze + log it in the checklist first, without fixing.

    -
    -
    - -
    - -
    - - -
    - -
    - Prereq: scaffold + dotnet new sln/blazor + library NuGets + dotnet build (deploys library agent files). -
    - -
    - -
    -
    1
    -
    -

    Brief + BRD + Architecture + Coding-Standards + CLAUDE.md + PROJECT-STATUS + .editorconfig

    -

    /analyst *day1-greenfield MyNewApp

    -

    Asks once for the concept (ANY length — sentence, bullets, half-baked notes) and once for optional harvest paths / drafting instructions, then produces the day-1 artifacts in bulk — including a TARGET architecture with stack defaults (Blazor Server + TrBlazeUI + TechieRag-if-AI + SQLite-for-dev) and the UI mockups (docs/<APP>-UIDesign.md + docs/mockups/*.html, §7.10) — plus the HTML render of every doc (no separate render step). Substitute MyNewApp with your actual app name. Task: .tfcore/tasks/day1-greenfield.md.

    -
    -
    - -
    - -
    -
    2
    -
    -

    Review the rendered HTMLs AND the mockups — manual checkpoint (approve before build)

    -

    No command — day-1 already rendered everything. Read the BRD/Architecture HTMLs AND open docs/mockups/*.html + docs/<APP>-UIDesign.md: this is the visual design the UI will be built to match, so flag any screen that's wrong NOW. Edit the .md sources and re-render with /generate-html @docs/file.md, or run *mockups <APP> --update. The BRD + Architecture + mockups are approved together before any build.

    -

    This is your last cheap chance to redirect before code gets written.

    -
    -
    - -
    - -
    -
    3–6
    -
    -

    Split BRD → Build phase → Verify → Handoff

    -

    Identical to brownfield steps 3–6. Same prompts, same one checklist, same unified *build-phase (which builds REQ-UI-* from your approved mockups via /trblazeui), same data + visual gates, same standards-compliance discipline.

    -
    -
    - -
    - -
    - -

    7.9 Evolving the day-1 docs (the concept/requirements changed)

    - -

    Day-1 produces a first-pass BRD + Architecture, but a project keeps moving — the greenfield concept is still being discussed, or a requirement shifts mid-development. You do not have to hand-edit and hope, and you should not silently let the docs drift out of date. Pick by how big the change is:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SituationCommandWhat it does
    Concept/requirements evolved — add / reword / drop features, a new integration, a stack tweak — but most of the docs are still right*amend-docs <APP> "<what changed>"
    (analyst or flow-master)
    Surgically amends the BRD + Architecture in place: appends new BRD-N (IDs are append-only — modified IDs are edited in place, removed ones struck through, never renumbered), updates the feature catalog + §4 Development-status, ripples new/changed REQs into the checklist (appends new rows, flags modified ones for re-verify — never blindly re-splits), re-renders the HTML, and runs the status gate. Confirms the parsed change-set once, then applies in bulk. Preserves every unchanged section — no OldDocs archive.
    Pure additions and you want to confirm each requirement one at a time*create-brd <APP> <topic>author-brd
    (analyst)
    Interactive, per-item elicitation; appends confirmed BRD-N to the existing BRD. (*amend-docs defers to this for the additive part if you ask.)
    "What's built" view is what you want refreshed (not the requirements)automatic, or *refresh-status <APP>The status gate already re-derives PROJECT-STATUS + the BRD §4 rollup at the end of every build/verify/handoff. *refresh-status rebuilds them on demand (see §8a).
    Full pivot — most of the BRD/Architecture is now wrongre-run *day1-greenfield / *day1-brownfieldThe §1.6 collision policy archives the old docs to docs/OldDocs/ and writes fresh. Use this only for a wholesale redo, never for an incremental change (it would discard unchanged content + history).
    You just hand-edited a .md/generate-html @docs/<file>.mdRe-renders that one doc's HTML so the human-readable view isn't stale.
    - -

    Rule of thumb: amend, don't regenerate. *amend-docs is the default for an evolving project; re-running day-1 is the escape hatch for a true restart. After amending, if new requirements were added to an already-split project, run *split-brd (or the build phase the report points at) to carry them into the build.

    - -

    7.10 Greenfield UI mockups (*mockups)

    - -

    A greenfield app has no code, so its UI is built freehand from prose — which is exactly why a new app's UI comes out broken (overlapping controls, wrong layout, nothing to verify against). The analyst produces mockups at day-1 to give the build an approved visual contract and the verifier a baseline to diff against:

    - -

    /analyst *mockups MyNewApp

    - -

    Reads the TrBlazeUI component catalog FIRST (so it designs only with controls that exist), then produces docs/<APP>-UIDesign.md — a per-screen spec with a region → TrBlazeUI control component map — plus rendered docs/mockups/*.html styled to look like TrBlazeUI, one per key screen from the BRD §9 feature catalog. Auto-run inside *day1-greenfield; re-runnable with *mockups <APP> --update to refresh only changed screens after an *amend-docs that added UI. The mockups are approved alongside the BRD + Architecture, are what /trblazeui builds from, and are the image the visual-truth gate (§4b) diffs the live screen against. Mockups are greenfield only — a greenfield app has no code to screenshot yet, so its mockups are the pre-build visual contract; brownfield already has built code and instead reviews real screenshots in the DevGuide's OBSERVE pass (§7.6). Note this is about mockups, not screenshots: once a greenfield app is built, its DevGuide OBSERVE pass captures real per-screen screenshots exactly like brownfield (§6). Task: .tfcore/tasks/mockups.md.

    - -

    7.11 Fixing issues you found by running the app (*fix-issues)

    - -

    When the verifier passed but the running UI is still broken — overlapping controls, blank screens, wrong data — there is ONE front door:

    - -

    /flow-master *fix-issues MyApp ./bugshots

    - -

    Drop a folder of screenshots (plus an optional bugs.md describing what's wrong on each) and flow-master: reads every screenshot (vision), reproduces each issue live with Playwright, triages it (layout / data / logic / RAG), and fans the fix out to the right builder/trblazeui for layout, its own subagents for data/logic, /techierag for RAG. You never invoke a builder agent yourself — flow-master calls them as sub-agents. It then re-smokes (data + visual) and re-verifies the affected REQs, and updates the DevGuide + the one checklist + PROJECT-STATUS. This is the answer to "the verifier passed but the UI is broken — who do I call?". Task: .tfcore/tasks/fix-issues.md. Only want the bugs analyzed and logged, not fixed? That's *triage-issues (§7.12).

    - -

    7.12 Analyzing UAT / production bugs WITHOUT fixing (*triage-issues)

    - -

    Bugs found at UAT or in production usually need a plan and a paper trail before anyone touches code: which REQs regressed, what's a brand-new defect, what still passes. *fix-issues is the wrong tool for that moment — its deliverable is fixed code. The analyze-only front door is:

    - -

    /flow-master *triage-issues MyApp ./uat-bugs  ·  add verify to also regression-re-verify sibling features

    - -

    Hand it a folder of screenshots and/or a written bug list (UAT reports often arrive as prose). Flow-master reproduces each issue live with Playwright and delivers documentation only: regressed REQs are demoted to Needs re-verify with a dated ⚠ UAT bug remark, unspecified defects become new Planned REQ rows with acceptance criteria, the DevGuide's known-issues lines are refreshed, and PROJECT-STATUS's "Next command" points at *fix-issues {App} {folder} naming the REQ IDs. With the optional verify argument it also EXECUTES a scoped verify-phase over the affected screens' sibling REQs ("check the rest still works"). It never edits src/ or tests/ and never spawns a builder sub-agent — fixing is a separate decision you make afterwards by running *fix-issues. Task: .tfcore/tasks/triage-issues.md.

    - - -

    8. Resuming a cold project#

    - -

    Two flavours of resume. Pick by asking one question: do you trust PROJECT-STATUS.md?

    - -
    - 8a. The session BROKE mid-phase → run *refresh-status first. -

    If the last session died in the middle of a build or verify — lost internet, model access revoked/changed mid-run, terminal killed, agent crashed — then the mandatory status gate never ran. PROJECT-STATUS.md is now stale or wrong: it may point at an old "Next command", undercount work that landed after its last write, or claim a passing build that's since broken. Do NOT trust it. Run the recovery command, which rebuilds PROJECT-STATUS from ground-truth evidence (the checklist Requirements Status table + what's actually in the working tree, including file modification times + a fresh dotnet build — no git; git is manual in this framework) and tells you the exact command to resume with:

    -

    /TechieFlow:agents:flow-master *refresh-status <APP>  —  OpenCode: /flow-master *refresh-status <APP>  ·  add verify to also re-run the verifier on any REQ whose true state is ambiguous.

    -

    It never edits source code — it only reconstructs status. When it finishes, follow its "next command", then drop into the clean flow below. This is the answer to "an experimental model lost access mid-development and I don't know where it left things."

    -
    - -

    8b. Clean cold resume (you came back to a project whose last phase did finish and wrote PROJECT-STATUS): trust the file and walk the four steps.

    - -
    -
    -
    1
    -
    -

    Open three HTML files in browser

    -

    PROJECT-STATUS.html (where am I), <APP>-BRD.html (why was I doing this), <APP>-Architecture.html (what's the shape). < 5 min.

    -
    -
    -
    -
    -
    2
    -
    -

    "What changed" check

    -
    cd /path/to/project
    -git log --oneline -20 && git status
    -dotnet build
    -
    -
    -
    -
    -
    3
    -
    -

    Re-verify

    -

    /verifier"Re-verify the checklist's Requirements Status table and run standards-compliance greps from docs/<APP>-Coding-Standards.md. Note regressions."

    -
    -
    -
    -
    -
    4
    -
    -

    Execute the "Next command" from PROJECT-STATUS.md

    -
    -
    -
    - -
    - Discipline: every phase (build, verify, handoff) ends with /flow-master updating PROJECT-STATUS. Never skip it. And when a run is cut off before it can — *refresh-status <APP> (§8a) is the safety net that rebuilds it from evidence. -
    - - -

    9. Custom library agents — routing rules#

    - -

    /trblazeui and /techierag are library agents — flow-master calls them as sub-agents during *build-phase and *fix-issues (and the analyst calls /trblazeui for its component catalog during *mockups). You drive the build/fix through flow-master; it routes each REQ cluster to the right builder by its prefix.

    - - - - - - - - - -
    TriggerRouted (by *build-phase / *fix-issues) toReads (besides <APP>-Coding-Standards.md)
    REQ-UI-* — UI work (pages, components, forms, dashboards)/trblazeui (sub-agent) — builds from the mockups.trblazeui/TrBlazeUI-AI-Reference.md + the Checklist + docs/mockups/*.html
    REQ-RAG-* — AI/RAG/LLM (embedding, vector store, chat, tools)/techierag (sub-agent).techierag/TechieRag-AI-Reference.md + the Checklist
    REQ-FN-* / REQ-NFR-* — backend / business logic / data / APIs / non-functional/flow-master own subagentsthe Checklist + Architecture
    Reverse-doc, BRD/Architecture/Standards, requirements splitting, mockups/analystcodebase / brief
    Verification of any scope (data + visual gates)/verifierthe Checklist + DevGuide + running app + standards grep checks
    Unified build / fix-issues / HTML renders / status / handoff / consolidation/flow-mastereverything
    - -

    9.1 Adding a new custom library agent

    -
      -
    1. Library's NuGet adds an MSBuild target that copies on consumer build: .<libname>/<LibName>-AI-Reference.md, .claude/<libname>.md, .opencode/command/<libname>.md.
    2. -
    3. Agent file's frontmatter: description, mode: primary, tools. Body: load reference doc on activation + REQUIRED READING clause for docs/<APP>-Coding-Standards.md.
    4. -
    5. Add routing row to the table above.
    6. -
    7. The new library gets its OWN feedback file — <APP>-<LibName>-Feedback.md with its own issue-ID prefix, from the shared template. Never share a feedback file between libraries.
    8. -
    9. Update build-phase's cluster-routing (and fix-issues's triage) to route the new ID prefix to the new sub-agent.
    10. -
    - -

    9.2 Library issue tracking flow

    -
      -
    1. Implementing agents log on encounter — into the OWNING library's file (<APP>-TrBlazeUI-Feedback.md for TR-NNN, <APP>-TechieRag-Feedback.md for TR-RAG-NNN). Clause baked into §7 prompts.
    2. -
    3. Verifier escalates library bugs → Blocked status in the checklist's Requirements Status table + entry in that library's feedback file.
    4. -
    5. /flow-master consolidates each file separately at handoff: dedupe, severity sort, summary header (splits + archives any legacy combined <APP>-Library-Feedback.md).
    6. -
    7. You hand each file to its team — they use this same framework, so the file drops straight into their flow — or file as GitHub issues: gh issue create --repo your-org/TrBlazeUI --title "…" --body-file …
    8. -
    - - -

    10. Coding standards — how enforcement actually works#

    - -

    "Every agent follows the standards" is achieved through three layered mechanisms. None alone is sufficient; all three together close the gaps.

    - -
    -
    -

    1. Prompt-level (every session)

    -

    Every implementation prompt in §7 starts with "REQUIRED READING: docs/<APP>-Coding-Standards.md". The agent loads it before writing any code. Cross-harness reliable.

    -
    -
    -

    2. CLAUDE.md auto-load

    -

    Claude Code auto-loads CLAUDE.md at project root into every session. It says "Always follow docs/<APP>-Coding-Standards.md before any code write." Catches even direct user prompts that forgot the boilerplate.

    -
    -
    -

    3. .editorconfig + verifier greps

    -

    Machine-checkable rules go in .editorconfig (Roslyn enforces in the IDE / build). Non-checkable rules (a/v prefixes, test-name underscores) become grep patterns the verifier runs in the verify pass.

    -
    -
    - -
    - Standing standard — Serilog file logging in EVERY .NET app (2026-07-09). Every executable head — Blazor web, API, MAUI, desktop, console/CLI, background service — wires Serilog with a rolling file sink (logs/<app>-.log, daily rolling; MAUI/desktop root it in the per-app data dir) at startup, logs unhandled exceptions, and exposes app logging only through ILogger<T> (class libraries reference logging abstractions, never Serilog). This is baked in end-to-end so you never have to ask: the BRD template carries a standing Observability NFR, day-1 always emits it (brownfield marks it Done (pre-existing) when an equivalent stack is already wired), *split-brd turns it into a REQ-NFR-* row, the coding-standards §Logging block carries the wiring recipe, and *build-phase wires Serilog into any new head it scaffolds even when no REQ names logging.
    - -
    - Standing standard — the primary head carries the PRODUCT name (2026-07-10). The product's primary executable head project is named exactly <APP> (src/<APP>/<APP>.csproj); a single-head product's one head IS <APP>. <APP>.App is banned — "App" says nothing the product name doesn't. Secondary heads of a multi-head product take a descriptive dotted suffix (<APP>.Api, <APP>.Desktop, <APP>.Cli); satellites keep their conventional names (<APP>.Core, <APP>UI RCL, <APP>.Core.Tests). Baked into the coding-standards canonical block (day1-brownfield §4 → "Project & solution naming") and *build-phase §3 (scaffold-time rule; an existing <APP>.App gets a rename REQ, never propagated).
    - -

    The grep patterns belong inside docs/<APP>-Coding-Standards.md under an "Enforcement" section. Sample grep block (already in §11.3 template):

    - -
    # Forbidden field forms
    -grep -rE "private(\s+readonly)?\s+\w+\s+_[a-z]" src/    # underscore prefix
    -# Instance field must start with `obj` (not bare PascalCase, not _underscore)
    -grep -rE "private(\s+readonly)?\s+\w+\s+(?!obj)[A-Z]\w+\s*[;=]" src/ | grep -v "static\|const"
    -
    -# Forbidden test-method form
    -grep -rE "public\s+(async\s+)?Task\s+\w+_\w+\s*\(" tests/
    -
    -# Parameter without a-prefix (heuristic; grep catches "(string Foo" but not all cases)
    -grep -rE "\(\s*\w+\s+[A-Z]\w+\s*[,)]" src/
    - - -

    11. MAUI builds & runs — from WSL (bridged) or macOS (native)#

    - -

    WSL (Windows) — bridge every dotnet call to the Windows side via winrun (§0):

    -
    cd /mnt/c/path/to/maui-project
    -winrun "dotnet build -c Release"
    -winrun "dotnet test"
    -winrun "dotnet build -t:Run -f net9.0-windows10.0.19041.0"
    -

    macOS — no bridge; dotnet runs natively (ladder §A):

    -
    cd /path/to/maui-project
    -dotnet build -c Release
    -dotnet test
    -dotnet build -t:Run -f net9.0-maccatalyst      # desktop head on Mac = Mac Catalyst
    -dotnet build -t:Run -f net9.0-android          # Android head (emulator via Android Studio)
    - -

    On macOS the Windows head (net9.0-windows…) can't build — the Mac desktop head is Mac Catalyst, and iOS builds natively too (Xcode required, §16). The winrun lines apply only inside WSL.

    - -

    For verifier on a MAUI Windows app: "This is a MAUI Windows app. Build/run/test via `winrun`. UI automation: FlaUI or Appium-Windows-driver Windows-side, NOT Playwright. Output evidence the same as Blazor projects." On a Mac the equivalent prompt names the Catalyst head and the local mac2 Appium driver instead.

    - -

    Mobile & Mac-desktop heads — runtime-observe over Appium

    -

    The §4a data-render and §4b visual-truth gates reach the MAUI Android / iOS / Mac Catalyst heads through an Appium WebDriver endpoint — the native analogue of Playwright (same screenshot + element-tree evidence, so the gates run unchanged). One-time host setup is §0b; the per-head driver map lives in build-invocation-ladder.md §D. Builds don't change — on WSL, Android still builds via cmd.exe (ladder rung #4) and iOS/Catalyst on the paired Mac; on a Mac-native setup all three build locally with plain dotnet build and the Appium endpoints are all localhost. This is purely how the verifier reaches the running UI after a green build.

    - - - - - - - - -
    HeadWhere it runs (WSL setup)Appium driverWSL reaches it viamacOS-native reaches it via
    MAUI Androidemulator on the Windows host (Android SDK)uiautomator2http://localhost:4723 (mirrored networking); verifier boots emulator + Appium itselfhttp://localhost:4723 — emulator + Appium run on the Mac itself
    MAUI iOSSimulator on a LAN Macxcuitesthttp://<mac-ip>:4723; Mac must be up or head is ⚠ STATIC-ONLYhttp://localhost:4723 — local Simulator (Xcode)
    MAUI Mac Catalystthe same LAN Mac (desktop .app)mac2http://<mac-ip>:4723http://localhost:4723 — the .app runs right here
    MAUI WindowsWindows sideFlaUI / Appium-Windows (unchanged)winrun / cmd.exen/a — this head doesn't exist on a Mac
    -

    Selectors target each control's AutomationId (a coding standard, §10). A head with no registered endpoint in core-config.yaml → runtimeVerification.appium, or an unreachable host, is stamped ⚠ STATIC-ONLY for that head — never a faked Verified.

    - -
    - Window binding & input discipline (all native heads, especially MAUI Windows): the driver session is bound to the app under test by identity — the PID the agent launched → that process's top-level window handle (Appium Windows appium:appTopLevelWindow / FlaUI Application.Attach(pid)), or the app package/bundle id on mobile — and every interaction is element-scoped via AutomationId inside that bound window, with focus verified before input and handles re-resolved after dialogs. Global keyboard/mouse injection (FlaUI Keyboard.Type, coordinate clicks, SendKeys) is banned: it types into whatever window happens to hold focus — historically, a completely different window than the app. Full rules: verify-phase.md §3b. -
    - - -

    12. Permissions (yolo-except-git-writes)#

    - -
    - TechieFlow's *yolo IS the permission switch now (2026-08-21): *yolo — or the word YOLO in any command, an active Claude Code /goal, or a tf-goal.sh run — writes the flag .tfcore/.session/yolo.json; the PreToolUse hook reads it and stops asking for deletes/sudo, allows read-only git, and the agent runs to completion with no pauses. Git writes stay denied in every mode. Rule: .tfcore/tasks/_yolo-mode.md; details in §12a below. -
    - -

    The pre-built config auto-allows Read/Glob/Grep/Edit/Write/MultiEdit and all Bash (bare "Bash") — so create/update/move run with zero prompts. Deletes and sudo ask via the hook (not a settings ask rule — Claude Code honours a settings ask even in bypass mode and even when a hook says allow, so it could never be switched off). Denies catastrophic rm -rf root/home paths and every git/gh WRITE subcommand (commit|push|add|reset|checkout|switch|restore|merge|rebase|stash|clean|pull|fetch|…, gh pr|issue|repo|release create|merge|close|…) — git is manual in TechieFlow; agents never write it, so it is a hard deny in every permission mode. Precedence is deny → ask → hook → allow. (Cross-project tip: to let a session work in another app's folder without per-path prompts, add that root to permissions.additionalDirectories in this project's settings.json — keep those machine-specific paths out of any shared template.)

    - -
    - The git ban is TWO layers, because prefix rules alone leak. Bash(git commit*) is a literal prefix match — it never sees cd src && git commit or echo done; git add -A, which the bare "Bash" allow would wave straight through. That is exactly how agents kept "accidentally" running git during status updates. So the config also wires a PreToolUse hook.tfcore/hooks/block-git.sh — that parses every Bash call (compound forms, bash -c, eval, $(…), wrappers like sudo/env/xargs) and classifies each git/gh node as read (status/log/diff/show/blame/grep/branch/tag -l/stash list/remote -v/config --get, gh pr list|view) or write (everything else). Writes are blocked always; reads are blocked outside YOLO and allowed in YOLO. The block message carries the local-evidence recipe (checklist tables + working-tree files + fresh build) so the agent continues correctly instead of flailing. You still run git yourself: in a separate terminal, or by typing !git … in the session (user-typed bang commands bypass agent tool permissions). -
    - -
    - Why bare "Bash" and NOT a per-command list: a Bash(prefix *) rule is a literal prefix match. On WSL the agent can't call plain dotnet (not in PATH) — it calls ~/.dotnet/dotnet build, cmd.exe /c "dotnet build", dotnet dev-certs, or compound cd … && …, none of which match an enumerated Bash(dotnet *). The old list-everything config prompted ~100× in a single UI phase. Allowing all Bash and restricting only deletes/sudo (ask) and git/gh (deny + hook) is immune to command-path variants. -
    - -
    - Config (canonical version in scaffold-brownfield.sh / scaffold-greenfield.sh) -
    {
    -  "permissions": {
    -    "defaultMode": "acceptEdits",
    -    "allow": [
    -      "Bash",
    -      "Edit", "Write", "MultiEdit", "NotebookEdit",
    -      "Read", "Glob", "Grep", "TodoWrite", "WebFetch", "WebSearch", "Task"
    -    ],
    -    "ask": [],
    -    "deny": [
    -      "Bash(rm -rf /)", "Bash(rm -rf /*)", "Bash(rm -rf ~)", "Bash(rm -rf ~/*)",
    -      "Bash(git commit*)", "Bash(git push*)", "Bash(git add*)", "Bash(git reset*)",
    -      "Bash(git checkout*)", "Bash(git switch*)", "Bash(git restore*)", "Bash(git merge*)",
    -      "… every other git WRITE subcommand (rebase, cherry-pick, revert, clean, pull, fetch, init, clone, …) …",
    -      "Bash(gh pr create*)", "Bash(gh pr merge*)", "Bash(gh issue create*)", "… every gh WRITE verb …"
    -    ]
    -  },
    -  "hooks": {
    -    "PreToolUse": [
    -      { "matcher": "Bash",
    -        "hooks": [ { "type": "command",
    -                     "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/block-git.sh\"" } ] },
    -      { "matcher": "Write|Edit|MultiEdit",
    -        "hooks": [ { "type": "command",
    -                     "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" },
    -                   { "type": "command",
    -                     "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify.sh\"" } ] }
    -    ]
    -  }
    -}
    -
    - -
    - PROJECT-STATUS shape is enforced mechanically too (2026-07-09). A second PreToolUse hook — .tfcore/hooks/guard-status.sh, matcher Write|Edit|MultiEdit — blocks any write to PROJECT-STATUS.md that violates the crisp fixed-shape snapshot rule: an H2 outside the template's section set (per-run dated sections like ## *verify all — coverage matrix (DATE) are the classic disease), a heading naming a command run, a paragraph stuffed into current_phase:, or a full-file write past ~120 lines. The block message tells the agent exactly how to reshape (overwrite the template sections in place, ONE Verification-log row per run, detail into the checklist Remarks). Same philosophy as the git ban: prose rules kept failing, so the harness enforces it. See .tfcore/tasks/_status-update-gate.md. -
    - -
    - Verified verdicts are enforced mechanically too (2026-07-10). A third PreToolUse hook — .tfcore/hooks/guard-verify.sh, matcher Write|Edit|MultiEdit — blocks any write to a *-Checklist.md that introduces a Verified status cell unless a same-day run ledger docs/.last-verify.json exists, which only an executed verify-phase run writes (verify-phase §6: boot → scoped tests → §4a data-render + §4b visual-truth gates → ledger → verdicts). This exists because a build orchestrator did its own smoke and wrote the Verified verdicts itself (TrSetup, 2026-07-09) — self-attestation the "chain the verifier" prose didn't stop. A self-smoke's ceiling is Implemented (_smoke-test-policy.md §"Smoke is NOT verify"); *refresh-status may reconcile a lagging Status column to a row's pre-existing dated verdict by writing the ledger with "mode":"reconcile". Demotions (e.g. Verified → Needs re-verify) are never blocked. -
    - -
    - Existing project still prompting? Just run the normal framework update — update-framework.sh now refreshes settings.json to this config by default, so every app stays in sync without any special flag: -

    WSL (Windows):

    -
    /mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/app
    -

    macOS:

    -
    /Volumes/MacD/MyCode/TechieFlow/update-framework.sh /path/to/app
    - It's idempotent (skips the write when already current), backs up any differing old file to settings.json.bak, and never touches settings.local.json. Restart Claude Code afterward. To opt out for a deliberately locked-down project, add --keep-permissions. -
    - - -

    12a. YOLO / goal mode — "I've given you all the permissions; run until it's done" (2026-08-21)#

    - -

    *yolo used to be agent-side only (skip elicitation) and the owner still got prompted for every delete and blocked on every git read — which is how a VM goal run took 3 days, mostly waiting for a human. Now *yolo, the word YOLO anywhere in a command, an active Claude Code /goal, or a tf-goal.sh run all mean the same thing, defined in .tfcore/tasks/_yolo-mode.md:

    - - - - - - - - - - - -
    NormalYOLO
    rm / rmdir / sudohook asksallowed, no prompt (catastrophic rm -rf //~ still denied)
    git/gh reads (status/log/diff/blame, gh pr view)blockedallowed
    git/gh writes (commit/push/add/reset/checkout/stash/tag, gh pr create|merge)blockedblocked — always
    Elicitation, phase-boundary pauses, "confirm the BRD-N list", "ask once" questionspausedecide the default, record it, continue
    Build pass scopewhole checklist (§2b)whole checklist + automatic FIX loop on verifier FAIL rows (build-phase §6c, ≤5 cycles)
    Turn endingmay hand backonly when the goal is complete (tf-yolo.sh done complete) or every remaining REQ is owner-gated (done blocked)
    - -
    - Mechanics. The flag is .tfcore/.session/yolo.json (bash .tfcore/utils/tf-yolo.sh on|off|status; never committed). block-git.sh reads it (plus TF_YOLO=1 and the hook payload's permission_mode — Claude Code's bypassPermissions/auto count as YOLO); the OpenCode plugin reads the same flag and auto-approves its rm */sudo * asks via permission.ask. Why the delete prompt moved out of settings.json: Claude Code honours a settings ask rule even in bypass mode and even when a hook says allow — so as long as Bash(rm *) sat in ask, no mode could stop the prompt. A hook-issued ask can be withheld; a settings ask cannot. -
    - -
    -
    Codex permission difference. .codex/hooks.json routes shell and file changes through .tfcore/hooks/codex-adapter.py, and .codex/rules/techieflow.rules supplies command policy. Codex blocks every agent-issued git and gh command in normal and YOLO modes, including reads. $techieflow-yolo changes TechieFlow pause/delete behavior but never relaxes this boundary. Trust the repository and inspect /hooks after scaffold/update.
    - - Usage limits (5-hour / weekly). Nothing inside a session can wait a limit out, so the wait lives in a supervisor: -
    bash .tfcore/utils/tf-goal.sh /path/to/App "Take MyApp to Handoff: build every open REQ, verify all, fix until every row is Verified."
    -bash .tfcore/utils/tf-goal.sh --harness opencode --model opencode-go/kimi-k3 /path/to/App @goal.md
    -bash .tfcore/utils/tf-goal.sh --resume /path/to/App      # after a reboot
    - It runs the goal headless (claude -p --permission-mode bypassPermissions / opencode run --auto), parses the reset time from the limit message (resets 7pm (Asia/Kolkata), resets in 2h 14m, usage limit reached|<epoch>, weekly resets Tue 3pm), sleeps until reset + 15 min (--buffer-min), logs RETRY AT … to .tfcore/.session/goal.log, and resumes the same session. Crashes back off 2 → 30 min; an agent that stops without finishing is re-prompted; it exits only on the agent's sentinel (0 complete, 3 owner-blocked, 4 max cycles). The agent's side of the bargain is the status gate — every phase ends with PROJECT-STATUS + checklist written, so a resume is lossless. -
    - -
    - Build passes are whole-checklist, YOLO or not. The other 3-day culprit: build-phase runs that implemented a few REQs, wrote "next command: *build-phase for the remaining REQs" and stopped. build-phase.md §2b bans that ending — a pass is done when every working-list REQ is ≥ Implemented (or a logged Blocked/owner-gated blocker), the verifier has been chained, and (in YOLO) its FAIL rows have been looped. Long list ⇒ more sub-agent clusters, never a shorter pass. _status-update-gate.md item 5 carries the matching rule for the next-command line. -
    - -

    13. Agent cheat sheet#

    - -
    - These six are the COMPLETE agent roster. The stock TechieFlow story-flow agents (dev, pm, po, qa, sm, ux-expert) and their story tasks/templates/workflows were trimmed from the scaffold on 2026-06-12 — the compressed flow never used them, and several behaved wrongly in it (wrong stack assumptions, dated docs/qa outputs). They are no longer included; obtain a full story-by-story agent set separately if you ever need that classic flow. -

    - Which agent when: -
      -
    • Starting or re-documenting a project → /analyst (day-1 tasks, mockups, split-brd).
    • -
    • Writing code → /flow-master *build-phase (the ONE unified build — it calls /trblazeui and /techierag as sub-agents; you don't invoke them directly).
    • -
    • Proving it works → /verifier (*verify ui|functional|all — filters the one checklist; data + visual gates).
    • -
    • Verifier passed but the running UI is broken → /flow-master *fix-issues (drop screenshots; it triages + routes the fix).
    • -
    • UAT / production bugs you want analyzed + logged in the checklist, NOT fixed yet → /flow-master *triage-issues (docs-only deliverable; fixing stays your call).
    • -
    • Developer code-map (screen → control → service → proc) → /flow-master *devguide.
    • -
    • End-user how-to manual (what each screen is for + how to do things) → /flow-master *productguide.
    • -
    • Docs/HTML/status/handoff chores → /flow-master.
    • -
    • Architecture deep-dive beyond what day-1 produced → /architect (optional).
    • -
    -
    - - - - - - - - - -
    CommandRoleBest forWrites
    /analystChanakya, business analystReverse-doc; BRD; Architecture; Coding Standards; mockups (*mockups, greenfield); the one Checklist (*split-brd); status init; .editorconfig; CLAUDE.md<APP>-BRD.md, <APP>-Architecture.md, <APP>-Coding-Standards.md, <APP>-UIDesign.md + docs/mockups/*.html, <APP>-Checklist.md, PROJECT-STATUS.md, CLAUDE.md, .editorconfig
    /trblazeuiBlazor + TrBlazeUI (library agent — called as a sub-agent by *build-phase / *fix-issues)REQ-UI-* per the mockups; reads coding standardssrc/ (Razor), <APP>-TrBlazeUI-Feedback.md
    /techieragTechieRag RAG/LLM (library agent — called as a sub-agent by *build-phase / *fix-issues)REQ-RAG-* items; reads coding standardssrc/ (RAG services), <APP>-TechieRag-Feedback.md
    /flow-masterMadhav, master & orchestratorThe single super-agent. Runs the unified *build-phase (clusters all open REQs, calls /trblazeui + /techierag as sub-agents, builds REQ-FN-*/REQ-NFR-* itself, self-smokes data+visual, chains the verifier); the *fix-issues bug-fix front door (triages screenshots → routes the fix) and its analyze-only sibling *triage-issues (logs UAT/prod bugs in the checklist, never fixes); HTML renders, status refresh, consolidation, handoff doc; the screen-by-screen Developer Guide (*devguide) and the end-user Product Guide (*productguide)src/, tests/unit/, <APP>-BRD.html, <APP>-Architecture.html, PROJECT-STATUS.md/html, <APP>-UsageGuide.md, <APP>-DevGuide.md/html, <APP>-ProductGuide.md/html, <APP>-Checklist.md (verdicts), <APP>-<Library>-Feedback.md (per-library consolidation)
    /verifierVidur, autonomous test runnerVerifying any scope + standards grep checks + the render gate (§4a) (every control listed in the DevGuide renders its data) AND the visual-truth gate (§4b) (no overlap, every control in-viewport and non-zero-size at desktop + mobile, screenshot inspected, diffed against the mockup when one exists) AND the perf gate (§4c) on any REQ declaring a perf-budget:. A REQ is Verified only if acceptance passes AND data renders AND the screen looks right. Done (pre-existing) gets the full sweep — stays Done only if it runtime-renders + looks right, else Needs re-verify. After each run Vidur writes verdicts to the one checklist AND refreshes the DevGuide's observed render/visual tags.<APP>-Checklist.md Requirements Status table (verdicts), DevGuide observed render/visual tags, tests/playwright/*, <APP>-<Library>-Feedback.md (on library bugs — the owning library's file)
    /architectSolutions architectOptional deep arch dive; /analyst does basic architecture by default<APP>-Architecture.md (delegated by analyst, optional)
    - - -

    14. Full command reference#

    - -
    - Slash-command syntax — Claude Code vs OpenCode: - Claude Code registers TechieFlow-native agents under the path-derived namespace TechieFlow:agents:<name>. OpenCode registers the equivalent agents as /flow-master, /flow-analyst, /flow-architect, and /flow-verifier. Each command cell below is an exact, copyable command. Use the macOS framework path where applicable. -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    GoalCommand Purpose / DefinitionClaude CommandOpenCode Command
    Scaffold (brownfield)Deploy the framework into an existing application.
    ./scaffold-brownfield.sh /path/to/existing-app
    ./scaffold-brownfield.sh /path/to/existing-app
    Scaffold (greenfield)Deploy the framework and starter folders into a new application.
    ./scaffold-greenfield.sh /path/to/new-app
    ./scaffold-greenfield.sh /path/to/new-app
    Update frameworkForce-refresh framework files in an existing project; preserves work product.
    /mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/app
    /mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/app
    Render MD → HTMLRe-render one or more human-facing Markdown documents as HTML.
    /generate-html @docs/File.md
    /generate-html @docs/File.md
    Day-1 brownfieldReverse-document an existing app and create the day-1 deliverables, UsageGuide, and DevGuide.
    /TechieFlow:agents:analyst *day1-brownfield {AppName}
    /flow-analyst *day1-brownfield {AppName}
    Day-1 greenfieldCreate the BRD, Architecture, Coding Standards, status, CLAUDE.md, and UI mockups for a new app.
    /TechieFlow:agents:analyst *day1-greenfield {AppName}
    /flow-analyst *day1-greenfield {AppName}
    Re-render workflow docsRefresh the BRD, Architecture, and PROJECT-STATUS HTML documents.
    /TechieFlow:agents:flow-master *render-workflow-docs {AppName}
    /flow-master *render-workflow-docs {AppName}
    Greenfield UI mockupsCreate or update the per-screen UI design specification and TrBlazeUI-styled mockups.
    /TechieFlow:agents:analyst *mockups {AppName}
    /flow-analyst *mockups {AppName}
    Split BRD → ChecklistTurn BRD items into the single Checklist with REQ-UI/FN/RAG/NFR requirements.
    /TechieFlow:agents:analyst *split-brd {AppName}
    /flow-analyst *split-brd {AppName}
    Amend docsApply an incremental requirements or architecture change without regenerating unchanged documents.
    /TechieFlow:agents:analyst *amend-docs {AppName} "<what changed>"
    /flow-analyst *amend-docs {AppName} "<what changed>"
    Build — unified phaseBuild all open REQs, route UI/RAG work to library agents, self-smoke, and chain verification.
    /TechieFlow:agents:flow-master *build-phase {AppName}
    /flow-master *build-phase {AppName}
    Fix issuesReproduce screenshot-reported issues, route fixes, then re-smoke and re-verify.
    /TechieFlow:agents:flow-master *fix-issues {AppName} {folder}
    /flow-master *fix-issues {AppName} {folder}
    Triage UAT/production bugsAnalyze and log bugs without editing code; optionally regression-verify sibling features.
    /TechieFlow:agents:flow-master *triage-issues {AppName} {evidence} [verify]
    /flow-master *triage-issues {AppName} {evidence} [verify]
    Verify gates and standardsRun acceptance tests, standards greps, data-render, visual-truth, and applicable performance gates.
    /TechieFlow:agents:verifier *verify <scope>
    /flow-verifier *verify <scope>
    End of sessionUpdate project status and regenerate its HTML representation.
    /TechieFlow:agents:flow-master Update PROJECT-STATUS.md (phase, next, log); regenerate PROJECT-STATUS.html.
    /flow-master Update PROJECT-STATUS.md (phase, next, log); regenerate PROJECT-STATUS.html.
    Final handoffGenerate the final UsageGuide, DevGuide, status, and library-feedback consolidation.
    /TechieFlow:agents:flow-master *handoff-phase {AppName}
    /flow-master *handoff-phase {AppName}
    Generate / refresh Developer GuideMap the implemented screens, observe the running app, and reconcile the Developer Guide.
    /TechieFlow:agents:flow-master *devguide {AppName}
    /flow-master *devguide {AppName}
    Generate / refresh Product GuideGenerate the screenshot-illustrated, task-oriented manual for external users.
    /TechieFlow:agents:flow-master *productguide {AppName} [scope] [--update]
    /flow-master *productguide {AppName} [scope] [--update]
    Token efficiency guideOpen the framework guidance for reducing unnecessary AI context and token usage.
    less .tfcore/TOKEN-GUIDE.md
    less .tfcore/TOKEN-GUIDE.md
    Recover broken sessionRebuild PROJECT-STATUS from ground truth after an interrupted phase.
    /TechieFlow:agents:flow-master *refresh-status {AppName}
    /flow-master *refresh-status {AppName}
    MAUI build + testBuild and test the MAUI project through the Windows bridge or natively on macOS.
    winrun "dotnet build && dotnet test"
    winrun "dotnet build && dotnet test"
    Resume projectCheck the working tree and perform a fresh build before continuing.
    git status && dotnet build
    git status && dotnet build
    Log a miss (§17)Record one missed requirement as telemetry + a checklist line. No boot, no repro, no code.
    /TechieFlow:agents:flow-master *log-miss {AppName} "{what was missed}"
    /flow-master *log-miss {AppName} "{what was missed}"
    Development telemetry reportGenerate the aggregated development metrics report.
    /TechieFlow:agents:flow-master *metrics {AppName}
    /flow-master *metrics {AppName}
    Telemetry quick lookPrint the read-only telemetry report directly in the terminal.
    .tfcore/telemetry/tf-metrics.sh --report .
    .tfcore/telemetry/tf-metrics.sh --report .
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    GoalCommand (Claude Code form)
    Scaffold (brownfield)./scaffold-brownfield.sh /path/to/existing-app (or run from the template repo)
    Scaffold (greenfield)./scaffold-greenfield.sh /path/to/new-app (or run from the template repo)
    Update framework in existing project (§3)WSL: /mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/app · macOS: /Volumes/MacD/MyCode/TechieFlow/update-framework.sh /path/to/app (add --dry-run to preview; restart Claude Code after)
    Render any MD → HTML (ad-hoc)/generate-html @docs/File.md (multiple @paths ok; @dir/ = top-level *.md, non-recursive; day-1 auto-renders its own docs — this is for re-renders after edits)
    Day-1 brownfield (6 deliverables + UsageGuide + DevGuide)/TechieFlow:agents:analyst *day1-brownfield {AppName}
    Day-1 greenfield (6 deliverables)/TechieFlow:agents:analyst *day1-greenfield {AppName}
    Re-render BRD/Architecture/Status trio (day-1 renders these automatically)/TechieFlow:agents:flow-master *render-workflow-docs {AppName}
    Greenfield UI mockups (§7.10)/TechieFlow:agents:analyst *mockups {AppName} — per-screen UIDesign spec + TrBlazeUI-styled docs/mockups/*.html; auto-run at greenfield day-1; --update refreshes changed screens. The visual contract the build matches and the verifier diffs against.
    Split BRD → the one Checklist/TechieFlow:agents:analyst *split-brd {AppName} — writes docs/{AppName}-Checklist.md (all REQ-UI/FN/RAG/NFR-* in one Requirements Status table).
    Amend docs as the project evolves (§7.9)/TechieFlow:agents:analyst *amend-docs {AppName} "<what changed>" (or /flow-master) — surgically updates BRD + Architecture in place (append-only IDs), ripples to PROJECT-STATUS / BRD §4 / the checklist, re-renders. Incremental alternative to re-running day-1.
    Build — ONE unified phase (chains self-smoke + verifier)/TechieFlow:agents:flow-master *build-phase {AppName} (OpenCode: /flow-master *build-phase {AppName}) — clusters all open REQs, calls /trblazeui (REQ-UI-*, from the mockups) and /techierag (REQ-RAG-*) as sub-agents, builds REQ-FN-*/REQ-NFR-* itself, self-smokes (data + visual), then chains the verifier. Re-run for FIX mode on flagged REQs. (The old separate UI / RAG / functional build commands are dissolved into this one.)
    Fix issues found by running the app (§7.11)/TechieFlow:agents:flow-master *fix-issues {AppName} {folder} — drop a folder of screenshots (+ optional bugs.md); flow-master reproduces each with Playwright, triages (layout/data/logic/RAG), fans the fix to the right builder (trblazeui / its subagents / techierag), re-smokes + re-verifies, updates DevGuide + checklist + PROJECT-STATUS. The answer to "verifier passed but the UI is broken."
    Analyze + log UAT/production bugs WITHOUT fixing (§7.12)/TechieFlow:agents:flow-master *triage-issues {AppName} {evidence} [verify] — screenshots folder and/or a written bug list; flow-master reproduces each with Playwright, demotes regressed REQs to Needs re-verify (dated ⚠ UAT bug remarks), adds new Planned rows for unspecified defects, optionally re-verifies sibling features (verify), updates DevGuide known-issues + PROJECT-STATUS (next command = the *fix-issues pointer). Never edits code — fixing stays your decision.
    Verify + standards greps + data, visual & perf gates/TechieFlow:agents:verifier *verify <scope> — scope is ui | functional | all | explicit REQ IDs; filters the one checklist by REQ prefix (no separate file). Runs standards-compliance greps, acceptance tests, the render gate (verify-phase §4a: every control renders its data) AND the visual-truth gate (§4b: no overlap, every control in-viewport and non-zero-size at desktop + mobile, screenshot inspected, mockup-diffed where one exists) and, for any REQ declaring a perf-budget:, the perf gate (§4c). A REQ is Verified only if acceptance passes AND data renders AND the screen looks right. Done (pre-existing) gets the full sweep. Verdicts land in the checklist; Vidur also refreshes the DevGuide's observed render/visual tags.
    End-of-session/TechieFlow:agents:flow-master Update PROJECT-STATUS.md (phase, next, log); regenerate PROJECT-STATUS.html.
    Final handoff (incl. DevGuide)/TechieFlow:agents:flow-master *handoff-phase {AppName}
    Generate / refresh Developer Guide/TechieFlow:agents:flow-master *devguide {AppName} — generates docs/{AppName}-DevGuide.md + .html (split per role for large apps) via the 3-pass MAP → OBSERVE → RECONCILE model (§6). Add --update to refresh only changed screens. Stamped ⚠ STATIC-ONLY if the app cannot be booted. Auto-run at handoff; re-runnable anytime. The DevGuide is the verifier's per-control test map — see §6.
    Generate / refresh end-user Product Guide (§6)/TechieFlow:agents:flow-master *productguide {AppName} [scope] [--update] — the screenshot-illustrated, task-oriented how-to manual for EXTERNAL users (what each screen is for + how to do things). Reuses the DevGuide's screen inventory + the screenshots under docs/screenshots/<APP>/ (re-shoots any missing via the verifier render sweep); fans out per role, splitting large apps into docs/productguides/. Always emits MD + HTML. On-demand; --update refreshes only changed screens. The user-facing sibling of the DevGuide.
    Token efficiency guide.tfcore/TOKEN-GUIDE.md — ships with every project. Explains where AI tokens go and the levers to keep usage low (don't load whole docs/repos; checklists markdown-only; fan out to subagents; incremental updates via *amend-docs / *devguide --update; recover with *refresh-status instead of re-running).
    Recover broken session (status stale/wrong)/TechieFlow:agents:flow-master *refresh-status {AppName} — rebuilds PROJECT-STATUS from ground truth (checklist tables + working-tree files & mtimes + fresh build; no git — git is manual) when a phase died before its status gate ran. Add verify to re-verify ambiguous REQs. Never edits source. See §8a.
    MAUI build+testWSL: winrun "dotnet build && dotnet test" · macOS: dotnet build && dotnet test (native, no bridge — §11)
    Resume cold project (status trustworthy)Open PROJECT-STATUS.html + <APP>-BRD.html + <APP>-Architecture.htmlgit status && dotnet build/TechieFlow:agents:verifier re-verify → execute "Next command". If the last session was cut off mid-phase, run *refresh-status first (row above).
    An agent missed something (§17)/TechieFlow:agents:flow-master *log-miss {AppName} "{what was missed}" — a record + the checklist line, in seconds
    Development telemetry report (§17)/TechieFlow:agents:flow-master *metrics {AppName} — writes docs/metrics/METRICS.md + .html
    Telemetry, quick look in the terminal (§17)cd /path/to/app && .tfcore/telemetry/tf-metrics.sh --report . — read-only, no agent
    - -

    Codex command equivalents

    -

    Codex exposes workflows as project skills rather than literal Claude/OpenCode slash commands. Type the $techieflow-* name with its arguments; scaffold/update generates these skills from .tfcore/tasks/.

    - - - - - - - - - - - - -
    WorkflowCodex command
    Day 1$techieflow-day1-brownfield {AppName} / $techieflow-day1-greenfield {AppName}
    Requirements$techieflow-author-brd {AppName} / $techieflow-amend-docs {AppName} "<change>"
    Design / checklist$techieflow-mockups {AppName} / $techieflow-split-brd {AppName}
    Build / verify$techieflow-build {AppName} / $techieflow-verify all {AppName}
    Fix / triage$techieflow-fix-issues {AppName} {evidence} / $techieflow-triage-issues {AppName} {evidence}
    Guides$techieflow-devguide {AppName} / $techieflow-productguide {AppName}
    Handoff / recovery$techieflow-handoff {AppName} / $techieflow-refresh-status {AppName}
    Rendering$techieflow-render-workflow-docs {AppName} / $techieflow-generate-html @docs/File.md
    Metrics / YOLO$techieflow-metrics-report {AppName} / $techieflow-yolo on|off|status
    Unattended goalbash .tfcore/utils/tf-goal.sh --harness codex <app-dir> "<goal>"
    -
    Codex boundary: trust the repository and review /hooks. Every agent-issued git and gh command stays forbidden even in YOLO. The supervisor uses codex exec --json --sandbox workspace-write -c approval_policy="never" and resumes its recorded session after limits or restarts.
    - -

    The trblazeui and techierag personas are NuGet-deployed to .claude/<name>.md and .opencode/command/<name>.md. Claude Code only scans .claude/commands/, so the scaffold/update scripts copy them to .claude/commands/<name>.md (the short /trblazeui /techierag forms then work). If the short form is missing: dotnet build, then re-run update-framework.sh. Library maintainers must update the package source files listed in docs/TechieFlow-Library-Persona-Propagation.md; consumer copies are not the source of truth.

    - - -

    15. FAQ & gotchas#

    - -
    - Rendering docs to HTML burns enormous tokens every phase. Is the model really writing all that by hand? -

    It was, until 2026-08-27. It no longer should be. There was no renderer in the framework: html-render-shell.md was a 494-line prose specification, and both render tasks told the agent to implement it by hand (“Use the Write tool — never bash heredocs”). One phase that re-rendered four documents emitted ~300 KB of HTML, roughly 75–80k output tokens, for documents whose content barely changed — and PROJECT-STATUS.html is mandatory at the end of every phase.

    -

    There is now one command:

    -
    bash .tfcore/utils/tf-render-html.sh docs/MyApp-BRD.md PROJECT-STATUS.md
    -

    It writes a sibling .html for each input and is dependency-free — Python 3 standard library only, no pandoc, no node, no pip. Crucially it extracts the §2 CSS, §3 theme script and §7 JS out of html-render-shell.md at render time rather than duplicating them, so the shell cannot drift from its own documentation: edit the spec and the next render picks it up.

    -

    Three problems went away with the tokens. Drift — hand-authored output was never reproducible, and the old instruction to “verify each HTML mentally” was self-review by the model that had just written it. Truncation — a 130 KB file emitted in one generation could silently cut off, and no gate read rendered output. An unenforceable rule — projects quietly wrote their own renderers instead, diverging from the shell.

    -

    The checklist ban is now mechanical: passing a *-Checklist.md is refused with exit 2 rather than depending on the agent remembering. The renderer also runs the §5.5 Mermaid self-check and warns about unquoted flowchart labels or a reserved end node id — those are defects in the source markdown; fix the diagram and re-render, never edit generated HTML.

    -

    html-render-shell.md remains the specification. Read it to understand or change the shell — you no longer need to read it to render. Reported by the TfLens team as TF-003; it was a gap, not a regression (no task ever referenced a missing script).

    -
    - -
    - Does update-framework.sh update the root opencode.jsonc? Mine looks very old. -

    It does now — it did not until 2026-08-27, and that was a real defect. The 2026-08-20 two-file split moved framework config into the framework-owned .opencode/opencode.jsonc (refreshed every run, wins on conflicting keys) and demoted the root file to project-only keys: extra agents, MCP, LSP. It was then preserved unconditionally, forever.

    -

    A survey of all ten apps carrying the file found that none of them has any project-only content — no project agent, no project command, no mcp or lsp block. Nine held a 138–154 line copy of a template that had grown to 925 lines, and all nine were missing the trblazeui/techierag agent blocks. So the preserve rule protected nothing while guaranteeing the file rotted.

    -

    Worse, two apps still wired build-ui-phase / build-rag-phase / build-functional-phase — commands dissolved on 2026-06-26 — as {file:} references to task files that no longer exist. A dead {file:} ref hard-fails OpenCode’s entire config load, so those repos were silently loading no framework agents or commands at all.

    -

    The root file now refreshes the same way .claude/settings.json does: replaced when nothing project-owned would be lost (old file kept as opencode.jsonc.bak), preserved — with the project-only keys named in the output — when there is, and every dead {file:} ref reported either way.

    -
    - -
    - An agent says a framework file — tf-metrics.sh, a task, a template — is “not present anywhere in this tree”. It obviously is. Why? -

    Because the framework is invisible to every default file-search tool, and until 2026-08-27 nothing told the agents that. Two independent filters stack, and you have to defeat both:

    -
      -
    • .tfcore/, .claude/, .codex/, .opencode/ and .agents/skills/ are hidden dot-directories — ripgrep (which backs the agents’ Grep tool) skips hidden paths by default.
    • -
    • They are also in the managed .gitignore block that the scaffolders write into every app (§3) — and ripgrep honours .gitignore by default too.
    • -
    -

    So rg --hidden is not enough in an app repo; it takes rg --hidden --no-ignore (rg -uu). And because nothing under .tfcore/ is tracked in an app, git grep and git ls-files return zero rows as well. An agent that globs for a filename gets nothing back and reasonably concludes the framework is not installed.

    -

    This is not a bug to fix by un-ignoring the framework. The ignore block is deliberate and load-bearing — the deployed copies are re-synced from the template by update-framework.sh and must never be committed in an app (§3). The fix is on the agent side: every framework file has exactly one canonical path, and whatever needs it names that path, so existence is confirmed by reading the literal path, never by searching for the name. That rule now lives in .tfcore/tasks/_status-update-gate.md §“The framework tree is INVISIBLE to search” (a shared include, so it reaches every checklist-executing task), is restated in verify-phase’s verdict rules, and ships in the AGENTS.md / CLAUDE.md hard rules for new apps.

    -

    Why it matters more than a wasted search: the false negative does not stay in the transcript. It gets written into a checklist Remarks cell, a BRD §4 status row, or a blocker — and the next agent reads it as established fact and inherits the conclusion. A verify pass can close a gate that was never actually blocked.

    -

    When it really is missing: a fresh clone genuinely has no .tfcore/, because it was never committed. That repo needs update-framework.sh <repo> run once on that machine (see §16). One command — never a reason to reimplement what the missing file does.

    -
    - -
    - My repo root is full of test-results/, test-results-cluster-a/ or scripts-cluster-b/ folders. Why? -

    That was a real defect, fixed 2026-08-10. Playwright writes run artifacts to test-results/ by default, and a verify fan-out that wanted per-cluster isolation invented siblingstest-results-cluster-a/, test-results-tr054/, one per run, forever. The ignore block only carried the bare test-results/, which does not match the suffixed names, so they surfaced untracked at every commit — exactly the triage burden the block exists to prevent.

    -

    The fix: verify-phase §1 now pins outputDir: './tests/.artifacts/test-results' in playwright.config.ts (creating the config if absent, editing it if it lacks the setting), bans repo-root artifact directories and --output test-results-<slug> outright, and permits isolation only as a subfolder (--output tests/.artifacts/<slug>). tests/.artifacts/ is in the managed ignore block, and Playwright wipes it at the start of each run, so it stops growing. Legacy root-level dirs are deleted by verify-phase §1 on its next pass, or by you right now — they are pure machine output and fully regenerable.

    -

    The scripts-cluster-*/ variant was the same bug one layer up, fixed the same day. That first fix pinned where the tools write but never said where an agent should put the harness scripts it authors, so the next fan-out simply moved the litter: four scripts-cluster-b|f|g|i/ folders holding hand-written smoke-*.mjs. Those scripts also imported the Playwright library directly, which bypasses playwright.config.ts entirely — so the outputDir pin could never have reached them, and one of them hardcoded an absolute path to a test-results-cluster-b it created itself. The rule now covers both halves: run harnesses go in tests/.artifacts/harness/, /scripts-*/ is in the ignore block, and verify-phase §1 sweeps root-level scripts-*/ alongside test-results*/. Your own scripts/ folder is safe — the pattern requires the hyphen and the root anchor, and the sweep excludes it explicitly.

    -

    What are the screenshots even for? Failure evidence with a session lifetime: screenshot: 'only-on-failure' means a passing run produces almost nothing, and the ones a failing run produces exist so §6 can cite a path in a checklist Remark today. They are never work product and never committed. The screenshots that are deliberately kept and tracked are the DevGuide's reviewed set under docs/screenshots/<APP>/ — a different thing entirely.

    -
    - -
    - docs/metrics/commits.jsonl appeared in every repo and it's empty. Bug? -

    By design. install-metrics.sh seeds all five streams so every repo has the same shape and no writer has to check whether its file exists. Each then fills only when its event happens: gates.jsonl on the first *verify, runs.jsonl on the first framework command, sessions.jsonl when a session ends, and commits.jsonl on your first commit after telemetry was installed. Commit the empty files with everything else: a tracked empty stream makes the first record a one-line diff instead of a file appearing from nowhere. See §17.

    -
    - -
    - I committed everything, and commits.jsonl immediately shows as modified again. -

    That repo is still on the retired post-commit hook. Run update-framework.sh <repo> on that machine; the refresh installs the pre-commit hook and removes the old one.

    -

    Why it happened: post-commit could only describe commit N once N existed, so its line could never be inside N. The file was dirty the instant every commit finished — permanently, with no reachable clean state, because committing the pending line creates a new commit whose record is then pending in turn. On a repo worked from two machines it also blocked git pull whenever that file had changed upstream.

    -

    Since 2026-08-11 the record is written before the commit is sealed and staged into it, so the tree is clean when the commit finishes. The lag is unchanged — HEAD is still the previous commit at that point, so commit N's own record ships in N+1 — but it is committed rather than pending. See §17.

    -
    - -
    - So if I pull on another machine — or set the code up on a brand-new one — do I lose that last unpushed line? -

    No. That line describes a commit, and the commit itself is pushed. The other machine pulls it, and its hook walks git log, sees a commit the file doesn't carry, and writes the record. On a brand-new machine the same thing happens at a larger scale: git log holds the entire history, so one reconcile reproduces every record. This is exactly why the stream is maintained as a projection of the log rather than as an independent ledger — the log is the thing git already replicates, so it is the only thing that has to survive.

    -

    To fill a machine in immediately instead of waiting for its next commit: .tfcore/telemetry/tf-metrics.sh --backfill-commits .

    -

    The flip side, which is handled: the original machine also wrote that record locally. Once both versions are in the repo, merge=union keeps both — the same sha twice. That is the deliberate trade: union merge guarantees no record is ever dropped, and tf-metrics.sh de-duplicates commits on sha at read time (reporting how many it collapsed) so nothing is ever counted twice. Only commits.jsonl can duplicate this way; runs/gates/sessions record events that happen on one machine and can't be reconstructed elsewhere.

    -

    Since the record ships inside a commit rather than sitting pending afterwards, the file matches HEAD between commits — nothing to stage before a pull.

    -
    - -
    - I work on the same repo from two machines. How do I collect the telemetry from both? -

    You don't — it collects itself. The streams are tracked files, so they travel with push/pull. Two things make that work without friction, and the framework refresh sets up both:

    -
      -
    • .gitattributes gives docs/metrics/*.jsonl merge=union. Two machines appending to one append-only log would otherwise conflict on nearly every sync, and hand-resolving such a conflict is exactly how records get silently dropped. Union merge keeps both sides' lines. A record can end up duplicated or out of chronological order as a result — consumers sort on ts and de-duplicate commits on sha, so that costs nothing.
    • -
    • The pre-commit hook reconciles instead of appending one line: it records every commit reachable from HEAD that the file doesn't already carry, then stages that one file so the records ship inside the commit. Pull the other machine's work, commit here, and its history lands too. git log is itself an append-only log that push/pull already replicates; commits.jsonl is a projection of it.
    • -
    -

    The one thing that does not travel is the hook: .git/hooks/ is not part of the repository, so every clone needs its own. Run update-framework.sh <repo> once on each machine — tf-metrics.sh --report also warns when the clone you're standing in has no hook. To fill a machine's history immediately rather than waiting for a commit, run .tfcore/telemetry/tf-metrics.sh --backfill-commits . (idempotent — already-recorded shas are skipped).

    -
    - -
    - GitHub on Windows says: "This file uses 'LF' line endings, but Git is configured to convert them to 'CRLF' the next time the file is checked out." -

    That is core.autocrlf=true (the Git-for-Windows default) meeting a repo whose .gitattributes says only * text=auto. text=auto normalizes the committed blob to LF but still lets the working tree be smudged to CRLF on checkout — so the file on disk and the file Git will next write disagree, and Git tells you so. It's the same mechanism that CRLF-broke every *.sh in this framework (including the guard hooks) on 2026-07-11.

    -

    Fixed by pinning the working tree instead of leaving it to per-machine config. The scaffold/update scripts now manage a .gitattributes block in every repo:

    -
    * text=auto eol=lf
    -*.bat text eol=crlf
    -*.cmd text eol=crlf
    -docs/metrics/*.jsonl text eol=lf merge=union
    -

    eol=lf pins the working tree to LF on macOS, WSL and native Windows alike; *.bat/*.cmd keep CRLF because the Windows command processor requires it. It matters most for docs/metrics/*.jsonl, which is appended to by machine — a log must never acquire mixed line endings.

    -

    Run update-framework.sh <repo> to get the block, then once per repo, yourself: git add --renormalize . && git commit -m "Normalize line endings" so the committed blobs match the new rules. The script prints that reminder when it adds the block. (Agents can't run it — git is manual here.)

    -
    - -
    - What if the agent ignores the coding standards mid-implementation? -

    The verifier's standards-compliance grep checks (§10, item 3) catch the most common violations and produce coverage misses. When you see a miss like STANDARDS: underscore-field in src/Foo.cs:42 flagged in the checklist's Requirements Status table, tell the implementing agent: "Fix the standards violations flagged in the Requirements Status table of docs/<APP>-Checklist.md per docs/<APP>-Coding-Standards.md." Loop until clean.

    -
    - -
    - What if existing brownfield code doesn't use the obj field prefix? -

    First: the prefix only applies if THIS project chose obj — the field prefix is a per-project day-1 decision recorded in docs/<APP>-Coding-Standards.md (e.g. AstroLyfe uses bare PascalCase, no prefix). If it did, the analyst flags it as standards drift in the day-1 output summary. You then either: (a) let the standards-compliance grep checks in the verify pass catch them and fold the fixes into the regular build loop (the implementing agent renames as it touches the file), or (b) explicitly ask flow-master for a one-shot rename pass: "Rename every non-obj-prefixed instance field in src/ to use the obj prefix per docs/<APP>-Coding-Standards.md, in one commit per file." Option (a) is lower-risk; option (b) is faster if you want a clean baseline.

    -
    - -
    - I want to change the coding standards mid-project. Will the agents pick it up? -

    Yes — they read docs/<APP>-Coding-Standards.md on every invocation. Update the file, then in the next implementation prompt include "NOTE: the coding standards file was updated; conform new code to it and flag any existing non-conforming areas in your output summary."

    -
    - -
    - Should the architecture document be updated as the code changes? -

    Yes. For a deliberate change to scope/structure (new module, new flow, stack tweak), run *amend-docs <APP> "<what changed>" (§7.9) — it amends Architecture.md (and the BRD) in place and re-renders. For incidental "as-built" drift discovered during implementation, the implementing agent notes it and /flow-master reconciles at handoff ("update Architecture.md to reflect 'as-built', then regenerate the HTML").

    -
    - -
    - scaffold-brownfield.sh / scaffold-greenfield.sh re-run wiped my work? -

    No for your work product — framework files copy with rsync --ignore-existing. EXCEPTION: the harness agent mirror under .claude/commands/TechieFlow/agents/ is force-synced from .tfcore/agents/ on every run — edit agents only in .tfcore/agents/. (The old .opencode/command/TechieFlow/ mirror no longer exists — OpenCode loads agents/tasks from opencode.jsonc {file:./.tfcore/...} references.)

    -
    - -
    - *yolo doesn't stop Bash prompts. -

    It does now (2026-08-21, §12a). *yolo writes .tfcore/.session/yolo.json; the PreToolUse hook reads it and stops asking for rm/rmdir/sudo and allows read-only git. Still prompted? (1) The app's .claude/settings.json predates the change and still has Bash(rm *) under ask — run update-framework.sh <app>; a settings ask prompts in every mode, even bypass. (2) The agent forgot tf-yolo.sh on — type *yolo again or run it yourself. Git writes prompt for nobody — denied outright in every mode.

    -
    - -
    - My unattended goal run stopped on "You've hit your limit · resets …". -

    Use the supervisor, not a bare session: bash .tfcore/utils/tf-goal.sh <app-dir> "<goal>" (§12a). It parses the reset time, sleeps until reset + 15 min, and resumes the same session; --resume <app-dir> continues after a reboot. Watch .tfcore/.session/goal.log.

    -
    - -
    - *build-phase implemented a few REQs and told me to run it again for the rest. -

    That ending is banned (build-phase §2b). Re-run it — FRESH/FIX detection picks up the open rows — and if it happens again, quote §2b back: a pass is done when every working-list REQ is ≥ Implemented; context pressure means more sub-agent clusters, not a shorter pass.

    -
    - -
    - Mermaid not rendering in BRD.html / Architecture.html. -

    (1) Offline + CDN script blocked — inline mermaid.min.js instead. (2) Missing mermaid.initialize — check end of HTML. (3) Malformed code fence — confirm ```mermaid on its own line and valid Mermaid syntax.

    -
    - -
    - Verifier says "Playwright not installed". -

    Vidur self-heals browser binaries. If install fails: did you run §0?

    -
    - -
    - Agent implemented things not in the requirements doc. -

    "Revert anything not tied to a REQ-* ID." Add new REQ first if you actually want it.

    -
    - -
    - Slash command /trblazeui shows "no skill". -

    Run dotnet build once to fire the TrBlazeUI NuGet deploy target. If still missing: dotnet build -t:TrBlazeUIRedeployAgentFiles. Restart Claude Code so it rescans skills.

    -
    - -
    - Should I commit the HTML files? -

    Yes for all of PROJECT-STATUS.html, <APP>-BRD.html, <APP>-Architecture.html, <APP>-<Library>-Feedback.md. Browseable on GitHub without cloning, and they're the human-facing artifacts.

    -
    - -
    - Standard TechieFlow story-by-story flow — ever? -

    Only with a second person. For solo + Claude Max, the compressed flow is strictly faster. The stock story-flow agents and tasks no longer ship with this scaffold (trimmed 2026-06-12) — obtain a full story-by-story agent set separately if that day comes.

    -
    - -
    - How do I keep token usage down? -

    Read .tfcore/TOKEN-GUIDE.md (ships with every project). Key levers: don't load whole docs or repo trees into context; checklists stay markdown-only (never rendered to HTML — HTML adds weight with no AI benefit); fan work out to subagents instead of loading everything in one session; use *amend-docs and *devguide --update for incremental refreshes instead of re-running phases from scratch; use *refresh-status to recover a broken session instead of re-running the whole phase.

    -
    - -
    - A screen passed verification but was rendering blank OR was visually broken — how is it prevented now? -

    Before the gates, verification only checked acceptance-test pass/fail (HTTP 200, no exception, element present). A screen could pass while its data table showed zero rows, or while every control rendered its data but the controls overlapped / sat off-screen / were clipped so the running app was unusable. Two gates close both holes:

    -
      -
    • Render gate (verify-phase §4a): the verifier asserts every control listed in the DevGuide actually renders its data — no blank table, no count-over-zero-rows, no empty chart.
    • -
    • Visual-truth gate (§4b): it then asserts the screen LOOKS right — no control overlap, every control in-viewport and non-zero-size, checked at desktop + mobile widths, the screenshot inspected, and diffed against the mockup when one exists.
    • -
    -

    A REQ is Verified only if acceptance passes AND data renders AND the screen looks right; otherwise it drops to Needs re-verify / FAIL. Done (pre-existing) is treated as an unverified migrated claim and gets the same sweep. The DevGuide's observed render/visual tags are refreshed each run, keeping DevGuide ⇄ Checklist ⇄ Verifier runtime-true.

    -
    - -
    - The verifier passed but the running UI is broken — who do I call? -

    /TechieFlow:agents:flow-master *fix-issues {AppName} {folder} (§7.11). Drop a folder of screenshots of the broken screens (optionally a bugs.md naming what's wrong on each). Flow-master reads them (vision), reproduces each issue live with Playwright, triages it (layout / data / logic / RAG), and fans the fix out to the right builder/trblazeui for layout, its own subagents for data/logic, /techierag for RAG. You never invoke a builder agent yourself. It then re-smokes (data + visual), re-verifies the affected REQs, and updates the DevGuide + checklist + PROJECT-STATUS.

    -
    - -
    - I found bugs at UAT / in production — I want them analyzed and logged, but NOT fixed yet -

    /TechieFlow:agents:flow-master *triage-issues {AppName} {evidence} (§7.12). Same evidence channel as *fix-issues (a screenshots folder), plus it accepts a written bug list. Flow-master reproduces each issue live and delivers docs only: regressed REQs demoted to Needs re-verify with dated ⚠ UAT bug remarks, new Planned REQ rows for unspecified defects, refreshed DevGuide known-issues, and a PROJECT-STATUS whose next command is the *fix-issues pointer naming the REQ IDs. Add verify to also regression-re-verify the affected screens' sibling REQs. It never edits src//tests/ and never spawns a builder — you decide when the fixing starts.

    -
    - -
    - How does a developer understand or verify the AI-generated code? -

    Run /TechieFlow:agents:flow-master *devguide {AppName} (also auto-run at handoff). It produces docs/{AppName}-DevGuide.md + a styled .html: every screen grouped by user role, each with a flowchart tracing the full stack (Razor page → service → data-access → stored procedure/query), a Controls table (with observed render-status from the OBSERVE pass), and a Data-lineage table. Use it to find the right service method for a bug, confirm the correct stored procedure is called, or check that a control is bound to the right DTO property. Re-run with --update after implementing changes to refresh only the affected screens. See §6 for the full schema and the 3-pass generation model.

    -
    - -

    16. Running on macOS / native Windows / Linux#

    - -

    This framework was built on the owner's WSL-on-Windows machine, so §0/§11 and the build-invocation ladder describe that setup. But the framework itself is portable — the agents, tasks, templates, and slash-commands are plain Markdown and run identically under Claude Code or OpenCode on macOS, native Windows, or native Linux. Only two things are environment-specific: how dotnet is invoked, and the runtime-verification bridges (headless Playwright for Blazor; the §0b Appium endpoints for MAUI Android/iOS/Mac-Catalyst; FlaUI/Appium-Windows for the MAUI Windows head). Here is what changes per platform.

    - -
    - What is the SAME everywhere: the scaffold-*.sh / update-framework.sh scripts (bash), all /TechieFlow:* slash commands, the day-1 → split → build → verify → handoff flow, every doc template, and the .claude/settings.json permission model. The scaffold scripts need bash + rsync + realpath (preinstalled on macOS 12.3+ and Linux; on native Windows run them from WSL or Git Bash). -
    - - - - - - - - - - -
    ConcernWSL-on-Windows (reference)macOSnative Windowsnative Linux
    dotnet invocationladder §B: ~/.dotnet/dotnet, cmd.exe /c, winrunladder §A: dotnet build (in PATH)ladder §C: dotnet build (in PATH)ladder §A: dotnet build (in PATH)
    MAUI iOS / Mac CatalystWindows side via cmd.exe (needs paired Mac for iOS)native — needs Xcode + sudo dotnet workload install mauineeds a paired Mac build hostnot supported (no Apple toolchain)
    MAUI AndroidWindows side via cmd.exenative — Android SDK + JDKnative — Android SDK + JDKnative — Android SDK + JDK
    MAUI Windows headWindows side via cmd.exenot supportednativenot supported
    Browser verification (verifier)headless Playwright CLI (§0 bootstrap)Playwright with system ChromiumPlaywright with system browserheadless Playwright CLI
    Native UI verification (Android/iOS/Catalyst)Appium endpoints (§0b): Android on Windows host, iOS/Catalyst on a LAN Maclocal Appium (Android emulator + iOS Sim + Catalyst, all native)local Appium (Android emulator + Catalyst); iOS via the paired Mac's Appiumlocal Appium (Android only)
    Scaffold scriptsrun in WSLrun in Terminal (zsh/bash)run in WSL or Git Bashrun in shell
    - -

    The agents resolve this automatically: the build-invocation ladder (.tfcore/templates/v4custom/build-invocation-ladder.md) now starts with a platform-detection probe (uname -aDarwin = macOS, …microsoft… = WSL, plain Linux = native Linux, absent = native Windows) and picks ladder §A / §B / §C accordingly. On macOS/Windows/Linux there is a single rung — dotnet build — and a missing MAUI workload is fixed once with dotnet workload install maui (on macOS with sudo: the SDK dir /usr/local/share/dotnet is root-owned, and without it workload/SDK updates fail with "Inadequate permissions. Run the command with elevated privileges."), never logged as a project blocker.

    - -
    macOS quick start (Claude Code on Mac): -
      -
    1. Run the one-time §0a macOS bootstrap — Xcode CLT/license, Homebrew, .NET SDK + Node, Playwright per project; sudo dotnet workload install maui (sudo required on macOS) + Xcode / Android SDK only for MAUI apps.
    2. -
    3. Scaffold as usual: /path/to/TechieFlow/scaffold-brownfield.sh /path/to/your-app (or scaffold-greenfield.sh).
    4. -
    5. Start Claude Code in the app folder and run /TechieFlow:agents:analyst *day1-brownfield <AppName> — identical to WSL.
    6. -
    -The winrun bridge and cmd.exe rungs simply don't apply on Mac; the agents won't reach for them once uname reports Darwin.
    - -
    NuGet credentials for macOS: store the private GitHub Packages source in $HOME/.nuget/NuGet/NuGet.Config (for example /Users/srkra/.nuget/NuGet/NuGet.Config), not in the project. NuGet, Claude Code, and native OpenCode discover it automatically. Windows uses %AppData%\NuGet\NuGet.Config. OpenCode Docker mounts the Windows directory read-only at /root/.nuget/NuGet. A repository nuget.config, if needed for source mapping, must contain no PAT.
    - -
    Moving an existing project (or this framework repo) from Windows/WSL to a Mac: -
      -
    1. Can't see .tfcore/, .claude/, .opencode/ in Finder? Finder hides dot-files by default. Press Cmd+Shift+. in any Finder window to toggle them on (the setting sticks), or run defaults write com.apple.finder AppleShowAllFiles -bool true && killall Finder. The Terminal always sees them: ls -la. Nothing is missing just because Finder doesn't show it — check with ls -la first.
    2. -
    3. Moved an APP repo via git (clone/pull)? Then the framework folders genuinely AREN'T there — every deployed framework copy (.tfcore/, .claude/, .opencode/, /CLAUDE.md, /WORKFLOW.html, /opencode.jsonc) is gitignored by design (they're copies; this repo is the source of truth). Re-deploy them: ls -la the app — if .tfcore/ exists, run /path/to/TechieFlow/update-framework.sh /path/to/app; if it's absent, run /path/to/TechieFlow/scaffold-brownfield.sh /path/to/app (safe on an app with existing docs/code — it uses --ignore-existing and never touches src/, docs/, or tests). Add --dry-run to update-framework.sh to preview.
    4. -
    5. Per-project gitignored files don't come back from a scaffold. CLAUDE.md, .tfcore/core-config.yaml customizations, and .claude/settings.local.json are per-project work product that git never carried. A plain folder copy from the old machine keeps them; a git clone loses them — copy them over from the Windows machine, or regenerate (CLAUDE.md comes back via day-1 / *refresh-status).
    6. -
    7. Scripts won't execute (permission denied)? A copy through a Windows filesystem drops the executable bit. Fix once: chmod +x /path/to/TechieFlow/*.sh /path/to/TechieFlow/.tfcore/hooks/*.sh (or run them as bash script.sh). Hooks inside apps are invoked via bash so they don't need it, but the same chmod doesn't hurt.
    8. -
    9. No path edits needed: since 2026-07-11 the three scripts locate the framework from their own directory (no hardcoded /mnt/c/…), and they run fine on macOS's stock bash/rsync.
    10. -
    11. Afterwards, restart Claude Code in the app folder so the freshly deployed agent/task definitions and settings.json load.
    12. -
    - -
    native Windows quick start (Claude Code / OpenCode on Windows, not WSL): -
      -
    1. Install the .NET SDK (winget / official installer); confirm dotnet --info.
    2. -
    3. For MAUI: dotnet workload install maui. Windows + Android heads build natively; iOS / Mac Catalyst need a paired Mac build host.
    4. -
    5. Run the scaffold scripts from WSL or Git Bash (they're bash). Then drive the framework from Claude Code on Windows normally — the ladder uses §C (dotnet build).
    6. -
    - -
    OpenCode in a Windows Docker container: use the dedicated Containerized OpenCode host bridge under §0. The container is Linux, so it uses the SSH-backed winrun wrapper rather than direct cmd.exe interop.
    - -
    native Linux quick start: -
      -
    1. Install the .NET SDK via your distro or the official script; confirm dotnet --info.
    2. -
    3. MAUI on Linux supports the Android head only (net9.0-android) — iOS / Mac Catalyst / Windows heads can't build without their toolchains (a genuine platform limit, not a wrong-rung error).
    4. -
    5. Scaffold and run exactly as on macOS (ladder §A).
    6. -
    - -

    Notes. The framework never requires MAUI — many apps are Blazor-only and build with plain dotnet build everywhere. The full per-platform dotnet invocation detail lives in .tfcore/templates/v4custom/build-invocation-ladder.md (what the agents read); this section is the human-facing summary.

    - -

    17. Development telemetry — what the framework measures about itself#

    - -
    Full guide — annotated example records for every stream, the report walkthrough, how model/tokens/cost land on run records, and the FAQ: docs/TechieFlow-Telemetry-Guide.md (+ rendered .html) in the TechieFlow framework repo. Field-by-field contract: .tfcore/telemetry/SCHEMA.md (deployed in this app).
    - -

    TechieFlow already produced this evidence and used to throw it away. Every *verify run applies four separately-named gates to individually-identified requirements and writes verdicts into the Requirements Status table; docs/.last-verify.json recorded the run. Then the next run overwrote it, the table was mutated in place, and the history was gone. Telemetry keeps it.

    - -

    Four questions, and nothing else:

    -
      -
    1. First-pass rate — what fraction of REQs reach Verified on attempt 1?
    2. -
    3. Gate catch distribution — of all failures, which gate caught them?
    4. -
    5. Escape rate — what fraction of defects reached UAT/production (logged by *triage-issues) instead of being caught by a gate?
    6. -
    7. Miss attribution and rework cost (added 2026-08-28) — what was missed, which phase / agent / model let it through, and what did fixing it cost?
    8. -
    - -

    There is deliberately no cycle-time-per-feature. The unit of work here is the run, not the ticket.

    - -

    Where it lands

    - -

    Five append-only JSONL streams in docs/metrics/, tracked by version control on purpose — this is the project's own development history, and the one thing the framework cannot reconstruct afterwards.

    - - - - - - - - - - -
    FileOne record perWritten by
    runs.jsonlframework command runeach phase task, at completion (the status gate is the trigger)
    gates.jsonlREQ verdict per verify run — the primary streamverify-phase §6a, and triage-issues for escapes
    sessions.jsonlagent sessionthe SessionEnd hook (.tfcore/hooks/metrics-session.sh)
    commits.jsonlcommityour own pre-commit hook — never an agent
    misses.jsonla requirement or behaviour an agent missed (miss), what repairing it cost (miss-fix), and a field completed later (miss-amend)verify-phase, build-phase, triage-issues, fix-issues, amend-docs, *log-miss
    - -

    Schema, enums and every known limitation: .tfcore/telemetry/SCHEMA.md. Doctrine for agents: .tfcore/tasks/_metrics-emit-gate.md.

    - -

    Setup — there isn't one

    - -

    Telemetry rides the normal framework refresh. There is no separate install command:

    - -

    on WSL/Linux:

    -
    /mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/YourApp
    -
    - -

    on macOS:

    -
    /Volumes/MacD/MyCode/TechieFlow/update-framework.sh /path/to/YourApp
    -
    - -

    That one command creates docs/metrics/, seeds the five streams, installs the pre-commit hook, and warns if an ignore rule would swallow the data. The scaffolds do the same on a fresh project. It is idempotent, so every later refresh keeps it current.

    - -

    project_type is auto-detected once (a packable .csprojlibrary; .razor/.xaml present → app; no source → docs; this repo → framework), printed loudly, written to core-config.yaml, and then never guessed again — core-config.yaml is a preserved file, so your classification survives every refresh. Correct a wrong guess with:

    - -
    .tfcore/telemetry/install-metrics.sh . --type app|library|docs|framework
    -
    - -

    Nothing here invokes git. The hooks directory is found by reading the filesystem — installing a hook is a file copy, not a git operation — so block-git.sh is untouched and no permission prompt appears, whoever runs the refresh.

    - -

    Updating the metrics — you don't

    - -

    There is no "update metrics" step. The streams fill themselves as you work: every *build-phase, *verify all, *fix-issues, *triage-issues, *handoff-phase and day-1 run appends its own record as its last action, the SessionEnd hook adds a session, and your own commits add themselves. If you never think about telemetry again, it still accumulates.

    - -

    The only thing that needs doing per repo is the framework refresh you already run:

    - -
    /mnt/c/3AIGenCode/TechieFlow/update-framework.sh /path/to/YourApp
    -
    - -

    Viewing the metrics — two ways

    - -

    1. Quick look, in the terminal. No agent, no tokens, read-only — run it from inside the app repo:

    - -
    cd /path/to/YourApp
    -.tfcore/telemetry/tf-metrics.sh --report .
    -
    - -

    Prints first-pass rate, gate catch distribution, escape rate, rework ratio, batch size, throughput, commit cadence and the miss report — already segmented, with insufficient data wherever there are fewer than three supporting records.

    - -

    2. The full report, as a document. Ask flow-master; it writes docs/metrics/METRICS.md and renders docs/metrics/METRICS.html (open that in a browser — it uses the same themed shell as your other docs):

    - -

    Claude Code:

    -
    /TechieFlow:agents:flow-master *metrics YourApp
    -
    - -

    OpenCode:

    -
    /flow-master *metrics YourApp
    -
    - -

    Across several projects at once

    - -
    .tfcore/telemetry/tf-metrics.sh --rollup . /path/to/OtherApp /path/to/Third
    -
    - -

    Still segmented — app and library repos are reported side by side, never averaged together. Or hand the same paths to the command: *metrics YourApp /path/to/OtherApp.

    - -

    Seeding history from what you already have (optional, once per repo)

    - -

    Your repos have years of history the streams know nothing about. Two backfills can import some of it. Both are yours to run, never an agent's — and always preview with --dry-run first:

    - -
    cd /path/to/YourApp
    -.tfcore/telemetry/tf-metrics.sh --backfill-commits . --dry-run
    -.tfcore/telemetry/tf-metrics.sh --backfill-commits .
    -
    - -

    Commit backfill is trustworthy. It walks the commit log, which is itself an append-only log, so reconstructed commits are exactly as good as live ones and are reported together with them. This is also the reason the pre-commit hook is optional.

    - -
    .tfcore/telemetry/tf-metrics.sh --backfill-gates . --dry-run
    -
    - -

    Gate backfill is context, not evidence. It reads each REQ's current row in <APP>-Checklist.md plus any dated remarks. But that table is a snapshot that gets overwritten in place, not a log — a REQ that failed three times and then passed looks identical to one that passed first try. Every record it writes is stamped backfilled: true with an inferred list, is reported in its own separate column, and can never support a published first-pass rate. Useful for volume and shape; not for a number you would defend.

    - -

    If a project is classified wrong

    - -

    The refresh auto-detects project_type once and prints what it chose. If it guessed wrong — the giveaway is a library reported as though the visual gate could fire on it — correct it and it stays corrected:

    - -
    .tfcore/telemetry/install-metrics.sh . --type app|library|docs|framework
    -
    - -

    What to expect early on

    - -

    The streams start empty, and the report refuses to invent numbers from thin data — you will see insufficient data (n=…) on most rows until roughly three verify runs have happened. That is the report working, not a fault. The first genuinely interesting reading usually comes after a build → verify → fix cycle or two.

    - - - - - - - - - - - - -
    You want to…Run
    Turn telemetry on for a repoupdate-framework.sh /path/to/YourApp (nothing else)
    Glance at the numbers now.tfcore/telemetry/tf-metrics.sh --report .
    Produce the readable report*metrics YourAppdocs/metrics/METRICS.html
    Compare several projectstf-metrics.sh --rollup . /path/to/OtherApp
    Import commit historytf-metrics.sh --backfill-commits .
    Import checklist historytf-metrics.sh --backfill-gates . (context only)
    Fix a wrong classificationinstall-metrics.sh . --type library
    - -

    Misses — the fourth question

    - -

    A gates.jsonl record says a REQ failed. A miss record says what was missed, which phase let it through, and — through its miss-fix sibling — what the repair cost. It is the only stream that can hold a design-phase miss (“the BRD never specified the export screen”), because it is the only one not written by the verifier: by the time verification runs, a specification gap has either been papered over or built wrongly.

    - -
    The 20-second front door.
    -
    /TechieFlow:agents:flow-master *log-miss {AppName} "the export button ignores the active date filter"
    -/flow-master *log-miss {AppName} "..."          (OpenCode)          # add --fixed if it is already repaired
    -It classifies what you said, attributes it, writes the misses.jsonl record and the checklist line (a demotion with a dated ⚠ miss remark, or a new Planned row with acceptance when nothing owns it). It never boots the app, never reproduces, never edits code*triage-issues does all three, which is right for UAT triage and far too heavy for one sentence. That friction is exactly why these misses used to be recorded nowhere at all.
    - -

    Everything else emits automatically: verify-phase on every failing gate, build-phase when a REQ turns out to be unbuildable as written, triage-issues on a UAT or production bug, fix-issues when it closes one.

    - -

    One defect is one miss. A REQ that fails three verify passes collapses onto the record already open for it, so the miss count measures quality rather than retry patience. A new record is written only when the kind of failure changes — which is genuinely new information.

    - -

    “Open” and “live” are deliberately different. A miss closed as wont-fix is a decision, not a backlog item, so the report does not count it as open — but the collapse check still treats it as a live defect, so a repeat failure on that REQ will not open a duplicate. Two questions, two predicates; making them agree would break one of them. deferred stays open, because postponed work is still work.

    - -

    Which practice failed — why_missed

    - -

    miss_class names the defect. why_missed names the practice that let it through, and it is the more decision-changing of the two: it says whether your specification or your verification is the weak one. Ported from the AI-First-Playbook on 2026-08-28, whose /analyze-fix phase has always produced exactly this judgement in prose and then thrown it away.

    - - - - - - - - - - - - -
    ValueThe practice that failed
    missing-checklist-itemNo REQ or acceptance bullet covered the behaviour. The spec had a hole.
    insufficient-verify-methodAcceptance existed; the gate that ran could not catch this class of defect. The spec was fine, the test was too weak.
    code-audit-limitation⚠ STATIC-ONLY — no runtime bridge, so it was never observable.
    ambiguous-acceptanceTwo honest readings; build and verifier took different ones.
    dependency-not-declaredThe REQ depended on something no document stated.
    instruction-ignoredA written framework rule existed and was not followed. TechieFlow's own addition — not a spec gap, not a weak gate, and no gate change fixes it.
    otherNothing above fits. Do not stretch a label to avoid this.
    - -
    Optional, and null is honest — many misses have no clear answer and a forced one is noise. But an escape without one wastes the most valuable record in the stream: something got past every gate, and why nothing caught it is the entire question. --report warns when an owner or production miss arrives with the field empty, and reports the distribution against records that carry it, never against all misses.
    - -

    Completing a record without editing it — miss-amend

    - -

    A stream is append-only, so “the correction is a new record, never an edit” is the rule. That rule needs a record kind to name, and until 2026-08-28 this stream had none: a field left empty — most sharply a why_missed on a record written before the field existed — was unreachable. Reported by the TfLens team as TF-005, and fixed with a third kind rather than a licence to edit.

    - -
    bash .tfcore/utils/tf-emit.sh --amend MISS-App-20260828-01 why_missed missing-checklist-item
    - -
      -
    • It may fill a field that is null. It may never overwrite one that is not — including a value set by an earlier amend. So it completes a record rather than altering a fact, and a reader that ignores amendments entirely still sees nothing false, only less.
    • -
    • Only closed-vocabulary judgements are amendable (why_missed today). Nothing the emitter derives — origin_model, origin_confidence, cost_attribution, tokens, dollars — ever is, and an observation about a finished run is never backfilled.
    • -
    • It prints its refusal rather than failing quietly, and still exits 0: telemetry has no veto, and neither does a refused amend. A hand-written amend piped onto the stream faces the identical checks.
    • -
    • Never hand-edit a .jsonl stream. If no record kind can carry the correction you need, report it as a framework defect — which is exactly how this one arrived.
    • -
    - -
    A record written before a field existed is not “unassessed”. why_missed arrived on 2026-08-28; misses older than that had no field to fill, so they leave that field’s denominator and --report states how many did. Same rule as a gate added mid-stream (§3.5 of the schema): never backfill a record with a verdict nobody made at the time.
    - -

    What a miss costs — the honest version

    - - - - - - - - -
    HarnessTokensDollars
    OpenCodeyesreal, measured — the provider's own per-message cost
    Claude Codeyesnone — cost_usd is null, permanently: transcripts carry no cost
    Codexyesnone — ChatGPT credits are never fabricated as cost_usd
    - -

    So “how much money did that miss cost” has a real answer only on OpenCode; elsewhere the answer is a token count. No rate card is ever applied to make a dollar figure appear. An estimate presented as a measurement poisons every comparison built on it.

    - -
    Two exclusions, both applied AND displayed.
    -(1) Attribution. A miss's origin_model is looked up from runs.jsonl, never typed by an agent — and when the lookup fails, the model is forced to null and the record marked inferred. Only linked records feed a per-phase / per-agent / per-model figure. A per-model miss rate computed partly from guesses is a routing decision made on invented evidence, and nothing in the output would reveal it.

    -(2) Cost. A fix run that repaired three misses has one token window; dividing it three ways is arithmetic, not measurement. cost_attribution marks each record sole, shared:n or none, and the report shows measured and apportioned as separate columns — never one blended number. A miss fixed inline with no distinct run cannot be costed at all, and is printed as such rather than dropped.

    -Both exclusions are reported with the figures they bound: an exclusion you cannot see is indistinguishable from a bug.
    - -

    Escape rate keeps its existing definition and its existing source (gates.jsonl gate:"escaped"). The miss stream's own “found by a human” share sits beside it, never merged into it — one word cannot mean two things on one page.

    - -

    Two things that trip people up

    - -
    You do not need the commit hook to get metrics. Four of the five streams need no version-control hook at all: runs, gates and misses come from tf-emit.sh inside the phase tasks, and sessions from the Claude Code SessionEnd hook (or the OpenCode plugin) — neither of which is a version-control hook. Only commits.jsonl uses .git/hooks/pre-commit, and only for commit volume and cadence: nothing about what was missed, who missed it, or what the fix cost depends on it.

    -And even that hook is optional. .tfcore/telemetry/tf-metrics.sh --backfill-commits . reconstructs the identical records perfectly from the commit log at any time — that log is itself append-only, which is why commit records are the one backfill exempt from the live-vs-backfilled separation. The hook only makes it automatic.
    - -
    A greenfield project is born labelled docs, and that used to stick. The greenfield scaffold classifies the repo at scaffold time, when it genuinely is docs-only — the day-1 documents exist and src/ does not — and the old “auto-detected once, then never re-guessed” rule froze it there. TfLens accumulated 225 gate records, visual gates included, under a project_type whose own definition says gates cannot fire, and whose figures never pool with the apps it belongs beside.

    -Since 2026-08-28 a later update-framework.sh re-examines docs alone and only upgrades: once real heads or a published package appear, the tree has answered a question that was unanswerable at scaffold time. app / library / framework are never re-guessed, an owner's --type always wins, and a genuine docs repo never grows a head so it is never touched. Correct one by hand at any time: -
    .tfcore/telemetry/install-metrics.sh . --type app
    -Records written before a correction keep the old value. The streams are append-only and corrections happen at read time, never by rewriting history — so a reclassified project appears under both segments, which the provenance rule forbids pooling. --report states the split explicitly rather than letting one project look like two; read each segment as a period of the project, not the whole of it.
    - -
    Why some figures are never combined. The report will not print a single first-pass rate, gate catch distribution, or escape rate that pools live with backfilled records, or pools app with library/docs — not as a total row, not as an "overall" line. A backfilled attempt count is inferred from a status table that never recorded attempts, so a merged first-pass rate cannot be defended when someone asks how attempts were counted. A pooled gate distribution understates the visual gate, because library and docs projects never had screens to fail on. One indefensible figure contaminates every other number on the page. Commit-derived metrics are exempt — the commit log is a real append-only log, and commit volume is comparable across project types.
    - -
    The one-commit lag. At pre-commit time HEAD is still the previous commit, so the record for the commit you are making ships inside the next one. Metrics lag reality by a commit, and that is unavoidable in either direction — a record of commit N cannot predate N. What changed on 2026-08-11 is that the record is now committed rather than left pending: writing after the commit was sealed (the original post-commit design) left commits.jsonl permanently dirty with no reachable clean state. Because the hook reconciles from the log rather than appending a single line, the lag never becomes a loss either: whichever machine commits next writes the missing record. - -

    What it costs, stated plainly: the hook stages one file — docs/metrics/commits.jsonl, never a directory, never -A — into the commit you are making. On a partial commit (git commit -- <paths>) it writes the record but does not stage, so it cannot add a file to a commit you deliberately scoped down, and it can never fail your commit (every path exits 0). Agents still never run git: this is your own git commit, no agent path reaches it, and block-git.sh is unchanged.
    - -
    Several machines, one repo. The streams are tracked files, so they travel with push/pull. .gitattributes (managed by the scaffold/update scripts) gives docs/metrics/*.jsonl merge=union so two machines appending never conflict — hand-resolving a conflict in an append-only log is how records get silently dropped — and eol=lf so a machine-appended log never acquires mixed line endings. The pre-commit hook records every commit reachable from HEAD that the file lacks and stages it into the commit, so after a pull, your next commit picks up the other machine's history as well. The hook itself lives in .git/hooks/ and therefore does not travel: run update-framework.sh once per clone, or tf-metrics.sh --backfill-commits . to fill that machine's history straight away.
    - -
    Privacy — assume every record could become public. Records carry IDs, counts, durations, verdicts and file paths at most. Never requirement text, prompt text, file contents, or commit subjects — only a commit's conventional-commit prefix (feat/fix/…) is kept, and the subject is discarded on the spot. failure_class is a closed vocabulary for exactly this reason. This framework is used on employer projects, and these files are append-only: a leaked field is not something you fix later.
    - -
    Telemetry has no veto. No metrics write can fail a build, block a tool call, abort a phase, or print an error. tf-emit.sh exits 0 unconditionally — missing directory, malformed JSON, absent python3, full disk — and the event is simply dropped. Same fail-open posture as the guard-status / guard-verify hooks. A telemetry bug must never cost you a working session.
    - -

    17b. Model routing — run cheap phases on cheap models#

    - -
    Full guide — the complete phase AND persona/subagent tier tables, per-tier change recipes with examples, the TUI walkthrough, what gets generated under the hood, and troubleshooting: docs/TechieFlow-Routing-Guide.md (+ rendered .html) in the TechieFlow framework repo.
    - -

    What it is, in one example. Without routing, day-1 architecture and re-rendering HTML cost the same per token. With routing ON, each phase gets a tier, each tier maps to a real model:

    - - - - - - -
    TierMeant forClaudeOpenCode (defaults — yours to change)Codex
    frontierexpensive thinking: day-1, author-brd, amend-docs, fix-issuesopusopencode-go/kimi-k3gpt-5.6
    standardeveryday building: build, verify, mockups, split-brd, triage, devguide + builder subagentssonnetopencode-go/kimi-k2.7-codegpt-5.6-terra
    economymechanical: metrics, renders, refresh-status, productguide, handoffhaikuopencode-go/deepseek-v4-flashgpt-5.6-luna
    - -

    Measured example (TechieBlog pilot): a complete metrics-report phase on economy — 14k output tokens, full report — cost $0.036. Builders stay on standard deliberately: a cheap builder that ships a blank table costs an entire verify-and-fix cycle.

    - -

    Turn it on / off — one command

    -
    cd /path/to/YourApp
    -bash .tfcore/utils/tf-routing.sh status     # read-only: the live tier/model/phase table
    -bash .tfcore/utils/tf-routing.sh on         # enable — generates ~23 binding files
    -bash .tfcore/utils/tf-routing.sh off        # disable — removes exactly those files
    - -
    IMPORTANT — what you will (and won't) see in the TUI. Opening OpenCode looks exactly the same as before. That is deliberate: your normal chat runs on the default build agent, and routing never touches it — your conversation stays on the model YOU picked. Routing becomes visible in three places only: (1) run a phase command — /techieflow:tasks:metrics-report YourApp executes on the tier model and the footer shows it; (2) press Tab to a persona (flow-master, flow-verifier, …) — each carries its bound model; (3) the telemetry — every run lands in docs/metrics/runs.jsonl with declared tier, observed model, routed: true/false, tokens and (OpenCode) real cost.
    - -
    Verified gotcha: after a routed command, that TUI session stays on the phase's model — it does not bounce back. Pick your model from the model list or start a new session if you keep chatting. Claude Code is the mirror image: a /tf:<phase> wrapper's model lasts exactly one turn, then reverts automatically.
    - -

    A concrete first run (safe even mid-UAT)

    -

    metrics-report reads the telemetry streams and writes only docs/metrics/METRICS.md + .html — no code, no status, no checklist:

    -
    1. bash .tfcore/utils/tf-routing.sh on
    -2. opencode
    -3. /techieflow:tasks:metrics-report YourApp
    -4. watch the footer: it runs on deepseek-v4-flash, not your chat model
    -5. tail -1 docs/metrics/sessions.jsonl   →  "model":"opencode-go/deepseek-v4-flash","cost_usd":0.03…
    - -

    Changing the map — one command per change

    -
    bash .tfcore/utils/tf-routing.sh set-tier  mockups   frontier      # promote a phase
    -bash .tfcore/utils/tf-routing.sh set-tier  verify-phase economy    # demote a phase
    -bash .tfcore/utils/tf-routing.sh set-model standard opencode opencode-go/qwen3.7-max
    -bash .tfcore/utils/tf-routing.sh set-model economy  claude   haiku
    -bash .tfcore/utils/tf-routing.sh set-model standard codex    gpt-5.6-terra
    -bash .tfcore/utils/tf-routing.sh bind      # re-apply after hand-editing routing.yaml
    -

    Claude takes aliases or full ids; OpenCode takes provider/model ids; Codex takes model slugs available to the installed Codex CLI. A skill in the main Codex conversation inherits the active model; generated custom agents and tf-goal.sh --harness codex are the pinned-model path.

    - -

    Escalation — advisory, applied by you at launch

    -

    routing.yaml also carries an escalation: block, shipped as fix-issues: after_attempts: 2, tier: frontier — "if the same REQs have been through fix-issues twice without reaching Verified, launch the third run on frontier". It is advisory: bash .tfcore/utils/tf-emit.sh --next-run-attempt fix-issues <REQ-IDs> reads the attempt history (the attempt field now stamped on every runs.jsonl record that touches REQs), tf-routing.sh status prints the policy, and tf-routing.sh set-escalation <phase> <attempts> <tier> tunes it. If the number exceeds the threshold, you launch the next run on the escalation tier (Claude: /model opus then the command; OpenCode: pick the model, then the command). Nothing ever switches a running phase's model — neither harness can (DECISIONS.md 2026-08-21; Routing Guide §6.4).

    - -

    Reading the results

    -

    Each run's record shows declared vs observed: "tier":"standard", "model":"opencode-go/kimi-k2.7-code", "routed":true, "tokens_out":42310, "cost_usd":0.41. The deciding question after a couple of weeks: does the rework ratio rise on cheaper tiers? If standard builders hold the first-pass rate, demote more; if mode:"fix" re-entries climb, promote. Data corrects the map, not opinion.

    - -

    FAQ

    -
      -
    • "I turned it on and the TUI looks the same." Expected — see the IMPORTANT panel. Run a phase command or Tab to a persona.
    • -
    • "How do I make my normal chat cheaper too?" Not routing's job — pick a cheaper model in the TUI model list, or set "model" in ~/.config/opencode/opencode.jsonc.
    • -
    • "status says flag and bindings disagree." bash .tfcore/utils/tf-routing.sh bind.
    • -
    • "Does update-framework.sh wipe my routing?" No — routing.yaml is preserved forever; refreshes re-generate your bindings from YOUR map (verified in the pilot).
    • -
    • "What gets generated?" OpenCode: .opencode/opencode.json. Claude: .claude/commands/tf/*.md + .claude/agents/tf-*.md. Codex: .codex/agents/*.toml + .agents/skills/techieflow-*/. All are gitignored and regenerated from the preserved routing map.
    • -
    - -

    18. Team edition — the AI-First Playbook#

    - -

    TechieFlow is the solo edition. Its team-scale sibling is the -AI-First Development Playbook — -the same philosophy (spec-driven, independently verified, execution-proven AI development), sized for -an engineering team with QA, BA and business stakeholders instead of one developer and a portfolio.

    - - - - - - - - - - - - - -
    TechieFlow (this framework — solo edition)AI-First Playbook (team edition)
    Optimized forOne developer + AI, portfolio of ~8–10 appsA team (~5–50 devs) on one large product
    LifecycleCompressed 5 phases — Day-1 → Split → Build → Verify → Handoff10 steps, 4 gates, per-feature loop
    Unit of workThe run (hand a phase a checklist and go)The feature (one living implementation checklist each)
    ChecklistOne per app, all REQ-UI/FN/NFR/RAG-* rows in one tableOne per feature
    VerifierPlaywright + Appium + dotnet test; data-render + visual-truth gates; hook-enforced verify ledgerFresh-context verifier agent; execution-proven verdicts written inline
    HarnessClaude Code (.tfcore/ + .claude/), OpenCode mirrorOpenCode + BMAD v4 personas
    Human docsDevGuide, UsageGuide, ProductGuideDeveloper-Flow-Guide, Business-Verification-Reference
    Also hasTelemetry (§17), git ban + guard hooks, MAUI/Appium bridge, *fix-issues / *triage-issuesJira/Confluence integration, post-verification bug loop, team enablement & onboarding material
    - -

    Shared by both: markdown as the source of truth, Mermaid-only diagrams, HTML for human docs only, -single-source-of-truth checklists, and "verify by executing, not by reading."

    - -
    Which one do you want? If you are one person driving agents across several apps, stay here. If you are rolling a process out to a team — where the hard parts are onboarding, review gates and shared standards rather than raw throughput — start with the Playbook; it ships enablement and onboarding material this framework deliberately does not carry. The two are independent repos: nothing here depends on the Playbook, and nothing there depends on this.
    - -
    -

    Last revised 2026-08-28 (rev 2). Edit freely. When the workflow changes, update README.md and WorkFlow-Context.md (the durable cross-harness context document) too.

    - -
    -
    - - - - - - diff --git a/WorkFlow-Context.md b/WorkFlow-Context.md index b52784b..80c0bde 100644 --- a/WorkFlow-Context.md +++ b/WorkFlow-Context.md @@ -5,7 +5,7 @@ | | | |---|---| | Repo | `/mnt/c/3AIGenCode/TechieFlow` on Windows/WSL and `/Users/MyCode/TechieFlow` on the owner's Mac, synced through GitHub. This is the framework template, not an application. | -| Last updated | 2026-09-07, at the close of Session 6 of the reset. | +| Last updated | 2026-09-07, at the close of the reset. | | Branch | Work since Session 3 is on `dev`. The owner commits; agents never run git. | --- @@ -14,7 +14,7 @@ TechieFlow is a software development harness companion. One person with domain knowledge takes a product through the whole life cycle, with AI agents doing the work and the person managing and reviewing every output. It is the template copied into each application, not an application itself. -It runs in two harnesses, **Claude Code** and **OpenCode**, and must behave the same in both. A Codex adapter exists, is frozen, and is not propagated. +It runs in two harnesses, **Claude Code** and **OpenCode**, and must behave the same in both. Nothing else is supported. The owner's portfolio is .NET, Blazor, TrBlazeUI and MAUI, but **the framework itself is technology-neutral**. No persona, task or shared rule names a language, database, UI library or host. Those facts live in a stack answer set (`docs/TechieFlow-Stack-Defaults-DotNet.md` is the owner's) and in each project's Architecture document. @@ -26,7 +26,7 @@ Alongside the work the framework measures the work: five append-only streams und |---|---| | `docs/TechieFlow-How-It-Works.md` | What every command does, what surrounds it, what it costs, where the design falls short. | | `docs/TechieFlow-Document-Schemas.md` | The required shape, size and row rules of every document the framework produces. | -| `docs/TechieFlow-Requirements.md` | The framework's own checklist: 61 lines, each with a way to check it. Agent document. | +| `docs/TechieFlow-Requirements.md` | The framework's own checklist: 63 lines, each with a way to check it. Agent document. | | `docs/TechieFlow-Telemetry-Explained.md` | The five report numbers, with real figures and the sentence to say about each. | | `docs/TechieFlow-Reset-Plan-2026-09-04.md` | The seven sessions that shrank the framework, one Done line each. | | `README.md` | How a person installs it and drives it. | @@ -81,7 +81,7 @@ Four personas: **analyst** (documents), **flow-master** (build, bugs, guides, st | `.tfcore/tasks/` | One file per command, plus the three shared rule files every command loads (`_status-update-gate`, `_smoke-test-policy`, `_metrics-emit-gate`) and `_yolo-mode`. | | `.tfcore/templates/v4custom/` | Nineteen templates. Each human document's template opens with its schema block. | | `.tfcore/standards/` | The technology-neutral coding standards and the .NET set. | -| `.tfcore/hooks/` | Eleven shell hooks plus the Codex adapter. Eight refuse an action; three do housekeeping. | +| `.tfcore/hooks/` | Eleven shell hooks. Eight refuse an action; three do housekeeping. | | `.tfcore/utils/` | The scripts, `tf-*`. Every mechanical step of every task is one of these. | | `.tfcore/telemetry/` | `SCHEMA.md` (read before emitting), `install-metrics.sh`, `tf-metrics.sh`, the `pre-commit` template the owner installs. | | `.tfcore/core-config.yaml` | Per-project settings: application name, size, kind, phase, which documents load. | @@ -90,7 +90,6 @@ Four personas: **analyst** (documents), **flow-master** (build, bugs, guides, st | `opencode.jsonc`, `.opencode/` | OpenCode's registrations and its guard-bridge plugin. There is no OpenCode mirror; it reads `.tfcore/` through file references. | | `tests/` | The self-tests: `mirror`, `doc-check`, `bugs`, `verify`, `goal`. | | `scaffold-*.sh`, `update-framework.sh` | Deploy the framework into a project, or refresh it. | -| `WORKFLOW.html` | The old human workflow reference, force-deployed into every project. Last revised before the reset and now out of date; see the open items. | --- @@ -104,12 +103,9 @@ If a run died mid-phase, the status gate never ran and `PROJECT-STATUS.md` is st | Item | Whose | |---|---| -| The **Codex adapter** is frozen. Whether it is removed now or after the reset is undecided (D-14, FR-42). | Owner decision | -| **Distribution**: the framework has no package or release pipeline. That work runs on `main` from `docs/TechieFlow-Distribution-Pipeline-Prompt.md` (D-22, FR-48 to FR-52). | In progress, separate branch | -| **Session 7** of the reset writes version 2 of the Playbook review prompt. | Next session | +| **Distribution**: the framework is an npm package with a validation workflow, merged from `main` on 2026-09-07. Publishing it is the remaining step (FR-48 to FR-52). | Owner action | | Three requirements name a **script that has not been written**: FR-58 (refuse `done complete` while rows are unfinished), FR-60 (refuse a banned head name in a brief), FR-61 (grade a row not observable when the environment lacks the data). The idea-stage commands still emit no run record (FR-34, FR-60). | Maintainer | -| **`WORKFLOW.html` is three sessions out of date** and is force-deployed to every project: 227 KB, last revised 2026-08-28, still teaching commands removed in Sitting 4c (`MISS-TechieFlow-20260907-10`). Either it is regenerated from the README and the documents of §1, or it is dropped and projects are pointed at those documents. No check names it until that is decided, because a self-test that fails every day teaches people to ignore it. | Owner decision | -| **Four misses stay open** with their outcome named in `docs/TechieFlow-Misses.md`. | Maintainer | +| **Open misses** are listed with their outcome in `docs/TechieFlow-Misses.md`; the one this maintainer owes a fix for is 12 of 2026-09-07, that a hidden framework folder is invisible to search and nothing enforces the rule. | Maintainer | | **TrStudio is not on this machine.** It is a named fixture and could not be refreshed here. | Owner action | | **TfLens** needs the miss stream read into its pages before its figures are quotable, and carries three fixes named in its own feedback file. | Separate repo | | **TrSetup has thousands of tracked build-output files.** Its ignore rules are correct and inert until the index entries go. `bash .tfcore/utils/tf-gitignore-audit.sh ` prints the commands. Agents never run version control. | Owner action | @@ -128,6 +124,7 @@ When you change the framework, change all of these together. 1. **Mirror parity.** Any edit to `.tfcore/agents/` or `.tfcore/tasks/` is copied byte-for-byte to `.claude/commands/TechieFlow/`. Prove it with `bash tests/mirror/run.sh`. Never create `.opencode/command/TechieFlow/`. 2. **A new task needs four wirings**: the file under `.tfcore/tasks/`, the mirror, the command registered on its owning persona (mirrored too), and an entry in `opencode.jsonc`. +2b. **Anything that changes what a project receives goes into both delivery routes**: the shell scripts and `scripts/install.mjs`. A hook registration, a new folder under `.tfcore/`, a change to how an existing project is refreshed. `npm run test:install` compares the two routes file by file and is the check; run it on a normal filesystem, because a Windows mount reports every file executable and yields one false difference. 3. **A new rule is a script or a hook, not a paragraph.** A rule ignored twice never gets a third paragraph. Task files hold steps only; explanation and history belong in the documents of §1 and in the changelog. 4. **Prove every script by running it.** A script the maintainer has not run on a real project is not done. Both harnesses. 5. **Log every gap as a miss** through `tf-log-miss.sh`, with its sort, before proposing the fix. diff --git a/docs/Adapter-Design.md b/docs/Adapter-Design.md index fce2619..b26a652 100644 --- a/docs/Adapter-Design.md +++ b/docs/Adapter-Design.md @@ -1,5 +1,7 @@ # Adapter Design — the harness boundary and per-phase model routing +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + > **Codex implementation addendum (2026-08-24):** the boundary is now > three-way. Codex uses repository `AGENTS.md`, `.agents/skills/techieflow-*`, > `.codex/agents/*.toml`, hooks, rules, and project config. Generation comes from diff --git a/docs/CHANGELOG.html b/docs/CHANGELOG.html index 6656ca9..cb6de6c 100644 --- a/docs/CHANGELOG.html +++ b/docs/CHANGELOG.html @@ -114,6 +114,8 @@

    TechieFlow — Changelog

  • The reset (2026-09-04 to 2026-09-07)
  • The 2026-08-28 review, as it stood at the top of the old briefing
  • Maintenance log (newest first)
  • +
  • 2026-09-07 — the Codex adapter removed, and WORKFLOW.html dropped
  • +
  • 2026-09-07 — main merged into dev: the npm installer brought back in step with the shell scripts
  • 2026-09-07 — Session 7: the Playbook review prompt, version 2, and the end of the reset
  • 2026-09-07 — Session 6: the repository made readable, and the framework deployed to all 23 projects
  • 2026-08-31 (3) — propagated to all 19 repos, and the propagation found a 4,635-file instance of the defect it was carrying the fix for
  • @@ -211,6 +213,23 @@

    The 20

    Maintenance log (newest first)#

    +

    2026-09-07 — the Codex adapter removed, and WORKFLOW.html dropped#

    +

    Both on the owner's decision, closing D-14 (open since the Session 1 review) and MISS-TechieFlow-20260907-10.

    +

    Codex. The framework supported three harnesses on paper and two in practice. The adapter was frozen through the reset and is now gone: .codex/ and .agents/skills/ deleted here, the binder, the telemetry reader and the adapter hook deleted with them, and every Codex branch taken out of the goal supervisor, the harness resolver, the emitter's harness detection, the routing scripts, routing.yaml, three guard hooks, four templates, the user guide and the telemetry schema. Both delivery routes stopped deploying it and started removing it, so one propagation pass cleaned every project rather than anyone deleting folders by hand. The telemetry schema keeps codex as a retired harness value, because records written before today carry it and a reader must still understand them.

    +

    WORKFLOW.html. 227 KB, last revised before the reset, still teaching three commands removed in Sitting 4c, and force-deployed into every project. Everything it said is now in the README and the documents under docs/. Dropped, and removed from every project the same way.

    +

    Across the estate: 23 projects refreshed, all exit 0, and verified afterwards to hold no .codex/, no .agents/, no WORKFLOW.html and none of the three Codex scripts. Checks: FR-42's check is built as two greps in tests/mirror/run.sh — the shipped framework and the readable files may not say Codex at all, and no delivery script may carry a Codex code path — and the install test now seeds a retired adapter into a project and proves both routes remove it. At the close: mirror 15, doc-check 12, bugs 51, verify 67, goal 29; test:install 31 checks and validate 4, with the package down from 211 files to 204.

    +

    The eleven documents that describe the adapter's design keep their content under a banner saying it was removed, so the history stays traceable without misleading anyone.

    +

    2026-09-07 — main merged into dev: the npm installer brought back in step with the shell scripts#

    +

    The owner merged main, which carries the distribution pipeline, into dev, which carries the reset. The validation workflow then failed five of thirty checks in npm run test:install, all of them the same shape: the installer had been written on main while the shell scripts were changing on dev, so the two routes no longer produced the same project.

    +
      +
    • .claude/settings.json differed in every install path. The installer registered nine hooks; the shell scripts register fourteen. Missing were guard-status, guard-metrics, guard-db and guard-build on Bash, and guard-metrics on writes. A project installed from the package therefore ran without the guards that refuse a hand-edited telemetry file, a database write outside build and fix, and a backgrounded build in unattended mode. Logged as MISS-TechieFlow-20260907-13, severity blocker. +
    • +
    • .tfcore/standards/ never arrived when a project was migrated from the old .bmad-core layout, because the installer's list of framework subfolders was written before Session 3 added that folder. The project came out with no coding standards file. Logged as -14. +
    • +
    +

    Both are fixed in scripts/install.mjs. On a clean filesystem the suite now reads: validate 4 checks, test:install 30 checks, none failing, and npm pack --dry-run shipping 211 files.

    +

    A third difference is local only and not a defect. Run on a Windows mount, the test reports opencode.jsonc as differing between the two routes. The content is identical; the executable bit is not. Every file under /mnt/c reports mode 777, so the installer marks the file executable while the shell route's cp keeps the existing 644. On ext4, which is what the workflow runs, both routes agree. The requirement now says to run the test on a normal filesystem.

    +

    The rule that was missing. Nothing in the maintenance contract said that a change to what a project receives has to go into both delivery routes, which is why the settings drifted through four sittings unnoticed. Added as FR-63, and as item 2b of the contract in WorkFlow-Context.md. The check already existed and did its job the moment the branches met.

    2026-09-07 — Session 7: the Playbook review prompt, version 2, and the end of the reset#

    docs/AI-First-Playbook-Review-Prompt.md rewritten as version 2 (3,180 words), carrying the methods the reset proved: the keep-as-words / turn-into-a-script / delete table, the schema block with a budget as a target and a maximum, the four-question miss sort with its sort field, the acceptance-line form, the instruction budget per model tier, and the rule that a rule ignored twice becomes a script or is deleted. It carries a list of what went wrong in the seven sessions, and a section on what a corporate team changes: OpenCode has no blocking end-of-turn hook, a rule that depends on remembering fails faster with more people, the review gates have named humans, and onboarding is a deliverable rather than documentation.

    Version 1's central assumption was wrong, and measuring said so. It told the review to look for TechieFlow's disease, prose rules outgrowing enforcement. The Playbook's shouted rules number 61 against TechieFlow's 622, and its ten phase documents average 357 words. The weight is elsewhere: the shipped verifier agent is 8,630 words and the fifteen command files total 29,800, so its verify path has the same shape as the 11,850-word file that caused 63 of TechieFlow's 128 recorded misses. And verification/ holds 174 files and 196,498 words of committed run evidence, 62 percent of the repository, keeping whole copies of an installed target — the thing TechieFlow bans by sweeping run material after seven days.

    diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1af9762..cac057f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -35,6 +35,31 @@ Everything below predates the reset and is preserved as it was written. ## Maintenance log (newest first) +### 2026-09-07 — the Codex adapter removed, and WORKFLOW.html dropped + +Both on the owner's decision, closing D-14 (open since the Session 1 review) and `MISS-TechieFlow-20260907-10`. + +**Codex.** The framework supported three harnesses on paper and two in practice. The adapter was frozen through the reset and is now gone: `.codex/` and `.agents/skills/` deleted here, the binder, the telemetry reader and the adapter hook deleted with them, and every Codex branch taken out of the goal supervisor, the harness resolver, the emitter's harness detection, the routing scripts, `routing.yaml`, three guard hooks, four templates, the user guide and the telemetry schema. Both delivery routes stopped deploying it and started **removing** it, so one propagation pass cleaned every project rather than anyone deleting folders by hand. The telemetry schema keeps `codex` as a **retired** `harness` value, because records written before today carry it and a reader must still understand them. + +**WORKFLOW.html.** 227 KB, last revised before the reset, still teaching three commands removed in Sitting 4c, and force-deployed into every project. Everything it said is now in the README and the documents under `docs/`. Dropped, and removed from every project the same way. + +**Across the estate:** 23 projects refreshed, all exit 0, and verified afterwards to hold no `.codex/`, no `.agents/`, no `WORKFLOW.html` and none of the three Codex scripts. **Checks:** FR-42's check is built as two greps in `tests/mirror/run.sh` — the shipped framework and the readable files may not say Codex at all, and no delivery script may carry a Codex code path — and the install test now seeds a retired adapter into a project and proves both routes remove it. **At the close:** mirror 15, doc-check 12, bugs 51, verify 67, goal 29; `test:install` 31 checks and `validate` 4, with the package down from 211 files to 204. + +The eleven documents that describe the adapter's design keep their content under a banner saying it was removed, so the history stays traceable without misleading anyone. + +### 2026-09-07 — main merged into dev: the npm installer brought back in step with the shell scripts + +The owner merged `main`, which carries the distribution pipeline, into `dev`, which carries the reset. The validation workflow then failed five of thirty checks in `npm run test:install`, all of them the same shape: the installer had been written on `main` while the shell scripts were changing on `dev`, so the two routes no longer produced the same project. + +- **`.claude/settings.json` differed in every install path.** The installer registered nine hooks; the shell scripts register fourteen. Missing were `guard-status`, `guard-metrics`, `guard-db` and `guard-build` on Bash, and `guard-metrics` on writes. A project installed from the package therefore ran without the guards that refuse a hand-edited telemetry file, a database write outside build and fix, and a backgrounded build in unattended mode. Logged as `MISS-TechieFlow-20260907-13`, severity blocker. +- **`.tfcore/standards/` never arrived** when a project was migrated from the old `.bmad-core` layout, because the installer's list of framework subfolders was written before Session 3 added that folder. The project came out with no coding standards file. Logged as `-14`. + +Both are fixed in `scripts/install.mjs`. On a clean filesystem the suite now reads: validate 4 checks, `test:install` **30 checks, none failing**, and `npm pack --dry-run` shipping 211 files. + +**A third difference is local only and not a defect.** Run on a Windows mount, the test reports `opencode.jsonc` as differing between the two routes. The content is identical; the executable bit is not. Every file under `/mnt/c` reports mode 777, so the installer marks the file executable while the shell route's `cp` keeps the existing 644. On ext4, which is what the workflow runs, both routes agree. The requirement now says to run the test on a normal filesystem. + +**The rule that was missing.** Nothing in the maintenance contract said that a change to what a project receives has to go into both delivery routes, which is why the settings drifted through four sittings unnoticed. Added as FR-63, and as item 2b of the contract in `WorkFlow-Context.md`. The check already existed and did its job the moment the branches met. + ### 2026-09-07 — Session 7: the Playbook review prompt, version 2, and the end of the reset `docs/AI-First-Playbook-Review-Prompt.md` rewritten as version 2 (3,180 words), carrying the methods the reset proved: the keep-as-words / turn-into-a-script / delete table, the schema block with a budget as a target and a maximum, the four-question miss sort with its `sort` field, the acceptance-line form, the instruction budget per model tier, and the rule that a rule ignored twice becomes a script or is deleted. It carries a list of what went wrong in the seven sessions, and a section on what a corporate team changes: OpenCode has no blocking end-of-turn hook, a rule that depends on remembering fails faster with more people, the review gates have named humans, and onboarding is a deliverable rather than documentation. diff --git a/docs/Capability-Matrix.md b/docs/Capability-Matrix.md index 29aed23..46f0f7f 100644 --- a/docs/Capability-Matrix.md +++ b/docs/Capability-Matrix.md @@ -1,5 +1,7 @@ # Capability Matrix — Claude Code, OpenCode, and Codex (for TechieFlow's harness adapter) +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + > **Codex addendum (2026-08-24):** Codex is implemented as the third adapter. > Current facts and paths are in `CodexChanges.md`. The historical two-column > investigation below remains the evidence for the original adapter. diff --git a/docs/Coupling-Points.md b/docs/Coupling-Points.md index c738b22..342496e 100644 --- a/docs/Coupling-Points.md +++ b/docs/Coupling-Points.md @@ -1,5 +1,7 @@ # Coupling Points — where TechieFlow depends on harness behaviour +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + > **Codex addendum (2026-08-24):** `.codex/`, `.agents/skills/`, > `codex-adapter.py`, and `tf-codex-bind.py` form the third adapter. Exact-parity > gaps are maintained in `CodexChanges.md` §5; the historical register below is diff --git a/docs/Miss-Telemetry-AI-First-Playbook.md b/docs/Miss-Telemetry-AI-First-Playbook.md index ffa0c85..8f9fd0f 100644 --- a/docs/Miss-Telemetry-AI-First-Playbook.md +++ b/docs/Miss-Telemetry-AI-First-Playbook.md @@ -1,5 +1,7 @@ # Miss telemetry — AI-First Playbook (the team edition) +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + **Status:** DESIGN — nothing in the Playbook is implemented yet. **The solo edition shipped its half on 2026-08-28**, so this design now has a working reference implementation to copy from rather than a sibling design document: `.tfcore/telemetry/SCHEMA.md` §5.5, `.tfcore/utils/tf-emit.sh`, `.tfcore/telemetry/tf-metrics.sh`. **Target repo:** `/mnt/c/3AIGenCode/AI-First-Playbook` (public team edition; source of truth is the private source repo). **Siblings:** `docs/Miss-Telemetry-TechieFlow.md` (the solo edition's version — read it first, especially its §0 implementation status) · `docs/Miss-Telemetry-TfLens.md`. diff --git a/docs/Miss-Telemetry-TechieFlow.md b/docs/Miss-Telemetry-TechieFlow.md index 6957145..63fcf41 100644 --- a/docs/Miss-Telemetry-TechieFlow.md +++ b/docs/Miss-Telemetry-TechieFlow.md @@ -1,5 +1,7 @@ # Miss telemetry — TechieFlow (the framework itself) +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + **Status: IMPLEMENTED — shipped 2026-08-28**, plus the `miss-amend` follow-up the same day (§0.35). Everything in §4–§8 below is built, deployed and verified end to end in this repo. **`.tfcore/telemetry/SCHEMA.md` §5.5 is now the authoritative field reference**; this document is the design record and the *why*, kept because the reasoning behind the three provenance rules is the part that will be re-litigated, not the field list. **Audience:** the framework owner + whichever agent extends it. **Siblings:** `docs/Miss-Telemetry-TfLens.md` (how the numbers get displayed — **not implemented**) · `docs/Miss-Telemetry-AI-First-Playbook.md` (the team edition's version of the same idea — **not implemented**). diff --git a/docs/TechieFlow-Distribution-Pipeline-Prompt.md b/docs/TechieFlow-Distribution-Pipeline-Prompt.md index bb5006b..159d714 100644 --- a/docs/TechieFlow-Distribution-Pipeline-Prompt.md +++ b/docs/TechieFlow-Distribution-Pipeline-Prompt.md @@ -1,5 +1,7 @@ # TechieFlow — Distribution Pipeline: Session Prompt +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + | | | |---|---| | Purpose | A prompt for one Claude Code session that gives TechieFlow the same distribution the AI-First Playbook already has: an npm package, a one-shot installer, pre-publish checks, and a GitHub Actions release pipeline. | diff --git a/docs/TechieFlow-FAQ.md b/docs/TechieFlow-FAQ.md index 14dd16a..3511899 100644 --- a/docs/TechieFlow-FAQ.md +++ b/docs/TechieFlow-FAQ.md @@ -31,7 +31,7 @@ It does now; it did not until 2026-08-27. The 2026-08-20 split moved framework c Because the framework is invisible to every default file-search tool, and until 2026-08-27 nothing told the agents that. **Two independent filters stack**, and you have to defeat both: -- `.tfcore/`, `.claude/`, `.codex/`, `.opencode/` and `.agents/skills/` are **hidden dot-directories** — ripgrep (which backs the agents' Grep tool) skips hidden paths by default. +- `.tfcore/`, `.claude/` and `.opencode/` are **hidden dot-directories** — ripgrep (which backs the agents' Grep tool) skips hidden paths by default. - They are also in the **managed `.gitignore` block** the scaffolders write into every app (§3) — and ripgrep honours `.gitignore` by default too. So `rg --hidden` is *not* enough in an app repo; it takes `rg --hidden --no-ignore` (`rg -uu`). And since nothing under `.tfcore/` is *tracked* in an app, `git grep` and `git ls-files` return zero rows as well. An agent that globs for a filename gets nothing and reasonably concludes the framework isn't installed. diff --git a/docs/TechieFlow-How-It-Works.md b/docs/TechieFlow-How-It-Works.md index 19dac98..e5aba58 100644 --- a/docs/TechieFlow-How-It-Works.md +++ b/docs/TechieFlow-How-It-Works.md @@ -398,7 +398,7 @@ Each defect is also recorded in the framework repository's own miss stream, `doc | D-11 | Telemetry | No run record exists for `*day1-greenfield`, `*day1-brownfield`, `*devguide`, `*productguide` in any repository. | Confirm whether those tasks emit; if they do not, they must. | | D-12 | Telemetry | The run record does not carry whether YOLO mode was on, so cost cannot be reported by mode. | Record the mode on every run so the cost table in §5 can be split by mode. | | D-13 | Personas | The analyst's idea-stage commands (brainstorm, project brief, competitor analysis, market research, research prompt) were absent from the previous version of this file and emit no telemetry. | Documented (§3.2). Telemetry for them decided in Session 2. | -| D-14 | Harnesses | The framework still carries a Codex adapter. The owner supports two harnesses only. | Codex frozen now; removal decided after the reset. TfLens's harness page will list Claude Code and OpenCode only. | +| D-14 | Harnesses | The framework still carries a Codex adapter. The owner supports two harnesses only. | **Closed 2026-09-07: the adapter is removed** from the framework, both delivery routes and all 23 projects (FR-42). TfLens's harness page will list Claude Code and OpenCode only. | | D-15 | `*log-miss`, `*triage-issues`, `*fix-issues` | `*log-miss` is never called automatically. The nesting diagram (§3.1) shows it standing alone. Triage finds root causes and fix repairs them, yet neither records the miss or its cost unless the owner types the log command by hand. | Triage calls log-miss automatically for every root cause it identifies, recording the discovery cost. Fix calls log-miss automatically when a fix is complete, recording the fix cost. The owner never types log-miss for a bug that went through triage or fix. | | D-16 | Bug handling | The owner's actual way of working is one sequence typed as a long prompt every time: compare every screen to its mockup, triage (analyse only), log-miss for discovery cost, fix, log-miss for fix cost, metrics, all in YOLO mode, with a summary per step. No command does this. | One command, working name `*triage-and-fix {App} {evidence}`, runs that exact sequence in YOLO mode and ends with a per-step summary: screens compared, root causes found and logged with cost, fixes made and logged with cost, metrics refreshed. The separate triage and fix commands remain for when only one is wanted. | | D-17 | Telemetry, every phase with an owner review | When the owner reviews a phase's output (for example the BRD, Architecture and mockups after day-1 stage 1) and gives corrections, nothing is recorded: not how many gaps were found, not what producing the documents cost, not what applying the corrections cost. Only build and verify phases are measured for misses. | Every phase that ends in an owner review records a review outcome: the number of corrections given, the cost of producing the reviewed output, and the cost of applying the corrections. Whether this is a new record kind ("correction") or a miss with a review origin is decided in Session 2; the requirement is that the cost of deviation is measurable in every phase, not only in build and verify. | diff --git a/docs/TechieFlow-Installation.md b/docs/TechieFlow-Installation.md index 10aaad6..c9dc3b5 100644 --- a/docs/TechieFlow-Installation.md +++ b/docs/TechieFlow-Installation.md @@ -17,7 +17,7 @@ You need four things on the machine. |---|---|---| | Node.js 20 or newer | Runs the installer. Nothing is added to your project. | `node --version` | | bash | The framework's guard hooks and helper scripts run under bash. macOS and Linux have it. On Windows, work inside WSL or Git Bash. | `bash --version` | -| Python 3.10 or newer | Powers the HTML renderer, the telemetry writer and the guard hooks. An older Python 3 works but skips the Codex files. | `python3 --version` | +| Python 3 | Powers the HTML renderer, the telemetry writer and the guard hooks. | `python3 --version` | | Claude Code or OpenCode | The harness that runs the agents. Either one. Both work from the same install. | `claude --version` or `opencode --version` | For a .NET project you also need the .NET SDK. Run `dotnet --version` to check. @@ -42,8 +42,6 @@ The npm package itself holds the framework folders, this document and a `scripts | `.opencode/opencode.jsonc` | The framework's OpenCode configuration. OpenCode reads the personas and tasks straight from `.tfcore/` through it. | Refreshed. | | `.opencode/command/generate-html.md` | The short `/generate-html` command for OpenCode. | Added on update only. | | `opencode.jsonc` | The root OpenCode configuration, for your own additions. | Refreshed only when it holds nothing of yours, with the old file kept as `opencode.jsonc.bak`. | -| `.codex/` and `.agents/skills/` | The Codex adapter, generated from the same personas and tasks. | Refreshed. `config.toml` is yours and is kept. | -| `WORKFLOW.html` | The human workflow guide. Open it in a browser. | Refreshed. | | `.tf-scaffold-note.txt` | A note with your next command. Delete it when read. | Left alone. | | `docs/metrics/` | Five empty telemetry files and a README. This is your project's history: commit it. | Left alone. | | `.gitignore` | Lines that keep the copies above out of your commits. Appended, never rewritten. | Appended. | @@ -242,7 +240,6 @@ The installer and the updater never write to: - `.tfcore/core-config.yaml` and `.tfcore/routing.yaml` after the first install - `.claude/settings.local.json` - `.claude/commands/trblazeui.md`, `.claude/commands/techierag.md` and the same names under `.opencode/command/`. These come from the NuGet packages. -- `.codex/config.toml` after the first install - `opencode.jsonc` at the root when it holds a key of your own Existing lines in `.gitignore` and `.gitattributes` are never rewritten. The managed lines are appended once and recognised on every later run. @@ -316,7 +313,6 @@ It installs the framework if it is not there yet, then removes the leftover pack | The framework is under `node_modules/@techierathore/techieflow/` and nowhere else | You ran `npm install`. See section 9. Run `npx @techierathore/techieflow@latest install`. | | `bash was not found` | On Windows, run the command inside WSL or Git Bash. | | `python3 was not found` | Install Python 3 and run the command again. On macOS: `brew install python3`. On Ubuntu or WSL: `sudo apt-get install -y python3`. | -| `Codex bindings could not be generated` | Your Python is older than 3.10. Everything except the Codex files is installed. Upgrade Python and run `update` to add them. | | `Refusing to install into the framework itself` | You ran the command inside a clone of this repository. Pass `--target=`. | | `does not look installed` on update | The folder has no `.tfcore/`. Run `install` first. | | Claude Code does not show the `/TechieFlow:agents:analyst` command | Restart Claude Code in the project folder. Check that `.claude/commands/TechieFlow/agents/analyst.md` exists. | diff --git a/docs/TechieFlow-Library-Persona-Propagation.md b/docs/TechieFlow-Library-Persona-Propagation.md index 5b2560c..d3a8387 100644 --- a/docs/TechieFlow-Library-Persona-Propagation.md +++ b/docs/TechieFlow-Library-Persona-Propagation.md @@ -1,5 +1,7 @@ # Library Persona Propagation +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + This is the source-of-truth map for the AI instructions shipped by the TrBlazeUI and TechieRag NuGet packages. Do not fix only the copies in this TechieFlow repository: the next consumer build can overwrite them from the diff --git a/docs/TechieFlow-Misses.html b/docs/TechieFlow-Misses.html index f031c9e..17ef605 100644 --- a/docs/TechieFlow-Misses.html +++ b/docs/TechieFlow-Misses.html @@ -111,8 +111,8 @@

    TechieFlow — Misses

    @@ -120,18 +120,17 @@

    TechieFlow — Misses

    AppTechieFlow -Count118 logged: 35 open, 82 fixed, 1 will not fix +Count120 logged: 33 open, 86 fixed, 1 will not fix Sourcedocs/metrics/misses.jsonl, one row per miss record. Rewritten by tf-misses-md.sh on every new record. Never edit it: a wrong row is corrected by a new record. Updated2026-09-07

    Whose gap answers the four questions of the miss protocol: the app's spec did not say it, so the checklist line is fixed; the framework never said it, so one requirement line and a check are added; the check was too weak (a review, or a script that did not fire), so the check is fixed; said and ignored, so the rule becomes a hook or is deleted. not sorted means the record predates the sort or nobody has answered yet; bash .tfcore/utils/tf-emit.sh --amend <miss> sort <spec|unsaid|weak-check|ignored> completes it.

    -

    Open (35)#

    +

    Open (33)#

    - @@ -150,7 +149,6 @@

    Open (35)#

    - @@ -167,11 +165,14 @@

    Open (35)#

    MissFoundWhose gapWhat went wrong
    MISS-TechieFlow-20260907-122026-09-07 by ownersaid and ignoredThe D-21 rule that hidden framework folders are invisible to search was written down and still cost 58 percent of a folder's true size when OpenCode measured the Playbook with a shell glob.
    MISS-TechieFlow-20260907-102026-09-07 by ownerthe check was too weakWORKFLOW.html is force-deployed to every project and still teaches three commands removed in Sitting 4c, because the removed-command check looks at the task files and the harness registrations but never at the human reference.
    MISS-TechieFlow-20260907-042026-09-07 by gatethe check was too weakThe readable miss file's unchanged check compared only the header above the Updated line, so an amend that changed a row but no count left the file stale; the bugs self-test caught it before release and the check now compares everything but the date.
    MISS-TechieFlow-20260907-032026-09-07 by agent-reviewthe check was too weakThe cross-project rollup keyed a requirement by its id alone, so REQ-UI-001 of TfLens and REQ-UI-001 of TechieBlog counted as one requirement and the combined first-pass rate printed 72% where the true figure is 48%; found by re-reading the numbers before the explainer, fixed by keying on project and id.
    MISS-TechieFlow-20260907-012026-09-07 by agent-reviewsaid and ignoredThe OpenCode verify on TechieBlog-oc (gpt-5.6-terra, 2026-09-07) skipped step 4 of the verify task: the old specs died on missing environment variables and no test was written for the 101 rows without one, so the run ended in twelve minutes with 101 rows not tested and only the screens checks graded; the task's step 4 was read and not done, which is the instruction-ignored pattern, and the Claude run on the same project wrote and repaired tests for three hours.
    MISS-TechieFlow-20260904-172026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
    MISS-TechieFlow-20260904-162026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
    MISS-TechieFlow-20260904-152026-09-04 by ownernot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
    MISS-TechieFlow-20260904-142026-09-04 by ownernot sortedno sentence recorded (scope-creep, other, why: missing-checklist-item)
    MISS-TechieFlow-20260904-132026-09-04 by agent-reviewnot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
    MISS-TechieFlow-20260904-122026-09-04 by agent-reviewnot sortedno sentence recorded (unspecified-gap, other, why: missing-checklist-item)
    MISS-TechieFlow-20260904-112026-09-04 by agent-reviewnot sortedno sentence recorded (unspecified-gap, other, why: insufficient-verify-method)
    (no id, record 55)2026-09-05 by ownernot sortedThe first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule.
    -

    Fixed (82)#

    +

    Fixed (86)#

    + + + @@ -226,6 +227,7 @@

    Fixed (82)#

    + diff --git a/docs/TechieFlow-Misses.md b/docs/TechieFlow-Misses.md index 48ac4bf..0883b2b 100644 --- a/docs/TechieFlow-Misses.md +++ b/docs/TechieFlow-Misses.md @@ -3,18 +3,17 @@ | | | |---|---| | App | TechieFlow | -| Count | 118 logged: 35 open, 82 fixed, 1 will not fix | +| Count | 120 logged: 33 open, 86 fixed, 1 will not fix | | Source | `docs/metrics/misses.jsonl`, one row per miss record. Rewritten by `tf-misses-md.sh` on every new record. Never edit it: a wrong row is corrected by a new record. | | Updated | 2026-09-07 | **Whose gap** answers the four questions of the miss protocol: **the app's spec** did not say it, so the checklist line is fixed; **the framework never said it**, so one requirement line and a check are added; **the check was too weak** (a review, or a script that did not fire), so the check is fixed; **said and ignored**, so the rule becomes a hook or is deleted. **not sorted** means the record predates the sort or nobody has answered yet; `bash .tfcore/utils/tf-emit.sh --amend sort ` completes it. -## Open (35) +## Open (33) | Miss | Found | Whose gap | What went wrong | |---|---|---|---| | MISS-TechieFlow-20260907-12 | 2026-09-07 by owner | said and ignored | The D-21 rule that hidden framework folders are invisible to search was written down and still cost 58 percent of a folder's true size when OpenCode measured the Playbook with a shell glob. | -| MISS-TechieFlow-20260907-10 | 2026-09-07 by owner | the check was too weak | WORKFLOW.html is force-deployed to every project and still teaches three commands removed in Sitting 4c, because the removed-command check looks at the task files and the harness registrations but never at the human reference. | | MISS-TechieFlow-20260907-04 | 2026-09-07 by gate | the check was too weak | The readable miss file's unchanged check compared only the header above the Updated line, so an amend that changed a row but no count left the file stale; the bugs self-test caught it before release and the check now compares everything but the date. | | MISS-TechieFlow-20260907-03 | 2026-09-07 by agent-review | the check was too weak | The cross-project rollup keyed a requirement by its id alone, so REQ-UI-001 of TfLens and REQ-UI-001 of TechieBlog counted as one requirement and the combined first-pass rate printed 72% where the true figure is 48%; found by re-reading the numbers before the explainer, fixed by keying on project and id. | | MISS-TechieFlow-20260907-01 | 2026-09-07 by agent-review | said and ignored | The OpenCode verify on TechieBlog-oc (gpt-5.6-terra, 2026-09-07) skipped step 4 of the verify task: the old specs died on missing environment variables and no test was written for the 101 rows without one, so the run ended in twelve minutes with 101 rows not tested and only the screens checks graded; the task's step 4 was read and not done, which is the instruction-ignored pattern, and the Claude run on the same project wrote and repaired tests for three hours. | @@ -33,7 +32,6 @@ | MISS-TechieFlow-20260904-17 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | | MISS-TechieFlow-20260904-16 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | | MISS-TechieFlow-20260904-15 | 2026-09-04 by owner | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | -| MISS-TechieFlow-20260904-14 | 2026-09-04 by owner | not sorted | no sentence recorded (scope-creep, other, why: missing-checklist-item) | | MISS-TechieFlow-20260904-13 | 2026-09-04 by agent-review | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | | MISS-TechieFlow-20260904-12 | 2026-09-04 by agent-review | not sorted | no sentence recorded (unspecified-gap, other, why: missing-checklist-item) | | MISS-TechieFlow-20260904-11 | 2026-09-04 by agent-review | not sorted | no sentence recorded (unspecified-gap, other, why: insufficient-verify-method) | @@ -49,11 +47,14 @@ | MISS-TechieFlow-20260904-01 | 2026-09-04 by owner | not sorted | no sentence recorded (wrong-behaviour, other, why: instruction-ignored) | | (no id, record 55) | 2026-09-05 by owner | not sorted | The first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule. | -## Fixed (82) +## Fixed (86) | Miss | Found | Closed | Whose gap | What went wrong | |---|---|---|---|---| +| MISS-TechieFlow-20260907-14 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the framework never said it | The npm installer's framework subfolder list left out standards, so a project migrated from the old layout came out with no coding standards file. | +| MISS-TechieFlow-20260907-13 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the framework never said it | The npm installer wrote a settings.json missing five hook registrations the shell scripts had gained, so a project installed from the package ran without the metrics, database and build guards. | | MISS-TechieFlow-20260907-11 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the check was too weak | The Playbook review prompt was drafted from folder word counts taken only at the top level, so 174 files and 196,498 words counted as zero and the prompt nearly shipped the wrong headline finding. | +| MISS-TechieFlow-20260907-10 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the check was too weak | WORKFLOW.html is force-deployed to every project and still teaches three commands removed in Sitting 4c, because the removed-command check looks at the task files and the harness registrations but never at the human reference. | | MISS-TechieFlow-20260907-09 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the check was too weak | A run record with no ended at all was accepted and could never be costed: the guard only replaced an ended that lied, so this session's own record landed with no duration. | | MISS-TechieFlow-20260907-08 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the check was too weak | The updater kept a project's root opencode.jsonc because its dead BMAD-era registrations looked like project content, so that repo loaded no framework agents in OpenCode at all and only a warning was printed. | | MISS-TechieFlow-20260907-07 | 2026-09-07 by owner | 2026-09-07 by fix-issues | the framework never said it | Nothing capped or checked the two files a person reads first, so the briefing reached 344 KB and the README 121 KB and both still named commands the framework had removed. | @@ -108,6 +109,7 @@ | MISS-TechieFlow-20260905-03 (FR-39) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the check was too weak | tf-emit.sh appends a miss record that has no miss_id, although miss_id is the join key to its fix record, so a caller that skips --next-miss-id writes an orphan. | | MISS-TechieFlow-20260905-02 (FR-40) | 2026-09-05 by agent-review | 2026-09-07 by fix-issues | the check was too weak | Six task files edited in .tfcore on 2026-08-31 were never copied to the Claude Code mirror, so the two harnesses ran different smoke, metrics, mockup, render and verify rules for five days; no parity check ran. | | MISS-TechieFlow-20260905-01 | 2026-09-05 by owner | 2026-09-07 by fix-issues | said and ignored | The first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule. | +| MISS-TechieFlow-20260904-14 | 2026-09-04 by owner | 2026-09-07 by fix-issues | not sorted | no sentence recorded (scope-creep, other, why: missing-checklist-item) | | MISS-TechieFlow-20260831-10 | 2026-08-31 by agent-review | 2026-08-31 by fix-issues | not sorted | no sentence recorded (wrong-behaviour, src, why: insufficient-verify-method) | | MISS-TechieFlow-20260831-09 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (unspecified-gap, src, why: insufficient-verify-method) | | MISS-TechieFlow-20260831-08 | 2026-08-31 by library-feedback | 2026-08-31 by fix-issues | not sorted | no sentence recorded (spec-contradiction, src, why: missing-checklist-item) | diff --git a/docs/TechieFlow-Permissions-And-YOLO.md b/docs/TechieFlow-Permissions-And-YOLO.md index 1241c8f..36ee62d 100644 --- a/docs/TechieFlow-Permissions-And-YOLO.md +++ b/docs/TechieFlow-Permissions-And-YOLO.md @@ -34,8 +34,6 @@ The pre-built config **auto-allows** Read/Glob/Grep/Edit/Write/MultiEdit and **a **Build passes are whole-checklist, YOLO or not.** The other 3-day culprit: build-phase runs that implemented a few REQs, wrote "next command: `*build-phase` for the remaining REQs" and stopped. `build-phase.md §2b` now bans that ending — a pass is done when **every** working-list REQ is ≥ `Implemented` (or a logged `Blocked`/owner-gated blocker), the verifier has been chained, and (in YOLO) its FAIL rows have been looped. Long list ⇒ more sub-agent clusters, never a shorter pass. `_status-update-gate.md` item 5 carries the matching rule for the next-command line. -**Codex permission difference.** `.codex/hooks.json` routes shell and file changes through `.tfcore/hooks/codex-adapter.py`, while `.codex/rules/techieflow.rules` provides the command policy. Codex keeps every agent-issued `git` and `gh` command blocked in normal and YOLO modes (including reads); this is intentionally stricter than the Claude/OpenCode YOLO table above. Trust the repository and inspect `/hooks` after scaffold/update. `$techieflow-yolo` changes TechieFlow pause/delete behavior, but never relaxes Codex's version-control boundary. - **Q: Config (canonical version in scaffold-brownfield.sh / scaffold-greenfield.sh)** ```json @@ -81,9 +79,9 @@ The pre-built config **auto-allows** Read/Glob/Grep/Edit/Write/MultiEdit and **a **A stale `PROJECT-STATUS.html` blocks the end of the turn (2026-08-25).** The first **Stop** hook — `.tfcore/hooks/guard-status-html.sh` — refuses to let a turn end while `PROJECT-STATUS.html` is older than `PROJECT-STATUS.md`, or missing. `_status-update-gate.md` §8 ("re-render in the same turn, full stop") had failed twice in a row; the owner spent 4h40m reading a page that still listed retracted owner-actions. Stop, not PostToolUse, because the rule is about the turn — an agent legitimately renders the HTML several tool calls after writing the markdown. It honours `stop_hook_active` so a turn that genuinely cannot render still terminates. mtime only: content parity stays an agent responsibility. -**Expired run material is deleted automatically (2026-08-26).** Pinning artifacts under `tests/.artifacts/` fixed *where* they land but not that they ever leave — Playwright wipes only its own `outputDir`, so per-cluster subfolders, harness scripts and multi-hundred-MB host logs piled up (TechieBlog: 1.1 GB under `tests/.artifacts/` + 101 MB of `.verify/*.log`, mostly two weeks stale). A **SessionStart** hook — `.tfcore/hooks/sweep-artifacts.sh`, no veto, exit 0 always — deletes files under `tests/.artifacts/` and `.verify/` older than the retention window (default **7 days**; `artifactRetentionDays: N` in `.tfcore/core-config.yaml` or `TF_ARTIFACT_RETENTION_DAYS=N`; `0` disables the age sweep), prunes emptied dirs, and removes banned repo-root legacy dirs (`test-results*/`, `scripts-*/`, `playwright-report/`) regardless of age. Files newer than the window are untouched, so a run in flight is never disturbed and a mixed-age `harness/` keeps its recent scripts. Never follows symlinks, never leaves the project root, never touches tracked `tests/verify/` or the project's own `scripts/`. Throttled to once per hour per project (`.tfcore/.session/sweep.stamp`); `TF_SWEEP_DRY_RUN=1` previews. Codex runs it from `codex-adapter.py session-start`; OpenCode from the plugin on the first root `session.created`. The one-line summary of what was removed is surfaced into the session. +**Expired run material is deleted automatically (2026-08-26).** Pinning artifacts under `tests/.artifacts/` fixed *where* they land but not that they ever leave — Playwright wipes only its own `outputDir`, so per-cluster subfolders, harness scripts and multi-hundred-MB host logs piled up (TechieBlog: 1.1 GB under `tests/.artifacts/` + 101 MB of `.verify/*.log`, mostly two weeks stale). A **SessionStart** hook — `.tfcore/hooks/sweep-artifacts.sh`, no veto, exit 0 always — deletes files under `tests/.artifacts/` and `.verify/` older than the retention window (default **7 days**; `artifactRetentionDays: N` in `.tfcore/core-config.yaml` or `TF_ARTIFACT_RETENTION_DAYS=N`; `0` disables the age sweep), prunes emptied dirs, and removes banned repo-root legacy dirs (`test-results*/`, `scripts-*/`, `playwright-report/`) regardless of age. Files newer than the window are untouched, so a run in flight is never disturbed and a mixed-age `harness/` keeps its recent scripts. Never follows symlinks, never leaves the project root, never touches tracked `tests/verify/` or the project's own `scripts/`. Throttled to once per hour per project (`.tfcore/.session/sweep.stamp`); `TF_SWEEP_DRY_RUN=1` previews. OpenCode runs it from the plugin on the first root `session.created`. The one-line summary of what was removed is surfaced into the session. -Both new guards run in every harness: Codex through `codex-adapter.py` (`pre-tool` and the new `stop` mode wired in `.codex/hooks.json`), OpenCode through `.opencode/plugin/techieflow.js` (the bash guard in `tool.execute.before`; the Stop check as a one-shot follow-up prompt on root `session.idle`, since OpenCode has no blocking Stop hook). Hooks load at session start — neither takes effect in an already-running session. +Both new guards run in both harnesses: OpenCode through `.opencode/plugin/techieflow.js` (the bash guard in `tool.execute.before`; the Stop check as a one-shot follow-up prompt on root `session.idle`, since OpenCode has no blocking Stop hook). Hooks load at session start — neither takes effect in an already-running session. **PROJECT-STATUS shape is enforced mechanically too (2026-07-09).** A second PreToolUse hook — `.tfcore/hooks/guard-status.sh`, matcher `Write|Edit|MultiEdit` — blocks any write to `PROJECT-STATUS.md` that violates the crisp fixed-shape snapshot rule: an H2 outside the template's section set (per-run dated sections like `## *verify all — coverage matrix (DATE)` are the classic disease), a heading naming a command run, a paragraph stuffed into `current_phase:`, or a full-file write past ~120 lines. The block message tells the agent exactly how to reshape (overwrite the template sections in place, ONE Verification-log row per run, detail into the checklist Remarks). Same philosophy as the git ban: prose rules kept failing, so the harness enforces it. See `.tfcore/tasks/_status-update-gate.md`. diff --git a/docs/TechieFlow-Release-Guide.md b/docs/TechieFlow-Release-Guide.md index ecf48df..536d798 100644 --- a/docs/TechieFlow-Release-Guide.md +++ b/docs/TechieFlow-Release-Guide.md @@ -197,7 +197,7 @@ npx @techierathore/techieflow@latest install --greenfield ls -a ``` -You should see `.tfcore`, `.claude`, `.opencode`, `.codex`, `.agents`, `opencode.jsonc`, `WORKFLOW.html`, `docs`, `src`, `tests` and no `node_modules`. +You should see `.tfcore`, `.claude`, `.opencode`, `opencode.jsonc`, `docs`, `src`, `tests` and no `node_modules`. --- diff --git a/docs/TechieFlow-Requirements.md b/docs/TechieFlow-Requirements.md index bd724fd..2bda419 100644 --- a/docs/TechieFlow-Requirements.md +++ b/docs/TechieFlow-Requirements.md @@ -128,7 +128,7 @@ The same four questions are asked for a miss in an application, against that app |---|---|---|---| | FR-40 | works identically in Claude Code and OpenCode; every task is registered in both, and every hook has an OpenCode equivalent or a documented gap. | script (built 2026-09-07 from MISS-TechieFlow-20260905-02): `bash tests/mirror/run.sh` fails when a persona or task differs from the Claude Code mirror, when the mirror holds a removed file, when a command task is not referenced from `opencode.jsonc`, or when a reference there does not resolve; it also holds FR-43 and FR-44 to their budgets. The hook parity table has no undocumented row (review). | owner 2026-09-04 | | FR-41 | honours YOLO mode in every command; `*build-phase` and `*verify` default to it. | fixture run: each command with the flag on completes without a prompt; build and verify complete without the flag. | D-18 | -| FR-42 | carries no Codex-specific code path once the Codex adapter is removed. | script: no `codex` reference outside the changelog. | D-14 | +| FR-42 | carries no Codex-specific code path. The adapter was removed on 2026-09-07: no file deploys, generates or dispatches to Codex, and no shipped document or template names it. The telemetry schema keeps `codex` as a retired `harness` value, because records written before that date carry it and must stay readable. | script (built 2026-09-07): `bash tests/mirror/run.sh` greps `.tfcore/`, the Claude mirror, `.opencode/`, the three shell scripts, the installer, `package.json`, the README and the briefing for `codex`, allowing only the two retired-value notes in `SCHEMA.md`. | D-14 | | FR-58 | runs one command to completion in YOLO and stops at the next owner review; it never starts the following phase on its own. | fixture run: `*day1-greenfield MyDiary` in YOLO ends after stage 1 with no checklist; `*build-phase` in YOLO ends after its verify with no handoff. Script candidate: `tf-yolo.sh done` refuses `complete` when the phase marker names a command other than the goal's. | MISS-TechieFlow-20260905-04 (sorted `unsaid`, Session 5); D-18 | | FR-59 | starts every unattended run through the goal supervisor `tf-goal.sh`, never through a bare harness command, so a usage limit, a crash or an early stop is survived. | review, to become a script: every run record with `yolo: true` written outside an interactive session has a `goal.json` under `.tfcore/.session/` whose start precedes it. | MISS-TechieFlow-20260905-08 (sorted `unsaid`, Session 5); How-It-Works §3.10 | | FR-60 | reads the project's Stack answer set before any command proposes a project, folder or head name, the idea-stage commands included, so a banned name such as `.App` never enters a brief. | script candidate: the document checker refuses a project brief that names `.App` when the .NET answer set is chosen. Until then, review. Not built: the idea-stage commands also emit no run record yet (FR-34 unmet for them). | MISS-TechieFlow-20260905-17 (sorted `unsaid`, Session 5) | @@ -154,6 +154,7 @@ The same four questions are asked for a miss in an application, against that app | FR-49 | installs for both harnesses from the one package: the Claude Code mirror and settings, and the OpenCode registrations. | script: after install, `.claude/commands/TechieFlow/` is byte-identical to `.tfcore/`, and every `opencode.jsonc` file reference resolves. | D-22; FR-40 | | FR-50 | is versioned through GitHub releases and published by a pipeline that runs automated checks first: mirror parity, OpenCode reference resolution, `bash -n` on every script, the installer's own tests, and a dry-run pack. | script: the release workflow fails when any check fails; the published package version equals the release tag. | D-22; Playbook release process | | FR-51 | keeps the shell scripts (`scaffold-*.sh`, `update-framework.sh`) working from a local clone, and the installer produces the same result, so both routes stay valid. | script: the FR-48 diff, run from both routes. | D-22 | +| FR-63 | carries every change that alters what a project receives into **both** routes in the same pass: the shell scripts and the npm installer. That includes a new or removed hook registration in `.claude/settings.json`, a new folder under `.tfcore/`, and any change to how an existing project's files are refreshed. | script: `npm run test:install` installs by each route into identical folders and compares every path, its content and its executable bit; it failed on five checks when a hook registration and the `standards` folder were in the shell scripts only. Run it on a normal filesystem: a Windows mount reports every file executable and produces one false difference. | MISS-TechieFlow-20260907-13 and -14 (both sorted `unsaid`); D-22 | | FR-52 | ships an Installation document that a person outside the owner's machines can follow to a working project in under ten minutes. | review, then fixture run: a fresh machine with Node installed, following the document only, reaches a working `*day1-greenfield` on MyDiary. | D-22 | --- diff --git a/docs/TechieFlow-Reset-Plan-2026-09-04.md b/docs/TechieFlow-Reset-Plan-2026-09-04.md index 2c55898..27a8ec8 100644 --- a/docs/TechieFlow-Reset-Plan-2026-09-04.md +++ b/docs/TechieFlow-Reset-Plan-2026-09-04.md @@ -62,6 +62,8 @@ A session ends when its output exists and the owner understands it. Session 4 is **Output:** a corrected How-It-Works. A list of commands by usage. The "never use" list is removed in Session 4. +**Done 2026-09-04:** `TechieFlow-How-It-Works.md` reviewed with the owner and corrected line by line; the twenty-two defects D-1 to D-22 recorded in its §8 and mirrored into the framework's own miss stream; every command given a verdict, which produced the list of seven never-used commands that Sitting 4c removed from both harnesses; the session itself recorded as the first `framework-reset` run (08:18 to 14:43 UTC, 569,794 output tokens). + ### Session 2 — The framework's own requirements, and the standing .NET decisions **Goal:** the framework gets what every app gets: a checklist with testable lines. And the .NET decisions each app has been inventing on its own get written down once. @@ -163,6 +165,8 @@ The ten questions: **Output:** the version 2 prompt. The Playbook sessions then follow their own plan. +**Done 2026-09-07:** `AI-First-Playbook-Review-Prompt.md` rewritten as version 2 (3,180 words) carrying the reset's methods, the list of what went wrong in the seven sessions, and what a corporate team changes; version 1's premise refuted by measurement, the Playbook's weight being its 8,630-word shipped verifier, its 29,800 words of commands and 196,498 words of committed run evidence rather than its 61 prose rules; Step 1 proven by a real OpenCode run on the Playbook, which under-reported one folder by 58 percent and so found D-21 recurring; misses 11 and 12 of 2026-09-07 logged, 11 closed and 12 left open. **The seven-session reset is complete.** Afterwards, `main` was merged into `dev` and the npm installer was brought back in step with the shell scripts (misses 13 and 14, FR-63): validate 4 checks, `test:install` 30 checks, none failing. + --- ## 3. What comes after the frameworks are fixed — order only, no dates diff --git a/docs/TechieFlow-Routing-Guide.md b/docs/TechieFlow-Routing-Guide.md index 9756957..057af6a 100644 --- a/docs/TechieFlow-Routing-Guide.md +++ b/docs/TechieFlow-Routing-Guide.md @@ -1,5 +1,7 @@ # TechieFlow — Model Routing Guide +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + | | | |---|---| | Purpose | Which model runs which command, where that is written down, and how to change it. | diff --git a/docs/TechieFlow-Session-7-Restart-Prompt.md b/docs/TechieFlow-Session-7-Restart-Prompt.md index 62ef1b1..587b0de 100644 --- a/docs/TechieFlow-Session-7-Restart-Prompt.md +++ b/docs/TechieFlow-Session-7-Restart-Prompt.md @@ -28,11 +28,11 @@ Open work for this session: 4. Build it in Claude Code and test it only in OpenCode, per the plan's rule for Playbook work. 5. Close the reset: one `framework-reset` run record, mode `session-7`; propose the Done line for Session 7; refresh the memory file; and tell the owner plainly that the seven sessions are complete and what happens next (the distribution pipeline on `main`, then the Playbook's own sessions, then the blog, then building resumes). -Three decisions are still the owner's and are carried into this session: +All three decisions the prompt carried are now taken (2026-09-07): -1. The **Codex adapter**: removed now, or after the reset (D-14, FR-42)? It is frozen, not propagated, and every project still carries it. -2. **`WORKFLOW.html`**: regenerated, or dropped? It is 227 KB, last revised 2026-08-28, still teaches three commands removed in Sitting 4c, and `update-framework.sh` force-deploys it into all 23 projects (`MISS-TechieFlow-20260907-10`, open). Session 6 deliberately added no check for it, because a check that fails every day gets ignored. -3. **Which documents count as public-facing** for FR-47. Session 6 scoped the check to the README, the briefing and the templates, and deliberately left the reset's own working documents out, because they name MyDiary and other fixtures on purpose. If the owner wants a wider scope, the names come out of those documents and the check's file list grows. +1. The **Codex adapter is removed** — from the framework, both delivery routes and all 23 projects (D-14 closed, FR-42 built as a check). +2. **`WORKFLOW.html` is dropped**, not regenerated; the README and the documents under `docs/` say what it said, and the updater removes it from a project that still has one. +3. **FR-47's scope stands** as Session 6 set it: the README, the briefing and the templates, with the reset's working documents deliberately out. Method, unchanged: tables for anything the owner rules on, questions numbered after the table with a suggested answer, every script proven by a real run with its output shown, files mirrored to `.claude/commands/TechieFlow/` (`bash tests/mirror/run.sh` proves it), `opencode.jsonc` checked, both harnesses. Plain words. Owner-reviewed documents change only after the owner's yes. Every gap is logged as a miss through `tf-log-miss.sh` with its sort, the maintainer's own included. Every open decision is restated in full at the end of a message as a yes-or-no question. Fable 5.1 only in the reset session; Sonnet for long Claude runs; OpenCode through `tf-goal.sh --harness opencode --model openai/gpt-5.6-terra`. diff --git a/docs/TechieFlow-Setup.md b/docs/TechieFlow-Setup.md index be20921..ccc53ad 100644 --- a/docs/TechieFlow-Setup.md +++ b/docs/TechieFlow-Setup.md @@ -110,7 +110,7 @@ Use `dotnet build` for Linux-compatible projects and `winrun "dotnet build -c Re The SSH directory is intentionally mounted read-only. Docker Desktop can expose the mounted private key with Linux mode `0777`, which OpenSSH rejects, and the mounted directory cannot accept a new `known_hosts` file. The image's `winrun` wrapper copies the key to writable `/tmp/opencode-docker/opencode-docker` with mode `0600` and creates its writable host-trust file there. Do not try to repair the mounted file from inside the container. -If the test reports `Permission denied (publickey)`, do not enter the VPS password or Windows password. Because the generated key has no passphrase, this means public-key authentication was rejected. If `whoami /groups | Select-String 'S-1-5-32-544'` prints a result, the account is an Administrator and Windows OpenSSH uses `%ProgramData%\ssh\administrators_authorized_keys` rather than the profile `authorized_keys` file. Add the same public key there from elevated PowerShell and apply `icacls` permissions, as shown in `WORKFLOW.html`. +If the test reports `Permission denied (publickey)`, do not enter the VPS password or Windows password. Because the generated key has no passphrase, this means public-key authentication was rejected. If `whoami /groups | Select-String 'S-1-5-32-544'` prints a result, the account is an Administrator and Windows OpenSSH uses `%ProgramData%\ssh\administrators_authorized_keys` rather than the profile `authorized_keys` file. Add the same public key there from elevated PowerShell and apply `icacls` permissions, as shown in the OpenSSH documentation. ### NuGet credentials @@ -276,7 +276,7 @@ The build ladder (`.tfcore/templates/v4custom/build-invocation-ladder.md`) auto- **Moving an existing project (or this framework repo) from Windows/WSL to a Mac:** 1. **Can't see `.tfcore/`, `.claude/`, `.opencode/` in Finder?** Finder hides dot-files by default. Press **Cmd+Shift+.** in any Finder window to toggle them on (the setting sticks), or run `defaults write com.apple.finder AppleShowAllFiles -bool true && killall Finder`. The Terminal always sees them: `ls -la`. Nothing is missing just because Finder doesn't show it — check with `ls -la` first. -2. **Moved an APP repo via git (clone/pull)?** Then the framework folders genuinely AREN'T there — every deployed framework copy (`.tfcore/`, `.claude/`, `.opencode/`, `/CLAUDE.md`, `/WORKFLOW.html`, `/opencode.jsonc`) is *gitignored by design* (they're copies; this repo is the source of truth). Re-deploy them: `ls -la` the app — if `.tfcore/` exists, run `/path/to/TechieFlow/update-framework.sh /path/to/app`; if it's absent, run `/path/to/TechieFlow/scaffold-brownfield.sh /path/to/app` (safe on an app with existing docs/code — it uses `--ignore-existing` and never touches `src/`, `docs/`, or tests). Add `--dry-run` to `update-framework.sh` to preview. +2. **Moved an APP repo via git (clone/pull)?** Then the framework folders genuinely AREN'T there — every deployed framework copy (`.tfcore/`, `.claude/`, `.opencode/`, `/CLAUDE.md`, `/opencode.jsonc`) is *gitignored by design* (they're copies; this repo is the source of truth). Re-deploy them: `ls -la` the app — if `.tfcore/` exists, run `/path/to/TechieFlow/update-framework.sh /path/to/app`; if it's absent, run `/path/to/TechieFlow/scaffold-brownfield.sh /path/to/app` (safe on an app with existing docs/code — it uses `--ignore-existing` and never touches `src/`, `docs/`, or tests). Add `--dry-run` to `update-framework.sh` to preview. 3. **Per-project gitignored files don't come back from a scaffold.** `CLAUDE.md`, `.tfcore/core-config.yaml` customizations, and `.claude/settings.local.json` are per-project work product that git never carried. A plain *folder copy* from the old machine keeps them; a git clone loses them — copy them over from the Windows machine, or regenerate (`CLAUDE.md` comes back via day-1 / `*refresh-status`). 4. **Scripts won't execute (`permission denied`)?** A copy through a Windows filesystem drops the executable bit. Fix once: `chmod +x /path/to/TechieFlow/*.sh /path/to/TechieFlow/.tfcore/hooks/*.sh` (or run them as `bash script.sh`). Hooks inside apps are invoked via `bash` so they don't need it, but the same `chmod` doesn't hurt. 5. **No path edits needed:** since 2026-07-11 the three scripts locate the framework from their own directory (no hardcoded `/mnt/c/…`), and they run fine on macOS's stock `bash`/`rsync`. diff --git a/docs/TechieFlow-Telemetry-Guide.md b/docs/TechieFlow-Telemetry-Guide.md index 8a0b3bc..27f42a8 100644 --- a/docs/TechieFlow-Telemetry-Guide.md +++ b/docs/TechieFlow-Telemetry-Guide.md @@ -1,5 +1,7 @@ # TechieFlow Development Telemetry Guide +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + > **Codex:** headless/goal runs parse authoritative `codex exec --json` > `turn.completed.usage` through `tf-codex-telemetry.py`. Interactive > `SessionEnd` records identify the session/model but leave token and cost fields diff --git a/docs/Telemetry-Hooks.md b/docs/Telemetry-Hooks.md index e68377e..3859876 100644 --- a/docs/Telemetry-Hooks.md +++ b/docs/Telemetry-Hooks.md @@ -1,5 +1,7 @@ # Telemetry Hook Points — per-phase model, tier, tokens, attempt, verdict +> **The Codex adapter was removed on 2026-09-07** (D-14, FR-42). The framework supports Claude Code and OpenCode. Everything below that describes `.codex/`, `.agents/skills/` or a Codex code path is history, kept for traceability; see `docs/CHANGELOG.md`. + > **Codex addendum (2026-08-24):** `.codex/hooks.json` writes the session pointer > via `codex-adapter.py`; `codex exec --json` is parsed by > `tf-codex-telemetry.py` for authoritative headless usage. Interactive diff --git a/docs/metrics/README.md b/docs/metrics/README.md index 7e710ed..310d5bf 100644 --- a/docs/metrics/README.md +++ b/docs/metrics/README.md @@ -12,13 +12,12 @@ after the fact. | `commits.jsonl` | commit | the repo's own `pre-commit` hook | | `misses.jsonl` | a requirement/behaviour an agent MISSED, and what fixing it cost | `verify-phase`, `build-phase`, `triage-issues`, `fix-issues`, `amend-docs`, `*log-miss` | -`misses.jsonl` is the one stream with **two** record kinds — `miss` (opened: what -was missed, which phase/agent/model let it through, who found it) and `miss-fix` -(closed: the repair run and its token/cost window, linked by `miss_id`). It is +`misses.jsonl` is the one stream with **three** record kinds — `miss` (opened: what +was missed, which phase/agent/model let it through, who found it), `miss-fix` +(closed: the repair run and its token/cost window, linked by `miss_id`) and +`miss-amend` (completes a field the `miss` left empty — it can fill a `null` and +can never overwrite a value, so it adds to the history without revising it). It is what makes "how much did that miss cost to fix" answerable. SCHEMA.md §5.5. -The readable version is `docs/-Misses.md`: one row per miss with the owner's -sentence and whose gap it was, rebuilt from this stream after every miss record -(SCHEMA.md §5.5.10). Read that file; never edit it. Schema, enums, and every known limitation: `.tfcore/telemetry/SCHEMA.md`. Report: `/TechieFlow:agents:flow-master *metrics ` (OpenCode: `/flow-master *metrics `) → `METRICS.md`. @@ -34,7 +33,13 @@ these empty files along with the rest — a tracked empty stream is what makes t first record a one-line diff instead of a new file appearing from nowhere. **Never edit these files by hand, never sort them, never compact them.** They are -a log. Rewriting one destroys exactly the history it exists to keep. +a log. Rewriting one destroys exactly the history it exists to keep. To correct or +complete a record, append another one: a later `gates.jsonl` record supersedes an +earlier verdict, a `miss-fix` closes a `miss`, and +`bash .tfcore/utils/tf-emit.sh --amend ` fills a field a +`miss` left empty (it refuses to overwrite one that is not empty). If nothing fits +what you need to correct, say so rather than editing — that is a framework defect +worth reporting, and it is how the amend path came to exist. **No secrets, no content, no client data** — records carry IDs, counts, durations, verdicts and file paths at most. Never requirement text, prompt text, file diff --git a/docs/metrics/commits.jsonl b/docs/metrics/commits.jsonl index abcb7f0..85e28a3 100644 --- a/docs/metrics/commits.jsonl +++ b/docs/metrics/commits.jsonl @@ -38,3 +38,15 @@ {"v":1,"ts":"2026-09-04T17:03:13Z","kind":"commit","app":"TechieFlow","sha":"53a4597","files":12,"insertions":2348,"deletions":0,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} {"v":1,"ts":"2026-09-04T19:48:27Z","kind":"commit","app":"TechieFlow","sha":"b77c2ce","files":65,"insertions":4179,"deletions":1112,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} {"v":1,"ts":"2026-09-07T07:57:53Z","kind":"commit","app":"TechieFlow","sha":"7996099","files":205,"insertions":14835,"deletions":11508,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-04T19:07:07Z","kind":"commit","app":"TechieFlow","sha":"b59c866","files":16,"insertions":2573,"deletions":22,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-04T19:30:44Z","kind":"commit","app":"TechieFlow","sha":"5b61a08","files":2,"insertions":134,"deletions":33,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-04T19:57:47Z","kind":"commit","app":"TechieFlow","sha":"893df67","files":1,"insertions":1,"deletions":1,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-04T20:03:52Z","kind":"commit","app":"TechieFlow","sha":"c835e3c","files":1,"insertions":9,"deletions":1,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-04T20:08:19Z","kind":"commit","app":"TechieFlow","sha":"8e5d513","files":0,"insertions":0,"deletions":0,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-05T06:02:48Z","kind":"commit","app":"TechieFlow","sha":"1bdbbc3","files":7,"insertions":303,"deletions":6,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-05T06:04:11Z","kind":"commit","app":"TechieFlow","sha":"0d700a5","files":0,"insertions":0,"deletions":0,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-05T07:13:06Z","kind":"commit","app":"TechieFlow","sha":"343b1e3","files":1,"insertions":47,"deletions":6,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-05T07:14:09Z","kind":"commit","app":"TechieFlow","sha":"9be837f","files":0,"insertions":0,"deletions":0,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-05T07:14:43Z","kind":"commit","app":"TechieFlow","sha":"b3b07a1","files":1,"insertions":1,"deletions":0,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-05T07:15:22Z","kind":"commit","app":"TechieFlow","sha":"78fa676","files":0,"insertions":0,"deletions":0,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} +{"v":1,"ts":"2026-09-07T07:59:23Z","kind":"commit","app":"TechieFlow","sha":"a74672e","files":0,"insertions":0,"deletions":0,"subject_prefix":null,"branch":"dev","project_type":"framework","harness":null} diff --git a/docs/metrics/misses.jsonl b/docs/metrics/misses.jsonl index 1b83b88..1f20b1c 100644 --- a/docs/metrics/misses.jsonl +++ b/docs/metrics/misses.jsonl @@ -252,3 +252,9 @@ {"kind":"miss","miss_id":"MISS-TechieFlow-20260907-11","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"insufficient-verify-method","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:50:26Z","failure_class":"other","what":"The Playbook review prompt was drafted from folder word counts taken only at the top level, so 174 files and 196,498 words counted as zero and the prompt nearly shipped the wrong headline finding.","sort":"weak-check","v":1,"ts":"2026-09-07T07:50:26Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} {"kind":"miss","miss_id":"MISS-TechieFlow-20260907-12","req_id":null,"req_class":null,"miss_class":"wrong-behaviour","artifact":"other","severity":"major","why_missed":"instruction-ignored","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T07:53:13Z","failure_class":"other","what":"The D-21 rule that hidden framework folders are invisible to search was written down and still cost 58 percent of a folder's true size when OpenCode measured the Playbook with a shell glob.","sort":"ignored","v":1,"ts":"2026-09-07T07:53:13Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} {"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-11","req_id":null,"fix_run_id":"2026-09-07T07:33:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T07:53:37Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":168,"tokens_out":100681,"tokens_cache_read":28046246,"tokens_cache_write":125296,"cost_usd":null,"tokens_scope":"main","model":"claude-opus-5","cost_attribution":"sole"} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-13","req_id":null,"req_class":null,"miss_class":"partial-implementation","artifact":"config","severity":"blocker","why_missed":"missing-checklist-item","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T08:27:58Z","failure_class":"other","what":"The npm installer wrote a settings.json missing five hook registrations the shell scripts had gained, so a project installed from the package ran without the metrics, database and build guards.","sort":"unsaid","v":1,"ts":"2026-09-07T08:27:58Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss","miss_id":"MISS-TechieFlow-20260907-14","req_id":null,"req_class":null,"miss_class":"partial-implementation","artifact":"config","severity":"major","why_missed":"missing-checklist-item","origin_phase":"build-phase","origin_agent":"flow-master","found_by":"owner","found_phase":"log-miss","found_gate":null,"found_run_id":"2026-09-07T08:27:58Z","failure_class":"other","what":"The npm installer's framework subfolder list left out standards, so a project migrated from the old layout came out with no coding standards file.","sort":"unsaid","v":1,"ts":"2026-09-07T08:27:59Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","origin_confidence":"inferred","origin_model":null,"origin_harness":null} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-13","req_id":null,"fix_run_id":"2026-09-07T08:10:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T08:28:24Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":120,"tokens_out":39397,"tokens_cache_read":24174545,"tokens_cache_write":58742,"cost_usd":null,"tokens_scope":"main","model":"claude-opus-5","cost_attribution":"shared:2"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-14","req_id":null,"fix_run_id":"2026-09-07T08:10:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T08:28:24Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":120,"tokens_out":39397,"tokens_cache_read":24174545,"tokens_cache_write":58742,"cost_usd":null,"tokens_scope":"main","model":"claude-opus-5","cost_attribution":"shared:2"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260904-14","req_id":null,"fix_run_id":"2026-09-07T08:55:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T08:57:35Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":18,"tokens_out":4476,"tokens_cache_read":4817291,"tokens_cache_write":4746,"cost_usd":null,"tokens_scope":"main","model":"claude-opus-5","cost_attribution":"shared:2"} +{"kind":"miss-fix","miss_id":"MISS-TechieFlow-20260907-10","req_id":null,"fix_run_id":"2026-09-07T08:55:00Z","fix_cmd":"fix-issues","fix_attempt":1,"verdict_after":"Verified","reopened":false,"v":1,"ts":"2026-09-07T08:57:35Z","project_type":"framework","harness":"claude-code","app":"TechieFlow","tokens_in":18,"tokens_out":4476,"tokens_cache_read":4817291,"tokens_cache_write":4746,"cost_usd":null,"tokens_scope":"main","model":"claude-opus-5","cost_attribution":"shared:2"} diff --git a/docs/metrics/runs.jsonl b/docs/metrics/runs.jsonl index f05c1f6..4c9f014 100644 --- a/docs/metrics/runs.jsonl +++ b/docs/metrics/runs.jsonl @@ -34,3 +34,7 @@ {"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:50:26Z","ended":"2026-09-07T07:50:26Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:50:26Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":1739},"tokens_in":2,"tokens_out":1739,"tokens_cache_read":347855,"tokens_cache_write":1548,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} {"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T07:53:13Z","ended":"2026-09-07T07:53:13Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T07:53:13Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":302},"tokens_in":2,"tokens_out":302,"tokens_cache_read":359283,"tokens_cache_write":739,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} {"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"session-7","started":"2026-09-07T07:33:00Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":4,"build_result":"pass","yolo":false,"v":1,"ts":"2026-09-07T07:53:37Z","ended":"2026-09-07T07:53:37Z","duration_s":1237,"project_type":"framework","harness":"claude-code","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":100681},"tokens_in":168,"tokens_out":100681,"tokens_cache_read":28046246,"tokens_cache_write":125296,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T08:27:58Z","ended":"2026-09-07T08:27:58Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T08:27:58Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":1240},"tokens_in":2,"tokens_out":1240,"tokens_cache_read":414873,"tokens_cache_write":516,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} +{"kind":"run","app":"TechieFlow","cmd":"log-miss","mode":null,"started":"2026-09-07T08:27:58Z","ended":"2026-09-07T08:27:58Z","reqs_touched":[],"reqs_count":0,"subagents":[],"files_written":0,"build_result":"not-run","v":1,"ts":"2026-09-07T08:27:59Z","yolo":false,"duration_s":0,"project_type":"framework","harness":"claude-code","tier":"economy","tier_model":"haiku","model":"claude-opus-5","model_tokens_out":{"claude-opus-5":1240},"tokens_in":2,"tokens_out":1240,"tokens_cache_read":414873,"tokens_cache_write":516,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0,"routed":false} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"session-7-merge-fix","started":"2026-09-07T08:10:00Z","reqs_touched":["FR-51","FR-63"],"reqs_count":2,"subagents":[],"files_written":5,"build_result":"pass","yolo":false,"v":1,"ts":"2026-09-07T08:28:24Z","ended":"2026-09-07T08:28:24Z","duration_s":1104,"project_type":"framework","harness":"claude-code","attempt":1,"model":"claude-opus-5","model_tokens_out":{"claude-opus-5":39397},"tokens_in":120,"tokens_out":39397,"tokens_cache_read":24174545,"tokens_cache_write":58742,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} +{"kind":"run","app":"TechieFlow","cmd":"framework-reset","mode":"codex-removal","started":"2026-09-07T08:55:00Z","reqs_touched":["FR-42","FR-62"],"reqs_count":2,"subagents":[],"files_written":41,"build_result":"pass","yolo":false,"v":1,"ts":"2026-09-07T08:57:35Z","ended":"2026-09-07T08:57:35Z","duration_s":155,"project_type":"framework","harness":"claude-code","attempt":2,"model":"claude-opus-5","model_tokens_out":{"claude-opus-5":4476},"tokens_in":18,"tokens_out":4476,"tokens_cache_read":4817291,"tokens_cache_write":4746,"cost_usd":null,"tokens_scope":"main","subagent_runs":0,"tokens_out_subagents":0} diff --git a/package.json b/package.json index 5573404..88b0a8e 100644 --- a/package.json +++ b/package.json @@ -33,11 +33,7 @@ ".opencode/command", "!.opencode/command/trblazeui.md", "!.opencode/command/techierag.md", - ".codex/config.toml", - ".codex/hooks.json", - ".codex/rules", "opencode.jsonc", - "WORKFLOW.html", "scripts/install.mjs", "scripts/npm-postinstall.mjs", "scripts/npm-cleanup.mjs", diff --git a/scaffold-brownfield.sh b/scaffold-brownfield.sh index 3aba510..b29bc85 100755 --- a/scaffold-brownfield.sh +++ b/scaffold-brownfield.sh @@ -11,7 +11,7 @@ # What it does: # - Adds .tfcore/, .claude/commands/ if missing # - Adds .claude/settings.json (yolo-except-git-writes-writes; hook-gated deletes) if missing -# - Copies WORKFLOW.html and opencode.jsonc if missing +# - Copies opencode.jsonc if missing # - Drops a note pointing to the brownfield day-1 /analyst prompt # # What it deliberately does NOT touch: @@ -48,8 +48,8 @@ fi # -------------------------------------------------------------------------- # python3 is a HARD prerequisite, not a nice-to-have (added 2026-08-27 after a -# macOS scaffold failed on a missing python3). It powers the Codex bindings -# (tf-codex-bind.py), the HTML renderer (tf-render-html.py), the opencode.jsonc +# macOS scaffold failed on a missing python3). It powers the HTML renderer +# (tf-render-html.py), the opencode.jsonc # audit, tf-metrics.sh and every guard hook. Missing it does not fail loudly at # the point of use — the hooks fail OPEN by design — so a scaffold that skipped # it would look like it worked and leave the repo silently unguarded. @@ -60,7 +60,7 @@ fi tf_ensure_python3() { if command -v python3 >/dev/null 2>&1; then return 0; fi - echo " python3 not found — it is required (Codex bindings, HTML renderer, telemetry, guard hooks)." + echo " python3 not found — it is required (HTML renderer, telemetry, guard hooks)." if [[ "${TF_NO_INSTALL:-0}" == "1" ]]; then echo " TF_NO_INSTALL=1 set — not installing. Install python3 and re-run." >&2 @@ -113,7 +113,7 @@ tf_ensure_python3() { if ! tf_ensure_python3; then echo "" >&2 echo "Refusing to continue without python3: the scaffold would appear to succeed" >&2 - echo "while leaving the repo with no Codex bindings and no working guard hooks." >&2 + echo "while leaving the repo with no working guard hooks." >&2 exit 1 fi @@ -189,7 +189,6 @@ rsync -a \ .tfcore/agents/ .claude/commands/TechieFlow/agents/ # 4. Reference files at root — only if missing -[[ -f WORKFLOW.html ]] || cp "$TEMPLATE/WORKFLOW.html" . [[ -f opencode.jsonc ]] || cp "$TEMPLATE/opencode.jsonc" . # 4b. OpenCode harness bridge — framework-owned, ALWAYS refreshed (like 3b): @@ -206,16 +205,6 @@ done # ./.tfcore/ to ../.tfcore/ for the copy living inside .opencode/. sed 's|{file:\./\.tfcore/|{file:../.tfcore/|g' "$TEMPLATE/opencode.jsonc" > .opencode/opencode.jsonc -# 4c. Codex adapter — repository skills, custom agents, hooks and exec policy. -# Config is a project baseline and is preserved when already present; the -# framework-owned hooks/rules and generated agents/skills are refreshed. -mkdir -p .codex/agents .codex/rules .agents/skills -[[ -f .codex/config.toml ]] || cp "$TEMPLATE/.codex/config.toml" .codex/config.toml -cp "$TEMPLATE/.codex/hooks.json" .codex/hooks.json -cp "$TEMPLATE/.codex/rules/techieflow.rules" .codex/rules/techieflow.rules -python3 .tfcore/utils/tf-codex-bind.py "$TARGET" || echo " ⚠ Codex bindings could not be generated (python3 required)" -echo " Codex adapter installed — trust this repository and review /hooks before relying on guards" - # 5. .claude/settings.json — yolo-except-git-writes, only if missing if [[ ! -f .claude/settings.json ]]; then cat > .claude/settings.json <<'JSON' @@ -390,7 +379,6 @@ Added (only missing files filled — re-runs are safe): .tfcore/ ← TechieFlow v4 customized (agents, tasks, templates) .claude/commands/ ← Claude Code slash commands (TechieFlow agents) .claude/settings.json ← yolo-except-git-writes permissions - WORKFLOW.html ← the human workflow guide (open in a browser; §17 = macOS / Windows / Linux) opencode.jsonc ← OpenCode config (loads agents/tasks from .tfcore/ via {file:...} refs) .gitignore ← framework entries appended (deployed copies stay uncommitted) @@ -430,8 +418,8 @@ fi # package for library personas) and must never be committed in the app repo. # Append-only + idempotent: existing anchored/slash variants are respected; # user content is never rewritten. -GI_LINES=(".tfcore/" ".claude/" ".opencode/" ".codex/" ".agents/skills/" "/CLAUDE.md" "/WORKFLOW.html" "/opencode.jsonc" "/.tf-scaffold-note.txt") -GI_PATS=('^/?\.tfcore/?$' '^/?\.claude/?$' '^/?\.opencode/?$' '^/?\.codex/?$' '^/?\.agents/skills/?$' '^/?CLAUDE\.md$' '^/?WORKFLOW\.html$' '^/?opencode\.jsonc$' '^/?\.tf-scaffold-note\.txt$') +GI_LINES=(".tfcore/" ".claude/" ".opencode/" "/CLAUDE.md" "/opencode.jsonc" "/.tf-scaffold-note.txt") +GI_PATS=('^/?\.tfcore/?$' '^/?\.claude/?$' '^/?\.opencode/?$' '^/?CLAUDE\.md$' '^/?opencode\.jsonc$' '^/?\.tf-scaffold-note\.txt$') GI_MISSING=() for i in "${!GI_LINES[@]}"; do # tr strips CR so CRLF .gitignore files (Windows-authored) still match the $-anchor @@ -566,5 +554,4 @@ echo "" echo "✔ Done. Existing source tree was NOT touched." echo "" echo "Next: cd \"$TARGET\"" -echo " open WORKFLOW.html in a browser" -echo " start Claude Code, follow §7 brownfield day-1 /analyst prompt" +echo " start Claude Code and run the brownfield day-1 command from .tf-scaffold-note.txt" diff --git a/scaffold-greenfield.sh b/scaffold-greenfield.sh index 8678eab..8163b8d 100755 --- a/scaffold-greenfield.sh +++ b/scaffold-greenfield.sh @@ -17,10 +17,6 @@ # `dotnet build` once the project adds the NuGet packages: # .claude/commands/trblazeui.md, .opencode/command/trblazeui.md, .trblazeui/ # .claude/commands/techierag.md, .opencode/command/techierag.md, .techierag/ -# .codex/agents/{trblazeui,techierag}.toml — library-owned once the package -# ships it (TrBlazeUI.Components >= 2.0.3); until then tf-codex-bind.py writes -# a compat wrapper + the `./.codex-agent-package-owned` marker so the -# package may replace it. # # Idempotent: re-running won't overwrite existing files — with one exception: # the harness agent mirror (.claude/commands/TechieFlow/agents/) is force-synced @@ -52,8 +48,8 @@ fi # -------------------------------------------------------------------------- # python3 is a HARD prerequisite, not a nice-to-have (added 2026-08-27 after a -# macOS scaffold failed on a missing python3). It powers the Codex bindings -# (tf-codex-bind.py), the HTML renderer (tf-render-html.py), the opencode.jsonc +# macOS scaffold failed on a missing python3). It powers the HTML renderer +# (tf-render-html.py), the opencode.jsonc # audit, tf-metrics.sh and every guard hook. Missing it does not fail loudly at # the point of use — the hooks fail OPEN by design — so a scaffold that skipped # it would look like it worked and leave the repo silently unguarded. @@ -64,7 +60,7 @@ fi tf_ensure_python3() { if command -v python3 >/dev/null 2>&1; then return 0; fi - echo " python3 not found — it is required (Codex bindings, HTML renderer, telemetry, guard hooks)." + echo " python3 not found — it is required (HTML renderer, telemetry, guard hooks)." if [[ "${TF_NO_INSTALL:-0}" == "1" ]]; then echo " TF_NO_INSTALL=1 set — not installing. Install python3 and re-run." >&2 @@ -117,7 +113,7 @@ tf_ensure_python3() { if ! tf_ensure_python3; then echo "" >&2 echo "Refusing to continue without python3: the scaffold would appear to succeed" >&2 - echo "while leaving the repo with no Codex bindings and no working guard hooks." >&2 + echo "while leaving the repo with no working guard hooks." >&2 exit 1 fi @@ -170,7 +166,6 @@ rsync -a \ .tfcore/agents/ .claude/commands/TechieFlow/agents/ # 4. Reference files at project root — only if missing -[[ -f WORKFLOW.html ]] || cp "$TEMPLATE/WORKFLOW.html" . [[ -f opencode.jsonc ]] || cp "$TEMPLATE/opencode.jsonc" . # 4b. OpenCode harness bridge — framework-owned, ALWAYS refreshed (like 3b): @@ -187,14 +182,6 @@ done # ./.tfcore/ to ../.tfcore/ for the copy living inside .opencode/. sed 's|{file:\./\.tfcore/|{file:../.tfcore/|g' "$TEMPLATE/opencode.jsonc" > .opencode/opencode.jsonc -# 4c. Codex adapter — repository skills, custom agents, hooks and exec policy. -mkdir -p .codex/agents .codex/rules .agents/skills -[[ -f .codex/config.toml ]] || cp "$TEMPLATE/.codex/config.toml" .codex/config.toml -cp "$TEMPLATE/.codex/hooks.json" .codex/hooks.json -cp "$TEMPLATE/.codex/rules/techieflow.rules" .codex/rules/techieflow.rules -python3 .tfcore/utils/tf-codex-bind.py "$TARGET" || echo " ⚠ Codex bindings could not be generated (python3 required)" -echo " Codex adapter installed — trust this repository and review /hooks before relying on guards" - # 5. .claude/settings.json — yolo-except-git-writes. ONLY write if missing, # so per-project tweaks survive scaffold re-runs. if [[ ! -f .claude/settings.json ]]; then @@ -361,7 +348,6 @@ Folders/files created (only missing files filled — re-runs are safe): .tfcore/ ← TechieFlow v4 customized (agents, tasks, templates) .claude/commands/ ← Claude Code slash commands (TechieFlow agents) .claude/settings.json ← yolo-except-git-writes permissions - WORKFLOW.html ← the human workflow guide (open in a browser; §17 = macOS / Windows / Linux) opencode.jsonc ← OpenCode config (loads agents/tasks from .tfcore/ via {file:...} refs) .gitignore ← framework entries appended (deployed copies stay uncommitted) tests/playwright/ tests/unit/ src/ @@ -408,8 +394,8 @@ done # package for library personas) and must never be committed in the app repo. # Append-only + idempotent: existing anchored/slash variants are respected; # user content is never rewritten. -GI_LINES=(".tfcore/" ".claude/" ".opencode/" ".codex/" ".agents/skills/" "/CLAUDE.md" "/WORKFLOW.html" "/opencode.jsonc" "/.tf-scaffold-note.txt") -GI_PATS=('^/?\.tfcore/?$' '^/?\.claude/?$' '^/?\.opencode/?$' '^/?\.codex/?$' '^/?\.agents/skills/?$' '^/?CLAUDE\.md$' '^/?WORKFLOW\.html$' '^/?opencode\.jsonc$' '^/?\.tf-scaffold-note\.txt$') +GI_LINES=(".tfcore/" ".claude/" ".opencode/" "/CLAUDE.md" "/opencode.jsonc" "/.tf-scaffold-note.txt") +GI_PATS=('^/?\.tfcore/?$' '^/?\.claude/?$' '^/?\.opencode/?$' '^/?CLAUDE\.md$' '^/?opencode\.jsonc$' '^/?\.tf-scaffold-note\.txt$') GI_MISSING=() for i in "${!GI_LINES[@]}"; do # tr strips CR so CRLF .gitignore files (Windows-authored) still match the $-anchor @@ -545,4 +531,4 @@ echo "✔ Done." echo "" echo "Next: cd \"$TARGET\"" echo " dotnet new sln + blazor + add TrBlazeUI/TechieRag NuGets + dotnet build" -echo " open WORKFLOW.html, start Claude Code, follow §7 greenfield day-1 prompt" +echo " start Claude Code and run the greenfield day-1 command from .tf-scaffold-note.txt" diff --git a/scripts/install.mjs b/scripts/install.mjs index 3c5056c..42ad9f4 100755 --- a/scripts/install.mjs +++ b/scripts/install.mjs @@ -20,7 +20,7 @@ // scripts/test-install.mjs proves that by running both routes and comparing the results. // // Needs: node, bash and python3. The framework's own helper scripts (telemetry setup, -// build-output ignore audit, Codex bindings) are run exactly as the shell scripts run them. +// build-output ignore audit) are run exactly as the shell scripts run them. import { spawnSync } from "node:child_process"; import { @@ -201,7 +201,7 @@ function checkTools() { } pythonCommand = findPython(); if (!pythonCommand) { - throw new Error("python3 was not found. It powers the HTML renderer, the telemetry writer, the Codex bindings and every guard hook.\n" + throw new Error("python3 was not found. It powers the HTML renderer, the telemetry writer and every guard hook.\n" + " Install it and run this command again:\n" + " macOS: brew install python3\n" + " Ubuntu / WSL: sudo apt-get install -y python3\n" @@ -237,8 +237,8 @@ function ensureSafeTarget() { // Same lines, same patterns and same header text as the shell scripts. Append-only: an // entry already present in any anchored or slash variant is respected, nothing is rewritten. const frameworkIgnore = { - lines: [".tfcore/", ".claude/", ".opencode/", ".codex/", ".agents/skills/", "/CLAUDE.md", "/WORKFLOW.html", "/opencode.jsonc", "/.tf-scaffold-note.txt"], - patterns: [/^\/?\.tfcore\/?$/, /^\/?\.claude\/?$/, /^\/?\.opencode\/?$/, /^\/?\.codex\/?$/, /^\/?\.agents\/skills\/?$/, /^\/?CLAUDE\.md$/, /^\/?WORKFLOW\.html$/, /^\/?opencode\.jsonc$/, /^\/?\.tf-scaffold-note\.txt$/], + lines: [".tfcore/", ".claude/", ".opencode/", "/CLAUDE.md", "/opencode.jsonc", "/.tf-scaffold-note.txt"], + patterns: [/^\/?\.tfcore\/?$/, /^\/?\.claude\/?$/, /^\/?\.opencode\/?$/, /^\/?CLAUDE\.md$/, /^\/?opencode\.jsonc$/, /^\/?\.tf-scaffold-note\.txt$/], header: ["# TechieFlow framework — deployed copies, never commit (managed by scaffold/update-framework.sh)"], label: "framework entries", }; @@ -353,6 +353,22 @@ const canonicalSettings = String.raw`{ { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-artifacts.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-metrics.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-db.sh\"" + }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-build.sh\"" } ] }, @@ -363,6 +379,10 @@ const canonicalSettings = String.raw`{ "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-status.sh\"" }, + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-metrics.sh\"" + }, { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify.sh\"" @@ -426,7 +446,6 @@ Added (only missing files filled — re-runs are safe): .tfcore/ ← TechieFlow v4 customized (agents, tasks, templates) .claude/commands/ ← Claude Code slash commands (TechieFlow agents) .claude/settings.json ← yolo-except-git-writes permissions - WORKFLOW.html ← the human workflow guide (open in a browser; §17 = macOS / Windows / Linux) opencode.jsonc ← OpenCode config (loads agents/tasks from .tfcore/ via {file:...} refs) .gitignore ← framework entries appended (deployed copies stay uncommitted) @@ -468,7 +487,6 @@ Folders/files created (only missing files filled — re-runs are safe): .tfcore/ ← TechieFlow v4 customized (agents, tasks, templates) .claude/commands/ ← Claude Code slash commands (TechieFlow agents) .claude/settings.json ← yolo-except-git-writes permissions - WORKFLOW.html ← the human workflow guide (open in a browser; §17 = macOS / Windows / Linux) opencode.jsonc ← OpenCode config (loads agents/tasks from .tfcore/ via {file:...} refs) .gitignore ← framework entries appended (deployed copies stay uncommitted) tests/playwright/ tests/unit/ src/ @@ -515,15 +533,20 @@ function deployOpenCodeBridge() { writeText(join(target, ".opencode", "opencode.jsonc"), openCodeConfigCopy()); } -function deployCodexAdapter() { - say(" .codex/ + .agents/skills/ — Codex adapter"); - if (dryRun) { say(" WOULD preserve/create .codex/config.toml; refresh hooks/rules; regenerate agents/skills"); return; } - for (const folder of [".codex/agents", ".codex/rules", ".agents/skills"]) mkdirSync(join(target, folder), { recursive: true }); - copyFile(join(sourceRoot, ".codex", "config.toml"), join(target, ".codex", "config.toml"), { onlyIfMissing: true }); - copyFile(join(sourceRoot, ".codex", "hooks.json"), join(target, ".codex", "hooks.json")); - copyFile(join(sourceRoot, ".codex", "rules", "techieflow.rules"), join(target, ".codex", "rules", "techieflow.rules")); - const result = runPython([join(target, ".tfcore", "utils", "tf-codex-bind.py"), target]); - if (result.status !== 0) say(" ⚠ Codex bindings could not be generated (python3 required)"); +// The Codex adapter was removed on 2026-09-07 (D-14, FR-42) and WORKFLOW.html was dropped the +// same day: it was a second full description of the process, last revised before the reset, and +// it still taught commands that no longer exist. Both are taken out of a project that has them. +// Nothing here is project content: .codex/ held a config file plus generated bindings, +// .agents/skills/ was generated in full from .tfcore/tasks/, and WORKFLOW.html was a copy. +function removeRetiredFiles() { + for (const path of [".codex", ".agents/skills", "WORKFLOW.html"]) { + const full = join(target, path); + if (!existsSync(full)) continue; + if (dryRun) { say(` ${would}remove ${path} — no longer part of the framework`); continue; } + rmSync(full, { recursive: true, force: true }); + say(` removed ${path} — no longer part of the framework`); + } + if (!dryRun) { try { rmdirSync(join(target, ".agents")); say(" removed the emptied .agents/"); } catch {} } } function deployHousekeeping() { @@ -619,13 +642,11 @@ async function install() { say(" syncing agent files from .tfcore/agents/ → .claude/commands/TechieFlow/agents/"); copyTree(dryRun ? join(sourceRoot, ".tfcore", "agents") : join(target, ".tfcore", "agents"), join(target, ".claude", "commands", "TechieFlow", "agents")); // 4. reference files at the root, only if missing - copyFile(join(sourceRoot, "WORKFLOW.html"), join(target, "WORKFLOW.html"), { onlyIfMissing: true }); copyFile(join(sourceRoot, "opencode.jsonc"), join(target, "opencode.jsonc"), { onlyIfMissing: true }); // 4b. OpenCode bridge, always refreshed deployOpenCodeBridge(); - // 4c. Codex adapter - deployCodexAdapter(); - if (!dryRun) say(" Codex adapter installed — trust this repository and review /hooks before relying on guards"); + // 4c. files earlier versions deployed and the framework no longer ships + removeRetiredFiles(); // 5. Claude Code permissions, only if missing if (writeText(join(target, ".claude", "settings.json"), `${canonicalSettings}\n`, { onlyIfMissing: true })) { if (!dryRun) say(" created .claude/settings.json"); } else say(" .claude/settings.json already exists — preserved"); @@ -650,13 +671,15 @@ async function install() { say(""); say(`Next: cd "${target}"`); if (greenfield) say(" dotnet new sln + blazor + add TrBlazeUI/TechieRag NuGets + dotnet build"); - say(" open WORKFLOW.html in a browser"); say(` start Claude Code or OpenCode and run the ${greenfield ? "greenfield" : "brownfield"} day-1 command from .tf-scaffold-note.txt`); } // ---------------------------------------------------------------- update (update-framework.sh) -const frameworkSubdirs = ["agents", "tasks", "telemetry", "templates", "checklists", "data", "utils", "hooks", "workflows", "agent-teams"]; +// Keep this in step with FRAMEWORK_SUBDIRS in update-framework.sh. `standards` arrived with the +// document schemas and was missing here, so a project migrated from the old layout came out +// without its coding standards (found by scripts/test-install.mjs, 2026-09-07). +const frameworkSubdirs = ["agents", "tasks", "telemetry", "templates", "checklists", "data", "utils", "hooks", "standards", "workflows", "agent-teams"]; const frameworkTopFiles = ["enhanced-ide-development-workflow.md", "user-guide.md", "working-in-the-brownfield.md", "install-manifest.yaml", "TOKEN-GUIDE.md"]; const legacyConfigPattern = /\.bmad-core|bmad-(master|orchestrator|analyst|architect|verifier)|BMad:/; @@ -866,16 +889,10 @@ function update() { deployOpenCodeBridge(); refreshRootOpenCodeConfig(); - // 4b. Codex adapter - deployCodexAdapter(); - if (!dryRun) say(" Codex hooks changed or installed — trust this repository and review /hooks"); + // 4b. files earlier versions deployed and the framework no longer ships + removeRetiredFiles(); for (const file of libraryPersonas) if (existsSync(join(target, ".opencode", "command", file))) say(` .opencode/command/${file} — preserved (NuGet-deployed)`); - // 4. WORKFLOW.html, always refreshed - if (existsSync(join(sourceRoot, "WORKFLOW.html"))) { - copyFile(join(sourceRoot, "WORKFLOW.html"), join(target, "WORKFLOW.html")); - say(" WORKFLOW.html"); - } // 5. NuGet persona shims: gap-fill only, never an overwrite shimLegacyPersonas({ gapOnly: true }); // 6, 7. messages about stale references in files the owner owns @@ -924,29 +941,18 @@ function uninstall() { plan(".opencode/opencode.jsonc"); plan(".opencode/opencode.json", "(generated by routing)"); for (const file of shippedFiles(".opencode/command", ".md")) if (!libraryPersonas.has(file)) plan(`.opencode/command/${file}`); - plan(".codex/hooks.json"); - plan(".codex/rules/techieflow.rules"); - if (existsSync(join(target, ".codex", "config.toml"))) { - if (sameAsShipped(".codex/config.toml", ".codex/config.toml")) plan(".codex/config.toml"); - else keep(".codex/config.toml", "you changed it"); - } - const codexAgents = join(target, ".codex", "agents"); - if (existsSync(codexAgents)) for (const f of readdirSync(codexAgents)) { - if (f.endsWith(".toml") && /description = "TechieFlow [a-z-]+ (specialist|role)\."/.test(read(join(codexAgents, f)))) plan(`.codex/agents/${f}`); - else keep(`.codex/agents/${f}`, "not written by the framework"); - } - const skills = join(target, ".agents", "skills"); - if (existsSync(skills)) for (const f of readdirSync(skills)) if (f.startsWith("techieflow-")) plan(`.agents/skills/${f}`); + // Retired on 2026-09-07 but still present in a project set up by an older version. + for (const path of [".codex", ".agents/skills"]) if (existsSync(join(target, path))) plan(path, "(retired Codex adapter)"); for (const lib of [".trblazeui", ".techierag"]) { if (!existsSync(join(target, lib))) continue; const rest = readdirSync(join(target, lib)).filter((f) => f !== ".codex-agent-package-owned" && f !== ".gitignore"); plan(`${lib}/.codex-agent-package-owned`); if (rest.length === 0) plan(`${lib}/.gitignore`); } - for (const [path, shipped] of [["WORKFLOW.html", "WORKFLOW.html"], ["opencode.jsonc", "opencode.jsonc"]]) { - if (!existsSync(join(target, path))) continue; - if (sameAsShipped(path, shipped)) plan(path); - else keep(path, "you changed it"); + if (existsSync(join(target, "WORKFLOW.html"))) plan("WORKFLOW.html", "(retired human workflow guide)"); + if (existsSync(join(target, "opencode.jsonc"))) { + if (sameAsShipped("opencode.jsonc", "opencode.jsonc")) plan("opencode.jsonc"); + else keep("opencode.jsonc", "you changed it"); } plan(".tf-scaffold-note.txt"); const hook = join(target, ".git", "hooks", "pre-commit"); diff --git a/scripts/test-install.mjs b/scripts/test-install.mjs index 8b07cd3..79620e2 100644 --- a/scripts/test-install.mjs +++ b/scripts/test-install.mjs @@ -41,20 +41,6 @@ const failures = []; const notes = []; let passed = 0; -// The Codex binder (.tfcore/utils/tf-codex-bind.py) needs Python 3.10 or newer. On an older -// Python both routes print a warning and skip the Codex agents and skills, so those files are -// only expected when the binder can run. -const pythonVersion = (() => { - const result = spawnSync("python3", ["--version"], { encoding: "utf8" }); - const match = /Python (\d+)\.(\d+)/.exec(`${result.stdout ?? ""}${result.stderr ?? ""}`); - return match ? `${match[1]}.${match[2]}` : "unknown"; -})(); -const codexBinderRuns = (() => { - const [major, minor] = pythonVersion.split(".").map(Number); - return major > 3 || (major === 3 && minor >= 10); -})(); -if (!codexBinderRuns) notes.push(`python3 is ${pythonVersion}; the Codex binder needs 3.10 or newer, so neither route generates .codex/agents/ or .agents/skills/ on this machine (CI does).`); - // ---------------------------------------------------------------- helpers function check(name, fn) { @@ -205,13 +191,18 @@ function perturbInstalledProject(dir) { writeFileSync(join(dir, ".claude", "commands", "trblazeui.md"), "# NuGet persona v2\n"); rmSync(join(dir, ".claude", "commands", "TechieFlow", "tasks", "verify-phase.md")); writeFileSync(join(dir, ".claude", "commands", "TechieFlow", "tasks", "old.md"), "stale mirror file\n"); + // What a project scaffolded before 2026-09-07 still carries. Both routes must remove it: + // the Codex adapter was retired (D-14, FR-42) and WORKFLOW.html was dropped. + mkdirSync(join(dir, ".codex", "rules"), { recursive: true }); + writeFileSync(join(dir, ".codex", "config.toml"), "# retired adapter\n"); + writeFileSync(join(dir, ".codex", "rules", "techieflow.rules"), "retired\n"); + mkdirSync(join(dir, ".agents", "skills", "techieflow-build"), { recursive: true }); + writeFileSync(join(dir, ".agents", "skills", "techieflow-build", "SKILL.md"), "retired\n"); + writeFileSync(join(dir, "WORKFLOW.html"), "the retired workflow guide\n"); mkdirSync(join(dir, ".opencode", "command"), { recursive: true }); writeFileSync(join(dir, ".opencode", "command", "techierag.md"), "# NuGet persona\n"); appendFileSync(join(dir, ".opencode", "plugin", "techieflow.js"), "\n// local edit\n"); - appendFileSync(join(dir, "WORKFLOW.html"), "\n"); appendFileSync(join(dir, "opencode.jsonc"), "// a comment only, no project keys\n"); - appendFileSync(join(dir, ".codex", "config.toml"), "\n# owner tuned\n"); - appendFileSync(join(dir, ".codex", "hooks.json"), "\n"); writeFileSync(join(dir, "PROJECT-STATUS.md"), "# status\n"); writeFileSync(join(dir, "CLAUDE.md"), "# claude\n"); writeFileSync(join(dir, "docs", "MyApp-BRD.md"), "# BRD\n"); @@ -269,9 +260,7 @@ try { ".tfcore/tasks/build-phase.md", ".tfcore/core-config.yaml", ".tfcore/routing.yaml", ".claude/commands/TechieFlow/agents/analyst.md", ".claude/commands/TechieFlow/tasks/build-phase.md", ".claude/commands/generate-html.md", ".claude/settings.json", ".claude/commands/trblazeui.md", - ".opencode/plugin/techieflow.js", ".opencode/opencode.jsonc", "opencode.jsonc", "WORKFLOW.html", - ".codex/config.toml", ".codex/hooks.json", ".codex/rules/techieflow.rules", - ...(codexBinderRuns ? [".codex/agents/analyst.toml", ".agents/skills/techieflow-build/SKILL.md"] : []), + ".opencode/plugin/techieflow.js", ".opencode/opencode.jsonc", "opencode.jsonc", ".tf-scaffold-note.txt", "docs/metrics/runs.jsonl", "docs/metrics/gates.jsonl", "docs/metrics/sessions.jsonl", "docs/metrics/commits.jsonl", "docs/metrics/misses.jsonl", "docs/metrics/README.md", @@ -289,7 +278,7 @@ try { }); check("brownfield: managed .gitignore entries present, docs/metrics not ignored", () => { const all = new Set(lines(join(brownNpm, ".gitignore"))); - for (const entry of [".tfcore/", ".claude/", ".opencode/", ".codex/", ".agents/skills/", "/CLAUDE.md", "/WORKFLOW.html", "/opencode.jsonc", "/.tf-scaffold-note.txt", "node_modules/", "/package.json", "/package-lock.json", "bin/", "obj/"]) { + for (const entry of [".tfcore/", ".claude/", ".opencode/", "/CLAUDE.md", "/opencode.jsonc", "/.tf-scaffold-note.txt", "node_modules/", "/package.json", "/package-lock.json", "bin/", "obj/"]) { assert(all.has(entry), `.gitignore is missing ${entry}`); } assert(!all.has("docs/") && !all.has("docs/metrics/"), ".gitignore hides docs/metrics"); @@ -338,18 +327,24 @@ try { shell(join(template, "update-framework.sh"), [updateShell]); node([installer, "update", `--target=${updateNpm}`]); check("update: installer result equals update-framework.sh result", () => assertSameTree(updateShell, updateNpm)); + check("update: the retired Codex adapter and WORKFLOW.html are removed by both routes", () => { + for (const dir of [updateShell, updateNpm]) { + for (const path of [".codex", ".agents/skills", "WORKFLOW.html"]) { + assert(!existsSync(join(dir, path)), `${path} survived the update in ${basename(dir)}`); + } + } + }); + check("update: framework files refreshed", () => { assert(read(join(updateNpm, ".tfcore/tasks/build-phase.md")) === read(join(template, ".tfcore/tasks/build-phase.md")), "edited task not restored"); assert(!existsSync(join(updateNpm, ".tfcore/tasks/stale-old-task.md")), "stale task not deleted"); assert(!existsSync(join(updateNpm, ".tfcore/workflows")), "stale stock folder not removed"); assert(existsSync(join(updateNpm, ".claude/commands/TechieFlow/tasks/verify-phase.md")), "deleted mirror file not restored"); assert(!existsSync(join(updateNpm, ".claude/commands/TechieFlow/tasks/old.md")), "stale mirror file not deleted"); - assert(read(join(updateNpm, "WORKFLOW.html")) === read(join(template, "WORKFLOW.html")), "WORKFLOW.html not refreshed"); assert(read(join(updateNpm, "opencode.jsonc")) === read(join(template, "opencode.jsonc")), "root opencode.jsonc with no project keys not refreshed"); assert(existsSync(join(updateNpm, "opencode.jsonc.bak")), "old opencode.jsonc not backed up"); assert(read(join(updateNpm, ".opencode/plugin/techieflow.js")) === read(join(template, ".opencode/plugin/techieflow.js")), "plugin not refreshed"); assert(existsSync(join(updateNpm, ".opencode/command/generate-html.md")), "short-form OpenCode command not deployed"); - assert(read(join(updateNpm, ".codex/hooks.json")) === read(join(template, ".codex/hooks.json")), "Codex hooks not refreshed"); assert(existsSync(join(updateNpm, ".claude/settings.json.bak")), "old settings.json not backed up"); assert(!read(join(updateNpm, ".claude/settings.json")).includes("ownerKey"), "settings.json not refreshed"); }); @@ -360,7 +355,6 @@ try { assert(read(join(updateNpm, ".claude/settings.local.json")) === "{ \"local\": true }\n", "settings.local.json touched"); assert(read(join(updateNpm, ".claude/commands/trblazeui.md")) === "# NuGet persona v2\n", "NuGet persona under .claude/commands overwritten"); assert(read(join(updateNpm, ".opencode/command/techierag.md")) === "# NuGet persona\n", "NuGet persona under .opencode/command overwritten"); - assert(read(join(updateNpm, ".codex/config.toml")).includes("# owner tuned"), ".codex/config.toml replaced"); for (const path of ["PROJECT-STATUS.md", "CLAUDE.md", "docs/MyApp-BRD.md", "docs/notes.md", "src/App/Program.cs"]) { assert(existsSync(join(updateNpm, path)), `${path} missing after update`); } @@ -453,7 +447,7 @@ try { const ignore = read(join(uninstallDir, ".gitignore")); assert(ignore.startsWith("# Project rules\n/dist/\n"), "project .gitignore rules changed"); const all = new Set(ignore.replace(/\r/g, "").split("\n")); - for (const entry of [".tfcore/", ".claude/", "/WORKFLOW.html", "/opencode.jsonc"]) assert(!all.has(entry), `.gitignore still lists ${entry}`); + for (const entry of [".tfcore/", ".claude/", "/opencode.jsonc"]) assert(!all.has(entry), `.gitignore still lists ${entry}`); assert(!ignore.includes("deployed copies, never commit"), ".gitignore still carries the framework block header"); assert(all.has("node_modules/") && all.has("bin/"), "uninstall removed ignore rules that describe the project's own build output"); }); @@ -534,7 +528,7 @@ try { assert(existsSync(join(jsTarget, "node_modules", "leftpad")), "the project's own dependency was removed from node_modules"); assert(!existsSync(join(jsTarget, "node_modules", "@techierathore")), "the framework package is still under node_modules"); assert(!existsSync(join(jsTarget, "node_modules", ".bin", "techieflow")), "the techieflow shim is still under node_modules/.bin"); - for (const path of [".tfcore/agents/analyst.md", ".claude/commands/TechieFlow/agents/analyst.md", ".opencode/opencode.jsonc", "WORKFLOW.html"]) { + for (const path of [".tfcore/agents/analyst.md", ".claude/commands/TechieFlow/agents/analyst.md", ".opencode/opencode.jsonc"]) { assert(existsSync(join(jsTarget, path)), `${path} was not installed`); } }); diff --git a/scripts/validate.mjs b/scripts/validate.mjs index d1d74b8..be3c9f3 100644 --- a/scripts/validate.mjs +++ b/scripts/validate.mjs @@ -116,9 +116,7 @@ function deployedFiles() { for (const f of filesUnder(join(root, ".opencode", "command"))) { if (f.endsWith(".md") && !libraryPersonas.has(f.split("/").pop())) out.push(rel(f)); } - out.push(".codex/config.toml", ".codex/hooks.json"); - for (const f of filesUnder(join(root, ".codex", "rules"))) out.push(rel(f)); - out.push("opencode.jsonc", "WORKFLOW.html"); + out.push("opencode.jsonc"); return out.filter((r) => !/(^|\/)(\.DS_Store|Thumbs\.db|desktop\.ini)$/.test(r) && !r.endsWith(".bak")); } @@ -128,11 +126,11 @@ const mustNotShip = [ ".claude/commands/trblazeui.md", ".claude/commands/techierag.md", ".opencode/command/trblazeui.md", ".opencode/command/techierag.md", ".opencode/node_modules", ".opencode/package.json", ".opencode/package-lock.json", ".opencode/.gitignore", - ".codex/agents", ".agents", ".techierag", ".trblazeui", ".github", + ".techierag", ".trblazeui", ".github", "scaffold-brownfield.sh", "scaffold-greenfield.sh", "update-framework.sh", "scripts/test-install.mjs", "scripts/validate.mjs", "docs/TechieFlow-Requirements.md", "docs/TechieFlow-How-It-Works.md", "docs/metrics", - "DECISIONS.md", "WorkFlow-Context.md", "CodexChanges.md", + "DECISIONS.md", "WorkFlow-Context.md", ]; // The package ships the framework plus the installer's own three files under scripts/. // Those never reach a project: the installer copies the framework folders only. diff --git a/tests/mirror/run.sh b/tests/mirror/run.sh index 86cc965..c378531 100644 --- a/tests/mirror/run.sh +++ b/tests/mirror/run.sh @@ -123,6 +123,37 @@ else ok "FR-47: skipped, no private-name list at $priv_file" fi +# 5d. FR-42 — the Codex adapter is gone and stays gone (removed 2026-09-07, D-14). +# The one allowed mention is the telemetry schema's note that `codex` is a RETIRED harness +# value: records written before the removal carry it and a reader must still understand them. +# Two checks, because the delivery scripts legitimately still name the paths they REMOVE. +# (a) the shipped framework and the readable files may not say Codex at all; +# (b) the delivery scripts may not carry a Codex code path — the markers that would deploy, +# generate or dispatch to it. The install test is exempt: its job is to prove removal. +codex_hits=0 +while IFS= read -r hit; do + file="${hit%%:*}" + [[ "$file" == "$ROOT/.tfcore/telemetry/SCHEMA.md" ]] && continue + bad "FR-42: $(realpath --relative-to="$ROOT" "$file") still names Codex" + codex_hits=$((codex_hits+1)) +done < <(grep -rilI 'codex' "$ROOT/.tfcore" "$ROOT/.claude/commands" "$ROOT/.opencode" \ + "$ROOT/package.json" "$ROOT/README.md" "$ROOT/WorkFlow-Context.md" 2>/dev/null \ + | grep -v '/\.session/\|/node_modules/' | sed 's/$/:/') +[[ $codex_hits -eq 0 ]] && ok "FR-42: the shipped framework and the readable files name no Codex" + +paths=0 +for f in "$ROOT/scaffold-brownfield.sh" "$ROOT/scaffold-greenfield.sh" "$ROOT/update-framework.sh" "$ROOT/scripts/install.mjs"; do + if grep -qiE 'tf-codex-bind|tf-codex-telemetry|codex-adapter|codex exec|harness codex|deployCodexAdapter' "$f" 2>/dev/null; then + bad "FR-42: $(basename "$f") still carries a Codex code path"; paths=$((paths+1)) + fi +done +[[ $paths -eq 0 ]] && ok "FR-42: no delivery script carries a Codex code path" +for leftover in "$ROOT/.codex" "$ROOT/.agents" "$ROOT/WORKFLOW.html" \ + "$ROOT/.tfcore/hooks/codex-adapter.py" "$ROOT/.tfcore/utils/tf-codex-bind.py"; do + [[ -e "$leftover" ]] && bad "FR-42: $(basename "$leftover") is still on disk" +done +[[ ! -e "$ROOT/.codex" && ! -e "$ROOT/WORKFLOW.html" ]] && ok "FR-42/FR-62: .codex/, .agents/ and WORKFLOW.html are gone from the framework" + echo echo "mirror self-test: $pass passed, $fail failed" [[ $fail -eq 0 ]] diff --git a/update-framework.sh b/update-framework.sh index 67bb236..86ec357 100755 --- a/update-framework.sh +++ b/update-framework.sh @@ -46,7 +46,6 @@ # PreToolUse block-git + guard-artifacts + guard-status + # guard-verify, Stop guard-status-html — 2026-08-25; # SessionStart sweep-artifacts — 2026-08-26) -# WORKFLOW.html # # OpenCode agents/tasks are NOT mirrored to .opencode/command/TechieFlow/ (that # subtree was removed — it only registered phantom slash commands). OpenCode @@ -54,7 +53,7 @@ # # Ensured (append-only, idempotent): # .gitignore — framework block (.tfcore/, .claude/, .opencode/, /CLAUDE.md, -# /WORKFLOW.html, /opencode.jsonc, /.tf-scaffold-note.txt): deployed copies +# /opencode.jsonc, /.tf-scaffold-note.txt): deployed copies # must never be committed in an app repo. Existing entries are respected; # nothing is removed or rewritten. # @@ -69,10 +68,6 @@ # .claude/settings.local.json (per-machine one-off approvals — never touched) # .claude/commands/{trblazeui,techierag}.md (NuGet-deployed library agents) # .opencode/command/{trblazeui,techierag}.md -# .codex/agents/{trblazeui,techierag}.toml (library-owned once the package -# ships it — TrBlazeUI >= 2.0.3; the -# compat wrapper is regenerated only -# while it is still the framework's own) # opencode.jsonc (may have project-specific agents; the # framework's keys now arrive via the # refreshed .opencode/opencode.jsonc) @@ -126,8 +121,8 @@ fi # -------------------------------------------------------------------------- # python3 is a HARD prerequisite, not a nice-to-have (added 2026-08-27 after a -# macOS scaffold failed on a missing python3). It powers the Codex bindings -# (tf-codex-bind.py), the HTML renderer (tf-render-html.py), the opencode.jsonc +# macOS scaffold failed on a missing python3). It powers the HTML renderer +# (tf-render-html.py), the opencode.jsonc # audit, tf-metrics.sh and every guard hook. Missing it does not fail loudly at # the point of use — the hooks fail OPEN by design — so a scaffold that skipped # it would look like it worked and leave the repo silently unguarded. @@ -138,7 +133,7 @@ fi tf_ensure_python3() { if command -v python3 >/dev/null 2>&1; then return 0; fi - echo " python3 not found — it is required (Codex bindings, HTML renderer, telemetry, guard hooks)." + echo " python3 not found — it is required (HTML renderer, telemetry, guard hooks)." if [[ "${TF_NO_INSTALL:-0}" == "1" ]]; then echo " TF_NO_INSTALL=1 set — not installing. Install python3 and re-run." >&2 @@ -191,7 +186,7 @@ tf_ensure_python3() { if ! tf_ensure_python3; then echo "" >&2 echo "Refusing to continue without python3: the scaffold would appear to succeed" >&2 - echo "while leaving the repo with no Codex bindings and no working guard hooks." >&2 + echo "while leaving the repo with no working guard hooks." >&2 exit 1 fi @@ -687,8 +682,8 @@ if [[ -f opencode.jsonc ]]; then # framework agent and command until it is fixed. # Collect FIRST, into plain variables. This script runs under `set -euo # pipefail`, where `printf | grep ... | while ...` aborts the WHOLE script the - # moment grep matches nothing — silently skipping every step below (the Codex - # adapter, WORKFLOW.html, the .gitignore block, metrics). Grep-in-a-pipeline is + # moment grep matches nothing — silently skipping every step below (the + # .gitignore block, metrics). Grep-in-a-pipeline is # not safe here; `|| true` on the assignment is. OC_DEAD_LINES="$(printf '%s\n' "$OC_AUDIT" | grep '^DIAG|dead-ref|' || true)" OC_BASH_LINES="$(printf '%s\n' "$OC_AUDIT" | grep '^DIAG|bare-bash-allow|' || true)" @@ -716,19 +711,22 @@ if [[ -f opencode.jsonc ]]; then fi fi -# 4b. Codex adapter. Preserve project-owned config.toml; refresh the framework -# policy files and regenerate agents/skills from canonical .tfcore content. -echo " .codex/ + .agents/skills/ — Codex adapter" -if [[ $DRY_RUN -eq 0 ]]; then - mkdir -p .codex/agents .codex/rules .agents/skills - [[ -f .codex/config.toml ]] || cp "$TEMPLATE/.codex/config.toml" .codex/config.toml - cp "$TEMPLATE/.codex/hooks.json" .codex/hooks.json - cp "$TEMPLATE/.codex/rules/techieflow.rules" .codex/rules/techieflow.rules - python3 .tfcore/utils/tf-codex-bind.py "$TARGET" || echo " ⚠ Codex bindings could not be generated (python3 required)" - echo " Codex hooks changed or installed — trust this repository and review /hooks" -else - echo " WOULD preserve/create .codex/config.toml; refresh hooks/rules; regenerate agents/skills" -fi +# 4b. Codex adapter — REMOVED 2026-09-07 (D-14, FR-42). The framework supports two +# harnesses, Claude Code and OpenCode. The adapter was frozen through the reset and is +# now taken out of every project it was deployed to. Nothing here is project content: +# .codex/ held a config file plus generated bindings, and .agents/skills/ was generated +# in full from .tfcore/tasks/. A project that wants Codex back takes it from git history. +for legacy_codex in .codex .agents/skills; do + if [[ -e "$legacy_codex" ]]; then + if [[ $DRY_RUN -eq 1 ]]; then + echo " WOULD remove $legacy_codex/ — the Codex adapter is no longer part of the framework" + else + rm -rf "$legacy_codex" + echo " removed $legacy_codex/ — the Codex adapter is no longer part of the framework" + fi + fi +done +[[ $DRY_RUN -eq 0 && -d .agents ]] && rmdir .agents 2>/dev/null && echo " removed the emptied .agents/" # Library agents under .opencode/command/ root preserved for f in trblazeui.md techierag.md; do @@ -738,15 +736,18 @@ for f in trblazeui.md techierag.md; do done # -------------------------------------------------------------------------- -# 4. WORKFLOW.html — canonical workflow guide, always overwrite +# 4. WORKFLOW.html — DROPPED 2026-09-07. It was a second full description of the +# process, revised last before the reset, and it still taught commands that no +# longer exist. What it said is now in the framework's README and in the documents +# under docs/. A project keeps no copy, because a stale copy is worse than none. # -------------------------------------------------------------------------- -if [[ -f "$TEMPLATE/WORKFLOW.html" ]]; then +if [[ -f WORKFLOW.html ]]; then if [[ $DRY_RUN -eq 1 ]]; then - rsync $RSYNC_FLAGS "$TEMPLATE/WORKFLOW.html" "WORKFLOW.html" || true + echo " WOULD remove WORKFLOW.html — superseded by the README and docs/" else - cp "$TEMPLATE/WORKFLOW.html" "WORKFLOW.html" + rm -f WORKFLOW.html + echo " removed WORKFLOW.html — superseded by the README and docs/" fi - echo " WORKFLOW.html" fi # -------------------------------------------------------------------------- @@ -864,8 +865,8 @@ fi # if any of these are already tracked, the owner must run # `git rm -r --cached ` once (git is manual, owner-only). # -------------------------------------------------------------------------- -GI_LINES=(".tfcore/" ".claude/" ".opencode/" ".codex/" ".agents/skills/" "/CLAUDE.md" "/WORKFLOW.html" "/opencode.jsonc" "/.tf-scaffold-note.txt") -GI_PATS=('^/?\.tfcore/?$' '^/?\.claude/?$' '^/?\.opencode/?$' '^/?\.codex/?$' '^/?\.agents/skills/?$' '^/?CLAUDE\.md$' '^/?WORKFLOW\.html$' '^/?opencode\.jsonc$' '^/?\.tf-scaffold-note\.txt$') +GI_LINES=(".tfcore/" ".claude/" ".opencode/" "/CLAUDE.md" "/opencode.jsonc" "/.tf-scaffold-note.txt") +GI_PATS=('^/?\.tfcore/?$' '^/?\.claude/?$' '^/?\.opencode/?$' '^/?CLAUDE\.md$' '^/?opencode\.jsonc$' '^/?\.tf-scaffold-note\.txt$') GI_MISSING=() for i in "${!GI_LINES[@]}"; do # tr strips CR so CRLF .gitignore files (Windows-authored) still match the $-anchor
    MissFoundClosedWhose gapWhat went wrong
    MISS-TechieFlow-20260907-142026-09-07 by owner2026-09-07 by fix-issuesthe framework never said itThe npm installer's framework subfolder list left out standards, so a project migrated from the old layout came out with no coding standards file.
    MISS-TechieFlow-20260907-132026-09-07 by owner2026-09-07 by fix-issuesthe framework never said itThe npm installer wrote a settings.json missing five hook registrations the shell scripts had gained, so a project installed from the package ran without the metrics, database and build guards.
    MISS-TechieFlow-20260907-112026-09-07 by owner2026-09-07 by fix-issuesthe check was too weakThe Playbook review prompt was drafted from folder word counts taken only at the top level, so 174 files and 196,498 words counted as zero and the prompt nearly shipped the wrong headline finding.
    MISS-TechieFlow-20260907-102026-09-07 by owner2026-09-07 by fix-issuesthe check was too weakWORKFLOW.html is force-deployed to every project and still teaches three commands removed in Sitting 4c, because the removed-command check looks at the task files and the harness registrations but never at the human reference.
    MISS-TechieFlow-20260907-092026-09-07 by owner2026-09-07 by fix-issuesthe check was too weakA run record with no ended at all was accepted and could never be costed: the guard only replaced an ended that lied, so this session's own record landed with no duration.
    MISS-TechieFlow-20260907-082026-09-07 by owner2026-09-07 by fix-issuesthe check was too weakThe updater kept a project's root opencode.jsonc because its dead BMAD-era registrations looked like project content, so that repo loaded no framework agents in OpenCode at all and only a warning was printed.
    MISS-TechieFlow-20260907-072026-09-07 by owner2026-09-07 by fix-issuesthe framework never said itNothing capped or checked the two files a person reads first, so the briefing reached 344 KB and the README 121 KB and both still named commands the framework had removed.
    MISS-TechieFlow-20260905-03 (FR-39)2026-09-05 by agent-review2026-09-07 by fix-issuesthe check was too weaktf-emit.sh appends a miss record that has no miss_id, although miss_id is the join key to its fix record, so a caller that skips --next-miss-id writes an orphan.
    MISS-TechieFlow-20260905-02 (FR-40)2026-09-05 by agent-review2026-09-07 by fix-issuesthe check was too weakSix task files edited in .tfcore on 2026-08-31 were never copied to the Claude Code mirror, so the two harnesses ran different smoke, metrics, mockup, render and verify rules for five days; no parity check ran.
    MISS-TechieFlow-20260905-012026-09-05 by owner2026-09-07 by fix-issuessaid and ignoredThe first Session 4a task table put the owner questions inside table cells and the rows were too wide to read in a terminal, against the plain-words rule.
    MISS-TechieFlow-20260904-142026-09-04 by owner2026-09-07 by fix-issuesnot sortedno sentence recorded (scope-creep, other, why: missing-checklist-item)
    MISS-TechieFlow-20260831-102026-08-31 by agent-review2026-08-31 by fix-issuesnot sortedno sentence recorded (wrong-behaviour, src, why: insufficient-verify-method)
    MISS-TechieFlow-20260831-092026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (unspecified-gap, src, why: insufficient-verify-method)
    MISS-TechieFlow-20260831-082026-08-31 by library-feedback2026-08-31 by fix-issuesnot sortedno sentence recorded (spec-contradiction, src, why: missing-checklist-item)