@imfusion/web-ui 0.5.1-dev.41.g45c10e85 → 0.5.1-dev.45.gf3889fa8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,7 +69,7 @@ skill.
69
69
  | `/imf-web-ui-components` | Component reference — what exists and how it's meant to be used. |
70
70
  | `/imf-web-ui-ux` | UX guidance for building interfaces with the library. |
71
71
  | `/imf-web-ui-conventions` | The frontend conventions baseline, including the sanctioned styling seams. |
72
- | `/imf-web-ui-audit` | Read-only health check for a consumer project, with a reviewable audit report. |
72
+ | `/imf-web-ui-audit` | Read-only health check for a consumer project, ending in a plan you approve. |
73
73
  | `/imf-web-ui-update` | Update the library, skills, and optional hooks, then verify before committing. |
74
74
 
75
75
  Start at `/imf-web-ui` — it routes to the rest. Storybook's **User Guide → AI Agents** page covers the whole family.
@@ -1,6 +1,35 @@
1
- export declare const SUPPORTED_LANGUAGES: readonly ["tsx", "ts", "css", "shell", "json"];
2
- declare const LANGUAGE_ALIASES: readonly ["typescript", "bash", "sh", "zsh", "shellscript", "scss"];
3
- export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number] | (typeof LANGUAGE_ALIASES)[number];
1
+ declare const LANGUAGE_DEFINITIONS: {
2
+ readonly tsx: {
3
+ readonly aliases: readonly ["typescriptreact"];
4
+ };
5
+ readonly ts: {
6
+ readonly aliases: readonly ["typescript"];
7
+ };
8
+ readonly css: {
9
+ readonly aliases: readonly ["scss"];
10
+ };
11
+ readonly shell: {
12
+ readonly aliases: readonly ["bash", "sh", "zsh", "shellscript"];
13
+ };
14
+ readonly json: {
15
+ readonly aliases: readonly [];
16
+ };
17
+ readonly cpp: {
18
+ readonly aliases: readonly ["c++", "cc", "cxx", "hpp"];
19
+ };
20
+ readonly python: {
21
+ readonly aliases: readonly ["py"];
22
+ };
23
+ readonly cmake: {
24
+ readonly aliases: readonly [];
25
+ };
26
+ };
27
+ export declare const SUPPORTED_LANGUAGES: string[];
28
+ type CanonicalLanguage = keyof typeof LANGUAGE_DEFINITIONS;
29
+ type LanguageAlias = {
30
+ [Language in CanonicalLanguage]: (typeof LANGUAGE_DEFINITIONS)[Language]["aliases"][number];
31
+ }[CanonicalLanguage];
32
+ export type SupportedLanguage = CanonicalLanguage | LanguageAlias;
4
33
  export declare function isSupportedLanguage(value: string): value is SupportedLanguage;
5
34
  export declare function highlightToHtml(code: string, language: SupportedLanguage): string;
6
35
  export {};
@@ -0,0 +1,7 @@
1
+ import { HighlightTokenClass, TokenRange } from '@tanstack/highlight/core';
2
+ export type LanguagePattern = {
3
+ className: HighlightTokenClass;
4
+ group?: number;
5
+ regex: RegExp;
6
+ };
7
+ export declare function collectPatternRanges(code: string, patterns: readonly LanguagePattern[]): TokenRange[];
@@ -0,0 +1 @@
1
+ export declare const cmake: import('@tanstack/highlight/core').LanguageDefinition<"cmake">;
@@ -0,0 +1 @@
1
+ export declare const cpp: import('@tanstack/highlight/core').LanguageDefinition<"cpp">;
@@ -2,63 +2,192 @@ import { a as e, t } from "../meta-B8C51eyL.js";
2
2
  import { i as n, n as r, r as i } from "../code-qBbqAHK-.js";
3
3
  import { useMemo as a } from "react";
4
4
  import { jsx as o } from "react/jsx-runtime";
5
- import { createHighlighter as s, renderNodesToHtml as c, renderTokens as l } from "@tanstack/highlight/core";
6
- import { css as u } from "@tanstack/highlight/languages/css";
7
- import { json as d } from "@tanstack/highlight/languages/json";
8
- import { plaintext as f } from "@tanstack/highlight/languages/plaintext";
9
- import { shell as p } from "@tanstack/highlight/languages/shell";
10
- import { ts as m } from "@tanstack/highlight/languages/ts";
11
- import { tsx as h } from "@tanstack/highlight/languages/tsx";
12
- //#region src/integrations/code-highlight/highlighter.ts
13
- var g = [
14
- "tsx",
15
- "ts",
16
- "css",
17
- "shell",
18
- "json"
19
- ], _ = [
20
- "typescript",
21
- "bash",
22
- "sh",
23
- "zsh",
24
- "shellscript",
25
- "scss"
26
- ], v = [...g, ..._];
27
- function y(e) {
28
- return v.includes(e);
5
+ import { createHighlighter as s, defineLanguage as c, renderNodesToHtml as l, renderTokens as u } from "@tanstack/highlight/core";
6
+ import { css as d } from "@tanstack/highlight/languages/css";
7
+ import { json as f } from "@tanstack/highlight/languages/json";
8
+ import { plaintext as p } from "@tanstack/highlight/languages/plaintext";
9
+ import { python as m } from "@tanstack/highlight/languages/python";
10
+ import { shell as h } from "@tanstack/highlight/languages/shell";
11
+ import { ts as g } from "@tanstack/highlight/languages/ts";
12
+ import { tsx as _ } from "@tanstack/highlight/languages/tsx";
13
+ //#region src/integrations/code-highlight/language-patterns.ts
14
+ function v(e, t) {
15
+ let n = [], r = new Uint8Array(e.length);
16
+ for (let i of t) {
17
+ let t = new RegExp(i.regex.source, i.regex.flags.includes("g") ? i.regex.flags : `${i.regex.flags}g`), a;
18
+ for (; (a = t.exec(e)) !== null;) {
19
+ let e = i.group == null ? a[0] : a[i.group];
20
+ if (e === "") {
21
+ a[0] === "" && (t.lastIndex += 1);
22
+ continue;
23
+ }
24
+ let o = i.group == null ? 0 : a[0].indexOf(e), s = a.index + o, c = s + e.length;
25
+ if (s < c) {
26
+ let e = !1;
27
+ for (let t = s; t < c; t += 1) if (r[t]) {
28
+ e = !0;
29
+ break;
30
+ }
31
+ e || (n.push({
32
+ start: s,
33
+ end: c,
34
+ className: i.className
35
+ }), r.fill(1, s, c));
36
+ }
37
+ }
38
+ }
39
+ return n;
40
+ }
41
+ //#endregion
42
+ //#region src/integrations/code-highlight/languages/cmake.ts
43
+ var y = [
44
+ {
45
+ className: "string",
46
+ regex: /"(?:\\.|[^"\\\n])*"|\[(=*)\[[\s\S]*?\]\1\]/g
47
+ },
48
+ {
49
+ className: "comment",
50
+ regex: /#[^\n]*/g
51
+ },
52
+ {
53
+ className: "variable",
54
+ regex: /\$(?:ENV|CACHE)?\{[^}\n]+\}/g
55
+ },
56
+ {
57
+ className: "keyword",
58
+ regex: /\b(?:break|continue|else|elseif|endforeach|endfunction|endif|endmacro|endwhile|if|include|macro|return|while)\b/gi
59
+ },
60
+ {
61
+ className: "literal",
62
+ regex: /\b(?:FALSE|IGNORE|NOTFOUND|NO|OFF|ON|TRUE|YES)\b/gi
63
+ },
64
+ {
65
+ className: "function",
66
+ regex: /\b[A-Za-z_][\w-]*(?=\s*\()/g
67
+ },
68
+ {
69
+ className: "number",
70
+ regex: /\b\d+(?:\.\d+)?\b/g
71
+ },
72
+ {
73
+ className: "operator",
74
+ regex: /(?:LESS_EQUAL|GREATER_EQUAL|STREQUAL|IN_LIST|MATCHES|EQUAL|LESS|GREATER|AND|OR|NOT|[=<>+*/!-])/gi
75
+ }
76
+ ], b = c({
77
+ name: "cmake",
78
+ tokenize(e) {
79
+ return v(e, y);
80
+ }
81
+ }), x = [
82
+ {
83
+ className: "comment",
84
+ regex: /\/\/[^\n]*|\/\*[\s\S]*?\*\//g
85
+ },
86
+ {
87
+ className: "string",
88
+ regex: /"(?:\\.|[^"\\\n])*"|'(?:\\.|[^'\\\n])*'/g
89
+ },
90
+ {
91
+ className: "meta",
92
+ regex: /^[ \t]*#[ \t]*(?:include|define|if|ifdef|ifndef|elif|else|endif|pragma|undef|error|warning)\b/gm
93
+ },
94
+ {
95
+ className: "keyword",
96
+ regex: /\b(?:alignas|alignof|and|asm|atomic_cancel|atomic_commit|atomic_noexcept|auto|bitand|bitor|break|case|catch|co_await|co_return|co_yield|class|compl|concept|const|consteval|constexpr|constinit|const_cast|continue|default|delete|do|dynamic_cast|else|enum|explicit|export|extern|for|friend|goto|if|inline|mutable|namespace|new|noexcept|not|operator|or|private|protected|public|reflexpr|register|reinterpret_cast|requires|return|sizeof|static|static_assert|static_cast|struct|switch|synchronized|template|this|thread_local|throw|try|typedef|typeid|typename|union|using|virtual|volatile|while|xor)\b/g
97
+ },
98
+ {
99
+ className: "type",
100
+ regex: /\b(?:bool|char|char8_t|char16_t|char32_t|double|float|int|long|short|signed|unsigned|void|wchar_t)\b/g
101
+ },
102
+ {
103
+ className: "literal",
104
+ regex: /\b(?:NULL|nullptr|true|false)\b/g
105
+ },
106
+ {
107
+ className: "function",
108
+ regex: /\b[A-Za-z_]\w*(?=\s*\()/g
109
+ },
110
+ {
111
+ className: "number",
112
+ regex: /\b(?:0[xX][\da-fA-F']+|0[bB][01']+|\d[\d']*(?:\.\d[\d']*)?(?:[eE][+-]?\d[\d']*)?[uUlLfF]*)\b/g
113
+ },
114
+ {
115
+ className: "operator",
116
+ regex: /(?:<<=?|>>=?|->\*|->|\+\+|--|&&|\|\||==|!=|<=|>=|::|\+=|-=|\*=|\/=|%=|&=|\|=|\^=|[-+*/%=&|^!<>?:~])/g
117
+ }
118
+ ], S = c({
119
+ name: "cpp",
120
+ aliases: [
121
+ "c++",
122
+ "cc",
123
+ "cxx",
124
+ "hpp"
125
+ ],
126
+ tokenize(e) {
127
+ return v(e, x);
128
+ }
129
+ }), C = {
130
+ tsx: { aliases: ["typescriptreact"] },
131
+ ts: { aliases: ["typescript"] },
132
+ css: { aliases: ["scss"] },
133
+ shell: { aliases: [
134
+ "bash",
135
+ "sh",
136
+ "zsh",
137
+ "shellscript"
138
+ ] },
139
+ json: { aliases: [] },
140
+ cpp: { aliases: [
141
+ "c++",
142
+ "cc",
143
+ "cxx",
144
+ "hpp"
145
+ ] },
146
+ python: { aliases: ["py"] },
147
+ cmake: { aliases: [] }
148
+ }, w = Object.keys(C), T = [...w, ...Object.values(C).flatMap(({ aliases: e }) => e)];
149
+ function E(e) {
150
+ return T.includes(e);
151
+ }
152
+ function D(e, t) {
153
+ return e.aliases.includes(t);
29
154
  }
30
- var b = {
31
- shellscript: "shell",
32
- scss: "css"
33
- }, x = s({
155
+ function O(e) {
156
+ for (let [t, n] of Object.entries(C)) if (D(n, e)) return t;
157
+ return e;
158
+ }
159
+ var k = s({
34
160
  languages: [
161
+ _,
162
+ g,
163
+ d,
35
164
  h,
165
+ f,
166
+ S,
36
167
  m,
37
- u,
38
- p,
39
- d,
40
- f
168
+ b,
169
+ p
41
170
  ],
42
171
  fallbackLanguage: "plaintext"
43
172
  });
44
- function S(e, t) {
45
- let n = b[t] ?? t, { tokens: r } = x.tokenize(e, { lang: n });
46
- return c(l(r));
173
+ function A(e, t) {
174
+ let n = O(t), { tokens: r } = k.tokenize(e, { lang: n });
175
+ return l(u(r));
47
176
  }
48
177
  //#endregion
49
178
  //#region src/integrations/code-highlight/code-highlight.tsx
50
- var C = /* @__PURE__ */ e({
51
- Block: () => T,
52
- Inline: () => E,
179
+ var j = /* @__PURE__ */ e({
180
+ Block: () => N,
181
+ Inline: () => P,
53
182
  InlineLink: () => n
54
183
  });
55
- function w(e, t) {
184
+ function M(e, t) {
56
185
  return a(() => {
57
- if (!(t == null || e === "")) return /* @__PURE__ */ o("span", { dangerouslySetInnerHTML: { __html: S(e, t) } });
186
+ if (!(t == null || e === "")) return /* @__PURE__ */ o("span", { dangerouslySetInnerHTML: { __html: A(e, t) } });
58
187
  }, [e, t]);
59
188
  }
60
- function T({ language: e, children: t, ...n }) {
61
- let i = w(t, e);
189
+ function N({ language: e, children: t, ...n }) {
190
+ let i = M(t, e);
62
191
  return /* @__PURE__ */ o(r, {
63
192
  ...n,
64
193
  language: e,
@@ -66,19 +195,19 @@ function T({ language: e, children: t, ...n }) {
66
195
  children: t
67
196
  });
68
197
  }
69
- T.displayName = "Code.Block";
70
- function E({ language: e, children: t, ...n }) {
71
- let r = w(t, e);
198
+ N.displayName = "Code.Block";
199
+ function P({ language: e, children: t, ...n }) {
200
+ let r = M(t, e);
72
201
  return /* @__PURE__ */ o(i, {
73
202
  ...n,
74
203
  highlighted: r,
75
204
  children: t
76
205
  });
77
206
  }
78
- E.displayName = "Code.Inline";
207
+ P.displayName = "Code.Inline";
79
208
  //#endregion
80
209
  //#region src/integrations/code-highlight/code-highlight.meta.ts
81
- var D = {
210
+ var F = {
82
211
  name: "CodeHighlight",
83
212
  category: t.display,
84
213
  status: "experimental",
@@ -94,4 +223,4 @@ var D = {
94
223
  }
95
224
  };
96
225
  //#endregion
97
- export { C as Code, g as SUPPORTED_LANGUAGES, D as codeHighlightMeta, y as isSupportedLanguage };
226
+ export { j as Code, w as SUPPORTED_LANGUAGES, F as codeHighlightMeta, E as isSupportedLanguage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imfusion/web-ui",
3
- "version": "0.5.1-dev.41.g45c10e85",
3
+ "version": "0.5.1-dev.45.gf3889fa8",
4
4
  "description": "The official Web UI component library for ImFusion web apps",
5
5
  "author": "ImFusion GmbH",
6
6
  "license": "UNLICENSED",
@@ -4,49 +4,76 @@ description:
4
4
  "Read-only health check for an ImFusion frontend against the conventions baseline. Audit the full project or any topic,
5
5
  including library-setup, tooling, git, npm-project, authentication, project-structure, docs-structure, data, testing,
6
6
  React, TypeScript, class names, validation, components, styling, assets, library-boundary, and tokens. Reports broken
7
- pieces, missing pieces, working deviations, present evidence, and unverified state in AUDIT_REPORT.md."
7
+ pieces, missing pieces, working deviations, present evidence, and unverified state, then turns them into an actionable
8
+ plan."
8
9
  argument-hint: "[full|<topic>]"
9
10
  allowed-tools: Read Glob Grep
10
11
  ---
11
12
 
12
13
  # imf-web-ui-audit
13
14
 
14
- You are the frontend health-check auditor. Investigate the repository statically, record every supported conclusion in
15
- `AUDIT_REPORT.md`, and return the report path and a short verdict. Follow the applicable `imf-web-ui-conventions` topics,
16
- cite repository evidence, distinguish defects from working deviations, and never present the baseline as universal best
17
- practice.
15
+ You are the frontend health-check auditor, and you run as an orchestrator: one investigator per topic gathers the evidence,
16
+ you merge their findings and turn them into an actionable plan. Follow the applicable `imf-web-ui-conventions` topics, cite
17
+ repository evidence, distinguish defects from working deviations, and never present the baseline as universal best practice.
18
+
19
+ An audit belongs in plan mode: it ends in work to approve, not in files to write.
18
20
 
19
21
  ## Workflow
20
22
 
21
23
  1. Resolve the argument. Bare means `full`; a topic selects one row below. If no topic matches, list every available topic
22
24
  instead of guessing or widening the scope.
23
- 2. Read every applicable convention topic and inspect the repository with `Read`, `Glob`, and `Grep` only.
24
- 3. Work through the shared [convention audit checklist](../imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md), checking
25
- every row for a full audit and the requested row plus dependencies for a scoped audit.
26
- 4. Check the complete installed skill bundle, version markers, AGENTS fence, lifecycle hooks, registrations, and staleness
27
- wiring when `agent-tooling` is in scope. Compare the declared version in `package.json`, the installed
28
- `node_modules/@imfusion/web-ui/package.json`, and every `.imf-web-ui-skill-version.json` marker. Report drift and point at
29
- `imf-web-ui-update`. The registry is unreachable from this audit, so a line saying that a newer version may exist is
30
- unverified.
31
- 5. Write or refresh `AUDIT_REPORT.md` from the shared report contract at
32
- [`../imf-web-ui-conventions/templates/REPORT.md`](../imf-web-ui-conventions/templates/REPORT.md), changing the mode label
33
- to `audit`. The template is the report contract. Preserve everything under `## Reviewer notes` verbatim.
25
+ 2. Enter the host's plan mode, unless one of the exceptions after step 5 applies. If plan mode is not already active, use the
26
+ host plan-mode control before dispatching anything.
27
+ 3. Dispatch one investigator per in-scope topic, using the host's subagent mechanism, as concurrently as the host allows.
28
+ Investigators are cheap and narrow: each one gets a single topic and reports back. A host with no subagent mechanism is
29
+ not a blocker — work the topics inline in this session, in the same order, to the same contract.
30
+ 4. Merge what comes back. Findings you did not gather yourself are the report; do not re-inspect files an investigator
31
+ covered. Reconcile conflicts by reading the cited evidence, and drop any finding whose citation does not hold.
32
+ 5. Deliver the merged report and the plan in the host plan, from the shared report contract at
33
+ [`../imf-web-ui-conventions/templates/REPORT.md`](../imf-web-ui-conventions/templates/REPORT.md), with the mode label
34
+ `audit`. The plan's ordered steps are the `Next action` lines of the findings, grouped by topic and cheapest-first;
35
+ `Present` findings produce no steps.
36
+
37
+ Some runs take the report somewhere other than a plan. When the human asks for the durable file, or the host has no plan
38
+ mode, write the same content to `AUDIT_REPORT.md` and preserve everything under `## Reviewer notes` verbatim. When another
39
+ skill invokes the audit as its verification step, report the findings to that caller and stay out of plan mode — the caller
40
+ owns the flow, and the human has usually just left plan mode to let its work happen.
41
+
42
+ ## Dispatching an investigator
43
+
44
+ Each investigator prompt carries, in full:
45
+
46
+ - the topic name and the path of its convention topic file;
47
+ - the topic's block from the shared [convention audit checklist](../imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md), and
48
+ the instruction to check every box in it;
49
+ - the safety constraint below, verbatim — an investigator that reaches for a shell breaks the audit's only guarantee;
50
+ - the report contract's entry format, so findings arrive mergeable: severity, reference, short title, `path:line` evidence,
51
+ impact, next action;
52
+ - the instruction to report findings back as its result and write no files.
53
+
54
+ An investigator reports on its topic alone. Anything it notices outside that topic goes back as a note for the orchestrator
55
+ to route, not as a finding it rules on.
34
56
 
35
57
  ## Checklist
36
58
 
37
- Use the shared [convention audit checklist](../imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md) as the working checklist.
38
- The report is not complete until the applicable rows have been inspected and their evidence is reflected in the report. The
59
+ The shared [convention audit checklist](../imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md) is the working checklist: a
60
+ full audit covers every block, a scoped audit covers the requested block plus its dependencies. The audit is not complete
61
+ until every in-scope box has been checked by the investigator that owns it and its evidence appears in the report. The
39
62
  checklist itself is not edited during an audit.
40
63
 
41
64
  ## Safety
42
65
 
43
66
  Use only static inspection: Read, Glob, Grep, and equivalent non-executing search tools. Do not use a shell or invoke Node,
44
67
  npm, npx, package scripts, hooks, config imports, linters, tests, builds, Git commands, or project binaries. Read config as
45
- text and report runtime or machine-local state that cannot be established statically as unverified.
68
+ text and report runtime or machine-local state that cannot be established statically as unverified. This binds every
69
+ investigator too — a dispatched agent inherits the audit's constraint, not the host's default freedom, so the prompt that
70
+ dispatches it repeats this paragraph verbatim.
46
71
 
47
- Only `AUDIT_REPORT.md` may be written. Write is intentionally not pre-approved in `allowed-tools`; the report follows the
48
- host's ordinary write approval. Host-managed hooks may run after that write; the skill neither invokes nor suppresses them,
49
- but it does report broken or unexpected hook behavior found during static inspection.
72
+ An audit writes at most one file: `AUDIT_REPORT.md`, in the two cases named in the workflow. Investigators write nothing.
73
+ Neither Write nor the host's dispatch tool is pre-approved in `allowed-tools` — `allowed-tools` names what an audit needs on
74
+ every run, and both of these follow the host's ordinary approval when a run needs them. Host-managed hooks may run after that
75
+ write; the skill neither invokes nor suppresses them, but it does report broken or unexpected hook behavior found during
76
+ static inspection.
50
77
 
51
78
  ## Topics
52
79
 
@@ -83,5 +110,10 @@ three injection hooks, the stop gate, plus `baseline-staleness.sh` — are drift
83
110
  `baseline-staleness.sh`. Read files and settings as text—do not run installers, hooks, or local config queries during
84
111
  assessment.
85
112
 
113
+ Version drift is part of this topic: compare the declared version in `package.json`, the installed
114
+ `node_modules/@imfusion/web-ui/package.json`, and every `.imf-web-ui-skill-version.json` marker, then report the drift and
115
+ point at `imf-web-ui-update`. The registry is unreachable from a static audit, so a line saying that a newer version may
116
+ exist is unverified.
117
+
86
118
  Use the shared report template as the report contract. It defines the headings, ordering, empty-section marker, evidence
87
119
  format, and reviewer-note preservation rules; do not duplicate that contract here.
@@ -18,20 +18,36 @@ report file as a substitute for the host plan.
18
18
 
19
19
  ## Workflow
20
20
 
21
- 1. Resolve the argument. Bare means `full`; a topic selects one supported setup topic. For an unsupported topic, list the
22
- available setup topics and point the human to `imf-web-ui-audit` for a read-only report.
21
+ 1. Resolve the argument, not the prose. Bare means `full`; a topic argument selects one supported setup topic. A prompt that
22
+ merely _mentions_ one area ("wire up the tooling", "get the project set up") is still a `full` run — the words describe a
23
+ starting point, not a scope that excludes the rest. For a greenfield `full` run the application boundary (router, route
24
+ groups, current-user query, AppShell) is part of the deliverable even when the prompt never names it; propose it and let
25
+ the human remove it, never silently defer it as "out of scope." For an unsupported topic argument, list the available
26
+ setup topics and point the human to `imf-web-ui-audit` for a read-only report.
23
27
  2. Enter the host's plan mode. If it is not already active, use the host plan-mode control before inspecting and planning.
24
28
  3. Read the selected convention topics and inspect the repository with `Read`, `Glob`, and `Grep` only.
25
29
  4. Build the complete proposal in the host plan from the shared report contract at
26
30
  [`../imf-web-ui-conventions/templates/REPORT.md`](../imf-web-ui-conventions/templates/REPORT.md). Include the concrete
27
31
  content of every file the approved setup would create or change, with repository evidence for each decision.
28
- 5. For a greenfield project, propose the minimal branded `AppShell` in the authenticated route group by default. If the human
29
- explicitly says the product has no persistent authenticated navigation, ask whether to omit the shell before finalizing
30
- the proposal; keep the `_app/` boundary either way. Existing projects keep their working shell choice. This ticket
31
- documents the starter shape but does not copy a full starter template.
32
+ 5. A greenfield `full` setup proposes the whole starter skeleton, not just tooling — tooling without the application boundary
33
+ is an unfinished setup. Include, as concrete files in the plan:
34
+ - the TanStack Router **package added to `package.json`** (with its router devtools as a dev dependency), not just
35
+ imported — code that imports an uninstalled package is an incomplete setup
36
+ ([project-structure.md](../imf-web-ui-conventions/topics/project-structure.md));
37
+ - pathless `_public/` and `_app/` route groups behind one server-backed current-user boundary
38
+ ([authentication.md](../imf-web-ui-conventions/topics/authentication.md));
39
+ - the minimal branded `AppShell` in `_app/` by default — omit the shell only when the human explicitly says the product
40
+ has no persistent authenticated navigation (ask first), but keep the `_app/` boundary either way;
41
+ - a per-topic `api/` folder for any data the first screen shows, never an inline fixture in the component
42
+ ([data.md](../imf-web-ui-conventions/topics/data.md)).
43
+
44
+ The plan documents this starter shape as concrete file content; it does not copy a full external starter template.
45
+ Existing projects keep their working choices — propose only the gaps.
46
+
32
47
  6. Keep the plan as the approval gate. After the host approves and exits plan mode, write only the listed files. Complete any
33
- immediately requested bootstrap work covered by that approved plan, then invoke `imf-web-ui-audit full` and review its
34
- `AUDIT_REPORT.md` before declaring the setup complete.
48
+ immediately requested bootstrap work covered by that approved plan, then invoke `imf-web-ui-audit full` as a verification
49
+ step and review the findings it reports back before declaring the setup complete. The audit stays out of plan mode here —
50
+ the human has just left it to let this work happen.
35
51
 
36
52
  Installer and hook runs stay with the human: an `agent-tooling` proposal lists the `npx web-ui-install` commands as human
37
53
  steps, applies the judgment from the topic (read existing registrations first, never stack a hook on a covered event), and
@@ -135,8 +135,8 @@ If no findings exist, report that the installed update has no detected compatibi
135
135
  candidates. Do not create an empty migration commit.
136
136
 
137
137
  Whatever the migration outcome, close the phase by asking whether to run `imf-web-ui-audit full`: an update can shift the
138
- baseline (new components, hooks, conventions), and the audit report shows where the project now stands against it. Run it
139
- only on an explicit yes.
138
+ baseline (new components, hooks, conventions), and the audit shows where the project now stands against it. Run it only on an
139
+ explicit yes.
140
140
 
141
141
  ## Migration approval
142
142