Skip to content

Add Gate 2 thread visualizer (descent + break demo) - #7

Merged
cursor[bot] merged 1 commit into
mainfrom
cursor/thread-visualizer-7716
Aug 4, 2026
Merged

Add Gate 2 thread visualizer (descent + break demo)#7
cursor[bot] merged 1 commit into
mainfrom
cursor/thread-visualizer-7716

Conversation

@rdryfoos

@rdryfoos rdryfoos commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a read-only Thread Visualizer that projects Gate 2’s golden thread for demos: walker JSON (totals must match Gate 2) → single-requirement descent view → seeded “break the proof” state on AC-USER-04.

Traceability

Craft / demo tooling only (not product scope). Spec: docs/thread-visualizer/SPEC.md.

Proof

  • Walker totals match Gate 2: 79 IDs / 50 ACs / 45 with tests
  • Intact descent for AC-USER-04 includes PRD text, @covers modules, test_AC_*
  • --demo-break AC-USER-04 yields GAP with zero proof tests
  • Open docs/thread-visualizer/ via local static server and click Break the proof

How to try

bash scripts/thread-visualizer-refresh.sh
cd docs/thread-visualizer && python3 -m http.server 8765
# http://127.0.0.1:8765/
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Added a read-only Thread Visualizer for exploring requirements, implementation details, proof tests, coverage, and verification status.
    • Added intact and intentionally broken demonstration views with missing-proof warnings and gap indicators.
    • Added responsive styling, accessible controls, metadata, and thread selection.
  • Documentation

    • Added launch, refresh, regeneration, visualization-state, and traceability guidance.
    • Added specifications describing data generation, coverage validation, and completion criteria.
  • Chores

    • Added commands to regenerate visualizer data from current project traceability information.

Walker projects live PRD/@covers/test data into JSON matching Gate 2 totals;
descent UI shows one requirement unbroken, with a seeded break on AC-USER-04.

Co-authored-by: Rik Dryfoos <rdryfoos@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a read-only HomesFlow Thread Visualizer. A Python walker builds Gate 2 traceability projections from live PRD, source, and test data. The browser renders intact and seeded broken proof states. Documentation and a refresh gate describe regeneration.

Changes

Thread Visualizer

Layer / File(s) Summary
Specification and usage contract
docs/thread-visualizer/SPEC.md, docs/thread-visualizer/README.md, README.md, specs/001-mvp/craft-conventions.md
Defines live data rules, visualization states, launch instructions, regeneration commands, and the CI refresh gate.
Traceability walker and generated projections
scripts/thread-walker.py, scripts/thread-visualizer-refresh.sh, docs/thread-visualizer/data/*
Builds Gate 2-backed requirement threads, validates totals, supports the seeded AC-USER-04 proof break, and generates both JSON projections.
Browser visualization
docs/thread-visualizer/index.html, docs/thread-visualizer/visualizer.js, docs/thread-visualizer/visualizer.css
Adds the page structure, responsive styling, projection loading, thread selection, proof-state controls, accessibility behavior, and GAP rendering.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant RefreshScript as thread-visualizer-refresh.sh
  participant Walker as thread-walker.py
  participant Data as Gate 2, PRD, Swift, and test data
  participant Browser
  Developer->>RefreshScript: run refresh command
  RefreshScript->>Walker: generate projections
  Walker->>Data: read and resolve traceability data
  Walker-->>RefreshScript: write thread.json and thread-broken.json
  Browser->>Browser: load visualizer page
  Browser->>Data: fetch generated projection
  Browser->>Browser: render selected requirement and proof state
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the Gate 2 thread visualizer and its descent and break-demo functionality.
Description check ✅ Passed The description covers the summary, traceability, proof, and usage steps, although the delivery checklist is omitted.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/thread-visualizer-7716

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

sonarqubecloud Bot commented Aug 4, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
docs/thread-visualizer/SPEC.md (1)

32-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a language to the fenced code block.

markdownlint reports MD040 on this block. Add text after the opening fence.

📝 Proposed fix
-```
+```text
 objective/requirement (PRD registry ID + AC text)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/thread-visualizer/SPEC.md` around lines 32 - 37, Update the fenced code
block in the objective/requirement traceability section by adding the text
language identifier to its opening fence, while preserving the block’s contents
unchanged.

Source: Linters/SAST tools

docs/thread-visualizer/visualizer.css (1)

15-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stylelint flags the Georgia keyword case.

Stylelint reports value-keyword-case on line 15. Either lowercase the value or quote it so the rule treats it as a family name string.

🎨 Proposed fix
-  --display: "Fraunces", Georgia, serif;
+  --display: "Fraunces", "Georgia", serif;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/thread-visualizer/visualizer.css` at line 15, Update the --display
font-family value so the Georgia family name satisfies the value-keyword-case
Stylelint rule, either by using lowercase text or quoting Georgia while
preserving the existing font fallback order.

Source: Linters/SAST tools

docs/thread-visualizer/visualizer.js (1)

56-77: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Escape every interpolated value in the HTML templates.

escapeHtml is applied to the requirement text only. These values go into innerHTML unescaped: thread.id (lines 98, 110), thread.requirement.source (line 100), doneTasks (line 103), m.path (line 61), t.name and t.path (lines 73-74), and t.id in the <option value> (line 47).

The current data comes from the local walker, so the risk is contained today. The templates are still fragile if a path or PRD field ever carries <, &, or ". Route all interpolated values through escapeHtml.

🛡️ Proposed fix (representative)
-    .map((m) => `<li><code>${m.path}</code></li>`)
+    .map((m) => `<li><code>${escapeHtml(m.path)}</code></li>`)
-      const where = t.path ? ` <span class="muted">in <code>${t.path}</code></span>` : "";
-      return `<li><code>${t.name}</code>${where}</li>`;
+      const where = t.path ? ` <span class="muted">in <code>${escapeHtml(t.path)}</code></span>` : "";
+      return `<li><code>${escapeHtml(t.name)}</code>${where}</li>`;
-      <p>Source: <code>${thread.requirement.source}</code></p>
+      <p>Source: <code>${escapeHtml(thread.requirement.source)}</code></p>

Also applies to: 95-127, 138-149

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/thread-visualizer/visualizer.js` around lines 56 - 77, Update all HTML
template interpolations in the visualizer, including moduleList, proofList,
option values, and thread/task rendering, to pass every dynamic value such as
thread.id, thread.requirement.source, doneTasks, m.path, t.name, t.path, and
t.id through the existing escapeHtml helper before inserting them into
innerHTML. Keep the surrounding markup and rendering behavior unchanged.

Source: Linters/SAST tools

scripts/thread-walker.py (1)

116-166: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Scan the source tree once instead of once per AC.

files_covering runs rglob("*.swift") and reads every Swift file for each AC. files_for_tests does the same over the test directories. build_threads calls both for all 50 ACs, so the walker reads the whole tree about 100 times.

Build one map of ID → files and one map of test name → file before the loop, then look up per AC. Runtime drops from O(ACs × files) to O(files).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/thread-walker.py` around lines 116 - 166, Refactor files_covering and
files_for_tests so Swift source and test trees are scanned once before
build_threads processes ACs, constructing reusable maps from coverage IDs to
implementation files and test names to test files. Update build_threads to build
or receive these indexes and perform per-AC lookups instead of invoking
full-tree scans, while preserving result formats, deduplication, and
missing-test behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/thread-visualizer/index.html`:
- Around line 31-34: Update the toggle buttons btnIntact and btnBreak to expose
their selected state with aria-pressed, and synchronize those attributes in
setMode using the current mode so only the active button reports true.

In `@scripts/thread-walker.py`:
- Around line 221-251: Update scripts/thread-walker.py in apply_demo_break to
move totals recomputation outside the thread loop, recompute acsTrackedDebt, and
derive gate2Passed from the recomputed acsGap. Regenerate
docs/thread-visualizer/data/thread-broken.json using bash
scripts/thread-visualizer-refresh.sh so gate2Passed is false. In
docs/thread-visualizer/visualizer.js, derive gateOk solely from t.gate2Passed
and remove the state.mode === "intact" condition.

In `@specs/001-mvp/craft-conventions.md`:
- Line 94: Add CI coverage for the Thread visualizer craft gate by updating the
craft-gate workflow to run scripts/thread-visualizer-refresh.sh and regenerate
docs/thread-visualizer/data/ during validation, or remove the Thread visualizer
entry from the craft conventions if it is not required. Ensure the selected
approach prevents stale generated data from merging silently.

---

Nitpick comments:
In `@docs/thread-visualizer/SPEC.md`:
- Around line 32-37: Update the fenced code block in the objective/requirement
traceability section by adding the text language identifier to its opening
fence, while preserving the block’s contents unchanged.

In `@docs/thread-visualizer/visualizer.css`:
- Line 15: Update the --display font-family value so the Georgia family name
satisfies the value-keyword-case Stylelint rule, either by using lowercase text
or quoting Georgia while preserving the existing font fallback order.

In `@docs/thread-visualizer/visualizer.js`:
- Around line 56-77: Update all HTML template interpolations in the visualizer,
including moduleList, proofList, option values, and thread/task rendering, to
pass every dynamic value such as thread.id, thread.requirement.source,
doneTasks, m.path, t.name, t.path, and t.id through the existing escapeHtml
helper before inserting them into innerHTML. Keep the surrounding markup and
rendering behavior unchanged.

In `@scripts/thread-walker.py`:
- Around line 116-166: Refactor files_covering and files_for_tests so Swift
source and test trees are scanned once before build_threads processes ACs,
constructing reusable maps from coverage IDs to implementation files and test
names to test files. Update build_threads to build or receive these indexes and
perform per-AC lookups instead of invoking full-tree scans, while preserving
result formats, deduplication, and missing-test behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 89cfd463-1b5f-494e-8f98-ac3200bdca73

📥 Commits

Reviewing files that changed from the base of the PR and between f1b2660 and 38092ab.

📒 Files selected for processing (11)
  • README.md
  • docs/thread-visualizer/README.md
  • docs/thread-visualizer/SPEC.md
  • docs/thread-visualizer/data/thread-broken.json
  • docs/thread-visualizer/data/thread.json
  • docs/thread-visualizer/index.html
  • docs/thread-visualizer/visualizer.css
  • docs/thread-visualizer/visualizer.js
  • scripts/thread-visualizer-refresh.sh
  • scripts/thread-walker.py
  • specs/001-mvp/craft-conventions.md

Comment on lines +31 to +34
<div class="break-toggle" role="group" aria-label="Thread state">
<button type="button" id="btnIntact" class="chip is-active">Intact</button>
<button type="button" id="btnBreak" class="chip chip-danger">Break the proof</button>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Expose the active toggle state to assistive technology.

btnIntact and btnBreak form a toggle pair. The active state is conveyed only by the is-active class and colour. A screen reader user cannot tell which state is selected. Add aria-pressed and keep it in sync in setMode.

♿ Proposed fix
-      <button type="button" id="btnIntact" class="chip is-active">Intact</button>
-      <button type="button" id="btnBreak" class="chip chip-danger">Break the proof</button>
+      <button type="button" id="btnIntact" class="chip is-active" aria-pressed="true">Intact</button>
+      <button type="button" id="btnBreak" class="chip chip-danger" aria-pressed="false">Break the proof</button>

In docs/thread-visualizer/visualizer.js, inside setMode:

$("btnIntact").setAttribute("aria-pressed", String(mode === "intact"));
$("btnBreak").setAttribute("aria-pressed", String(mode === "broken"));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/thread-visualizer/index.html` around lines 31 - 34, Update the toggle
buttons btnIntact and btnBreak to expose their selected state with aria-pressed,
and synchronize those attributes in setMode using the current mode so only the
active button reports true.

Comment thread scripts/thread-walker.py
Comment on lines +221 to +251
for thread in out["threads"]:
if thread["id"] != break_id:
continue
thread["proof"]["tests"] = []
covered = thread["implementation"]["covered"]
pending = thread["requirement"]["pendingTasks"]
if covered and pending:
thread["gateStatus"] = "implemented-test-pending"
thread["status"] = VIS_TRACKED
elif covered and not pending:
thread["gateStatus"] = "gap"
thread["status"] = VIS_GAP
elif pending:
thread["gateStatus"] = "planned"
thread["status"] = VIS_TRACKED
else:
thread["gateStatus"] = "gap"
thread["status"] = VIS_GAP
out["totals"]["acsWithTests"] = sum(
1 for t in out["threads"] if t["proof"]["tests"]
)
out["totals"]["acsVerified"] = sum(
1 for t in out["threads"] if t["status"] == VIS_VERIFIED
)
out["totals"]["acsGap"] = sum(
1 for t in out["threads"] if t["status"] == VIS_GAP
)
break
else:
raise SystemExit(f"Demo break ID not found in threads: {break_id}")
return out

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Stale gate2Passed in the broken projection. apply_demo_break recomputes acsWithTests, acsVerified, and acsGap, but it copies gate2Passed and acsTrackedDebt from the intact run. Gate 2 fails the build on a silent gap, so the broken projection asserts a passing gate next to a GAP, and the UI has to work around it.

  • scripts/thread-walker.py#L221-L251: recompute acsTrackedDebt and set gate2Passed from the recomputed acsGap. Move the totals recomputation out of the loop.
  • docs/thread-visualizer/data/thread-broken.json#L9-L18: regenerate with bash scripts/thread-visualizer-refresh.sh so gate2Passed becomes false.
  • docs/thread-visualizer/visualizer.js#L26-L35: after the data is correct, derive gateOk from t.gate2Passed alone and drop the state.mode === "intact" guard.
📍 Affects 3 files
  • scripts/thread-walker.py#L221-L251 (this comment)
  • docs/thread-visualizer/data/thread-broken.json#L9-L18
  • docs/thread-visualizer/visualizer.js#L26-L35
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/thread-walker.py` around lines 221 - 251, Update
scripts/thread-walker.py in apply_demo_break to move totals recomputation
outside the thread loop, recompute acsTrackedDebt, and derive gate2Passed from
the recomputed acsGap. Regenerate docs/thread-visualizer/data/thread-broken.json
using bash scripts/thread-visualizer-refresh.sh so gate2Passed is false. In
docs/thread-visualizer/visualizer.js, derive gateOk solely from t.gate2Passed
and remove the state.mode === "intact" condition.

|------|---------|--------|
| **Gate 0** | `xcodebuild build` + `xcodebuild test` (`HomesFlowTests`) | macOS |
| **Gate 2** | `bash scripts/check-traceability.sh` | Linux or macOS |
| **Thread visualizer** | `bash scripts/thread-visualizer-refresh.sh` → `docs/thread-visualizer/` | Linux or macOS |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check whether any CI workflow invokes the thread visualizer refresh or the walker.
set -euo pipefail

fd -t f -e yml -e yaml . .github 2>/dev/null | while IFS= read -r f; do
  rg -n -C3 'thread-visualizer|thread-walker' "$f" || true
done

# Also show the jobs declared in CI for context.
fd -t f 'ci.ya?ml' .github | xargs -r rg -n '^\s{0,4}[a-zA-Z0-9_-]+:\s*$' -m 60

Repository: rdryfoos/HomesFlow

Length of output: 342


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflows =="
git ls-files .github | sed -n '1,120p'

echo
echo "== thread visualizer references in workflows/docs/scripts =="
rg -n -C3 'thread-visualizer|thread-walker|craft-gate|craft-conventions|thread-visualizer-refresh' .github specs scripts docs.json 2>/dev/null || true

echo
echo "== craft gate references in repo =="
rg -n 'CI craft gates|Craft gates|Thread visualizer|thread visualizer|scripts/thread-visualizer-refresh' specs docs scripts 2>/dev/null || true

Repository: rdryfoos/HomesFlow

Length of output: 15486


Add a CI job for the thread visualizer refresh or remove it from the CI craft gates.

.github/workflows/ci.yml includes a craft-gate job, but no workflow runs scripts/thread-visualizer-refresh.sh. If this is a required PR gate, add a CI step that regenerates docs/thread-visualizer/data/ so stale data cannot merge silently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specs/001-mvp/craft-conventions.md` at line 94, Add CI coverage for the
Thread visualizer craft gate by updating the craft-gate workflow to run
scripts/thread-visualizer-refresh.sh and regenerate docs/thread-visualizer/data/
during validation, or remove the Thread visualizer entry from the craft
conventions if it is not required. Ensure the selected approach prevents stale
generated data from merging silently.

@cursor
cursor Bot merged commit 9bd6ae5 into main Aug 4, 2026
7 checks passed
@cursor
cursor Bot deleted the cursor/thread-visualizer-7716 branch August 4, 2026 01:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants