Conversation
…ction, and dashboard improvements Spec Workflow - Parallel Multi-Agent Verification: - Split monolithic spec-verifier into spec-reviewer-compliance (plan alignment, DoD, risk mitigations) and spec-reviewer-quality (code quality, security, testing, performance) - Add adversarial plan-challenger agent alongside plan-verifier for plan review phase - Both verification points now launch paired agents in parallel via Task() calls - Agents persist findings to session files for reliable retrieval across agent lifecycles - spec-verify early-launch pattern: review agents start at Step 3.0 and work in background while tests, lint, and feature parity checks run in Steps 3.1-3.4 - Update spec-plan.md, spec-verify.md, and workflow-enforcement.md for new agent topology - Update settings.json and tool_redirect.py whitelists for new agent types Standards Migration (Skills → Rules): - Convert 13 standards-* skills to concise conditional rules with frontmatter/glob patterns - Delete all pilot/skills/standards-* directories (3,967 lines removed) - Add 8 new standards rules files (957 lines) — accessibility, api, components, css, migration, models, queries, responsive - Rename standards-design from skill to rule; merge golang/python/typescript standards into existing language rule files - Add installer cleanup for legacy standards-* skill directories on upgrade Hooks System: - Add SessionEnd hook that only stops the worker when no other sessions are active - Replace raw bun worker-stop with Python session-aware shutdown - Add Bash matcher to PreToolUse hook for tool_redirect coverage - Add cross-session task isolation documentation to workflow-enforcement Console Dashboard: - Add VaultStatus card with sx Team Vault integration (installed assets, catalog, status) - Add VaultRoutes API endpoints for vault status and management - Refactor dashboard cards with Workspace badges and flexible height layout - Simplify VexorStatus and VaultStatus to display-only mode (remove interactive buttons) Marketing Website: - Add blog section with 19 SEO-optimized articles on Claude Code topics - Blog infrastructure: react-markdown rendering, typography plugin, search/filter/sort - 3D voxel logo component, scroll-to-hash navigation, cross-page routing - Sitemap with blog URLs, structured data for BlogPosting schema - Updated NavBar and Footer with blog links and cross-page section navigation Installer & Launcher: - Add discount code display for expired trials and failed license validations - Add sx update command alongside install - Test coverage for session_end hook, context cache cleanup, and banner display
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
Claude finished @maxritter's task in 3m 1s —— View job Code Review CompleteI've completed a comprehensive review of PR #49. This is a substantial architectural improvement with well-executed changes across multiple domains. SummaryOverall Assessment: Strong implementation with excellent architectural decisions. The parallel multi-agent verification system and standards migration are particularly well-designed. Found a few minor issues and suggestions for improvement. Key Strengths:
Findings1. Parallel Multi-Agent Verification ✅ ExcellentReviewed Files:
Strengths:
Minor Issues: Issue 1: Missing error handling for missing findings files
Issue 2: No validation that session ID is actually set
2. Standards Migration (Skills → Rules) ✅ ExcellentReviewed Files:
Strengths:
Verification:
Minor Issue: Issue 3: Rule file consolidation creates long files
3. Blog Section Implementation ✅ GoodReviewed Files:
Strengths:
Minor Issues: Issue 4: Blog article metadata duplicated between .md and .ts files
Issue 5: No reading time calculation verification
4. Console Dashboard Improvements ✅ GoodReviewed Files:
Strengths:
Minor Issues: Issue 6: Process timeout cleanup doesn't guarantee kill
const timeoutId = setTimeout(() => {
try {
proc.kill('SIGTERM');
setTimeout(() => proc.kill('SIGKILL'), 1000); // Force kill after 1s
} catch {}
}, timeoutMs);Issue 7: VaultStatus component doesn't handle loading state during status fetch
5. Hooks System Improvements ✅ ExcellentReviewed Files:
Strengths:
No issues found in hooks implementation. 6. Installer & Launcher Changes ✅ GoodReviewed Files:
Strengths:
Minor Issue: Issue 8: No verification that discount code is still valid
7. Documentation & Test Coverage
|
|
Caution Review failedThe pull request is closed. WalkthroughThis PR introduces the Team Vault feature with backend API routes and UI components, updates terminology from "skills" to "standards" across documentation and UI, adds a comprehensive blog system with 13 articles, implements a 3D voxel logo component, integrates routing-based navigation, replaces GitStatus with VaultStatus on the dashboard, and updates installer messaging with promotional offers. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Dashboard Client
participant API as VaultRoutes API
participant SX as SX Binary/CLI
rect rgba(100, 200, 100, 0.5)
Note over Client,SX: GET /api/vault/status Flow
Client->>API: Request /api/vault/status
API->>API: Check cache (15s timeout)
alt Cache hit & valid
API-->>Client: Return cached VaultStatus
else Cache miss or expired
API->>API: Resolve SX binary path
alt SX found
API->>SX: Run 'sx config' (15s timeout)
SX-->>API: Config JSON + assets
API->>SX: Run 'sx catalog' (15s timeout)
SX-->>API: Catalog items
API->>API: Flatten assets, derive status fields
API->>API: Cache status result
API-->>Client: Return populated VaultStatus
else SX not found
API->>API: Generate empty status
API-->>Client: Return empty VaultStatus
end
end
end
rect rgba(200, 100, 100, 0.5)
Note over Client,SX: POST /api/vault/install Flow
Client->>API: Request /api/vault/install
alt Install already in progress
API-->>Client: 409 Conflict
else No concurrent install
API->>API: Set isInstalling=true, clear cache
API->>API: Resolve SX binary
alt SX found
API->>SX: Run 'sx install' (60s timeout)
SX-->>API: Install result
API->>API: Log result, reset isInstalling
API-->>Client: 200 OK
else SX not found
API-->>Client: 500 Error
API->>API: Reset isInstalling
end
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~80 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
The VexorStatus component was simplified to remove the Re-index button and interactive functionality. Update tests to match the new display-only behavior.
|
Claude finished @maxritter's task in 1m 7s —— View job Incremental Code Review - New PushCommit: Changes AnalysisThis push updates VexorStatus widget tests to reflect the component's new display-only mode (no interactive Re-index button). Specific Changes:
Review Findings✅ No new issues introduced The test updates correctly reflect the component changes described in the PR (VexorStatus simplified to display-only mode). Test assertions are appropriate and align with the new behavior. Quality observations:
Still Open from Previous ReviewThe following issues from the initial review remain unresolved:
RecommendationThis incremental change is approved ✅ - the test updates are correct and well-implemented. However, the high-priority issues from the initial review still need to be addressed before the PR should be merged (particularly Issues #9, #10, #6, and #4). |
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
installer/cli.py (1)
52-57:⚠️ Potential issue | 🟠 Major
subprocess.runtimeout is handled at the call site but not here.
_validate_license_keycallssubprocess.run(…, timeout=30)but doesn't catchsubprocess.TimeoutExpired. If the external binary hangs, the exception propagates unhandled to_prompt_license_key, which also has no handler for it. Compare with_start_trial(Line 118) which explicitly catchesTimeoutExpired.🛡️ Proposed fix
if result.returncode == 0: ... else: ... return False + + except subprocess.TimeoutExpired: + console.error("License validation timed out") + return False(Wrap the
subprocess.runblock in a try/except similar to_start_trial.)As per coding guidelines,
installer/**/*.py: "Proper error handling and user feedback".
🤖 Fix all issues with AI agents
In `@docs/site/public/sitemap.xml`:
- Around line 9-32: The sitemap only contains 3 of ~19 blog entries; fix by
generating sitemap.xml from the canonical source: the articles array exported in
docs/site/src/content/blog/index.ts. Implement a build-time step (script or Vite
plugin) that reads the articles array (and each article's slug/metadata),
outputs entries into docs/site/public/sitemap.xml with
lastmod/changefreq/priority populated from article metadata or sensible
defaults, and run this step as part of the site build so sitemap.xml always
mirrors the articles array; update any static sitemap.xml to be generated
instead of hand-edited.
In `@docs/site/src/components/VoxelLogo3D.tsx`:
- Around line 333-348: The instancedMesh currently attaches both onClick and
onPointerDown to the same handler (handleClick), causing double invocation and
restarting the scatter animation (scattered and returnProgress) mid-flight;
remove the redundant event binding by deleting the onClick prop and keep only
onPointerDown on the instancedMesh (ensure handleClick remains wired to
onPointerDown and that any references to onClick are removed) so a single
pointer-down triggers the scatter behavior.
In `@docs/site/src/components/WorkflowSteps.tsx`:
- Line 236: The sentence in the WorkflowSteps component is using mixed
terminology: it starts "rules and standards" but ends with "creates new skills";
update the trailing word to match the Standards migration (e.g., change "creates
new skills" to "creates new standards" or rephrase to "creates new standards and
conventions") in the JSX string inside the WorkflowSteps.tsx component so the
terminology is consistent throughout the sentence.
In `@docs/site/src/content/blog/choosing-the-right-claude-model.md`:
- Around line 56-60: The fenced code block containing the CLI commands (/model
opus, /model sonnet, /model haiku) is missing a language identifier; update the
opening fence to include a language (e.g., change ``` to ```bash) so the block
is marked as shell/CLI and static analysis no longer flags it.
- Around line 7-11: Replace the outdated model names in the markdown table:
change the "Sonnet 4" cell to "Sonnet 4.5" and change the "Haiku 3.5" cell to
"Haiku 4.5" (leave "Opus 4.6" unchanged) and ensure any adjacent text
referencing the 1M token context beta still applies to Opus 4.6 and Sonnet 4.5;
update only the table cells containing the strings "Sonnet 4" and "Haiku 3.5".
In `@docs/site/src/content/blog/claude-code-hooks-guide.md`:
- Around line 65-85: The example hook naively greps the raw CLAUDE_TOOL_INPUT
JSON which can miss or mis-match commands because the actual command is likely
inside a JSON field with escaping; update the PreToolUse Bash hook (the
"matcher": "Bash" entry and its "command" string) to first parse the JSON and
extract the command field (for example using jq to read .command) before
applying the grep, or at minimum add a clear caveat in the prose noting this is
a simplified example and users should parse CLAUDE_TOOL_INPUT JSON (e.g., with
jq) to reliably match nested/escaped command strings.
In `@docs/site/src/content/blog/claude-code-rules-guide.md`:
- Around line 30-33: Add a language identifier to the fenced code block
containing the guideline example so the Markdown linter (MD040) stops flagging
it; update the opening fence from ``` to ```text for the block that includes "❌
Write clean, maintainable code..." and "✅ Functions must be under 30 lines..."
so the snippet is treated as plain text.
In `@docs/site/src/content/blog/claude-code-task-management.md`:
- Around line 39-46: The prose contradicts the shown task list (the example
lists a strict chain "Task 1 → Task 2 → Task 3 → Task 4") but the sentence
"Tasks 3 and 4 can also run in parallel once task 2 completes — just set both to
depend on task 2." implies an alternative configuration; fix by either (A)
updating the example task list to demonstrate the parallel variant (e.g., show
Task 3 and Task 4 both with [blockedBy: 2]) or (B) rewording the sentence to
explicitly present it as an alternative (e.g., "Alternatively, you can configure
Tasks 3 and 4 to run in parallel by setting both to depend on Task 2."). Locate
the change near the example task list and the sentence starting "Tasks 3 and
4..." and apply one of these two edits to remove the contradiction.
In `@docs/site/src/content/blog/context7-library-docs.md`:
- Around line 17-26: Update the mcpServers configuration for the context7 entry:
change the npm package name from "@upstreamapi/context7-mcp" to the correct
"@upstash/context7-mcp" and include the required "--api-key" argument in the
args array (keep the existing "-y" flag), so the context7 object's command/args
(mcpServers -> context7 -> command and args) use ["-y", "@upstash/context7-mcp",
"--api-key", "YOUR_API_KEY"].
In `@docs/site/src/content/blog/sandboxing-claude-code.md`:
- Around line 46-68: The bubblewrap example currently includes the
`--unshare-net` flag which disables network and therefore prevents Claude Code
from functioning; remove `--unshare-net` from the default bwrap invocation shown
(the multi-line `bwrap \ ... claude` block) so the example works out-of-the-box,
and instead surface `--unshare-net` as an optional flag in the Key flags list
(mark it explicitly as "optional — use for offline/local-only runs") so readers
know how to enable it when network access is not required.
In `@docs/site/src/content/blog/slash-commands-and-init.md`:
- Around line 115-119: Update the terminology in the slash-command descriptions
to use "standards" instead of "skills": change the `/sync` bullet from
"Synchronizes rules and skills with the codebase" to "Synchronizes rules and
standards with the codebase" and change the `/learn` bullet from "Extracts
reusable knowledge into skills" to "Extracts reusable knowledge into standards"
so the documentation consistently uses the new term.
In `@docs/site/src/content/blog/spec-driven-development.md`:
- Around line 66-68: Add a language identifier to the fenced code block that
currently contains "/spec Add password reset with email verification" so the
linter MD040 is satisfied; update the block delimiter from ``` to ```bash (or
```text) surrounding the same content in
docs/site/src/content/blog/spec-driven-development.md so the block is treated as
a CLI snippet.
In `@docs/site/src/content/blog/terminal-setup-for-claude-code.md`:
- Around line 25-27: The fenced code block containing the Ghostty config line
`keybind = shift+enter=text:\n` is missing a language identifier; update the
block to include a language (for example `text` or `ini`) so the fence becomes
```text (or ```ini) followed by the `keybind = shift+enter=text:\n` line and the
closing ```, ensuring markdown linting and rendering are satisfied.
In `@docs/site/src/content/blog/worktree-isolation-for-features.md`:
- Around line 18-21: The fenced code block showing the directory listing must
include a language identifier to satisfy MD040; update the triple-backtick fence
surrounding the lines "your-project/ ← Your branch (clean)" and
".worktrees/spec-auth/ ← Claude's branch (isolated)" to use ```text so the block
is treated as plain text by the linter.
In `@docs/site/src/pages/BlogPage.tsx`:
- Around line 26-30: The forEach callbacks inside the useMemo for allTags
implicitly return values (from the concise arrow bodies); change those arrow
callbacks to block bodies so they are statements instead of expressions.
Specifically, update the callback for articles.forEach and the inner
a.tags.forEach to use braces and statement form (use tagSet.add(t); inside the
inner block) so neither callback returns a value; this will satisfy the Biome
lint rule while keeping the same behavior in the allTags useMemo that builds
tagSet from articles and their tags.
🧹 Nitpick comments (18)
installer/steps/claude_files.py (1)
284-292: Minor: emptiness check on Line 288 isn't wrapped in thetryblock.If
skills_diris removed externally between theexists()anditerdir()calls,iterdir()raises an unhandledOSError. This mirrors the same pattern on Line 310 in_cleanup_legacy_project_dirs, so it's a pre-existing style — but worth noting for robustness.Suggested defensive tweak
- if skills_dir.exists() and not any(skills_dir.iterdir()): - try: - skills_dir.rmdir() - except (OSError, IOError): - pass + try: + if skills_dir.exists() and not any(skills_dir.iterdir()): + skills_dir.rmdir() + except (OSError, IOError): + passinstaller/steps/dependencies.py (1)
679-681: Awkward spinner messages for the update step.
_install_with_spinnerproduces messages like "Installing sx update...", "sx update installed", and "Could not install sx update – please install manually". These read oddly because the helper assumes an install action. Consider either passing a more natural label (e.g.,"sx (latest update)") or callingupdate_sxdirectly with bespoke UI messages, similar to how_install_claude_code_with_uihandles its own feedback.Example: direct UI calls
if _install_with_spinner(ui, "sx (team assets)", install_sx): installed.append("sx") - _install_with_spinner(ui, "sx update", update_sx) + if ui: + with ui.spinner("Updating sx to latest version..."): + updated = update_sx() + if updated: + ui.success("sx updated to latest version") + # Silently continue if update fails — non-critical + else: + update_sx()installer/cli.py (2)
102-105: Promo message duplicated in 4 places with inconsistent formatting.The "50% off / TRIAL50OFF" block is copy-pasted across
_run_trial_start,_prompt_license_key,_handle_license_flow(×2), plusui.py's banner. The subscribe-URL line at Line 244 uses[bold]Subscribe at:[/bold] [cyan]…[/cyan]while Lines 105, 265, and 300 use[cyan]Subscribe at: …[/cyan]— a visual inconsistency.Extract a small helper (or at least constants) to keep the promo text and styling consistent and easy to update when the campaign changes.
♻️ Suggested helper
+PROMO_CODE = "TRIAL50OFF" +SUBSCRIBE_URL = "https://claude-pilot.com" + + +def _print_promo(console: Console) -> None: + """Print the current promotional offer.""" + console.print( + f" [bold yellow]50% off your first month:[/bold yellow] [bold white]{PROMO_CODE}[/bold white]" + ) + console.print(f" [cyan]Subscribe at: {SUBSCRIBE_URL}[/cyan]")Also applies to: 243-244, 263-265, 299-302
41-70:_ = project_dir— unused parameter silencing may mask a future bug.
project_diris accepted, immediately discarded (_ = project_dir), and the function hardcodesPath.home() / ".pilot" / "bin" / "pilot"instead. The same pattern repeats in_start_trial,_check_trial_used, and_get_license_info. This is fine if intentional, but worth noting that the parameter is entirely vestigial across all four functions.installer/ui.py (1)
60-66:/dev/ttyfile handle may leak ifConsole.close()is never called.
_get_tty_inputopens/dev/ttyand stores it inself._tty, butConsoleis not used as a context manager andclose()is never called incli.py. If the installer exits normally, the handle leaks. Consider makingConsolea context manager (__enter__/__exit__) or closing in afinallyblock incmd_install.README.md (1)
394-398: Consider clarifying the relationship between "Language Standards" and "Built-in Coding Standards" sections.The documentation has two sections that could be clearer about their relationship:
- Lines 394-398 — "Language Standards (3 rules)" under "Built-in Rules"
- Lines 426-444 — "Built-in Coding Standards" with conditional activation table
From the PR summary, language-specific standards were merged into existing rule files (python-rules.md, typescript-rules.md, golang-rules.md), which now activate conditionally by file type. However, readers might be confused about whether:
- The 3 "Language Standards" rules are the same as the Python/TypeScript/Go entries in the "Built-in Coding Standards" table, or
- They are different layers (always-loaded base rules vs. conditional standards)
📝 Suggested clarification
Consider adding a brief note to one of these sections explaining their relationship. For example, at line 428:
### Built-in Coding Standards -Conditional rules activated by file type — loaded only when working with matching files: +Conditional rules activated by file type — loaded only when working with matching files. This includes the language-specific rules (Python, TypeScript, Go) which are now activated conditionally rather than loaded in every session:Or rename the subsection at line 394 to clarify:
-<summary><b>Language Standards (3 rules)</b></summary> +<summary><b>Language-Specific Rules (3 rules, conditionally loaded)</b></summary>Also applies to: 426-444
docs/site/src/components/WhatsInside.tsx (1)
59-63: Team Vault card still references "skills".Line 62:
"Push and pull rules, commands, and skills via a private Git repo."— If the terminology migration is comprehensive, this should also be updated. If "skills" here refers to learned knowledge (distinct from "standards"), consider clarifying to avoid confusion.docs/site/src/content/blog/types.ts (1)
7-10: Inconsistent collection types:tagsisstring[]butkeywordsisstring.
tagsuses an array whilekeywordsis a plain string. Ifkeywordsis intended as a comma-separated meta keywords string, this works but is inconsistent withtags. Consider aligning them — either both asstring[](and join for meta output) or document the distinction.console/src/services/worker/http/routes/VaultRoutes.ts (1)
159-186: Timeout kills the process but doesn't surface a distinct timeout error.When the timeout fires,
proc.kill()causes a non-zero exit code, and the resulting error says"sx exited with code ...". Callers (e.g.,handleStatus) can't distinguish a timeout from a genuine CLI failure. Consider throwing a dedicated error message so that logs are actionable.Suggested improvement
+ let timedOut = false; const timeoutId = setTimeout(() => { try { + timedOut = true; proc.kill(); } catch {} }, timeoutMs); try { const [stdout, stderr] = await Promise.all([ new Response(proc.stdout).text(), new Response(proc.stderr).text(), ]); const exitCode = await proc.exited; if (exitCode !== 0) { + if (timedOut) { + throw new Error(`sx command timed out after ${timeoutMs}ms`); + } throw new Error(`sx exited with code ${exitCode}: ${stderr.slice(0, 200)}`); } return stdout; } finally { clearTimeout(timeoutId); }docs/site/src/pages/BlogArticlePage.tsx (1)
109-112:prose-invertmay break readability in light mode.If the site ever supports a light theme,
prose-invertforces light-colored text regardless. Consider usingdark:prose-invertto scope it to dark mode only.Suggested fix
- <div className="prose prose-invert max-w-none"> + <div className="prose dark:prose-invert max-w-none">docs/site/package.json (1)
47-47:@types/threebelongs indevDependencies.Type definition packages are only needed at compile time. Moving it to
devDependencieskeeps the production dependency footprint smaller (relevant if this package is ever consumed or if install size matters for CI).Proposed fix
Move from
dependenciestodevDependencies:"dependencies": { ... - "@types/three": "^0.182.0", ... }, "devDependencies": { ... + "@types/three": "^0.182.0", ... }console/src/ui/viewer/hooks/useStats.ts (3)
154-161:loadVaultStatusdoesn't checkres.okbefore parsing JSON.If the endpoint returns a non-2xx response,
res.json()may throw or return an error body that gets set as vault status. The existingloadVexorStatushas the same pattern, but adding a guard here would prevent setting malformed data into state.Suggested improvement
const loadVaultStatus = useCallback(async () => { try { const res = await fetch('/api/vault/status'); + if (!res.ok) return; const data = await res.json(); setVaultStatus(data); } catch { } }, []);
275-305: Vault status is fetched once but never polled.
loadVaultStatusis called once at line 277 but not set up on an interval, unlikeloadVexorStatuswhich polls every 60s. If vault status can change (e.g., installation completes), the dashboard won't reflect it until a full page reload.
75-100: Import vault type definitions from shared route definitions to avoid type divergence.
VaultAsset,VaultCatalogItem, andVaultStatusare redefined locally but differ from the server definitions inVaultRoutes.ts. The localVaultCatalogItemis missing theupdatedAt: stringfield present in the server version, creating a type mismatch that could cause maintenance issues or unexpected behavior if the field is used downstream. Import these types from the shared route definitions instead of redefining them.docs/site/src/pages/BlogPage.tsx (1)
32-52: Redundant sort —articlesis already sorted by date descending inindex.ts.Line 49 re-sorts
resultby date descending, but the source array fromindex.tsis already sorted the same way, and no prior operation changes the order (filter preserves relative order). This is harmless but unnecessary work.console/src/ui/viewer/views/Dashboard/VaultStatus.tsx (1)
3-28:VaultAssetandVaultCatalogItemare now defined in three places.These interfaces are duplicated in
VaultRoutes.ts,useStats.ts, and here. Additionally,isInstallingandprofileare declared inVaultStatusPropsbut never used in the component (line 49 doesn't destructure them and the render paths don't reference them). Consider importing the shared types and removing unused props.docs/site/src/components/VoxelLogo3D.tsx (2)
171-182:displacedref is zero-initialized; first frame may read stale positions.
displacedis created asnew Float32Array(allCubes.length * 3)(all zeros), and actual target positions are written in auseEffect. BecauseuseFramecallbacks can fire on the first animation frame before effects flush, the very first render tick could read(0, 0, 0)for every cube. In practice R3F batches the first frame after mount so this is unlikely to manifest, but initializing inline removes the window entirely.Proposed fix
- const displaced = useRef<Float32Array>(new Float32Array(allCubes.length * 3)); - - useEffect(() => { - for (let i = 0; i < allCubes.length; i++) { - displaced.current[i * 3] = allCubes[i].targetX; - displaced.current[i * 3 + 1] = allCubes[i].targetY; - displaced.current[i * 3 + 2] = 0; - } - }, [allCubes]); + const displaced = useRef<Float32Array>( + (() => { + const arr = new Float32Array(allCubes.length * 3); + for (let i = 0; i < allCubes.length; i++) { + arr[i * 3] = allCubes[i].targetX; + arr[i * 3 + 1] = allCubes[i].targetY; + arr[i * 3 + 2] = 0; + } + return arr; + })(), + );
225-225:easeOutis re-created on every render; hoist to module scope.This pure function has no dependency on component state or props. Defining it inside the component body creates a new function object each render. It's used in the hot
useFrameloop, so while the performance impact is negligible, hoisting it keeps the component body leaner.Proposed fix
+const easeOut = (t: number) => 1 - Math.pow(1 - t, 3); + function VoxelMesh() { ... - const easeOut = (t: number) => 1 - Math.pow(1 - t, 3);
| return ( | ||
| <instancedMesh | ||
| ref={meshRef} | ||
| args={[undefined, undefined, allCubes.length]} | ||
| frustumCulled={false} | ||
| onClick={handleClick} | ||
| onPointerDown={handleClick} | ||
| > | ||
| <boxGeometry args={[CUBE, CUBE, CUBE]} /> | ||
| <meshStandardMaterial | ||
| color="#ffffff" | ||
| roughness={0.5} | ||
| metalness={0.1} | ||
| /> | ||
| </instancedMesh> | ||
| ); |
There was a problem hiding this comment.
onClick and onPointerDown both call handleClick, causing double scatter on click.
When the user clicks, onPointerDown fires on mouse-down and onClick fires on mouse-up. Both invoke handleClick, which resets scattered = true and returnProgress = 0. The second invocation restarts the scatter animation mid-flight, causing a visible hitch. If you want the responsive feel of pointer-down, drop onClick; if you prefer click semantics, drop onPointerDown.
Proposed fix (keep only onPointerDown for responsiveness)
<instancedMesh
ref={meshRef}
args={[undefined, undefined, allCubes.length]}
frustumCulled={false}
- onClick={handleClick}
onPointerDown={handleClick}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return ( | |
| <instancedMesh | |
| ref={meshRef} | |
| args={[undefined, undefined, allCubes.length]} | |
| frustumCulled={false} | |
| onClick={handleClick} | |
| onPointerDown={handleClick} | |
| > | |
| <boxGeometry args={[CUBE, CUBE, CUBE]} /> | |
| <meshStandardMaterial | |
| color="#ffffff" | |
| roughness={0.5} | |
| metalness={0.1} | |
| /> | |
| </instancedMesh> | |
| ); | |
| return ( | |
| <instancedMesh | |
| ref={meshRef} | |
| args={[undefined, undefined, allCubes.length]} | |
| frustumCulled={false} | |
| onPointerDown={handleClick} | |
| > | |
| <boxGeometry args={[CUBE, CUBE, CUBE]} /> | |
| <meshStandardMaterial | |
| color="#ffffff" | |
| roughness={0.5} | |
| metalness={0.1} | |
| /> | |
| </instancedMesh> | |
| ); |
🤖 Prompt for AI Agents
In `@docs/site/src/components/VoxelLogo3D.tsx` around lines 333 - 348, The
instancedMesh currently attaches both onClick and onPointerDown to the same
handler (handleClick), causing double invocation and restarting the scatter
animation (scattered and returnProgress) mid-flight; remove the redundant event
binding by deleting the onClick prop and keep only onPointerDown on the
instancedMesh (ensure handleClick remains wired to onPointerDown and that any
references to onClick are removed) so a single pointer-down triggers the scatter
behavior.
| </div> | ||
| <p className="text-xs text-muted-foreground"> | ||
| Syncs rules and skills with your codebase — explores patterns, updates project docs, discovers undocumented conventions, creates new skills. | ||
| Syncs rules and standards with your codebase — explores patterns, updates project docs, discovers undocumented conventions, creates new skills. |
There was a problem hiding this comment.
Inconsistent terminology within the same sentence.
The line starts with "rules and standards" but ends with "creates new skills". Since this line was updated as part of the Skills → Standards migration, the trailing "skills" looks like an oversight.
Proposed fix
- Syncs rules and standards with your codebase — explores patterns, updates project docs, discovers undocumented conventions, creates new skills.
+ Syncs rules and standards with your codebase — explores patterns, updates project docs, discovers undocumented conventions, creates new standards.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Syncs rules and standards with your codebase — explores patterns, updates project docs, discovers undocumented conventions, creates new skills. | |
| Syncs rules and standards with your codebase — explores patterns, updates project docs, discovers undocumented conventions, creates new standards. |
🤖 Prompt for AI Agents
In `@docs/site/src/components/WorkflowSteps.tsx` at line 236, The sentence in the
WorkflowSteps component is using mixed terminology: it starts "rules and
standards" but ends with "creates new skills"; update the trailing word to match
the Standards migration (e.g., change "creates new skills" to "creates new
standards" or rephrase to "creates new standards and conventions") in the JSX
string inside the WorkflowSteps.tsx component so the terminology is consistent
throughout the sentence.
| - `/spec` — Triggers the spec-driven development pipeline | ||
| - `/sync` — Synchronizes rules and skills with the codebase | ||
| - `/learn` — Extracts reusable knowledge into skills | ||
| - `/vault` — Manages shared team assets | ||
|
|
There was a problem hiding this comment.
Terminology inconsistency: "skills" should be "standards" or "rules".
Lines 116–117 reference "skills" (Synchronizes rules and skills, Extracts reusable knowledge into skills), but this PR is migrating terminology from "Skills" to "Standards" across the entire site and documentation. These references should be updated for consistency.
Proposed fix
-- `/sync` — Synchronizes rules and skills with the codebase
-- `/learn` — Extracts reusable knowledge into skills
+- `/sync` — Synchronizes rules and standards with the codebase
+- `/learn` — Extracts reusable knowledge into rules📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - `/spec` — Triggers the spec-driven development pipeline | |
| - `/sync` — Synchronizes rules and skills with the codebase | |
| - `/learn` — Extracts reusable knowledge into skills | |
| - `/vault` — Manages shared team assets | |
| - `/spec` — Triggers the spec-driven development pipeline | |
| - `/sync` — Synchronizes rules and standards with the codebase | |
| - `/learn` — Extracts reusable knowledge into rules | |
| - `/vault` — Manages shared team assets |
🤖 Prompt for AI Agents
In `@docs/site/src/content/blog/slash-commands-and-init.md` around lines 115 -
119, Update the terminology in the slash-command descriptions to use "standards"
instead of "skills": change the `/sync` bullet from "Synchronizes rules and
skills with the codebase" to "Synchronizes rules and standards with the
codebase" and change the `/learn` bullet from "Extracts reusable knowledge into
skills" to "Extracts reusable knowledge into standards" so the documentation
consistently uses the new term.
| ``` | ||
| /spec Add password reset with email verification | ||
| ``` |
There was a problem hiding this comment.
Add a language identifier to the fenced code block.
The linter flags this as MD040. Since it's a CLI command, use bash or text.
Proposed fix
-```
+```bash
/spec Add password reset with email verification</details>
<details>
<summary>🧰 Tools</summary>
<details>
<summary>🪛 markdownlint-cli2 (0.20.0)</summary>
[warning] 66-66: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
</details>
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
In @docs/site/src/content/blog/spec-driven-development.md around lines 66 - 68,
Add a language identifier to the fenced code block that currently contains
"/spec Add password reset with email verification" so the linter MD040 is
satisfied; update the block delimiter from tobash (or ```text)
surrounding the same content in
docs/site/src/content/blog/spec-driven-development.md so the block is treated as
a CLI snippet.
</details>
<!-- fingerprinting:phantom:medusa:phoenix -->
<!-- This is an auto-generated comment by CodeRabbit -->
| ``` | ||
| keybind = shift+enter=text:\n | ||
| ``` |
There was a problem hiding this comment.
Fenced code block missing language identifier.
The Ghostty config block should specify a language (e.g., text or ini) to satisfy markdown linting rules and improve rendering consistency with the rest of the article.
Proposed fix
-```
+```text
keybind = shift+enter=text:\n</details>
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)
[warning] 25-25: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In `@docs/site/src/content/blog/terminal-setup-for-claude-code.md` around lines 25
- 27, The fenced code block containing the Ghostty config line `keybind =
shift+enter=text:\n` is missing a language identifier; update the block to
include a language (for example `text` or `ini`) so the fence becomes ```text
(or ```ini) followed by the `keybind = shift+enter=text:\n` line and the closing
```, ensuring markdown linting and rendering are satisfied.
| ``` | ||
| your-project/ ← Your branch (clean) | ||
| .worktrees/spec-auth/ ← Claude's branch (isolated) | ||
| ``` |
There was a problem hiding this comment.
Add a language identifier to the fenced code block.
The linter flags MD040 here. Since this is a directory listing, use ```text to satisfy the rule.
Proposed fix
-```
+```text
your-project/ ← Your branch (clean)
.worktrees/spec-auth/ ← Claude's branch (isolated)</details>
<details>
<summary>🧰 Tools</summary>
<details>
<summary>🪛 markdownlint-cli2 (0.20.0)</summary>
[warning] 18-18: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
</details>
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
In @docs/site/src/content/blog/worktree-isolation-for-features.md around lines
18 - 21, The fenced code block showing the directory listing must include a
language identifier to satisfy MD040; update the triple-backtick fence
surrounding the lines "your-project/ ← Your branch (clean)" and
".worktrees/spec-auth/ ← Claude's branch (isolated)" to use ```text so the block
is treated as plain text by the linter.
</details>
<!-- fingerprinting:phantom:poseidon:phoenix -->
<!-- This is an auto-generated comment by CodeRabbit -->
| const allTags = useMemo(() => { | ||
| const tagSet = new Set<string>(); | ||
| articles.forEach((a) => a.tags.forEach((t) => tagSet.add(t))); | ||
| return Array.from(tagSet).sort(); | ||
| }, []); |
There was a problem hiding this comment.
Biome lint: forEach callbacks should not return a value.
The arrow functions implicitly return the result of tagSet.add() / inner forEach(). Wrap in braces to satisfy the linter.
Fix
const allTags = useMemo(() => {
const tagSet = new Set<string>();
- articles.forEach((a) => a.tags.forEach((t) => tagSet.add(t)));
+ articles.forEach((a) => { a.tags.forEach((t) => { tagSet.add(t); }); });
return Array.from(tagSet).sort();
}, []);🧰 Tools
🪛 Biome (2.3.14)
[error] 28-28: This callback passed to forEach() iterable method should not return a value.
Either remove this return or remove the returned value.
(lint/suspicious/useIterableCallbackReturn)
[error] 28-28: This callback passed to forEach() iterable method should not return a value.
Either remove this return or remove the returned value.
(lint/suspicious/useIterableCallbackReturn)
🤖 Prompt for AI Agents
In `@docs/site/src/pages/BlogPage.tsx` around lines 26 - 30, The forEach callbacks
inside the useMemo for allTags implicitly return values (from the concise arrow
bodies); change those arrow callbacks to block bodies so they are statements
instead of expressions. Specifically, update the callback for articles.forEach
and the inner a.tags.forEach to use braces and statement form (use
tagSet.add(t); inside the inner block) so neither callback returns a value; this
will satisfy the Biome lint rule while keeping the same behavior in the allTags
useMemo that builds tagSet from articles and their tags.
…ion, and update skills-to-standards references PR review fixes: - Add VaultRoutes API test coverage (13 tests: status caching, install concurrency, error handling) - Fix process kill reliability with SIGTERM-then-SIGKILL fallback - Add session ID validation with "default" fallback in spec-verify - Add 3-step fallback for missing findings files in spec-verify - Add loading skeleton UI to VaultStatus dashboard component Blog infrastructure: - Eliminate metadata duplication by adding YAML frontmatter to all 19 .md files - Rewrite index.ts to auto-discover articles via import.meta.glob - Delete all 19 per-article .ts export files (single .md file per article now) - Generate sitemap at build time via Vite plugin instead of static file - Show all category tags in filter bar instead of first 4 - Widen search bar for better usability - Fix rotating header animation with transition-based approach Documentation: - Update README and website to reflect skills-to-standards migration - Add Skills row to README for custom user skills created via /learn - Fix AgentRoster standards count (14 → 13) and listed domains - Add 400-line guidance for rule files to coding standards
# [6.4.0](v6.3.3...v6.4.0) (2026-02-12) ### Bug Fixes * consolidate website deployment into release pipelines and fix changelog duplication ([d69de9d](d69de9d)) * prevent changelog duplication from squash merge commits ([036e1ad](036e1ad)) ### Features * parallel multi-agent verification, standards migration, blog & dashboard ([#49](#49)) ([e91a8bd](e91a8bd))
|
🎉 This PR is included in version 6.4.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |

Summary
Major feature release introducing parallel multi-agent verification for the /spec workflow, migrating coding standards from skills to conditional rules, adding a full blog section to the marketing website, and improving the console dashboard.
Parallel Multi-Agent Verification (Spec Workflow)
The biggest change in this release — the /spec verification system has been fundamentally redesigned for higher reliability and coverage:
spec-verifierinto two focused reviewers:spec-reviewer-compliance— verifies implementation matches the plan (DoD criteria, risk mitigations, feature completeness)spec-reviewer-quality— verifies code quality, security, testing adequacy, performance, and error handlingplan-challengeragent alongside the existingplan-verifierfor plan review — challenges assumptions, finds untested failure modes, identifies hidden dependencies, and questions optimistic estimatesTask()calls in a single message, cutting review wall-clock time significantly~/.pilot/sessions/<id>/findings-*.json) for reliable retrieval across agent lifecyclesspec-plan.md,spec-verify.md,workflow-enforcement.md,settings.json, andtool_redirect.pyfor the new 4-agent topologyStandards Migration (Skills → Rules)
standards-*skills to concise conditional rules with frontmatter/glob patterns — reduces token usage since rules are loaded on-demand via file glob matching rather than always-on skill definitionspilot/skills/standards-*directories (3,967 lines removed)standards-*skill directories on upgradeHooks System Improvements
tool_redirect.pycoverageCLAUDE_CODE_TASK_LIST_IDscopes tasks per-session, preventing cross-contamination from Pilot Memory observationsConsole Dashboard
Marketing Website — Blog Section
react-markdownrendering with@tailwindcss/typography, search/filter/sort, category tags, reading time estimatesScrollToHashandScrollToTopcomponentsBlogPostingstructured data for SEOInstaller & Launcher
TRIAL50OFF) displayed for expired trials and failed license validationssx updatecommand added alongside install for keeping Team Vault CLI currentFiles Changed
Test plan
uv run pytest launcher/tests -q)cd console && bun run typecheck)cd docs/site && npm run build)/blogand/blog/:slughead -10 pilot/agents/plan-challenger.md)spec-verifierreferences (grep -r "spec-verifier" pilot/returns no matches)Summary by CodeRabbit
Release Notes
New Features
Documentation
Refactor
Tests
Chores