@techierathore/techieflow 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/.claude/commands/TechieFlow/agents/flow-master.md +2 -0
  2. package/.claude/commands/TechieFlow/agents/verifier.md +2 -2
  3. package/.claude/commands/TechieFlow/tasks/_develop-mode.md +34 -0
  4. package/.claude/commands/TechieFlow/tasks/_yolo-mode.md +2 -2
  5. package/.claude/commands/TechieFlow/tasks/amend-docs.md +1 -1
  6. package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +2 -2
  7. package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +4 -4
  8. package/.claude/commands/TechieFlow/tasks/develop-end-to-end.md +30 -0
  9. package/.claude/commands/TechieFlow/tasks/fix-issues.md +1 -0
  10. package/.claude/commands/TechieFlow/tasks/mockups.md +1 -1
  11. package/.claude/commands/TechieFlow/tasks/triage-and-fix.md +2 -2
  12. package/.claude/commands/TechieFlow/tasks/verify-phase.md +12 -11
  13. package/.opencode/plugin/techieflow.js +15 -7
  14. package/.tfcore/agents/flow-master.md +2 -0
  15. package/.tfcore/agents/verifier.md +2 -2
  16. package/.tfcore/core-config.yaml +2 -1
  17. package/.tfcore/hooks/guard-db.sh +106 -9
  18. package/.tfcore/hooks/guard-verify-deps.sh +16 -1
  19. package/.tfcore/hooks/yolo-allow.sh +27 -0
  20. package/.tfcore/standards/blazor-zoom.cs.txt +191 -0
  21. package/.tfcore/standards/coding-standards-dotnet.md +31 -3
  22. package/.tfcore/tasks/_develop-mode.md +34 -0
  23. package/.tfcore/tasks/_yolo-mode.md +2 -2
  24. package/.tfcore/tasks/amend-docs.md +1 -1
  25. package/.tfcore/tasks/day1-brownfield.md +2 -2
  26. package/.tfcore/tasks/day1-greenfield.md +4 -4
  27. package/.tfcore/tasks/develop-end-to-end.md +30 -0
  28. package/.tfcore/tasks/fix-issues.md +1 -0
  29. package/.tfcore/tasks/mockups.md +1 -1
  30. package/.tfcore/tasks/triage-and-fix.md +2 -2
  31. package/.tfcore/tasks/verify-phase.md +12 -11
  32. package/.tfcore/telemetry/SCHEMA.md +6 -2
  33. package/.tfcore/telemetry/tf-metrics.sh +19 -0
  34. package/.tfcore/templates/stack-defaults/dotnet.md +3 -1
  35. package/.tfcore/templates/v4custom/app-architecture-tmpl.md +5 -0
  36. package/.tfcore/templates/v4custom/app-brd-tmpl.md +2 -1
  37. package/.tfcore/templates/v4custom/app-checklist-tmpl.md +11 -0
  38. package/.tfcore/templates/v4custom/app-phases-tmpl.md +2 -1
  39. package/.tfcore/templates/v4custom/html-render-shell.md +2 -0
  40. package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
  41. package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
  42. package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
  43. package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
  44. package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
  45. package/.tfcore/utils/tf-appium.mjs +179 -0
  46. package/.tfcore/utils/tf-build-list.py +16 -1
  47. package/.tfcore/utils/tf-build.sh +75 -0
  48. package/.tfcore/utils/tf-checklist-edit.py +21 -3
  49. package/.tfcore/utils/tf-day1-files.py +55 -28
  50. package/.tfcore/utils/tf-develop.sh +264 -0
  51. package/.tfcore/utils/tf-devguide-list.py +6 -2
  52. package/.tfcore/utils/tf-doc-check.py +83 -21
  53. package/.tfcore/utils/tf-doc-tests.sh +40 -3
  54. package/.tfcore/utils/tf-emit.sh +40 -10
  55. package/.tfcore/utils/tf-fix-close.sh +13 -7
  56. package/.tfcore/utils/tf-maccatalyst-check.sh +92 -0
  57. package/.tfcore/utils/tf-menus.mjs +401 -0
  58. package/.tfcore/utils/tf-mockup-parity.mjs +355 -27
  59. package/.tfcore/utils/tf-mockup-parity.sh +14 -3
  60. package/.tfcore/utils/tf-phase.sh +24 -4
  61. package/.tfcore/utils/tf-render-html.py +19 -3
  62. package/.tfcore/utils/tf-req-ids.py +36 -0
  63. package/.tfcore/utils/tf-stack-check.sh +3 -0
  64. package/.tfcore/utils/tf-status-facts.py +90 -15
  65. package/.tfcore/utils/tf-theme.mjs +189 -0
  66. package/.tfcore/utils/tf-triage.py +12 -1
  67. package/.tfcore/utils/tf-verify-boot.sh +274 -19
  68. package/.tfcore/utils/tf-verify-emit.sh +37 -17
  69. package/.tfcore/utils/tf-verify-list.py +219 -20
  70. package/.tfcore/utils/tf-verify-list.sh +3 -1
  71. package/.tfcore/utils/tf-verify-native.mjs +307 -0
  72. package/.tfcore/utils/tf-verify-screens.mjs +178 -20
  73. package/.tfcore/utils/tf-verify-screens.sh +17 -4
  74. package/.tfcore/utils/tf-verify-tests.sh +107 -17
  75. package/.tfcore/utils/tf-verify-verdict.py +118 -15
  76. package/.tfcore/utils/tf-verify-zoom.mjs +193 -0
  77. package/.tfcore/utils/tf-verify-zoom.sh +29 -0
  78. package/.tfcore/utils/tf-yolo-alias.sh +84 -0
  79. package/.tfcore/utils/tf-yolo-perms.py +135 -0
  80. package/.tfcore/utils/tf-yolo-start.sh +44 -0
  81. package/.tfcore/utils/tf-yolo.sh +31 -3
  82. package/.tfcore/utils/tf_feedback.py +79 -16
  83. package/.tfcore/utils/tf_stack_check.py +93 -0
  84. package/README.md +2 -2
  85. package/opencode.jsonc +6 -2
  86. package/package.json +1 -1
  87. package/scripts/install.mjs +13 -2
  88. package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
  89. package/.tfcore/utils/__pycache__/tf-codex-bind.cpython-312.pyc +0 -0
  90. package/.tfcore/utils/__pycache__/tf-codex-telemetry.cpython-312.pyc +0 -0
  91. package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
  92. package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
  93. package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
  94. package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
  95. package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
  96. package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
  97. package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
  98. package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
@@ -0,0 +1,191 @@
1
+ // blazor-zoom.cs.txt — copy to the MAUI project's root as BlazorZoom.cs and set the namespace
2
+ // (coding-standards-dotnet.md §9b). Kept as .txt so a project whose .csproj sits at the repository root
3
+ // does not compile the framework's copy. Then two hooks:
4
+ // MainPage (the page holding the BlazorWebView):
5
+ // blazorWebView.BlazorWebViewInitialized += (sender, args) => BlazorZoom.Attach(args.WebView);
6
+ // Platforms/MacCatalyst/AppDelegate.cs (inside the MauiUIApplicationDelegate class):
7
+ // public override void BuildMenu(UIKit.IUIMenuBuilder builder) { base.BuildMenu(builder); BlazorZoom.BuildMenu(builder); }
8
+ // [Export("tfZoomIn:")] public void ZoomIn(NSObject aSender) => BlazorZoom.ZoomBy(1);
9
+ // [Export("tfZoomOut:")] public void ZoomOut(NSObject aSender) => BlazorZoom.ZoomBy(-1);
10
+ // [Export("tfZoomReset:")] public void ZoomReset(NSObject aSender) => BlazorZoom.Reset();
11
+ // Optional: set BlazorZoom.FirstLevel before the page is built (for example from the screen size) to open
12
+ // at more than 100% before anyone has zoomed; Cmd/Ctrl+0 still means 100%.
13
+ // Proved on Lekhak's BlogAdmin (macOS 27, 2026-10-11): bash .tfcore/utils/tf-verify-zoom.sh checks it.
14
+
15
+ #nullable enable
16
+ #if MACCATALYST
17
+ using Foundation;
18
+ using UIKit;
19
+ using WebKit;
20
+ #endif
21
+
22
+ namespace YourApp;
23
+
24
+ /// <summary>
25
+ /// Zoom in, zoom out and actual size for the BlazorWebView, with the operating system's keys.
26
+ /// </summary>
27
+ /// <remarks>
28
+ /// <para><b>Purpose:</b> [coding-standards-dotnet.md §9b] On a high-resolution Mac the text was too small and
29
+ /// Cmd+= did nothing: a Mac Catalyst WKWebView has no zoom of its own. Every Blazor Hybrid head zooms from 50% to
30
+ /// 300% in 10% steps, and the level is kept in MAUI Preferences across restarts.</para>
31
+ /// <para><b>Code Flow:</b> <c>MainPage</c> hands the platform web view to <see cref="Attach"/> once the BlazorWebView is
32
+ /// initialised, which applies the saved level. Mac Catalyst: the View menu's Zoom In (Cmd+=), Zoom Out (Cmd+-) and
33
+ /// Actual Size (Cmd+0), added by <see cref="BuildMenu"/> from the app delegate, call <see cref="ZoomBy"/> and
34
+ /// <see cref="Reset"/>, which set <c>WKWebView.PageZoom</c> on every attached web view, one per window. Windows: WebView2's own zoom (25% to 500%, uneven steps,
35
+ /// not kept) is turned off, and a script run on every page handles Ctrl+plus, Ctrl+minus, Ctrl+0 and Ctrl+wheel with
36
+ /// the same steps, posting each new level back to be saved (and keeping it for the session, so a page reload keeps
37
+ /// it). The WinUI WebView2 control does not expose WebView2's own ZoomFactor, so that cannot be saved instead.</para>
38
+ /// </remarks>
39
+ public static class BlazorZoom
40
+ {
41
+ /// <summary>The Preferences key the zoom level is kept under.</summary>
42
+ public const string PreferenceKey = "tf.zoom";
43
+
44
+ /// <summary>Smallest zoom, 50%.</summary>
45
+ public const double Min = 0.5;
46
+
47
+ /// <summary>Largest zoom, 300%.</summary>
48
+ public const double Max = 3.0;
49
+
50
+ /// <summary>One step, 10%.</summary>
51
+ public const double Step = 0.1;
52
+
53
+ /// <summary>The level used until one is saved; 1.0 unless the app sets it before the page is built.</summary>
54
+ public static double FirstLevel { get; set; } = 1.0;
55
+
56
+ /// <summary>The zoom level now in use, 1.0 being actual size.</summary>
57
+ public static double Level { get; private set; } = 1.0;
58
+
59
+ /// <summary>Rounds to a whole step and keeps the level inside 50% to 300%.</summary>
60
+ /// <param name="aLevel">The level asked for.</param>
61
+ /// <returns>The level that will be used.</returns>
62
+ public static double Clamp(double aLevel) => Math.Round(Math.Clamp(aLevel, Min, Max) * 10) / 10;
63
+
64
+ #if MACCATALYST
65
+ // every window's web view, weakly, so a Mac app with several BlazorWebView windows zooms them all (Chatur TF-010)
66
+ private static readonly List<WeakReference<WKWebView>> lstWebViews = new();
67
+ private static bool blnMenuSet;
68
+
69
+ /// <summary>Takes the platform web view and applies the saved level.</summary>
70
+ /// <param name="aPlatformWebView">The <c>BlazorWebViewInitializedEventArgs.WebView</c>.</param>
71
+ public static void Attach(object aPlatformWebView)
72
+ {
73
+ if (aPlatformWebView is WKWebView objWebView)
74
+ {
75
+ lstWebViews.Add(new WeakReference<WKWebView>(objWebView));
76
+ }
77
+
78
+ Apply(Preferences.Default.Get(PreferenceKey, FirstLevel));
79
+ if (!blnMenuSet && OperatingSystem.IsMacCatalystVersionAtLeast(26))
80
+ {
81
+ // From macOS 26 the main menu is built here: what an app delegate's BuildMenu changes is ignored.
82
+ // Format is off because its Font › Bigger and Smaller already own Cmd+= and Cmd+-, and UIKit
83
+ // silently drops a menu whose shortcut repeats one; Inspector is off because the app has none.
84
+ blnMenuSet = true;
85
+ var objConfig = new UIMainMenuSystemConfiguration
86
+ {
87
+ TextFormattingPreference = UIMenuSystemElementGroupPreference.Removed,
88
+ InspectorPreference = UIMenuSystemElementGroupPreference.Removed,
89
+ };
90
+ UIMainMenuSystem.SharedSystem.SetBuildConfiguration(objConfig, AddCommands);
91
+ }
92
+ }
93
+
94
+ /// <summary>Adds Zoom In, Zoom Out and Actual Size to the top of the View menu, before macOS 26.</summary>
95
+ /// <param name="aBuilder">The builder the app delegate's <c>BuildMenu</c> receives.</param>
96
+ /// <remarks>The selectors are answered by <c>[Export]</c> methods on the app delegate, which is on the
97
+ /// responder chain whatever has focus inside the web view. From macOS 26 <see cref="Attach"/> does this.</remarks>
98
+ public static void BuildMenu(IUIMenuBuilder aBuilder)
99
+ {
100
+ if (!OperatingSystem.IsMacCatalystVersionAtLeast(26) && aBuilder.System?.Handle == UIMenuSystem.MainSystem.Handle)
101
+ {
102
+ aBuilder.RemoveMenu(UIMenuIdentifier.Format.GetConstant()!);
103
+ AddCommands(aBuilder);
104
+ }
105
+ }
106
+
107
+ /// <summary>Zooms in (a positive step) or out (a negative one).</summary>
108
+ /// <param name="aSteps">How many 10% steps.</param>
109
+ public static void ZoomBy(int aSteps) => Apply(Level + (aSteps * Step));
110
+
111
+ /// <summary>Back to actual size.</summary>
112
+ public static void Reset() => Apply(1.0);
113
+
114
+ private static void AddCommands(IUIMenuBuilder aBuilder)
115
+ {
116
+ var objView = aBuilder.GetMenu(UIMenuIdentifier.View.GetConstant()!);
117
+ if (objView == null || objView.Children.Any(c => c.Title == "Zoom In"))
118
+ {
119
+ return;
120
+ }
121
+
122
+ var objZoom = UIMenu.Create(string.Empty, null, UIMenuIdentifier.None, UIMenuOptions.DisplayInline, new UIMenuElement[]
123
+ {
124
+ UIKeyCommand.Create("Zoom In", null, new ObjCRuntime.Selector("tfZoomIn:"), "=", UIKeyModifierFlags.Command, null),
125
+ UIKeyCommand.Create("Zoom Out", null, new ObjCRuntime.Selector("tfZoomOut:"), "-", UIKeyModifierFlags.Command, null),
126
+ UIKeyCommand.Create("Actual Size", null, new ObjCRuntime.Selector("tfZoomReset:"), "0", UIKeyModifierFlags.Command, null),
127
+ });
128
+ aBuilder.ReplaceMenu(UIMenuIdentifier.View.GetConstant()!, objView.GetMenuByReplacingChildren(new UIMenuElement[] { objZoom }.Concat(objView.Children).ToArray()));
129
+ }
130
+
131
+ private static void Apply(double aLevel)
132
+ {
133
+ Level = Clamp(aLevel);
134
+ Preferences.Default.Set(PreferenceKey, Level);
135
+ lstWebViews.RemoveAll(w => !w.TryGetTarget(out _));
136
+ foreach (var objRef in lstWebViews)
137
+ {
138
+ if (objRef.TryGetTarget(out var objWebView))
139
+ {
140
+ objWebView.PageZoom = (nfloat)Level;
141
+ }
142
+ }
143
+ }
144
+ #elif WINDOWS
145
+ private const string MessagePrefix = "tfzoom:";
146
+
147
+ /// <summary>Takes the platform web view, turns WebView2's own zoom off and installs the key and wheel handler.</summary>
148
+ /// <param name="aPlatformWebView">The <c>BlazorWebViewInitializedEventArgs.WebView</c>.</param>
149
+ public static async void Attach(object aPlatformWebView)
150
+ {
151
+ if (aPlatformWebView is not Microsoft.UI.Xaml.Controls.WebView2 objWebView || objWebView.CoreWebView2 == null)
152
+ {
153
+ return;
154
+ }
155
+
156
+ var objCore = objWebView.CoreWebView2;
157
+ Level = Clamp(Preferences.Default.Get(PreferenceKey, FirstLevel));
158
+ objCore.Settings.IsZoomControlEnabled = false;
159
+ objCore.WebMessageReceived += (sender, args) =>
160
+ {
161
+ var strMessage = args.TryGetWebMessageAsString();
162
+ if (strMessage != null && strMessage.StartsWith(MessagePrefix, StringComparison.Ordinal)
163
+ && double.TryParse(strMessage[MessagePrefix.Length..], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var dblLevel))
164
+ {
165
+ Level = Clamp(dblLevel);
166
+ Preferences.Default.Set(PreferenceKey, Level);
167
+ }
168
+ };
169
+ var strLevel = Level.ToString(System.Globalization.CultureInfo.InvariantCulture);
170
+ await objCore.AddScriptToExecuteOnDocumentCreatedAsync(
171
+ "(function(){var z=parseFloat(sessionStorage.getItem('tfzoom'))||" + strLevel + ",min=0.5,max=3;"
172
+ + "function set(v){z=Math.round(Math.min(max,Math.max(min,v))*10)/10;document.documentElement.style.zoom=z;"
173
+ + "sessionStorage.setItem('tfzoom',z);window.chrome.webview.postMessage('" + MessagePrefix + "'+z);}"
174
+ + "function apply(){document.documentElement.style.zoom=z;}"
175
+ + "if(document.documentElement){apply();}document.addEventListener('DOMContentLoaded',apply);"
176
+ + "window.addEventListener('keydown',function(e){if(!e.ctrlKey||e.altKey||e.metaKey)return;"
177
+ + "if(e.key==='='||e.key==='+'){set(z+0.1);}else if(e.key==='-'){set(z-0.1);}else if(e.key==='0'){set(1);}else return;"
178
+ + "e.preventDefault();},true);"
179
+ + "window.addEventListener('wheel',function(e){if(!e.ctrlKey)return;e.preventDefault();set(z+(e.deltaY<0?0.1:-0.1));},{passive:false});"
180
+ + "})();");
181
+ // the page already loaded before the script was added: give it the level too
182
+ await objCore.ExecuteScriptAsync("document.documentElement.style.zoom=" + strLevel + ";");
183
+ }
184
+ #else
185
+ /// <summary>No zoom on this head.</summary>
186
+ /// <param name="aPlatformWebView">Unused.</param>
187
+ public static void Attach(object aPlatformWebView)
188
+ {
189
+ }
190
+ #endif
191
+ }
@@ -69,13 +69,33 @@ Controller actions: the `a` prefix applies to `[FromRoute]`, `[FromQuery]` and `
69
69
  ## 7. Testability
70
70
 
71
71
  - Blazor: a stable `data-testid` (or element id) on every control the verifier must reach.
72
- - 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.
72
+ - 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. The tab, menu item or button that opens a screen carries `AutomationId="nav-<screen>"` (the screen's name, lower case, words joined by `-`), or a label equal to the screen's name; the Mac check reaches screens that way.
73
73
 
74
74
  ## 8. Security
75
75
 
76
76
  - No credentials in code. Parameterised queries. Validate inputs. Log security events.
77
77
 
78
- ## 9. Enforcement
78
+ ## 9. Mac Catalyst heads
79
+
80
+ Every MAUI project with a `net*-maccatalyst` target, Blazor Hybrid included:
81
+
82
+ - **Scene manifest.** `Platforms/MacCatalyst/Info.plist` carries `UIApplicationSceneManifest` with a `UIWindowSceneSessionRoleApplication` configuration named `__MAUI_DEFAULT_SCENE_CONFIGURATION__` whose `UISceneDelegateClassName` is `SceneDelegate`, and the project has `[Register("SceneDelegate")] public class SceneDelegate : MauiUISceneDelegate { }` beside the `AppDelegate`. On macOS 27 an app without it quits at its first window; the stock `dotnet new maui` template has none, so add it when the project is created.
83
+ - **Keychain and signing.** An app that asks for `keychain-access-groups` (MAUI `SecureStorage` needs it) must be signed with a development certificate and provisioning profile (`CodesignKey`, `CodesignProvisioningProfile`), or give Debug builds a second entitlements file without it and a Debug credential store that does not use the keychain. macOS refuses to start a locally signed build that asks for it. Decide which before the first Mac build.
84
+ - **Clean after an update.** After a .NET SDK or MAUI workload update, build the Mac head from clean (`--no-incremental`, or delete its `bin/` and `obj/`): an old precompiled build stops at start in `load_aot_module`.
85
+ - **Launching it.** Open the built `.app` with `open`, never its binary: macOS kills a sandboxed app started from its binary.
86
+ - **Blazor Hybrid.** On a Mac, `data-testid` and HTML `id` do not reach the test driver, so the Mac screens are checked without control names (blank window, error bar, overlap, screenshot) and the control-by-control check is the Windows head's. Keep the stock error bar (`#blazor-error-ui`, "An unhandled error has occurred"), or pass its text with `--error-text`.
87
+
88
+ ## 9b. Zoom in Blazor Hybrid heads
89
+
90
+ Every head that draws its screens in a `BlazorWebView` (Mac Catalyst and Windows) zooms with the operating system's keys: Cmd on the Mac, Ctrl on Windows, `=` in, `-` out, `0` actual size, and Ctrl+wheel on Windows. The Mac menu bar shows View › Zoom In, Zoom Out, Actual Size. The level runs from 50% to 300% in 10% steps and is kept in MAUI `Preferences` (key `tf.zoom`) across restarts.
91
+
92
+ - **Copy, do not rewrite.** `.tfcore/standards/blazor-zoom.cs.txt` is the code: copy it to the project as `BlazorZoom.cs`, set its namespace, and add the hooks its header names (one line in the page holding the `BlazorWebView`, four members on the Mac app delegate).
93
+ - **Mac Catalyst.** It sets `WKWebView.PageZoom`. From macOS 26 the main menu ignores what an app delegate's `BuildMenu` changes, so the commands are added through `UIMainMenuSystem.SharedSystem.SetBuildConfiguration`, with the Format menu off: its Font › Bigger and Smaller own Cmd+= and Cmd+-, and UIKit silently drops a menu whose shortcut repeats one.
94
+ - **Windows.** The WinUI `WebView2` control does not expose WebView2's `ZoomFactor`, and WebView2's own zoom steps unevenly from 25% to 500% and keeps nothing. So its zoom control is turned off and a script added to every page handles the keys and the wheel, and posts each level back to be saved.
95
+
96
+ `tf-maccatalyst-check.sh` warns about a Blazor Hybrid head without this; `tf-verify-zoom.sh` presses the keys on a booted head.
97
+
98
+ ## 10. Enforcement
79
99
 
80
100
  `.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.
81
101
 
@@ -90,4 +110,12 @@ grep -rE "public\s+(async\s+)?Task\s+\w+_\w+\s*\(" tests/ 2>/dev/null
90
110
  grep -rPE "private(\s+readonly)?\s+\w+\s+(?!obj)[A-Z]\w+\s*[;=]" src/ 2>/dev/null | grep -v "static\|const"
91
111
  ```
92
112
 
93
- Severity: error for file-scoped namespace and underscore field prefix; warning for nullable and the `Async` suffix; the rest informational.
113
+ A MAUI project with a Mac Catalyst target or a Blazor Hybrid head also runs, on any host (it only reads files):
114
+
115
+ ```bash
116
+ bash .tfcore/utils/tf-maccatalyst-check.sh # FAIL: scene manifest or delegate missing; WARN: keychain on a local build, or a Blazor Hybrid head without zoom (§9b)
117
+ ```
118
+
119
+ `tf-verify-boot.sh --head maccatalyst` runs it first and stops on a FAIL on macOS 27 or later.
120
+
121
+ Severity: error for file-scoped namespace and underscore field prefix; error for a Mac Catalyst FAIL; warning for nullable and the `Async` suffix; the rest informational.
@@ -0,0 +1,34 @@
1
+ # _develop-mode (shared rule — *develop-end-to-end: a brief to a UAT-ready app, nobody asked)
2
+
3
+ Develop mode is on while `.tfcore/.session/develop.json` exists. `tf-develop.sh` writes it when `*develop-end-to-end` starts and removes it when the run ends. Everything in `_yolo-mode.md` applies, and on top of it:
4
+
5
+ ## What changes
6
+
7
+ - **No question to the owner, ever.** Every question a task would ask is answered here, or by the brief (`docs/{App}-Brief.md`), or decided by you. A decision the owner would normally make is written down where the owner will see it: in the Architecture's Stack Decisions, or in the document it belongs to, under a line starting `Decided in develop mode:`, with one reason. Never stop to ask, and never write `done blocked` for a question you could decide.
8
+ - **The mockups are yours.** In this command you drew them on Day 1, so a difference between a mockup and what the stack mandates (a library's own control, a required layout) is never an owner decision: redraw the mockup to match the mandated component, write `Decided in develop mode:` with the reason in the UIDesign, re-render it, and verify again. The same for any document you wrote in this run. Only a thing the owner alone can supply is a blocker.
9
+ - **No owner-review stop.** This is the one command that crosses the reviews: `*day1-greenfield` stage 1 ends without "review them, then run --stage2"; the supervisor starts stage 2 itself. Everything else about each stage is unchanged.
10
+ - **Git is the supervisor's.** `tf-develop.sh` commits and pushes after each phase from its own shell. You still never run a git write: the guard is unchanged, and you do not need to.
11
+
12
+ ## Day 1 answers
13
+
14
+ | Question in `day1-greenfield.md` | Answer in develop mode |
15
+ |---|---|
16
+ | 1. The app name | `{App}` from `develop.json` |
17
+ | 2. The concept | `docs/{App}-Brief.md`, all of it |
18
+ | 3. Hints | none, unless the brief names notes or mockups to harvest |
19
+ | 4. The stack | the stack the brief names; when it names none, the owner's answer set under `.tfcore/templates/stack-defaults/` (the one `core-config.yaml` names, or the only one there). Each question that answer set leaves open is decided as below, and every answer is written into the Architecture's Stack Decisions |
20
+ | 5. The size | the size you propose from your count; a Large brief is built as its phase 1 only, and the other phases are listed in the Build Report as not built |
21
+
22
+ Questions an answer set commonly leaves open:
23
+
24
+ - **Authentication.** The mechanism the brief names. When it names none: the app's own sign-in with local accounts, stored in the app's database; a shared identity platform needs credentials an unattended run does not have.
25
+ - **Rendering or hosting model of the UI.** The one the brief implies; otherwise the answer set's first choice.
26
+ - **Hosting and production secrets.** Not asked: they belong after UAT.
27
+
28
+ ## The database
29
+
30
+ When the Architecture names a database server and none is reachable, create it locally and continue: a container named `{app}-db` (lower case) on a free port, with a development password kept in the app's development secrets, never in a committed file. Write the connection details into the UsageGuide's setup section. Create the schema only through the migration path the Stack Decisions name, during `*build-phase` (the database guard allows it there). This overrides the answer set's "never creates its own database container" for this command only.
31
+
32
+ ## What does not change
33
+
34
+ The status gate, the smoke policy, the verifier's checks and the guard hooks. A row is `Verified` only from an executed verify. A thing only the owner can supply (a paid account, a device, a credential for a third-party service the brief requires) is still a blocker: finish everything else first, list it under PROJECT-STATUS "Known blockers", then `done blocked`.
@@ -1,6 +1,6 @@
1
1
  # _yolo-mode (shared rule — YOLO / goal mode: run unattended to completion)
2
2
 
3
- YOLO means all permissions and all access are granted: run one command to completion and never stop to check in. It never crosses an owner review into the next phase; `*day1-greenfield` runs one stage, and stage 2 needs `--stage2`. `*build-phase` and `*verify` are in YOLO by default; every other command honours it when it is on.
3
+ YOLO means all permissions and all access are granted: run one command to completion and never stop to check in. It never crosses an owner review into the next phase; `*day1-greenfield` runs one stage, and stage 2 needs `--stage2`. The one command that does is `*develop-end-to-end`, under its own rule (`_develop-mode.md`). `*build-phase` and `*verify` are in YOLO by default; every other command honours it when it is on.
4
4
 
5
5
  ## When it is on
6
6
 
@@ -14,7 +14,7 @@ YOLO means all permissions and all access are granted: run one command to comple
14
14
 
15
15
  ## What changes
16
16
 
17
- - Deletes and `sudo` run without a prompt. Read-only git is allowed. Git writes stay refused in every mode; the owner commits.
17
+ - Nothing asks. Deletes and `sudo` run without a prompt; `tf-yolo.sh on` also makes OpenCode stop asking (from its next session) and the `yolo-allow.sh` hook approves every Claude Code call. Claude Code's own safety checks still ask unless the session was started with `tfyolo` (`tfyolo -c` keeps the conversation); say that in one line when one asks. Read-only git is allowed. Git writes stay refused in every mode; the owner commits.
18
18
  - No question, confirmation or pause: take the documented default and record it in the checklist Remarks. The one question that survives is a new test user (`_smoke-test-policy.md`): use a documented account; if none exists, create one named `{App}-yolo-tester`, record it in the UsageGuide, and continue.
19
19
  - Never end a turn with a question, a plan or "shall I". End only when the command is complete or everything left is owner-only.
20
20
  - A failed build, a dead port, a stopped service: fix it and continue. A `429` from a sub-agent or an API: wait 60 seconds, retry once, then route around it.
@@ -18,7 +18,7 @@ First: `bash .tfcore/utils/tf-phase.sh start amend-docs {App}` prints the start
18
18
  4. BRD. ADD: append a ledger item with the next id, its screen, its mockup link and its acceptance line "When <actor> <does what> on <screen>, then <observable result>"; a new screen gets a row in the Screens and flow table and a Planned row in the Development status table. MODIFY: edit the item in place, same id. REMOVE: `~~BRD-N~~ (removed {date}: reason)`, never deleted. If the additions would take a Small project past 50 requirements, raise it to Medium: `bash .tfcore/utils/tf-day1-files.sh {App} --size M` and the Size in every document header. If they would take a Medium project, or one phase, past 100 requirements or 20 screens, stop and propose a phase split: a new row in `docs/{App}-Phases.md` (written with `--size L` when absent) and a new `docs/{App}-Pn-BRD.md`, instead of growing this one.
19
19
  5. Architecture, when in scope and ARCH items exist: edit the affected sections in place, and add a Decisions log row (date, decision, why, status `planned` naming the BRD item) for every new decision or package. A reversed decision gets a new row; the old row stays.
20
20
  6. Mockups: when a screen was added or changed, run `.tfcore/tasks/mockups.md` with `--update`, so every screen in the BRD has a current mockup before the checklist changes.
21
- 7. Checklist, only if it exists. ADD: `bash .tfcore/utils/tf-split-brd.sh {App} --add-missing` appends rows for the new items; correct their class. MODIFY: set the rows derived from that item to `Needs re-verify` with a Remark saying what changed. REMOVE: set them to `N/A (removed {date})` and keep the rows. Where the mapping from an item to its rows is unclear, ask, do not guess. If the checklist does not exist yet, the next day-1 stage 2 picks everything up.
21
+ 7. Checklist, only if it exists. ADD: `bash .tfcore/utils/tf-split-brd.sh {App} --add-missing` appends rows for the new items; correct their class. MODIFY: set the rows derived from that item to `Needs re-verify` with a Remark saying what changed. REMOVE: set Status to `N/A`, `%` to `0` and the Remark to `removed {date}: <reason>`, and keep the rows; the check does not ask a removed row for an acceptance line, BRD item, mockup or detail entry. Where the mapping from an item to its rows is unclear, ask, do not guess. If the checklist does not exist yet, the next day-1 stage 2 picks everything up.
22
22
  8. If the amendment changed code-level flows, say that `*devguide {App} --update` is due after the next build.
23
23
  9. Close what this amendment fixed: `bash .tfcore/utils/tf-emit.sh --open-misses {App} --artifact-class doc` lists the open misses whose deficient artifact is a document. Close the ones this amendment resolves in one call: `bash .tfcore/utils/tf-fix-close.sh {App} --misses <id>,<id> --fix-cmd amend-docs --verdict Verified`. Where the mapping from a miss to this amendment is unclear, ask, do not guess. Without this a document miss can never close: `*fix-issues` and `*build-phase` close off checklist rows the verifier touched, which a BRD edit never is.
24
24
  10. Decide whether this amendment is a miss: should the original documents plainly have said this (a behaviour that was always required, or two sections that contradicted each other)? Then run `*log-miss {App} "<one sentence>"`. A change of mind or a feature decided today is scope arriving late, not a miss.
@@ -13,7 +13,7 @@ First, before anything else: `bash .tfcore/utils/tf-phase.sh start day1-brownfie
13
13
 
14
14
  ## Steps, in this order
15
15
 
16
- 1. `bash .tfcore/utils/tf-day1-files.sh {App} --size {S|M|L} --kind {app|library}` writes the size, the kind and the document paths into `core-config.yaml`; for Large it also writes `docs/{App}-Phases.md`, whose table you fill first: one row per phase, each within Medium, every screen in exactly one phase. Before rewriting any document that already exists, `bash .tfcore/utils/tf-day1-files.sh --archive <file> ...` moves it to `docs/OldDocs/`. Never write a `-v2` variant.
16
+ 1. `bash .tfcore/utils/tf-day1-files.sh {App} --size {S|M|L} --kind {app|library}` writes the size and the kind into `core-config.yaml` and nothing else; for Large it also writes `docs/{App}-Phases.md`, whose table you fill first: one row per phase, each within Medium, every screen in exactly one phase. Before rewriting any document that already exists, `bash .tfcore/utils/tf-day1-files.sh --archive <file> ...` moves it to `docs/OldDocs/`. Never write a `-v2` variant.
17
17
  2. `bash .tfcore/utils/tf-mockups-locate.sh` moves every mockup found elsewhere in the repository into `docs/mockups/`, the only mockup folder, and lists what is there.
18
18
  3. Architecture: `docs/{App}-Architecture.md` from `.tfcore/templates/v4custom/app-architecture-tmpl.md`, in the template's order, Status "Current". Harvest architecture material from the hint documents first, attributed; scan the code only for what they do not cover. Stack decisions come from the project files, citing the source per row; the Solution structure from the projects; the Component map from project references; the Data model from the migrations or entity classes; the Decisions log opens with "current stack as-is" and adds a `planned` row for every structural change the BRD calls for. Scan the instance fields for the dominant naming style; if none reaches about 80 percent, record the drift under Open questions. Quote every mermaid label; never use `end` as a node id.
19
19
  4. BRD, first half: `docs/{App}-BRD.md` from `app-brd-tmpl.md`: the header (App, Kind, Size, Stack answer set, Status, Date), Summary, Scope, Users and roles, and the Screens and flow table, one row per routed page found in the code. A dialog is a row under its parent screen with `on /route` in the Route column.
@@ -22,7 +22,7 @@ First, before anything else: `bash .tfcore/utils/tf-phase.sh start day1-brownfie
22
22
  Large: steps 4 to 6 run once per phase of the Phases table, phase 1 in `docs/{App}-BRD.md` and `docs/{App}-UIDesign.md`, phase n in `docs/{App}-Pn-BRD.md` (from `app-phase-brd-tmpl.md`: this phase's screens and requirements only, linking back to phase 1) and `docs/{App}-Pn-UIDesign.md` (from `app-phase-uidesign-tmpl.md`, the same way), each header carrying `Phase | n of m` and its own Size, ids running on across phases.
23
23
  7. Checklist: run `.tfcore/tasks/split-brd.md` for `{App}` (Large: with `--all-phases`, one checklist per phase). If a phased plan is among the harvested documents, split-brd migrates its statuses; the plan then moves to `docs/OldDocs/`.
24
24
  8. Coding Standards: `docs/{App}-Coding-Standards.md` from `app-coding-standards-tmpl.md`. "Standards applied" names `.tfcore/standards/coding-standards-core.md` and the stack file, plus the choices the stack file leaves open. Where the stack file leaves a naming convention open, it follows the drift scan of step 3: the dominant style if one reaches 80 percent, else the stack file's default; a drafting instruction overrides. "Project rules" holds only rules true of this project alone.
25
- 9. `bash .tfcore/utils/tf-day1-files.sh {App} --prefix {obj|none}` writes `.editorconfig`, `AGENTS.md` and `CLAUDE.md`.
25
+ 9. `bash .tfcore/utils/tf-day1-files.sh {App} --prefix {obj|none}` writes `.editorconfig`, `AGENTS.md` and `CLAUDE.md` and adds the document paths to `core-config.yaml`.
26
26
  10. `bash .tfcore/utils/tf-gitignore-audit.sh . --fix`. Read its output. Any untracking commands it prints go into your summary for the owner to run; agents never run git.
27
27
  11. Build once through `.tfcore/templates/v4custom/build-invocation-ladder.md`. A real compile error becomes one line under Known blockers; a wrong-rung error never does.
28
28
  12. UsageGuide: `docs/{App}-UsageGuide.md` from `app-usageguide-tmpl.md`. Test users: read the users table through the connection string in the configuration and list the existing accounts, one per role, marked as existing, with "ask owner" where a password is unknown; if the database is unreachable, list the intended accounts as planned. Create no user. The Execution guide from the code; one "How to test" section per screen in navigation order, naming the user, the steps, the expected result and the REQ ids.
@@ -1,12 +1,12 @@
1
1
  # day1-greenfield
2
2
 
3
- `*day1-greenfield {App}` starts a product that has no code. It runs in two stages with one owner review between them. Stage 1 produces the Architecture, the mockups and the BRD, then stops for review. `*day1-greenfield {App} --stage2` runs only after the owner has approved them and produces the checklist and the remaining documents. No mode skips the review: YOLO runs one stage to completion, never both.
3
+ `*day1-greenfield {App}` starts a product that has no code. It runs in two stages with one owner review between them. Stage 1 produces the Architecture, the mockups and the BRD, then stops for review. `*day1-greenfield {App} --stage2` runs only after the owner has approved them and produces the checklist and the remaining documents. No mode skips the review: YOLO runs one stage to completion, never both. The one exception is `*develop-end-to-end`, which runs both stages itself (`.tfcore/tasks/_develop-mode.md`).
4
4
 
5
5
  First, before anything else: `bash .tfcore/utils/tf-phase.sh start day1-greenfield {App}` prints the start time and marks the command running.
6
6
 
7
7
  ## Stage 1 — Architecture, mockups, BRD
8
8
 
9
- Ask once each, and nothing per section:
9
+ Ask once each, and nothing per section (in develop mode ask nothing: `_develop-mode.md` gives every answer):
10
10
 
11
11
  1. `{App}` if missing. PascalCase, no spaces.
12
12
  2. The concept: "Describe the app, any length: a sentence, paragraphs, bullets, half-baked notes, comparisons to other apps. You will edit the document afterwards." Read all of it. Never cap or summarise the owner's input.
@@ -16,7 +16,7 @@ Ask once each, and nothing per section:
16
16
 
17
17
  Then, in this order:
18
18
 
19
- 6. `bash .tfcore/utils/tf-day1-files.sh {App} --size {S|M|L} --kind {app|library}` writes the size, the kind and the document paths into `core-config.yaml`; for Large it also writes `docs/{App}-Phases.md`, whose table you fill first: one row per phase, each within Medium, every screen in exactly one phase. On a re-run of stage 1, first `bash .tfcore/utils/tf-day1-files.sh --archive docs/{App}-BRD.md docs/{App}-Architecture.md docs/{App}-UIDesign.md` moves the old copies to `docs/OldDocs/`. Never write a `-v2` variant; never ask merge-or-new.
19
+ 6. `bash .tfcore/utils/tf-day1-files.sh {App} --size {S|M|L} --kind {app|library}` writes the size and the kind into `core-config.yaml` and nothing else; for Large it also writes `docs/{App}-Phases.md`, whose table you fill first: one row per phase, each within Medium, every screen in exactly one phase. On a re-run of stage 1, first `bash .tfcore/utils/tf-day1-files.sh --archive docs/{App}-BRD.md docs/{App}-Architecture.md docs/{App}-UIDesign.md` moves the old copies to `docs/OldDocs/`. Never write a `-v2` variant; never ask merge-or-new.
20
20
  7. Architecture: `docs/{App}-Architecture.md` from `.tfcore/templates/v4custom/app-architecture-tmpl.md`, in the template's order. Stack decisions come from the answer set and the owner's answers, each row citing its source. Harvested notes carry into the sections attributed, never summarised away. Quote every mermaid label; never use `end` as a node id.
21
21
  8. BRD, first half: `docs/{App}-BRD.md` from `app-brd-tmpl.md`: the header (App, Kind, Size, Stack answer set, Status, Date), Summary, Scope, Users and roles, and the Screens and flow table. A dialog is a row under its parent screen with `on /route` in the Route column.
22
22
  9. Mockups: run `.tfcore/tasks/mockups.md` for `{App}`. It reads the Screens and flow table and produces `docs/{App}-UIDesign.md` and one `docs/mockups/<screen>.html` per screen. An app with no screens records "skipped — no UI".
@@ -33,7 +33,7 @@ Only with `--stage2`, and only when `docs/{App}-BRD.md` exists. Ask nothing.
33
33
  0. If the owner gave corrections at the review, apply them to the stage-1 documents first (ids never renumbered; `bash .tfcore/utils/tf-doc-check.sh --app {App}` must pass), count them, and after this run's record append a `review` record: `{"kind":"review","phase":"day1-review","reviewed_run_id":"<stage 1 started>","correction_run_id":"<this run's started>","corrections":N,"what":"<one sentence>"}` through `tf-emit.sh misses`.
34
34
  1. Checklist: run `.tfcore/tasks/split-brd.md` for `{App}` (Large: with `--all-phases`, one checklist per phase). The owner never types `*split-brd`.
35
35
  2. Coding Standards: `docs/{App}-Coding-Standards.md` from `app-coding-standards-tmpl.md`. "Standards applied" names `.tfcore/standards/coding-standards-core.md` and the stack file, plus the choices the stack file leaves open, taking that file's stated default for each. "Project rules" stays empty unless the concept demands a rule true of this project alone.
36
- 3. `bash .tfcore/utils/tf-day1-files.sh {App} --prefix {obj|none}` writes `.editorconfig`, `AGENTS.md` and `CLAUDE.md` from their templates.
36
+ 3. `bash .tfcore/utils/tf-day1-files.sh {App} --prefix {obj|none}` writes `.editorconfig`, `AGENTS.md` and `CLAUDE.md` from their templates and adds the document paths to `core-config.yaml`.
37
37
  4. UsageGuide: `docs/{App}-UsageGuide.md` from `app-usageguide-tmpl.md`. The Test users table lists the intended accounts, one per role, none created. The Execution guide is at roadmap level, marking commands that depend on projects not yet built. One "How to test" section per screen in navigation order, naming the user, the steps, the expected result and the REQ ids.
38
38
  5. `bash .tfcore/utils/tf-gitignore-audit.sh . --fix`. Read its output. Any untracking commands it prints go into your summary for the owner to run; agents never run git.
39
39
  6. Run the status gate; the run record carries `"mode":"stage2"`. The next command is `*build-phase {App}`.
@@ -0,0 +1,30 @@
1
+ # develop-end-to-end
2
+
3
+ `*develop-end-to-end {App} {brief}` takes a brief and develops a new app from it, unattended, to the point where the owner starts UAT: Day 1 (both stages), the build with its inline verify and fixes, and the handoff (UsageGuide, smoke checklist, test users). Greenfield only. No question, no confirmation, no review stop (`.tfcore/tasks/_develop-mode.md`). It commits and pushes after every phase and reports the time and tokens of each phase and of the whole build.
4
+
5
+ ## Before it starts
6
+
7
+ The owner has made a git repository for the app with a remote (GitHub) and scaffolded TechieFlow into it. `{brief}` is a file path or the brief's text; text is written to `docs/{App}-Brief.md` as given, never shortened.
8
+
9
+ Refuse, with one line saying what is missing, when: the folder is not a git repository, it has no remote, it already has a `docs/{App}-BRD.md` or source code (this command is greenfield only: use `*build-phase`), or a develop run is already active (`.tfcore/.session/develop.json` exists: offer `--resume`).
10
+
11
+ ## Steps
12
+
13
+ First: `bash .tfcore/utils/tf-phase.sh start develop-end-to-end {App}` prints the start time; keep it.
14
+
15
+ 1. Write the brief to `docs/{App}-Brief.md` when it was given as text. Then record this launch, before the run starts, so its record never overlaps the phases' own (`.tfcore/tasks/_metrics-emit-gate.md`): `tf-emit.sh runs` with `"cmd":"develop-end-to-end","mode":"launch"`, the start time and no rows, then `bash .tfcore/utils/tf-phase.sh end`. The phases record themselves; the Build Report adds them up.
16
+ 2. Start the supervisor detached, so it outlives this session, with the harness this session runs in:
17
+ `bash .tfcore/utils/tf-develop.sh . --app {App} --brief docs/{App}-Brief.md --harness <claude|opencode> --detach`
18
+ (`--model <id>` when the owner named one; `--no-push` only when the owner said not to push.)
19
+ 3. Wait until `.tfcore/.session/develop.log` shows `day1 started`, then say, in three lines: the run has started; it is followed in `.tfcore/.session/develop.log`; the result will be `docs/{App}-Build-Report.md`. End the turn. This session does none of the phases itself.
20
+
21
+ `*develop-end-to-end {App} --resume` continues a stopped run: `bash .tfcore/utils/tf-develop.sh . --resume --detach`. A phase already done is not run again.
22
+
23
+ ## What the run leaves
24
+
25
+ - One commit per phase (`TechieFlow develop-end-to-end: <phase> done — {App}`), pushed, and a last one with the final report.
26
+ - `docs/{App}-Build-Report.md`: built in how long, how long the agent worked, the tokens, one row per phase.
27
+ - `docs/metrics/develop-report.json`: the same figures for a comparison harness, per phase and in total, by model.
28
+ - The usual run records, misses and PROJECT-STATUS, written by each phase's own commands.
29
+
30
+ A usage limit pauses the run until the limit resets, then it resumes (`tf-goal.sh`). A phase the agent declares blocked stops the run with the blocker in PROJECT-STATUS "Known blockers" and in the report; `--resume` continues once the owner has dealt with it.
@@ -11,5 +11,6 @@ First: `bash .tfcore/utils/tf-phase.sh start fix-issues {App}` prints the start
11
11
  3. `bash .tfcore/utils/tf-build.sh` until PASS, then smoke every fixed screen with `tf-verify-screens.sh --screen …` (`.tfcore/tasks/_smoke-test-policy.md`); a screen that still fails re-enters step 2 for its rows. When the evidence was a failed CI run, `bash .tfcore/utils/tf-ci-repro.sh` must also print PASS; a local build with warm caches does not prove a CI fix. The report says the fix passed that reproduction and that the owner's next CI run is the final check, never that CI is fixed.
12
12
  4. Execute `.tfcore/tasks/verify-phase.md` inline, scoped to the touched rows, with its own step 0; rows of another phase's checklist are a second verify with `--phase N`. Only that run writes `Verified`.
13
13
  5. `bash .tfcore/utils/tf-verify-boot.sh stop`, then `bash .tfcore/utils/tf-fix-close.sh {App} --started <the step-0 time> --subagents <what was fanned out> --build <pass|fail>`: the fix-issues run record first, then one miss-fix per touched row with the verifier's verdict from the ledger. A row the verifier left below `Verified` stays open with that verdict on its miss-fix.
14
+ 5a. On a library (`metrics.project_type: library` in `core-config.yaml`), the documents the package ships describe this fix before the run ends, so one release carries the code and its documents: `.tfcore/tasks/handoff-phase.md` steps 1, 3, 3a and 4 (the UsageGuide, the HTMLs, the DevGuide with `--update`, the feedback replies), and `docs/{App}-AI-Reference.md` updated for every component or option this run changed. No separate handoff run record; this run's covers it. Write the log row's Phase cell as `fix-issues + handoff` (or `triage-and-fix + handoff`), so the gate offers the release, not `*handoff-phase`. Never tell the owner to run the handoff after the release.
14
15
  6. Run the status gate (`.tfcore/tasks/_status-update-gate.md`); the run record is the one step 5 wrote. When this run is the last step of a goal, write the sentinel (`.tfcore/tasks/_yolo-mode.md`).
15
16
  7. Report: evidence read, issues by kind, rows fixed and now `Verified`, rows still open and why, library gaps logged, and the next command from the gate.
@@ -11,7 +11,7 @@ First: `bash .tfcore/utils/tf-phase.sh start mockups {App}` prints the start tim
11
11
  3. Read the BRD's Screens and flow table: every row is a screen, with its route, roles and fields; a dialog row belongs to its parent screen. A screen that already has a mockup in `docs/mockups/` keeps it and is linked. An app with no screens records "skipped — no UI" in the UIDesign and stops.
12
12
  4. For each screen without a mockup (fan out one sub-agent per group of screens when there are many): decide the layout regions, map every region to a control, write `docs/mockups/<screen-slug>.html` with realistic placeholder data in the library's visual language, and fill the screen's section in `docs/{App}-UIDesign.md` from `.tfcore/templates/v4custom/app-uidesign-tmpl.md` — for phase n of a Large project, `docs/{App}-Pn-UIDesign.md` from `app-phase-uidesign-tmpl.md`, which carries that phase's screens only (mockup link, roles, regions-to-controls table, fields table, the dialogs it opens, the empty, loading and error states). One shell, one theme, one spacing system across the set.
13
13
  5. Make the set click through. The entry screen is the sign-in page (or onboarding, or the home page when there is neither). Every link and every navigation button goes to the mockup it names, written as `<a href="screen.html">` or a form action, never as a script (`onclick="location.href=…"` is refused: it dies in any viewer without scripts): sign in opens the home screen, logout returns to sign in, an "Add" button opens the add screen, a row opens its detail, every menu item leads to its screen. Every screen has a way out. A button that does not navigate shows a message with `onclick`. A dialog opens on its parent page. Sign-in needs no credentials: the button alone navigates.
14
- 6. Anchor every element the build must match with `data-testid`, using the names from the regions-to-controls table: the layout regions, every badge and status indicator, every icon button, every card, table and chart, and every value cell whose width matters. The verifier compares only anchored elements; a mockup without anchors is ungradeable.
14
+ 6. Anchor every element the build must match with `data-testid`, using the names from the regions-to-controls table: the layout regions, every badge and status indicator, every icon button, every card, table and chart, and every value cell whose width matters. The verifier compares only anchored elements; a mockup without anchors is ungradeable. A box drawn only in another state (an error, a newer version) carries `data-tf-state="<state>"`; a row of sample data a fresh app has none of carries `data-tf-sample`. Draw every menu and drop-down open, its box marked `data-tf-state="popup-open"` inside the anchored control that opens it, each item that opens a screen an `<a href="<that mockup>.html">`, and sample choices (branch names, past chats) `data-tf-sample`: verify opens each, clicks every item, and fails a missing one.
15
15
  7. `bash .tfcore/utils/tf-doc-check.sh --app {App}`: it refuses a broken link, an unreachable or dead-end screen, an inert button and an unanchored mockup. Fix every FAIL.
16
16
  8. When run on its own (not inside a day-1 task), run the status gate (`.tfcore/tasks/_status-update-gate.md`) with `"cmd":"mockups"`.
17
17
  9. Report: screens mocked, screens kept, controls used, library gaps logged, and the files written.
@@ -8,7 +8,7 @@ First: `bash .tfcore/utils/tf-phase.sh start triage-and-fix {App}` prints the st
8
8
 
9
9
  1. Compare. `bash .tfcore/utils/tf-verify-list.sh {App} all`, `tf-verify-boot.sh start`, then `tf-verify-screens.sh --list … --base <url>` and `tf-mockup-parity.sh --base <url> --screen … --json-out tests/.artifacts/verify/parity.json` over every screen. Every screen that renders empty, looks broken or drifts from its mockup is an issue, added to the owner's evidence with its screenshot.
10
10
  2. Triage. `.tfcore/tasks/triage-issues.md` steps 3 and 4 on the combined issue list, then `tf-triage.sh {App} close --started <the step-0 time> --cmd fix-issues`.
11
- 3. Fix. `.tfcore/tasks/fix-issues.md` steps 2 to 5 on the demoted rows (the builders, the build, the smoke, the verify, `tf-fix-close.sh`).
11
+ 3. Fix. `.tfcore/tasks/fix-issues.md` steps 2 to 5 on the demoted rows (the builders, the build, the smoke, the verify, `tf-fix-close.sh`), with `--cmd triage-and-fix` on `tf-fix-close.sh`: it writes this run's record, filed under this command, around the verify it chained. On a library (`metrics.project_type: library`), `fix-issues.md` step 5a too: the shipped documents are brought up to date now, before the gate.
12
12
  4. Metrics. Execute `.tfcore/tasks/metrics-report.md`.
13
- 5. Run the status gate (`.tfcore/tasks/_status-update-gate.md`) with `"cmd":"triage-and-fix"` and the rows touched. When this run is the last step of a goal, write the sentinel (`.tfcore/tasks/_yolo-mode.md`).
13
+ 5. Run the status gate (`.tfcore/tasks/_status-update-gate.md`); the run record is the one step 3 wrote, so do not emit another (one over the same window is refused). When this run is the last step of a goal, write the sentinel (`.tfcore/tasks/_yolo-mode.md`).
14
14
  6. Summary, one short section per step: screens compared and what the comparison found; root causes found and logged, each with its miss id and discovery cost; fixes made and logged, each with its verdict and fix cost; rows re-verified by status; metrics refreshed, the report's path; the next command from the gate.
@@ -1,6 +1,6 @@
1
1
  # verify-phase
2
2
 
3
- `*verify {scope} {App}` proves every checklist row in scope against the running application and writes the verdicts. Scope is `ui`, `functional`, `all` or a list of ids; a caller that chains this task passes its scope and nothing is asked. YOLO is its default mode. The application runs on this machine, never on a cloud host. This task never edits source, the BRD or the mockups. It writes tests under `tests/verify/`, evidence under `tests/.artifacts/verify/`, the ledger `docs/.last-verify.json`, and the checklist cells through the verdict script only: a `Verified` written by hand is refused by the hook.
3
+ `*verify {scope} {App} [--phase N|all]` proves every checklist row in scope against the running application and writes the verdicts. Scope is `ui`, `functional`, `all` or a list of ids; a caller that chains this task passes its scope and nothing is asked. YOLO is its default mode. The application runs on this machine, never on a cloud host. This task never edits source, the BRD or the mockups. It writes tests under `tests/verify/`, evidence under `tests/.artifacts/verify/`, the ledger `docs/.last-verify.json`, and the checklist cells through the verdict script only: a `Verified` written by hand is refused by the hook.
4
4
 
5
5
  First: `bash .tfcore/utils/tf-phase.sh start verify-phase {App}` prints the start time; keep it. Then `bash .tfcore/utils/tf-yolo.sh on --source verify` unless YOLO is already on.
6
6
 
@@ -10,11 +10,11 @@ Applied to every row in this order; the first that fails is the row's verdict.
10
10
 
11
11
  | Check | Question | Tool |
12
12
  |---|---|---|
13
- | build | does the application build and boot | `tf-verify-boot.sh` |
13
+ | build | does the application build and boot, and does its code reference every package the Architecture's `Required packages:` line lists | `tf-verify-boot.sh`; the verdict runs `tf-stack-check.sh` itself |
14
14
  | acceptance | does the test carrying the row's id pass | `tf-verify-tests.sh` |
15
15
  | render | does every control the mockup anchors show something, no header-only table, no blank page, no error | `tf-verify-screens.sh` |
16
16
  | assets | did every stylesheet and script the screen declares arrive | `tf-assets.sh` |
17
- | visual | nothing overlaps, nothing clipped or off-screen, no sideways scroll, a stylesheet loaded | `tf-verify-screens.sh` |
17
+ | visual | nothing overlaps, nothing clipped or off-screen, no sideways scroll, a stylesheet loaded, text readable (3.0 : 1) in light and in dark mode, no light panel in dark mode | `tf-verify-screens.sh` |
18
18
  | mockup | does the built screen carry the structure its mockup draws | `tf-mockup-parity.sh` |
19
19
  | speed | within the row's `perf-budget:` line, only when the row declares one | `tf-perf.sh`, `tf-perf-grade.sh` |
20
20
 
@@ -22,22 +22,23 @@ Standards has no script yet and is never listed as run. A row is `Verified` only
22
22
 
23
23
  ## Read
24
24
 
25
- The phase's checklist, UIDesign and `docs/mockups/` (`appPhase` in `core-config.yaml` names the phase); the UsageGuide's test users.
25
+ The phase's checklist, UIDesign and `docs/mockups/`; the UsageGuide's test users. The phase is `--phase N` when given, else `appPhase` in `core-config.yaml`. `--phase all` grades every phase's checklist in turn, phase 1 first: `tf-verify-list.sh {App} {scope} --phase all` prints `PHASES 1 2 3`, then steps 1 and 4 to 6 run once per phase, each with `--phase <n>`, on one boot (steps 2, 3 and 7 once). Each pass writes only its own phase's checklist; the ledger and telemetry name the phase.
26
26
 
27
27
  ## Steps
28
28
 
29
- 1. `bash .tfcore/utils/tf-verify-list.sh {App} {scope}` prints the rows to grade with their acceptance line, screen, route, mockup and perf budget, the screens to drive, and the test users; it writes `tests/.artifacts/verify/list.json`. Every row in scope is graded whatever its status; only N/A is skipped, so a `Verified` row is re-confirmed and an old `Done` claim is tested. NOTHING means run the status gate and stop. A row without an acceptance line cannot be graded: fix the checklist first. A dialog is listed under its parent screen and checked there; never give a dialog a route so it can be tested.
29
+ 1. `bash .tfcore/utils/tf-verify-list.sh {App} {scope} --phase <n>` prints the rows to grade with their acceptance line, screen, route, mockup and perf budget, the screens to drive, and the test users; it writes `tests/.artifacts/verify/list.json`. Every row in scope is graded whatever its status; only N/A is skipped, so a `Verified` row is re-confirmed and an old `Done` claim is tested. NOTHING means run the status gate and stop. A row without an acceptance line cannot be graded: fix the checklist first. A dialog is listed under its parent screen and checked there; never give a dialog a route so it can be tested.
30
30
  2. `bash .tfcore/utils/tf-verify-env.sh` prints READY, or the one command the owner must run once.
31
- 3. `bash .tfcore/utils/tf-verify-boot.sh start` boots the head it finds (`--head` or `--project` when there are several) and prints BOOTED with a URL, or a CDP URL for an embedded-browser desktop head, which the browser tools attach to. A dependent service (database, API) is yours to start first, from its own configuration, in order. NONE `kind=build-error`: every row fails the build check; go to step 6. NONE `kind=host` or `kind=no-driver` (an Android, iOS or Mac head): go to step 6 as well; those rows are written "not verified" with the reason. Never a static-only pass, never "the owner should start it".
32
- 4. Tests. Every UI and functional row has one test whose title starts with its id, written from the acceptance line, black box, under `tests/verify/`; an NFR row without a screen has a unit test carrying its id in the test project, or is not observable. Reuse a test that exists. Fan out one test-writer sub-agent per screen group in one turn, each with its rows, their acceptance lines, the URL and the test user; a test writer touches no source. Sign in as a UsageGuide test user (`.tfcore/tasks/_smoke-test-policy.md`).
31
+ 3. `bash .tfcore/utils/tf-verify-boot.sh start` boots the head it finds (`--head` or `--project` when there are several) and prints BOOTED with a URL, or a CDP URL for an embedded-browser desktop head, which the browser tools attach to. A dependent service (database, API) is yours to start first, from its own configuration, in order. NONE `kind=build-error`: every row fails the build check; go to step 6. NONE `kind=host` or `kind=no-driver` (an Android or iOS head): go to step 6 as well; those rows are written "not verified" with the reason. A Mac desktop head is booted on a Mac and prints `mode=appium` with the Appium address; when a project also has a web head the default stays `web`, so pass `--head maccatalyst` when the owner asked for the Mac head. A Mac head whose screens are drawn in a web view is checked without control names (blank, error bar, overlap, screenshot), and its rows say the names were not measured. Never a static-only pass, never "the owner should start it".
32
+ 4. Tests. Every UI and functional row has one test whose title starts with its id, written from the acceptance line, black box, under `tests/verify/`; an NFR row without a screen has a unit test carrying its id in the test project, or is not observable. Reuse a test that exists. A screen whose mockup draws data a fresh app lacks gets `tests/verify/seed/<mockup name>.sh`, which puts the running app (`$TF_BASE`) in the drawn state; `tf-verify-screens.sh` runs it first. Fan out one test-writer sub-agent per screen group in one turn, each with its rows, their acceptance lines, the URL and the test user; a test writer touches no source. Sign in as a UsageGuide test user (`.tfcore/tasks/_smoke-test-policy.md`).
33
33
  5. Run the checks, with `<url>` from step 3 and, on a signed-in app, the session cookie from the test login for the tools that take `--cookie`:
34
34
  - `bash .tfcore/utils/tf-verify-tests.sh --base <url>`
35
- - `bash .tfcore/utils/tf-verify-screens.sh --list tests/.artifacts/verify/list.json --base <url>` (or `--cdp <url>`; `--login-path /login --user … --password …` when the app has a sign-in page)
35
+ - `bash .tfcore/utils/tf-verify-screens.sh --list tests/.artifacts/verify/list.json --base <url>` (or `--cdp <url>`; or `--appium <url>` for a Mac desktop head, whose anchors are the controls' accessibility identifiers and whose mockup and asset checks do not run; `--login-path /login --user … --password …` when the app has a sign-in page)
36
36
  - `bash .tfcore/utils/tf-assets.sh --base <url> --paths "<every screen route, comma-separated>" --json-out tests/.artifacts/verify/assets.json`
37
- - `bash .tfcore/utils/tf-mockup-parity.sh --base <url> --screen <name>=<route> … --json-out tests/.artifacts/verify/parity.json` (one `--screen` per screen with a mockup; skip on a project with none)
37
+ - `bash .tfcore/utils/tf-mockup-parity.sh --base <url> --screen <name>=<route> … --json-out tests/.artifacts/verify/parity.json` (the `Mockup parity:` line step 1 printed; a screen whose route takes a value is added with the value filled in, `--screen <mockup name>=/processes/6/run`; skip on a project with no mockups). Screens and parity both run in light and, when the app has one, dark mode (`--themes` forces it). A driven screen with a mockup and no comparison fails the mockup check
38
38
  - for each row with a `perf-budget:` line: `bash .tfcore/utils/tf-perf.sh --base <url> --paths "<its screen's route>" --levels <its concurrency> --requests 24 --build-config <Release or Debug, as booted> --json-out tests/.artifacts/verify/perf/<REQ>.json`
39
+ - for the rows ending `zoom-check:` (a desktop head drawn in a web view; the stack's standard says how it zooms), on each booted head: `bash .tfcore/utils/tf-verify-zoom.sh --appium <url>` (or `--cdp <url>`) `--leave 3 --json-out tests/.artifacts/verify/zoom/<the keys row>.json`; stop and start the head; then the same with `--expect-restored 1.3 --json-out tests/.artifacts/verify/zoom/<the kept row>.json`
39
40
  Open every failing screenshot under `tests/.artifacts/verify/screens/` and look at it before step 6.
40
- 6. `bash .tfcore/utils/tf-verify-verdict.sh {App} --apply --started <the step-0 time>` applies the seven checks in order, writes the ledger and rewrites the Status, % and Remarks cells of every graded row, and prints the verdict table. Then `bash .tfcore/utils/tf-verify-emit.sh {App} --started <the step-0 time>` appends the gate records, one miss per newly failing row, and this run's record.
41
+ 6. `bash .tfcore/utils/tf-verify-verdict.sh {App} --apply --started <the step-0 time>` applies the seven checks in order, writes the ledger and rewrites the Status, % and Remarks cells of every graded row, and prints the verdict table. Both read the phase from `list.json`. Then `bash .tfcore/utils/tf-verify-emit.sh {App} --started <the step-0 time>` appends the gate records, one miss per newly failing row, and this run's record.
41
42
  7. `bash .tfcore/utils/tf-verify-boot.sh stop`.
42
43
  8. Run the status gate (`.tfcore/tasks/_status-update-gate.md`); its run record is the one step 6 wrote. A head that could not be driven is named under "Known blockers". When this run is the last step of a goal, write the sentinel (`.tfcore/tasks/_yolo-mode.md`). When the verifier was chained by a build or a fix, that caller runs the gate.
43
- 9. Report: what was booted (head, rung, URL); rows by verdict; the first-failing-check counts; screens driven and screenshots; the parity coverage line (screens graded, failed, ungradeable, without a mockup); the speed lines only for rows that carry a budget; every not-verified row with its reason; the evidence folder; the next command from the gate.
44
+ 9. Report: what was booted (head, rung, URL); rows by verdict, per phase; the first-failing-check counts; screens driven and screenshots; the parity coverage line (screens graded, failed, ungradeable, without a mockup); the speed lines only for rows that carry a budget; every not-verified row with its reason; the evidence folder; the next command from the gate.
@@ -93,7 +93,7 @@ So `tf-emit.sh` detects it and injects it. **Never write `harness` into an emit
93
93
 
94
94
  | Field | Type | Values / notes |
95
95
  |---|---|---|
96
- | `cmd` | string | `day1-brownfield` \| `day1-greenfield` \| `split-brd` \| `mockups` \| `build-phase` \| `verify-phase` \| `fix-issues` \| `triage-issues` \| `log-miss` \| `devguide` \| `productguide` \| `handoff-phase` \| `refresh-status` \| `amend-docs` \| `deploy-checklist` (added 2026-09-06) \| `metrics-report` \| `generate-html` \| `render-workflow-docs` \| `triage-and-fix` \| `framework-reset` (framework maintenance, D-19; all five added 2026-09-07, after FR-37's check was built and found 27 records already carrying a value the schema did not list). A value outside this list is a defect in the emitting task, not in the reader: the reader keeps it. |
96
+ | `cmd` | string | `day1-brownfield` \| `day1-greenfield` \| `split-brd` \| `mockups` \| `build-phase` \| `verify-phase` \| `fix-issues` \| `triage-issues` \| `log-miss` \| `devguide` \| `productguide` \| `handoff-phase` \| `refresh-status` \| `amend-docs` \| `deploy-checklist` (added 2026-09-06) \| `metrics-report` \| `generate-html` \| `render-workflow-docs` \| `triage-and-fix` \| `develop-end-to-end` (added 2026-10-08: only its launch; each phase records itself) \| `framework-reset` (framework maintenance, D-19; all five added 2026-09-07, after FR-37's check was built and found 27 records already carrying a value the schema did not list). A value outside this list is a defect in the emitting task, not in the reader: the reader keeps it. |
97
97
  | `mode` | string \| null | `build` \| `fix`. `build-phase` already distinguishes these (FIX mode) — capture it; the ratio is the rework metric. `null` for commands with no mode. |
98
98
  | `started` | string | ISO-8601 UTC. When the task began — the timestamp you noted at step 0, not "now minus a guess". |
99
99
  | `ended` | string | ISO-8601 UTC. Normally equal to `ts`. |
@@ -105,6 +105,7 @@ So `tf-emit.sh` detects it and injects it. **Never write `harness` into an emit
105
105
  | `build_result` | string \| null | `pass` \| `fail` \| `not-run`. |
106
106
  | `harness` | string \| null | Injected by `tf-emit.sh` (§1). **Do not emit it yourself.** |
107
107
  | `yolo` | bool | Whether YOLO / goal mode was on for this run (D-12, FR-35; added 2026-09-05). Written by the task from the flag file at the status gate; wired into every task in Session 4c. |
108
+ | `app_phase` | int | **`verify-phase` only, added 2026-10-11 (Sevak TF-005).** The phase whose checklist this pass graded: 1 is `docs/<App>-Checklist.md`, n is `docs/<App>-P<n>-Checklist.md`. Absent on older records, which graded `appPhase`. One `*verify --phase all` writes one run record per phase, each starting where the one before ended (§2.7b). |
108
109
 
109
110
  ---
110
111
 
@@ -236,6 +237,7 @@ Failure:
236
237
  | `gates_run` | string[] | Which gates actually executed this pass — subset of `["build","acceptance","render","assets","visual","mockup-parity","perf","standards"]`, in execution order. A gate absent here did **not** run and cannot be credited or blamed. For `perf`, `assets` and `mockup-parity` this carries real weight: see §3.5. |
237
238
  | `failure_class` | string \| null | Controlled vocabulary only, §3.3. `null` on a pass. |
238
239
  | `prior_verdict` | string \| null | The Status cell value **before** this run wrote over it. `null` if the row is new. |
240
+ | `app_phase` | int | The phase whose checklist holds `req_id` and was written by this pass (added 2026-10-11, Sevak TF-005); see §2. Absent on older records. |
239
241
  | `proof` | string \| null | Optional, §3.4. |
240
242
 
241
243
  ### 3.1 `attempt` — the field first-pass rate depends on entirely
@@ -281,7 +283,9 @@ Get this right; everything else on the record is secondary.
281
283
 
282
284
  ### 3.3 `failure_class` — controlled vocabulary, no free text
283
285
 
284
- `blank-data` · `zero-rows` · `overlap` · `clipped` · `offscreen` · `slow-ttfb` · `slow-load` · `timeout` · `exception` · `assert-fail` · `naming` · `build-error` · `missing-asset` · `mockup-drift` · `other`
286
+ `blank-data` · `zero-rows` · `overlap` · `clipped` · `offscreen` · `low-contrast` · `light-surface` · `slow-ttfb` · `slow-load` · `timeout` · `exception` · `assert-fail` · `naming` · `build-error` · `missing-asset` · `mockup-drift` · `other`
287
+
288
+ `low-contrast` and `light-surface` (added 2026-10-07, Lekhak TF-024) are visual findings: text below the contrast floor against the colour painted behind it, in either theme, and a large light panel in dark mode. The ratio and the colours stay in the run JSON and the Remark.
285
289
 
286
290
  `missing-asset` (added 2026-08-31) names an asset the page **declared** and never received — a 404, a non-200, or a 200 with an empty body on a `<link rel=stylesheet>` or `<script src>`. It says only that; **never** the URL, which is a file path inside the app and is exactly what constraint 7 keeps out of these streams. The path belongs in the checklist Remark a human reads.
287
291