viepilot 2.45.6 → 2.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -54,10 +54,15 @@ If `~/.viepilot/config.json` is absent, use defaults (en/en) — do not fail.
54
54
  | v2.32.0 | `## Admin & Governance` in PROJECT-CONTEXT.md | section absent AND `admin_imported` not in HANDOFF.json |
55
55
  | v2.33.0 | `## Content Management` in PROJECT-CONTEXT.md | section absent AND `content_imported` not in HANDOFF.json |
56
56
  | v2.34.0 | `## User Data Management` in PROJECT-CONTEXT.md | section absent AND `user_data_imported` not in HANDOFF.json |
57
+ | v2.46.0 | UI/Design scan not run (Signal Cat 13) | `ui_signals_imported` absent from HANDOFF.json AND any UI trigger condition met: `.css`/`.scss` files > 5 OR `tailwind.config.js`/`tailwind.config.ts` exists OR component files (`.jsx/.tsx/.vue/.svelte`) > 10 |
57
58
 
58
59
  Cross-check: confirm section is actually absent from current `PROJECT-CONTEXT.md` before listing
59
60
  as a gap — avoids false positives for projects that have the content already.
60
61
 
62
+ UI trigger re-check note: backend-only projects (no CSS, no Tailwind, no UI components) must
63
+ NOT see "UI scan gap" — the trigger condition re-check prevents false-positive upgrade noise
64
+ for CLI tools, data pipelines, and other non-UI projects.
65
+
61
66
  ### Upgrade menu
62
67
 
63
68
  **Claude Code (terminal) — REQUIRED:**
@@ -89,6 +94,10 @@ For each missing section, run only the corresponding Step 1D export item:
89
94
  - `## Admin & Governance` → Step 1D item 7 (ENH-063)
90
95
  - `## Content Management` → Step 1D item 8 (ENH-065)
91
96
  - `## User Data Management` → Step 1D item 9 (ENH-066)
97
+ - UI/Design scan (v2.46.0, ENH-079) → Re-run Signal Category 13 (Sub-scans A/B/C) then
98
+ proceed through Step 0-D (AUQ gate + workspace generation). AUQ gate is preserved — user
99
+ can still choose to skip. No brainstorm supplement check required (reads codebase directly).
100
+ After gate: writes `ui_signals_imported` to HANDOFF.json per Task 121.2 spec.
92
101
 
93
102
  For each item:
94
103
  1. Check if architect `notes.md` has the corresponding YAML section (`## admin`, `## content`,
@@ -269,7 +278,92 @@ When brownfield was triggered automatically (not via `--brownfield`), present th
269
278
  > - "Cancel" / "3" → exit without changes
270
279
 
271
280
  **When brownfield mode is active:**
272
- 1. Run the full 12-category codebase scanner (Signal Categories 1–12 below).
281
+
282
+ ### Brownfield Scan Trace — Initialization (ENH-081)
283
+
284
+ **Before running any signal category**, initialize the scan trace:
285
+
286
+ 1. Check if `.viepilot/BROWNFIELD-TRACE.md` exists.
287
+
288
+ 2. **If exists AND `Status: scan_complete`** — prior scan completed. Offer resume via AUQ:
289
+ ```
290
+ question: "Prior scan found (completed {date}). Re-scan or resume from gap-filling?"
291
+ header: "Brownfield Trace"
292
+ options:
293
+ a. "Resume — skip re-scan, go to gap-filling (Recommended)"
294
+ b. "Re-scan from scratch — overwrite trace"
295
+ ```
296
+ - On "Resume": skip to Interactive Gap-Filling (Step 0-B-ii), using data from prior trace.
297
+ - On "Re-scan": delete trace, continue to step 4 below.
298
+
299
+ 3. **If exists AND `Status: scanning`** — prior scan was interrupted. Read which category row
300
+ last shows `scanning` → identify interrupted category N. Offer via AUQ:
301
+ ```
302
+ question: "Prior scan interrupted at Signal Cat {N} — {Category Name}. How to proceed?"
303
+ header: "Interrupted Scan"
304
+ options:
305
+ a. "Resume from Signal Cat {N} (Recommended)"
306
+ b. "Re-scan from scratch"
307
+ ```
308
+ - On "Resume from Cat N": skip completed categories (1 through N-1), restart from Cat N.
309
+ - On "Re-scan": overwrite trace, continue to step 4 below.
310
+
311
+ 4. **If not exists (or user chose "Re-scan"):** create `.viepilot/BROWNFIELD-TRACE.md`:
312
+
313
+ ```markdown
314
+ # Brownfield Scan Trace
315
+
316
+ <!-- Generated by vp-crystallize --brownfield v{crystallize_version} -->
317
+
318
+ ## Session Info
319
+ - **Started**: {ISO-8601 timestamp}
320
+ - **Project**: {project_name or "unknown (pre-scan)"}
321
+ - **crystallize_version**: {semver}
322
+ - **Status**: scanning
323
+
324
+ ## Signal Coverage
325
+
326
+ | # | Category | Status | Files Checked | Signals Found | Started | Duration |
327
+ |---|----------|--------|---------------|---------------|---------|----------|
328
+ | 1 | Build Manifest & Package Identity | planned | — | — | — | — |
329
+ | 2 | Framework & Library Detection | planned | — | — | — | — |
330
+ | 3 | Architecture Layer Inference | planned | — | — | — | — |
331
+ | 4 | Database Schema Signals | planned | — | — | — | — |
332
+ | 5 | API Contract Files | planned | — | — | — | — |
333
+ | 6 | Infrastructure & Deployment Config | planned | — | — | — | — |
334
+ | 7 | Environment & Configuration Shape | planned | — | — | — | — |
335
+ | 8 | Test Coverage Signals | planned | — | — | — | — |
336
+ | 9 | Code Quality & Tooling | planned | — | — | — | — |
337
+ | 10 | Documentation Files | planned | — | — | — | — |
338
+ | 11 | Git History & Version Signals | planned | — | — | — | — |
339
+ | 12 | File Extension Language Survey | planned | — | — | — | — |
340
+ | 13 | UI/Design System Signals | planned | — | — | — | — |
341
+
342
+ ## Files Read Log
343
+
344
+ (Populated during scanning — one subsection per signal category)
345
+
346
+ ## Gap Filling Log
347
+
348
+ | Field | Status | Source | Value |
349
+ |-------|--------|--------|-------|
350
+
351
+ (Populated during interactive gap-filling — Step 0-B-ii)
352
+
353
+ ## Step Completion
354
+
355
+ | Step | Status | Completed At | Notes |
356
+ |------|--------|-------------|-------|
357
+ | 0-B Scanner | in_progress | {timestamp} | — |
358
+ | 0-C Brainstorm Stub | pending | — | — |
359
+ | 0-D UI Workspace | pending | — | N/A if not triggered |
360
+ ```
361
+
362
+ **Write this file to disk immediately** — before any signal category scanning begins.
363
+
364
+ ---
365
+
366
+ 1. Run the full 13-category codebase scanner (Signal Categories 1–13 below).
273
367
  2. Produce a structured **Scan Report** (see schema at end of this step).
274
368
  3. Classify every field as DETECTED / ASSUMED / MISSING per Gap Detection Rules.
275
369
  4. Present Scan Report summary to user; interactively fill every MISSING MUST-DETECT field.
@@ -676,6 +770,179 @@ Rule: `secondary_languages[]` = languages with ≥5 files that are not `primary_
676
770
 
677
771
  ---
678
772
 
773
+ ### Signal Category 13 — UI/Design System Signals
774
+
775
+ **Trigger condition (ANY of):**
776
+ - `.css` or `.scss` files > 5 in the project
777
+ - `tailwind.config.js` or `tailwind.config.ts` exists
778
+ - Component files (`.jsx`, `.tsx`, `.vue`, `.svelte`) > 10
779
+ - Route config files detected (see Sub-scan B sources below)
780
+
781
+ **Runs after:** Signal Category 12.
782
+ **Output stored in:** `ui_signals` Scan Report field.
783
+
784
+ ---
785
+
786
+ #### Sub-scan A — Design Token Extraction
787
+
788
+ **Sources (priority order):**
789
+
790
+ 1. `tailwind.config.js` / `tailwind.config.ts`
791
+ - Extract: `theme.extend.colors`, `theme.colors`, `theme.fontFamily`, `theme.spacing`, `theme.borderRadius`
792
+
793
+ 2. CSS custom properties in `*.css` / `globals.css` / `variables.css` / `_variables.scss`
794
+ - `--color-*`, `--primary-*`, `--bg-*`, `--text-*` → TOKEN_MAP.colors
795
+ - `--font-*`, `--text-size-*` → TOKEN_MAP.typography
796
+ - `--spacing-*`, `--gap-*`, `--padding-*` → TOKEN_MAP.spacing
797
+ - `--radius-*`, `--rounded-*` → TOKEN_MAP.rounded
798
+
799
+ 3. SCSS variables: `$color-primary`, `$font-size-base`, `$spacing-unit`, etc.
800
+
801
+ 4. Design token JSON files: `tokens.json`, `design-tokens.json`, `theme.json`
802
+
803
+ **Output:** Populate TOKEN_MAP for workspace generation step.
804
+ - ASSUMED prefix for any token not found in any source.
805
+ - Track `source_files[]` — list of all files scanned.
806
+
807
+ **Scan Report field:** `ui_tokens: { detected: N, assumed: M, source_files: [] }`
808
+
809
+ ---
810
+
811
+ #### Sub-scan B — Page/Route Inventory
812
+
813
+ | Framework | Detection method | Route extraction |
814
+ |-----------|-----------------|-----------------|
815
+ | Next.js 13+ App Router | `app/` directory with `page.tsx` or `page.jsx` | Directory structure → route paths |
816
+ | Next.js Pages Router | `pages/` directory (exclude `api/` and `_` prefixes) | Directory structure → route paths |
817
+ | React Router | `src/routes/*.{jsx,tsx}`, `src/App.{jsx,tsx}` | Parse `<Route path="...">` attrs |
818
+ | Vue Router | `src/router/index.{js,ts}` | Parse `routes: [{ path, component }]` |
819
+ | Angular | `src/app/app-routing.module.ts` | Parse `Routes` array |
820
+ | Nuxt 3 | `pages/` directory | Directory structure → route paths |
821
+ | Plain HTML | `*.html` at root, `src/`, `public/` | File names → page names |
822
+ | SvelteKit | `src/routes/+page.svelte` | Directory structure |
823
+
824
+ **Scan Report field:** `ui_pages: { count: N, routes: [], framework: string }`
825
+
826
+ Framework values: `"next-app"` | `"next-pages"` | `"react-router"` | `"vue-router"` | `"angular"` | `"nuxt3"` | `"sveltekit"` | `"html"` | `"unknown"`
827
+
828
+ ---
829
+
830
+ #### Sub-scan C — Component Inventory
831
+
832
+ **Sources:**
833
+
834
+ 1. Component file globs:
835
+ - `src/components/**/*.{jsx,tsx,vue,svelte}`
836
+ - `components/**/*.{jsx,tsx,vue}`
837
+ - `app/components/**/*`
838
+
839
+ 2. UI library detection from `package.json` dependencies:
840
+ - `@mui/material` → Material UI
841
+ - `@chakra-ui/react` → Chakra UI
842
+ - `@shadcn/ui` or `components/ui/` directory → shadcn/ui
843
+ - `antd` → Ant Design
844
+ - `@headlessui/react` → Headless UI
845
+ - `react-bootstrap` → Bootstrap
846
+ - `@radix-ui/*` → Radix primitives
847
+
848
+ 3. Common component pattern detection (by filename):
849
+ - Button, Modal, Dialog, Card, Form, Input, Select, Table, Navbar, Sidebar, Footer, Header
850
+
851
+ **Scan Report field:**
852
+ ```yaml
853
+ ui_components:
854
+ total: N
855
+ ui_library: string # "shadcn/ui" | "Material UI" | "Chakra UI" | "Ant Design" | "Headless UI" | "Bootstrap" | "Radix" | "none"
856
+ categories:
857
+ layout: N
858
+ forms: N
859
+ navigation: N
860
+ data-display: N
861
+ feedback: N
862
+ ```
863
+
864
+ ---
865
+
866
+ ### Brownfield Scan Trace — Per-Category Update Protocol (ENH-081)
867
+
868
+ For each signal category (1–13), apply this update protocol to `BROWNFIELD-TRACE.md`:
869
+
870
+ #### On category start
871
+
872
+ Update the category's row in `## Signal Coverage`:
873
+ - Status: `scanning`
874
+ - Started: current ISO timestamp (time portion only: `HH:MM:SS`)
875
+
876
+ #### On category complete
877
+
878
+ Update the category's row:
879
+ - **Status**: one of:
880
+ - `done` — evidence found, DETECTED tier (high confidence)
881
+ - `assumed` — evidence found, ASSUMED tier (low confidence / indirect signal)
882
+ - `skipped` — no relevant files found; category not applicable to this project
883
+ - **Files Checked**: integer count of files actually read
884
+ - **Signals Found**: comma-separated key results (e.g. `react, next.js`), or `none`
885
+ - **Duration**: elapsed seconds since category started (e.g. `3s`)
886
+
887
+ Append a new subsection to `## Files Read Log`:
888
+
889
+ ```markdown
890
+ ### Signal {N} — {Category Name}
891
+
892
+ - [x] {relative/path/to/file} → {signal or "none found"}
893
+ - [x] {another/file.json} → {result summary}
894
+ - [ ] {expected/missing.yaml} → not present
895
+ ```
896
+
897
+ Legend:
898
+ - `[x]` = file was read (exists on disk and was examined)
899
+ - `[ ]` = file was expected per workflow probe list but not found on disk
900
+
901
+ **Skipped category** (no files probed): append instead:
902
+ ```markdown
903
+ ### Signal {N} — {Category Name}
904
+
905
+ (No files probed — category not applicable to this project)
906
+ ```
907
+
908
+ #### Status transitions
909
+
910
+ Flow: `planned → scanning → done` (full evidence) | `scanning → assumed` (low confidence) | `scanning → skipped` (no files)
911
+
912
+ ```
913
+ planned → scanning (category starts)
914
+ scanning → done (evidence found — DETECTED)
915
+ scanning → assumed (evidence found — ASSUMED, low confidence)
916
+ scanning → skipped (no matching files found)
917
+ ```
918
+
919
+ #### Example — Signal Category 5 (API Contract Files)
920
+
921
+ Row after completion:
922
+ ```
923
+ | 5 | API Contract Files | done | 3 | openapi.yaml, swagger.json (REST) | 14:23:05 | 2s |
924
+ ```
925
+
926
+ Files Read Log append:
927
+ ```markdown
928
+ ### Signal 5 — API Contract Files
929
+
930
+ - [x] openapi.yaml → REST (OpenAPI 3.0)
931
+ - [x] swagger.json → REST (Swagger 2.0)
932
+ - [ ] graphql/schema.graphql → not present
933
+ - [x] src/routes/index.ts → REST route patterns detected
934
+ ```
935
+
936
+ #### Signal Category 13 special format
937
+
938
+ Signal Category 13 (UI/Design) Signals Found field format:
939
+ ```
940
+ tailwind ({N} colors), {M} routes ({framework}), {K} components ({ui_library})
941
+ ```
942
+ Example: `tailwind (12 colors), 8 routes (next-app), 24 components (shadcn/ui)`
943
+
944
+ ---
945
+
679
946
  ### Scan Report Schema (finalized)
680
947
 
681
948
  After running all 12 signal categories, produce this structured Scan Report:
@@ -741,6 +1008,26 @@ top_contributors: []
741
1008
  docs_extracted: [] # { file, summary, key_facts[] }
742
1009
  language_distribution: {} # { ts: 142, java: 38, ... }
743
1010
  open_questions: [] # root-level open questions (includes rollup from modules)
1011
+ ui_signals: # present only when Signal Category 13 triggered
1012
+ triggered: bool
1013
+ workspace_generated: bool # true after workspace generation step completes
1014
+ ui_tokens:
1015
+ detected: N # tokens extracted from sources
1016
+ assumed: M # tokens filled with ASSUMED values
1017
+ source_files: [] # files scanned (tailwind.config.js, globals.css, etc.)
1018
+ ui_pages:
1019
+ count: N
1020
+ routes: [] # [ { path, name, source_file } ]
1021
+ framework: string # "next-app" | "next-pages" | "react-router" | "vue-router" | "angular" | "nuxt3" | "sveltekit" | "html" | "unknown"
1022
+ ui_components:
1023
+ total: N
1024
+ ui_library: string # "shadcn/ui" | "Material UI" | "Chakra UI" | "Ant Design" | "Headless UI" | "Bootstrap" | "Radix" | "none"
1025
+ categories:
1026
+ layout: N
1027
+ forms: N
1028
+ navigation: N
1029
+ data-display: N
1030
+ feedback: N
744
1031
  ```
745
1032
 
746
1033
  ---
@@ -829,6 +1116,19 @@ Root gap tier: MISSING (worst across modules)
829
1116
 
830
1117
  After scanner completes:
831
1118
 
1119
+ **Coverage gate (ENH-081):** Before presenting the Scan Report, verify the trace:
1120
+
1121
+ 1. Read `## Signal Coverage` table from `.viepilot/BROWNFIELD-TRACE.md`.
1122
+ 2. Count rows still showing `planned` (never executed).
1123
+ 3. If any `planned` rows found: display warning (non-blocking):
1124
+ ```
1125
+ ⚠️ Coverage incomplete — {N} signal categories not executed:
1126
+ - Signal Cat {N}: {Category Name}
1127
+ Proceeding with partial Scan Report. Uncovered gap fields will be MISSING.
1128
+ ```
1129
+ 4. Update trace `## Session Info` → `Status: scan_complete`.
1130
+ 5. Update `## Step Completion` row "0-B Scanner" → `done` + current timestamp.
1131
+
832
1132
  1. Display Scan Report summary table to user (field | value | status).
833
1133
  2. **Per-module MISSING fields** — for each module with `gap_tier: MISSING`, pause and ask per field:
834
1134
  ```
@@ -843,6 +1143,14 @@ After scanner completes:
843
1143
  5. Capture all user responses; update Scan Report fields accordingly.
844
1144
  6. All remaining unresolved items → `open_questions[]` (roll up per-module `open_questions[]` into root).
845
1145
 
1146
+ **Gap Filling Log (ENH-081):** For each field where the user provides or confirms a value, append a row to `## Gap Filling Log` in `.viepilot/BROWNFIELD-TRACE.md`. Write after each user response — do not batch:
1147
+
1148
+ | Field | Status | Source | Value |
1149
+ |-------|--------|--------|-------|
1150
+ | {field_name} | MISSING → user-filled | user input | {value} |
1151
+ | {field_name} | ASSUMED → accepted | user accept | {assumed_value} |
1152
+ | {field_name} | DETECTED → user-confirmed | user confirm | {value} |
1153
+
846
1154
  ---
847
1155
 
848
1156
  ### Brownfield Brainstorm Stub Generation (Step 0-C)
@@ -869,6 +1177,120 @@ After gap-filling is complete, write:
869
1177
 
870
1178
  **Purpose:** This stub allows `vp-audit` and other ViePilot tools to not error on missing brainstorm session files. The presence of `session-brownfield-import.md` is treated as a valid brownfield import.
871
1179
 
1180
+ **Trace update (ENH-081):** Update `.viepilot/BROWNFIELD-TRACE.md` `## Step Completion` row
1181
+ "0-C Brainstorm Stub" → `done` + current timestamp + `stub: docs/brainstorm/session-brownfield-import.md`.
1182
+
1183
+ ---
1184
+
1185
+ ### UI Workspace Generation Gate (Step 0-D)
1186
+
1187
+ **Condition:** Run only when `ui_signals.triggered: true` in the Scan Report.
1188
+
1189
+ #### AUQ Gate — Brownfield UI Reverse-Engineering
1190
+
1191
+ **Claude Code (terminal) — REQUIRED:** Call AskUserQuestion:
1192
+ - question: "UI signals detected — generate ui-direction workspace from existing code?"
1193
+ - header: "Brownfield UI Reverse-Engineering"
1194
+ - options:
1195
+ a. "Yes — generate now (Recommended)" → proceed to workspace generation below
1196
+ b. "Skip — I will create ui-direction manually" → set `ui_signals.workspace_generated: false`; add note to `open_questions[]`; write to `.viepilot/HANDOFF.json`: `{ "ui_signals_imported": false, "ui_signals_skipped_at": "{ISO-8601 timestamp}" }` (merge — do not overwrite unrelated fields); continue
1197
+
1198
+ **Text fallback (non-terminal adapters):**
1199
+ ```
1200
+ UI signals detected. Options:
1201
+ 1. Yes — generate ui-direction workspace now (Recommended)
1202
+ 2. Skip — create ui-direction manually later
1203
+ ```
1204
+
1205
+ #### Workspace Generation
1206
+
1207
+ When user selects "Yes — generate now":
1208
+
1209
+ Generate session ID: `brownfield-{YYYY-MM-DD}` (or reuse existing if re-running).
1210
+
1211
+ Create artifacts under `.viepilot/ui-direction/{session-id}/`:
1212
+
1213
+ **`design.md`** (Design.MD v1 format):
1214
+ - Populate colors, typography, spacing, rounded from TOKEN_MAP (Sub-scan A)
1215
+ - Prefix ASSUMED for any token not extracted from source
1216
+ - Add `## Source` section listing all scanned files and extraction method
1217
+
1218
+ **`notes.md`**:
1219
+ ```markdown
1220
+ ---
1221
+ reverse_engineered: true
1222
+ source_project: {project_name}
1223
+ scan_date: {ISO date}
1224
+ signal_category: 13
1225
+ ---
1226
+
1227
+ ## pages_inventory
1228
+
1229
+ | Route | Page Name | Source File | Status |
1230
+ |-------|-----------|-------------|--------|
1231
+ {one row per route from ui_pages.routes[]}
1232
+
1233
+ ## components_inventory
1234
+
1235
+ | Component | Type | File | UI Library |
1236
+ |-----------|------|------|------------|
1237
+ {one row per component from ui_components scan}
1238
+
1239
+ ## design_tokens
1240
+
1241
+ - Detected: {ui_tokens.detected} tokens
1242
+ - Assumed: {ui_tokens.assumed} tokens
1243
+ - Sources: {ui_tokens.source_files[]}
1244
+ ```
1245
+
1246
+ **`index.html`** hub page:
1247
+ - Header: project name + UI library badge (if `ui_library != "none"`)
1248
+ - Table of contents: one link per page stub
1249
+ - Each entry: route path + source file reference
1250
+
1251
+ **`pages/{slug}.html`** stubs — one per discovered route:
1252
+ - Page title (from route name) + route path
1253
+ - "Reverse-engineered from: {source file}"
1254
+ - Component list (if detectable via import analysis)
1255
+ - Placeholder section for handover notes
1256
+
1257
+ **Generated artifact tree:**
1258
+ ```
1259
+ .viepilot/ui-direction/
1260
+ brownfield-{YYYY-MM-DD}/
1261
+ design.md ← design tokens (Design.MD v1)
1262
+ notes.md ← pages_inventory + components_inventory + design_tokens
1263
+ index.html ← hub page with all discovered pages linked
1264
+ pages/
1265
+ {slug}.html ← one per discovered route/page
1266
+ ```
1267
+
1268
+ After generation: set `ui_signals.workspace_generated: true` in Scan Report.
1269
+
1270
+ Also write to `.viepilot/HANDOFF.json` (merge — do not overwrite unrelated fields):
1271
+ ```json
1272
+ {
1273
+ "ui_signals_imported": true,
1274
+ "ui_signals_imported_at": "{ISO-8601 timestamp}",
1275
+ "ui_signals_version": "2.46.0"
1276
+ }
1277
+ ```
1278
+ This flag is used by Step 0-B upgrade re-scan (ENH-080) to detect whether Signal Cat 13 has
1279
+ been run for this project. A future `--upgrade` re-run can force re-evaluation by re-running
1280
+ Signal Cat 13 even when `ui_signals_imported: true` is present.
1281
+
1282
+ **Reverse-engineered workspace notice:** The workspace is a documentation approximation
1283
+ from static code analysis — not a pixel-perfect rendering. `pages/*.html` stubs describe
1284
+ what exists based on file structure and imports. The `reverse_engineered: true` flag allows
1285
+ downstream tools (`vp-audit`, `vp-crystallize`) to adjust expectations (e.g., skip
1286
+ "missing brainstorm" warnings).
1287
+
1288
+ **Trace update (ENH-081):** Update `.viepilot/BROWNFIELD-TRACE.md` `## Step Completion` row
1289
+ "0-D UI Workspace":
1290
+ - If workspace generated: `done` + timestamp + `workspace: .viepilot/ui-direction/{session-id}/`
1291
+ - If user skipped AUQ: `skipped` + timestamp + `reason: user skipped`
1292
+ - If Signal Category 13 did not trigger: `N/A` (update row to `N/A` + timestamp)
1293
+
872
1294
  ---
873
1295
 
874
1296
  ### Safety Rules