TUI ↔ Web Parity Check
Background
The Config Manager project enforces a permanent parity rule:
The UI repos in
.cm/project.json(those whose.roleis exactly "tui" or "web" — case-insensitive, anchored match) MUST be kept functionally and test-wise identical.
- Every feature implemented in one UI must exist in the other.
- Every security pattern (sanitization, body limits, token masking, input validation) must be applied identically in both.
- Test coverage must match — same edge cases, same error paths, same boundary conditions tested on both sides.
- Documentation (README, CONTRIBUTING, API docs) must stay in sync.
- When making changes to either UI, always check if the same change is needed in the other.
- This rule applies proactively: do not wait for a reviewer to find parity gaps.
Repos
Read TUI and Web repo paths from .cm/project.json if available. Discovery order:
$CM_REPO_BASE → cwd → parent directory → $HOME/repo. If no manifest is found,
ask the user for the required values before proceeding.
# Discover project manifest: $CM_REPO_BASE → cwd → parent → $HOME/repo (optional — ask user for context if unavailable)
_cm="${CM_REPO_BASE:+$CM_REPO_BASE/.cm/project.json}"
[ -f "${_cm:-}" ] || _cm=".cm/project.json" # cwd
[ -f "$_cm" ] || _cm="../.cm/project.json" # parent dir
[ -f "$_cm" ] || _cm="$HOME/repo/.cm/project.json" # fallback
if [ -f "$_cm" ]; then
jq '.repos[] | select((.role // "") | ascii_downcase | test("^(tui|web)$"))' "$_cm"
else
echo "No manifest found — ask the user for owner, repo names, and other context."
fi
| UI | Stack |
|---|---|
| TUI | Bubble Tea terminal UI |
| Web | htmx + Go templates web UI |
Procedure
Execute every step below in order. Do not skip steps.
Step 1 — Inventory Features
Scan both repos and build a feature matrix.
How to detect features:
- TUI: scan
menu.goforMenuItemdefinitions, scantui.gofor screen states, scanviews.gofor render functions. - Web: scan
routes.gofor HTTP handlers, scantemplates/for page templates, scanweb.gofor route registration.
Produce a table:
| Feature | TUI | Web | Status |
| --- | --- | --- | --- |
| Dashboard / System Info | ✅ | ✅ | ✅ Parity |
| Update status view | ✅ | ✅ | ✅ Parity |
| Pending packages list | ✅ | ❌ | ⚠️ Gap |
| … | … | … | … |
Mark each row:
- ✅ Parity — feature exists in both UIs.
- ⚠️ Gap — feature exists in one UI but not the other.
Step 2 — Inventory Security Patterns
Compare security implementations across both codebases. Discover the actual function names and locations dynamically — do NOT rely on hardcoded references (functions get renamed/moved between releases).
For each pattern below, grep -rnE both repos to locate the implementation,
then diff the logic:
| Pattern | How to Find | What to Check |
|---|---|---|
| Input sanitization (C0 + C1 control chars) | `grep -rnE --include='*.go' 'sanitize | Sanitize' .` |
| Body size limits | `grep -rnE --include='*.go' 'LimitReader | MaxBytesReader' .` |
| Token masking in logs | `grep -rnE --include='*.go' 'mask | Mask |
| Path traversal prevention | `grep -rnE --include='*.go' 'cleanPlugin | filepath.Clean |
| Error message sanitization | `grep -rnE --include='.go' 'sanitize.[Bb]ody | sanitize.*[Ee]rror' .` |
For each pattern, read the actual implementation in both repos and diff the logic. Flag any divergence.
Step 3 — Compare Test Coverage
For each feature identified in Step 1, locate the corresponding *_test.go
files in both repos and compare:
- Edge cases — empty input, nil, max-length strings, Unicode.
- Error paths — API unreachable, HTTP 403, 404, 500 responses.
- Boundary conditions — truncation, pagination, concurrent access.
- Table-driven test consistency — same test-case tables used on both sides.
Produce a per-feature comparison noting any test cases present in one repo but missing from the other.
Step 4 — Compare Documentation
Check the following files in both repos for content alignment:
| Document | What to Compare |
|---|---|
README.md |
Feature lists, badges, install instructions |
docs/USAGE.md |
Usage instructions, examples |
specs/SPEC.md |
Capability descriptions, supported operations |
CONTRIBUTING.md |
Contribution guidelines, development setup |
Flag any content that is present in one repo's docs but absent or contradictory in the other.
Step 5 — Generate Report
Compile all findings into a single report with this structure:
# TUI ↔ Web Parity Report
## Summary
- ✅ Features in sync: X
- ⚠️ Feature gaps: Y
- 🔴 Security divergence: Z
- 📝 Doc mismatches: W
## Feature Gaps
### ⚠️ {Feature Name}
- **Present in:** TUI (`menu.go:L42`)
- **Missing from:** Web
- **Impact:** Medium — users on web cannot see {feature}
- **Suggested fix:** Add handler in `routes.go`, template in `templates/{name}.html`
## Security Divergence
### 🔴 {Pattern Name}
- **TUI implementation:** `sanitizeText()` strips C1 chars (U+0080–U+009F)
- **Web implementation:** Only strips ASCII controls (missing C1 range)
- **Risk:** Medium — C1 control chars could reach browser
- **Fix:** Update web sanitizer to match TUI
## Doc Mismatches
### 📝 {Doc Name}
- **TUI says:** "Supports 5 themes"
- **Web says:** "Supports 3 themes"
- **Fix:** Update web README
## Test Coverage Gaps
### 🧪 {Feature / File}
- **TUI tests:** 12 cases covering empty, nil, max-length, Unicode
- **Web tests:** 8 cases — missing nil and Unicode edge cases
- **Fix:** Add missing test cases to `web/{file}_test.go`
Step 6 — Optionally Create Issues
Ask the user whether to create GitHub issues for each gap. If approved, run:
# Use the web repo name discovered from the manifest query in Step 1
_issue_body="$(mktemp)"
echo "{details}" > "$_issue_body"
gh issue create --repo {OWNER}/{web_repo_name} --title "Parity: Add {feature}" --body-file "$_issue_body"
rm -f "$_issue_body"
(Replace {web_repo_name} with the actual repo name from the manifest repos[] query above)
Create one issue per gap so they can be tracked and closed independently.
Important Notes
- This skill does not make changes — it only reports gaps.
- The user decides which gaps to fix and in what order.
- Always scan both repos completely — neither is the "source of truth."
- Some intentional differences may exist (TUI has keyboard navigation, Web has mouse interaction) — flag these but do not treat them as bugs.