Skip to content

Fix infinite recursion in logger and wikilink quote stripping in frontmatter#4

Open
byheaven wants to merge 22 commits intocasals:masterfrom
byheaven:master
Open

Fix infinite recursion in logger and wikilink quote stripping in frontmatter#4
byheaven wants to merge 22 commits intocasals:masterfrom
byheaven:master

Conversation

@byheaven
Copy link
Copy Markdown

Summary

This PR fixes two bugs found during real-world usage:

  • src/utils/debug.ts — infinite recursion crash on plugin load
    All methods in DebugLogger were calling debugLog.xxx() (the singleton itself) instead of console.xxx(). This caused a RangeError: Maximum call stack size exceeded crash every time the plugin was loaded, because any log call immediately recurse into itself infinitely.

  • src/utils/frontmatter.ts — wikilink quotes stripped, breaking Obsidian frontmatter
    updateFrontmatter was deleting all existing frontmatter keys and rewriting them from the metadata cache. Obsidian's YAML serializer does not re-add quotes around [[wikilink]] strings, so values like "[[AMIO]]" became [[AMIO]] (unquoted). Unquoted [[...]] is not valid YAML — Obsidian itself errors when reading the file back.

    The fix is to only write the keys supplied in newFrontmatter, leaving all user-defined properties (like projects, tags, etc.) untouched.

Test plan

  • Load plugin in Obsidian — no "Maximum call stack size exceeded" error in console
  • Create a note with a wikilink property, e.g. projects: - "[[SomeNote]]"
  • Run "Create Linear issue from note" on that note
  • Verify projects property still reads "[[SomeNote]]" with quotes intact after the command

🤖 Generated with Claude Code

byheaven and others added 2 commits March 13, 2026 15:33
- debug.ts: Replace all self-referential `debugLog.xxx()` calls inside
  DebugLogger methods with `console.xxx()`. The original code caused a
  "Maximum call stack size exceeded" crash on plugin load because every
  log call recursively invoked itself via the singleton reference.

- frontmatter.ts: Stop clearing all frontmatter before rewriting it.
  The previous implementation deleted every user-defined key and rewrote
  them from the metadata cache, causing Obsidian's YAML serializer to
  strip quotes from wikilink strings (e.g. "[[Note]]" → [[Note]]).
  Unquoted [[...]] is invalid YAML and caused Obsidian to error on the
  next file read. Now only the supplied keys are written; user-defined
  properties (projects, tags, etc.) are never touched.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…k quotes

updateNoteWithIssue treated any note without a linear_id as "new", causing
it to replace the entire note content with a generated template and manually
serialize frontmatter without quoting — stripping quotes from wikilink values
like "[[Note]]", which Obsidian cannot parse back.

Remove the isNewNote branch and always use updateFrontmatter (Obsidian's
processFrontMatter API), which only adds/updates the supplied keys and never
touches user-defined properties or note body content.

Also removes the now-unused addFrontmatterToContent private method which was
the source of the broken manual YAML serialization.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@byheaven
Copy link
Copy Markdown
Author

Update: Found a more critical bug in the same area.

updateNoteWithIssue treated any note without a linear_id as isNewNote = true, which caused "Create Linear issue from note" to:

  1. Replace the entire note content with a generated template (destroying the user's original content)
  2. Manually serialize frontmatter via string interpolation — stripping quotes from wikilink values like "[[Note]]", which Obsidian cannot parse back

Fixed by removing the isNewNote branch entirely and always using updateFrontmatter (processFrontMatter API). This commit also removes the now-unused addFrontmatterToContent method that contained the broken manual YAML serializer.

Latest commit: b6bcab1

byheaven and others added 20 commits March 13, 2026 16:04
- Add .github/workflows/release.yml: auto-builds and publishes release
  assets (main.js, manifest.json, styles.css) on tag push, enabling
  BRAT to track this fork instead of the upstream community plugin
- Fix findOrCreateNoteForIssue: vault-wide index lookup prevents
  duplicate notes and ensures original notes are updated on sync

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- manifest.json: update author to byheaven, remove casals fundingUrl,
  bump version to 1.0.1
- release.yml: bump manifest.json and versions.json to match tag before
  build, so BRAT sees correct version in release assets
- CLAUDE.md: add project documentation for future Claude sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LinearWorkspace now has syncFolder, teamIds, lastSyncTime, enabled fields
- LinearPluginSettings removes single-workspace fields (apiKey, teamId, syncFolder,
  lastSyncTime, multiWorkspaceSupport) and adds workspaces[], settingsVersion,
  defaultWorkspaceId (string | null)
- SyncManager.syncAll() fans out to per-workspace syncWorkspace() via Promise.allSettled()
- Each workspace creates its own LinearClient from workspace.apiKey
- findOrCreateNoteForIssue() and ensureSyncFolder() accept explicit syncFolder param
- getLastSyncTime()/setLastSyncTime() removed; workspace.lastSyncTime updated directly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace linearClient property with lazy _clientCache (Map<workspaceId, LinearClient>)
- Add getDefaultWorkspace() / getDefaultClient() with fallback logic
- loadSettings() resets to defaults on version mismatch, backs up old data
- saveSettings() clears client cache on every save
- SyncManager, KanbanGenerator, AgendaGenerator, CommentMirror, BatchOperationManager
  all updated to accept LinearClient | null with null guards
- createKanbanNote/createAgendaNote accept explicit syncFolder param
- createIssueFromNote has null guard with user-facing Notice
- batchCreateIssues short-circuits when no client configured

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace API key/team/sync-folder fields with per-workspace management
- Add/delete/enable/disable workspaces with collapse/expand UI
- Per-workspace: name, API key, Test Connection, sync folder, teams multi-select
- Default workspace selector (drives autocomplete, Kanban, issue creation)
- Test Connection button populates teams dropdown; nested try/catch gives
  distinct notices for connection vs team-loading failures
- Name field updates header in-place to avoid focus loss on keystroke

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- IssueCreateModal constructor now accepts plugin arg to read workspace list
- Workspace dropdown shown at top of form when 2+ enabled workspaces exist
- Switching workspace creates fresh LinearClient, resets team/state/assignee
  selection, reloads teams/users, and rebuilds form
- updateTimer field prevents stale DOM updates on rapid workspace switching
- main.ts createIssueFromNote() passes plugin instance to modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n empty sync

- settings-tab: normalize select.value to null (not '') when saving
  defaultWorkspaceId, keeping sentinel consistent with DEFAULT_SETTINGS
- sync-manager: advance workspace.lastSyncTime after any successful API fetch,
  including zero-result fetches — prevents redundant full-history queries in
  steady state once workspace is caught up

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace accordion workspace list with tab-based UI (one tab per
  workspace, + button to add, delete inside each tab)
- Persist cachedTeams on LinearWorkspace so teams checkboxes survive
  settings panel re-opens without re-running Test Connection
- Replace native <select multiple> with checkbox list for teams
  (avoids Cmd+click UX confusion in Electron)
- Bump version to 1.1.0 in manifest.json, package.json, versions.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add workspace-scoped note bindings so synced notes are matched by workspace id before falling back to legacy notes in the current sync folder.

Bootstrap a workspace with a full issue fetch when it has a last sync timestamp but no workspace-bound notes yet, while keeping incremental sync for established workspaces.

Cover the new matching and bootstrap behavior with Jest tests and add the tests root placeholder required by the existing Jest configuration.

Validation:

- npm test -- --runInBand sync-manager

- npm run typecheck

- npm run build
Track AGENTS.md in the repository so local development, release, and architecture notes are available on GitHub.

Update the sync architecture note to describe workspace-scoped note matching and legacy note claiming within the current sync folder.
- move managed issue fields into frontmatter and keep note bodies in a comment-first managed shell\n- add a local Obsidian CLI plus Linear API end-to-end suite with documented maintenance rules\n- harden multi-workspace sync, parser edge cases, comment mirroring, and test cleanup

Tests:\n- npm test -- --runInBand markdown-parser sync-manager\n- npm run typecheck\n- npm run build\n- npm run e2e:smoke\n- npm run e2e
Add workspace default team and team-scoped default project handling for issue creation.

Refresh workspace settings data automatically, show connection state icons, and tighten create-issue defaults plus project syncing behavior.

Expand unit and E2E coverage, including human-only default assignee selection and new default-team/default-project cases.
Document workspace default team and project behavior, settings auto-refresh, connection state indicators, and fork-specific release/support links.
Document that every version release must include GitHub release notes and that README.md must be updated when user-visible behavior changes.
Add a managed Document section to synced issue notes and sync its content to a single Linear issue document.

Normalize create-from-note and regular sync into the same managed shell, preserve local document and draft comment sections, and support wikilink-backed document sources.

Update unit tests, E2E coverage, and README to reflect the managed note structure and H1 section format.
Add a titled top-level sync summary notice with an explicit 10 second duration.

Route manual and scheduled sync entry points through a shared summary formatter while keeping internal refresh syncs silent.

Cover the behavior with unit, plugin, and Obsidian E2E tests, including a live command-path notice assertion.
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.

1 participant