Skip to content

fix(chat): harden local chat path links config defaults and self-heal#267

Open
DerrickBarra wants to merge 5 commits intodaggerhashimoto:masterfrom
GambitGamesLLC:feature/local-chat-links-self-heal-and-defaults
Open

fix(chat): harden local chat path links config defaults and self-heal#267
DerrickBarra wants to merge 5 commits intodaggerhashimoto:masterfrom
GambitGamesLLC:feature/local-chat-links-self-heal-and-defaults

Conversation

@DerrickBarra
Copy link
Copy Markdown
Contributor

@DerrickBarra DerrickBarra commented Apr 12, 2026

What

  • self-heal missing local CHAT_PATH_LINKS.json during local workspace reads
  • move chat-path-links config generation/parsing/normalization/template creation behind one shared helper
  • unify ConfigTab manual template creation with that shared helper
  • improve default path generation for Linux, macOS, and Windows-shaped local installs

In Plain English

This PR makes local chat path links much harder to break on the machine running Nerve.

If the local CHAT_PATH_LINKS.json file disappears, Nerve now regenerates it automatically instead of leaving local chat links in a broken/manual-recovery state. The PR also removes drift between runtime behavior and the UI's manual create-template flow by having both come from the same shared helper.

On top of that, the generated defaults are more OS-aware, so Linux, macOS, and Windows users get more sensible local path prefixes out of the box. This is intentionally a local-host hardening pass — not a remote/gateway parity change, and not a settings UX expansion.

Why

Closes #266.

This is the local-config hardening follow-up to the existing chat path links feature from #237 / #239.

Before this PR:

  • a missing local CHAT_PATH_LINKS.json could leave the local experience broken until the file was recreated manually
  • runtime regeneration logic and ConfigTab template creation could drift apart
  • default prefixes were weaker for cross-platform local installs, especially Windows-shaped paths

How

  • add a shared helper as the source of truth for chat path links config generation, normalization, parsing, and JSON serialization
  • update local GET /api/workspace/chatPathLinks handling to regenerate and persist CHAT_PATH_LINKS.json when it is missing, then return the regenerated content immediately
  • keep the helper in the server tree so the runtime build path stays correct
  • replace ConfigTab's hard-coded CHAT_PATH_LINKS.json create-template string with createChatPathLinksTemplate()
  • extend default generation so it derives stronger local defaults for Linux/macOS/Windows-shaped workspace roots and Windows username/platform fallback

Provenance

Clean branch commit stack:

  • 7bc75f2feat(chat): self-heal missing local chat path links config
  • a69f7bcfix(chat): keep chat path links helper in server tree
  • 7ed4496feat(chat): add windows-aware path link defaults

Validated downstream via local workhorse dogfood before packaging upstream, including the Windows-aware defaults follow-up while preserving Linux/Zorin behavior.

Scope / Non-goals

In scope:

  • local-host hardening in openclaw-nerve
  • missing-config self-heal for local reads
  • shared helper/template source of truth
  • ConfigTab template unification
  • richer OS-aware local defaults

Not in scope:

  • remote / gateway parity (deferred to a separate upstream openclaw lane)
  • settings/editor UX redesign

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to change)
  • 📝 Documentation update
  • 🔧 Refactor / chore (no functional change)

Checklist

  • npm run lint passes
  • npm run build && npm run build:server succeeds
  • npm test -- --run passes
  • New features include tests
  • UI changes include a screenshot or screen recording

Validation run:

  • npm test -- --run server/routes/workspace.test.ts src/features/chat/chatPathLinksConfig.test.ts src/features/workspace/tabs/ConfigTab.test.tsx
  • npm run build
  • downstream dogfood validation on local workhorse

Summary by CodeRabbit

  • New Features

    • Workspaces now auto-regenerate a missing chat path links config using a platform- and workspace-aware default template; UI create flow uses detected platform/workspace context.
  • Improvements

    • Config prefixes are normalized, deduplicated and pretty-serialized for consistent formatting and stable defaults.
    • Route now logs a warning when regenerating a missing local config.
  • Tests

    • Added tests for generation, parsing, serialization and route behavior when the config is absent.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99fe68fc-fa67-4fc9-858e-b6d531e6b506

📥 Commits

Reviewing files that changed from the base of the PR and between 2f92187 and 6687533.

📒 Files selected for processing (1)
  • src/features/workspace/tabs/ConfigTab.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/features/workspace/tabs/ConfigTab.test.tsx

📝 Walkthrough

Walkthrough

Centralizes chat path-links config into a new module, re-exports it server-side/client-side, adds platform-aware default/template generation, makes the workspace route auto-regenerate a missing local CHAT_PATH_LINKS.json, and updates tests and UI to use the shared template helper.

Changes

Cohort / File(s) Summary
Server shim & exports
server/lib/chat-path-links-config.ts
New re-export shim exposing config utilities/types for server-side use.
Workspace route & tests
server/routes/workspace.ts, server/routes/workspace.test.ts
GET now attempts local read; on ENOENT for chatPathLinks it regenerates CHAT_PATH_LINKS.json via shared template, writes file (creating dirs), logs a warning; tests added for regeneration and env isolation.
Feature module (new)
src/features/chat/chatPathLinksConfig.ts
New module: types, prefix normalization/deduplication, platform-aware default inference, parse/stringify, and template generation (createChatPathLinksTemplate, etc.).
Feature re-exports
src/features/chat/chatPathLinks.ts
Refactored to re-export config, parser and new utilities from chatPathLinksConfig instead of inline definitions.
Feature tests
src/features/chat/chatPathLinksConfig.test.ts
New Vitest suite covering defaults across platforms, normalization, parsing, stringifying, and deduplication.
ConfigTab UI & tests
src/features/workspace/tabs/ConfigTab.tsx, src/features/workspace/tabs/ConfigTab.test.tsx
ConfigTab now builds initial template via createChatPathLinksTemplate (uses navigator/platform and optional workspaceRoot fetch); tests updated to assert PUT body and UI flow.

Sequence Diagram

sequenceDiagram
    autonumber
    actor Client
    participant WorkspaceRoute as "Workspace Route"
    participant Filesystem as "Filesystem"
    participant ConfigModule as "ChatPathLinks Config Module"

    Client->>WorkspaceRoute: GET /api/workspace/chatPathLinks
    WorkspaceRoute->>Filesystem: read CHAT_PATH_LINKS.json
    alt file exists
        Filesystem-->>WorkspaceRoute: file content
        WorkspaceRoute-->>Client: 200 { ok: true, content }
    else file missing (ENOENT)
        Filesystem-->>WorkspaceRoute: ENOENT
        WorkspaceRoute->>ConfigModule: createChatPathLinksTemplate(platform, homeDir, user)
        ConfigModule-->>WorkspaceRoute: generated JSON template
        WorkspaceRoute->>Filesystem: write CHAT_PATH_LINKS.json (mkdir -p)
        Filesystem-->>WorkspaceRoute: write complete
        WorkspaceRoute->>WorkspaceRoute: console.warn("regenerated CHAT_PATH_LINKS.json")
        WorkspaceRoute-->>Client: 200 { ok: true, content: template }
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I nibbled defaults in moonlit code,

Stitching paths where stray files goad,
When CHAT_PATH_LINKS vanishes from sight,
I hop in, rebuild, and set things right,
A tiny rabbit patch — soft, swift, and light ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.85% 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 'fix(chat): harden local chat path links config defaults and self-heal' clearly and concisely summarizes the main changes: hardening chat path links config with better defaults and self-healing capabilities for missing files.
Description check ✅ Passed The PR description comprehensively covers all required template sections: What (self-healing, shared helper, UI unification, OS-aware defaults), Why (closes #266, prevents broken state), How (implementation details with file/architectural changes), Type of Change (both bug fix and new feature selected), and Checklist (build and tests marked as passing).
Linked Issues check ✅ Passed All primary objectives from issue #266 are met: self-healing regeneration [workspace.ts], shared helper consolidation [chatPathLinksConfig.ts], ConfigTab unification [ConfigTab.tsx], and OS-aware defaults [chatPathLinksConfig.ts] with comprehensive test coverage.
Out of Scope Changes check ✅ Passed All code changes directly support the linked issue objectives: new config helper, route regeneration logic, UI template unification, and tests. No extraneous changes to remote/gateway parity or settings UX found.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/features/workspace/tabs/ConfigTab.test.tsx (1)

81-115: Consider moving the assertion outside the mock for clearer test flow.

The inline expect(init.body) on line 95 inside the fetch mock works but can make test failures harder to diagnose. If the assertion fails, it throws inside the mock, which may produce a confusing error message.

An alternative is to capture the request body and assert after user.click:

♻️ Suggested refactor
   it('creates CHAT_PATH_LINKS.json with the shared template', async () => {
     const user = userEvent.setup();
     const expectedTemplate = createChatPathLinksTemplate();
+    let capturedBody: string | undefined;

     globalThis.fetch = vi.fn((input: string | URL | Request, init?: RequestInit) => {
       const url = String(input);
       // ...
       if (init?.method === 'PUT' && url === '/api/workspace/chatPathLinks') {
-        expect(init.body).toBe(JSON.stringify({ content: expectedTemplate, agentId: 'alpha' }));
+        capturedBody = init.body as string;
         return Promise.resolve(jsonResponse({ ok: true }));
       }
       // ...
     }) as typeof globalThis.fetch;

     // ... render and interact ...

+    expect(capturedBody).toBe(JSON.stringify({ content: expectedTemplate, agentId: 'alpha' }));
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/workspace/tabs/ConfigTab.test.tsx` around lines 81 - 115, The
test currently asserts the PUT request body inside the globalThis.fetch mock
(inside the arrow handling the '/api/workspace/chatPathLinks' PUT), which can
produce confusing failures; instead capture the request payload from that branch
into a local variable (e.g., let capturedBody: string | undefined) when
init?.method === 'PUT' and url === '/api/workspace/chatPathLinks', return the
mocked response as before, then after the user.click that triggers creation and
after awaiting the "File created" text, assert that capturedBody ===
JSON.stringify({ content: expectedTemplate, agentId: 'alpha' }); update the mock
references to expectedTemplate and agentId to keep behavior identical but move
the expect out of the fetch handler.
src/features/workspace/tabs/ConfigTab.tsx (1)

153-162: Consider passing seed context for richer defaults.

The UI calls createChatPathLinksTemplate() without any seed context, yielding only ["/workspace/"]. The server-side regeneration (in workspace.ts) passes platform, homeDir, username, and workspaceRoot to produce OS-aware prefixes.

This asymmetry means manually creating the file via the UI produces a minimal template, while the auto-regenerated file is richer. This may be intentional (user can customize), but worth confirming it aligns with the expected UX.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/workspace/tabs/ConfigTab.tsx` around lines 153 - 162, The UI
creates a minimal chatPathLinks template because handleCreate calls
createChatPathLinksTemplate() with no seed; update handleCreate (and the call
site using selectedKey === 'chatPathLinks') to pass the same seed context the
server uses (platform, homeDir, username, workspaceRoot) into
createChatPathLinksTemplate so the client-generated file matches server
regeneration; ensure you source those values from available props/context (or
derive them the same way as workspace.ts) and fall back to sensible defaults if
any are missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/chat/chatPathLinksConfig.ts`:
- Around line 1-10: The current file imports server-only symbols
(DEFAULT_CHAT_PATH_LINKS_CONFIG, createChatPathLinksTemplate,
createDefaultChatPathLinksConfig, normalizeChatPathLinkPrefixes,
parseChatPathLinksConfig, stringifyChatPathLinksConfig, ChatPathLinksConfig,
ChatPathLinksSeedContext) from a server-only module, which breaks client
production builds; fix by extracting the shared logic into a
client/server-available module (e.g., move the implementations and types into a
shared module under the app source tree) and then update this file to import
those same symbols from that new shared module so both client and server builds
resolve them correctly.

---

Nitpick comments:
In `@src/features/workspace/tabs/ConfigTab.test.tsx`:
- Around line 81-115: The test currently asserts the PUT request body inside the
globalThis.fetch mock (inside the arrow handling the
'/api/workspace/chatPathLinks' PUT), which can produce confusing failures;
instead capture the request payload from that branch into a local variable
(e.g., let capturedBody: string | undefined) when init?.method === 'PUT' and url
=== '/api/workspace/chatPathLinks', return the mocked response as before, then
after the user.click that triggers creation and after awaiting the "File
created" text, assert that capturedBody === JSON.stringify({ content:
expectedTemplate, agentId: 'alpha' }); update the mock references to
expectedTemplate and agentId to keep behavior identical but move the expect out
of the fetch handler.

In `@src/features/workspace/tabs/ConfigTab.tsx`:
- Around line 153-162: The UI creates a minimal chatPathLinks template because
handleCreate calls createChatPathLinksTemplate() with no seed; update
handleCreate (and the call site using selectedKey === 'chatPathLinks') to pass
the same seed context the server uses (platform, homeDir, username,
workspaceRoot) into createChatPathLinksTemplate so the client-generated file
matches server regeneration; ensure you source those values from available
props/context (or derive them the same way as workspace.ts) and fall back to
sensible defaults if any are missing.
🪄 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

Run ID: a5af9c91-5005-4099-96d5-fd6f35abecb8

📥 Commits

Reviewing files that changed from the base of the PR and between a5f7973 and 7ed4496.

📒 Files selected for processing (8)
  • server/lib/chat-path-links-config.ts
  • server/routes/workspace.test.ts
  • server/routes/workspace.ts
  • src/features/chat/chatPathLinks.ts
  • src/features/chat/chatPathLinksConfig.test.ts
  • src/features/chat/chatPathLinksConfig.ts
  • src/features/workspace/tabs/ConfigTab.test.tsx
  • src/features/workspace/tabs/ConfigTab.tsx

@DerrickBarra
Copy link
Copy Markdown
Contributor Author

Addressed the CodeRabbit follow-ups on this branch:

  • moved the actual chat-path-links shared implementation into src/features/chat/chatPathLinksConfig.ts so the client no longer reaches into a server-only import path
  • kept server/lib/chat-path-links-config.ts as a thin server wrapper over the shared module
  • seeded manual CHAT_PATH_LINKS.json creation from browser/workspace context when available so UI creation better matches server-side defaults
  • moved the ConfigTab PUT-body assertion out of the fetch mock and into the test body

Validation run:

  • npm test -- --run src/features/chat/chatPathLinksConfig.test.ts src/features/workspace/tabs/ConfigTab.test.tsx server/routes/workspace.test.ts
  • npm run build

Ready for re-review.

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.

Bug: harden local chat path links config regeneration and defaults

1 participant