feat: v0.3.0 — agent integration foundation (schema + exports)#39
feat: v0.3.0 — agent integration foundation (schema + exports)#39jnahian wants to merge 35 commits into
Conversation
Documents the web/ commands, the three SSR runtimes (Vite dev, Express, Vercel serverless), and points at the existing changelog guide and skill rather than duplicating instructions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three-stage rollout (v0.3 schema+exports, v0.4 MCP server, v0.5 trust model + audit UI) for making notes a first-class context source for coding agents — read+write loop, full structured schema, configurable agent-write trust mode. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
17 bite-sized TDD tasks covering: structured Note schema (type, scope, tags, priority, references, expiry, authorType), debounced atomic INDEX.json + AGENTS.md generation, sidebar type filter, configuration settings, changelog and version bump. Notes two deviations from spec discovered during planning: existing storage is bold-label markdown (not YAML frontmatter) and the layout is flat (.code-notes/<id>.md). Plan extends both rather than fighting them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds optional NoteType, NoteScope, NoteReference, AuthorType, NotePriority fields. All optional for backward compatibility — defaults applied on read in a follow-up task.
Lazily fills missing optional Note fields in memory. The on-disk file is unchanged until the note is next saved (lazy migration).
Extends markdownToNote to read Type/Scope/Priority/Tags/AuthorType/ ExpiresAt/References. Legacy notes without these fields parse exactly as before (fields remain undefined; defaults applied at NoteManager boundary in a follow-up task).
Extends noteToMarkdown to write Type/Scope/Priority/Tags/AuthorType/ ExpiresAt/References. Fields equal to their default are omitted to keep frontmatter compact for the common case (untouched legacy notes remain shape-identical after a save).
Notes leaving NoteManager always have optional fields populated. Legacy notes get defaults in memory; on-disk file unchanged until next save. This is the single boundary where lazy migration happens.
Pure functions buildIndex(notes, workspaceRoot) and buildDigest(notes, workspaceRoot). Deterministic output for the same input. No I/O — called by exportWriter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Owns the 200ms debounce timer for note-write bursts and the write-temp-then-rename atomic-write logic. Failures route through onError so a disk-full event never blocks note saves.
Hooks ExportWriter to NoteManager's noteChanged event. Initial export runs on activation. Disabled when codeContextNotes.exports.enabled is false.
Lets users force a fresh INDEX.json/AGENTS.md, e.g. after a disk-full recovery or a manual delete from the storage directory.
Three booleans: enabled (master switch), indexJson, agentsMarkdown. All default true. Read on every regeneration so toggling takes effect without reload.
Append · {type} to NoteTreeItem.description for non-context types,
and prepend [priority] to the tooltip for high/critical notes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New title-bar actions: filter by type (multi-select Quick Pick) and toggle expired-note visibility. Default hides expired notes.
Quick Pick driven flow: codeContextNotes.setNoteMetadata. Lets users enrich notes after creation. Future v0.5 work will inline this in the add/edit comment thread UI; this command unblocks v0.3 use cases.
Combines the previously drafted Search and Filter feature changelog with the new agent-integration features (structured schema, INDEX.json/AGENTS.md exports, sidebar enhancements). Both ship as v0.3.0.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR implements v0.3.0 of Code Context Notes, introducing structured note metadata (type, scope, tags, references, expiry, priority, author type) with corresponding defaults, automatic generation of INDEX.json and AGENTS.md export files via debounced atomic writes, new sidebar filtering commands, extended storage serialization, and comprehensive testing and documentation. ChangesSchema Extension and Export Generation (v0.3.0)
🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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 |
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/pages/ChangelogPage.tsx (1)
763-808:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove or update the stale "Coming in v0.3.0" section.
This section describes features that are now released in v0.3.0 (displayed at the top with the "Latest" badge). The outdated future-looking content creates confusion for users visiting the changelog.
Recommended action: Remove lines 763-808 entirely, since the search features are already documented in the v0.3.0 entry above.
Alternative: Update to "Coming in v0.4.0" with the MCP server content mentioned in the markdown changelog's "Coming Next" section.
🗑️ Proposed fix: Remove the stale section
- {/* Future Versions Note */} - <div className="relative grid grid-cols-1 md:grid-cols-[30%_70%] gap-8 items-start"> - {/* Timeline Node - Future */} - <div className="absolute left-0 md:left-[30%] transform -translate-x-1/2 top-2"> - <div className="w-4 h-4 rounded-full bg-purple-500 border-4 border-white dark:border-slate-900 shadow-lg animate-pulse"></div> - </div> - - {/* Left Column - Version Info */} - <div className="pl-8 md:pl-0 md:pr-12 text-left md:text-right space-y-2"> - <h3 className="text-2xl font-bold text-purple-600">Coming in v0.3.0</h3> - <div className="flex items-center gap-2 text-muted-foreground text-sm md:justify-end"> - <Search className="h-4 w-4" /> - <span>Future Release</span> - </div> - <p className="text-sm text-muted-foreground"> - What's next for Code Context Notes - </p> - </div> - - {/* Right Column - Changes */} - <div className="pl-8 md:pl-12"> - <Card className="bg-gradient-to-br from-purple-50 to-pink-50 dark:from-purple-950 dark:to-pink-950 border-2 border-purple-300 shadow-brand-drop"> - <CardContent> - <div className="space-y-2 text-sm text-muted-foreground"> - <div className="flex items-start space-x-2"> - <span className="text-purple-500 font-bold">→</span> - <span> - <strong>Search and Filter Notes</strong> - Full-text search across all note content with filters by author, date range, and file path - </span> - </div> - <div className="flex items-start space-x-2"> - <span className="text-purple-500 font-bold">→</span> - <span> - <strong>Regex Pattern Matching</strong> - Advanced search capabilities for power users - </span> - </div> - <div className="flex items-start space-x-2"> - <span className="text-purple-500 font-bold">→</span> - <span> - <strong>Background Indexing</strong> - Instant search results with automatic index updates - </span> - </div> - </div> - </CardContent> - </Card> - </div> - </div> -🤖 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 `@web/src/pages/ChangelogPage.tsx` around lines 763 - 808, The JSX block rendering the stale "Coming in v0.3.0" timeline card should be removed (or updated) — remove the entire section that includes the timeline node div, the h3 with text "Coming in v0.3.0", the Search icon usage, and the Card / CardContent that lists "Search and Filter Notes", "Regex Pattern Matching", and "Background Indexing"; alternatively, if you prefer to keep a future section, change the h3 to "Coming in v0.4.0" and replace the inner card contents with the MCP server notes from the markdown "Coming Next" section so it no longer duplicates already released v0.3.0 features.
🧹 Nitpick comments (7)
docs/superpowers/specs/2026-05-15-agent-integration-design.md (1)
133-134: ⚡ Quick winAlign storage format/path statements with the implemented v0.3 contract.
This spec still describes YAML frontmatter and nested note paths, while the v0.3 plan in this PR defines bold-label markdown and flat
.code-notes/<id>.md. Please normalize these sections to one canonical storage contract to avoid implementation drift in v0.4+ workstreams.Also applies to: 176-177, 317-317, 483-509
🤖 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/superpowers/specs/2026-05-15-agent-integration-design.md` around lines 133 - 134, The spec currently describes YAML frontmatter and nested note paths but must be updated to match the implemented v0.3 contract: replace any "Storage:" or frontmatter descriptions that reference YAML frontmatter and nested note paths with the canonical v0.3 description (bold-label markdown in-document metadata and flat storage under .code-notes/<id>.md), and normalize all occurrences (including the regions noted around lines 176-177, 317, and 483-509) so the document consistently specifies bold-label markdown metadata and the flat .code-notes/<id>.md path contract.src/test/runUnitTests.ts (1)
35-39: ⚡ Quick winRefactor the test file filter to use an array for better maintainability.
The boolean chain with 5 OR conditions is becoming difficult to maintain. Consider using an array with
.includes()for cleaner code that's easier to extend when adding new test files.♻️ Proposed refactoring
// Filter to only pure unit tests (no vscode dependency) +const pureUnitTests = [ + 'storageManager.test.js', + 'gitIntegration.test.js', + 'noteDefaults.test.js', + 'exportGenerator.test.js', + 'exportWriter.test.js' +]; + const unitTestFiles = files.filter(f => { const basename = path.basename(f); - // Only include tests that don't require vscode - return basename === 'storageManager.test.js' || basename === 'gitIntegration.test.js' || basename === 'noteDefaults.test.js' || basename === 'exportGenerator.test.js' || basename === 'exportWriter.test.js'; + return pureUnitTests.includes(basename); });🤖 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 `@src/test/runUnitTests.ts` around lines 35 - 39, The unitTestFiles filter uses a long boolean OR chain which is hard to maintain; refactor the files.filter callback in runUnitTests.ts to compute basename via path.basename(f), then test membership against an array of allowed test file names (e.g., const allowed = ['storageManager.test.js','gitIntegration.test.js','noteDefaults.test.js','exportGenerator.test.js','exportWriter.test.js']) and return allowed.includes(basename) inside the files.filter callback so adding/removing tests becomes a simple array change (update references to unitTestFiles and the files.filter callback).README.md (1)
367-368: ⚡ Quick winConsider clarifying the regeneration timing.
The phrase "on every note change" might suggest immediate regeneration, but the PR objectives indicate a 200ms debounce is in place. Consider adding "automatically" or "debounced" to clarify the behavior for users who make rapid successive changes.
✍️ Possible clarification
-When auto-exports are enabled, two files are regenerated in `.code-notes/` -on every note change: +When auto-exports are enabled, two files are automatically regenerated in `.code-notes/` +whenever notes change:🤖 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 `@README.md` around lines 367 - 368, Update the README sentence that currently reads "When auto-exports are enabled, two files are regenerated in `.code-notes/` on every note change:" to clarify the timing: mention that regeneration is automatic but debounced (200ms). For example, change it to state that files are regenerated automatically on note changes and add "(debounced by 200ms)" so users understand rapid successive edits won't trigger immediate repeated writes; edit the same sentence in README to include that debounced timing.src/storageManager.ts (1)
214-235: ⚡ Quick winUse shared defaults when deciding whether to omit structured fields.
These comparisons hardcode default literals (
context,line,normal,human). Pull fromNOTE_DEFAULTSso serialization behavior can’t drift if defaults change later.Proposed diff
import { Note, NoteStorage, NoteMetadata } from './types.js'; +import { NOTE_DEFAULTS } from './noteDefaults.js'; @@ - if (note.type && note.type !== 'context') { + if (note.type && note.type !== NOTE_DEFAULTS.type) { lines.push(`**Type:** ${note.type}`); } - if (note.scope && note.scope !== 'line') { + if (note.scope && note.scope !== NOTE_DEFAULTS.scope) { lines.push(`**Scope:** ${note.scope}`); } - if (note.priority && note.priority !== 'normal') { + if (note.priority && note.priority !== NOTE_DEFAULTS.priority) { lines.push(`**Priority:** ${note.priority}`); } @@ - if (note.authorType && note.authorType !== 'human') { + if (note.authorType && note.authorType !== NOTE_DEFAULTS.authorType) { lines.push(`**AuthorType:** ${note.authorType}`); }🤖 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 `@src/storageManager.ts` around lines 214 - 235, The serialization currently omits structured fields by comparing note properties to hardcoded literals (e.g., 'context', 'line', 'normal', 'human'); update those comparisons to use the centralized defaults from NOTE_DEFAULTS so behavior follows the shared defaults. Specifically, replace checks like note.type !== 'context', note.scope !== 'line', note.priority !== 'normal', and note.authorType !== 'human' with comparisons against NOTE_DEFAULTS.type, NOTE_DEFAULTS.scope, NOTE_DEFAULTS.priority, and NOTE_DEFAULTS.authorType respectively (leave other checks like tags/references/expiresAt unchanged) so changes to NOTE_DEFAULTS automatically affect serialization in the functions handling note formatting.src/test/suite/noteManager.test.ts (1)
506-537: ⚡ Quick winAdd a regression test for
updateNoteMetadata+ defaults boundary.Great legacy-load coverage. Please also add a case that calls
updateNoteMetadata(...)on a legacy/default-omitted note and then re-reads viagetNotesForFileto asserttype/scope/tagsremain defaulted after the metadata-update path.🤖 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 `@src/test/suite/noteManager.test.ts` around lines 506 - 537, Add a new test that writes a legacy-shaped note (omitting type/scope/tags) to storage as in the existing test, then call noteManager.updateNoteMetadata('legacy-1', {/* any metadata change that triggers an update, e.g. author or updatedAt */}) and finally re-read notes via noteManager.getNotesForFile('/abs/x.ts') and assert that the returned note still has type === 'context', scope === 'line', and tags deep-equals [] to ensure the updateNoteMetadata path applies defaults the same way as initial loads; reference the existing use of storage.saveNote, noteManager.updateNoteMetadata, and noteManager.getNotesForFile to locate where to add this case.src/exportGenerator.ts (1)
116-118: ⚡ Quick winMake
AGENTS.mdsection ordering deterministic across runs.Line 116 onward still depends on input order for equal-priority critical notes, handoffs, decisions, and same-line file notes. That can cause digest churn for identical note sets. Add a stable tiebreak sort (e.g.,
filePath,lineRange.start,lineRange.end,id) in each section.Proposed deterministic sort tweak
+const NOTE_TIEBREAK = (a: Note, b: Note): number => + relativePath(a.filePath, workspaceRoot).localeCompare(relativePath(b.filePath, workspaceRoot)) || + (a.lineRange.start - b.lineRange.start) || + (a.lineRange.end - b.lineRange.end) || + a.id.localeCompare(b.id); const critical = notes .filter(n => n.type === 'instruction' || n.type === 'warning') - .sort((a, b) => (PRIORITY_RANK[a.priority!] ?? 99) - (PRIORITY_RANK[b.priority!] ?? 99)); + .sort((a, b) => + ((PRIORITY_RANK[a.priority!] ?? 99) - (PRIORITY_RANK[b.priority!] ?? 99)) || + NOTE_TIEBREAK(a, b) + ); - const handoffs = notes.filter(n => n.type === 'handoff'); + const handoffs = notes.filter(n => n.type === 'handoff').sort(NOTE_TIEBREAK); - const decisions = notes.filter(n => n.type === 'decision'); + const decisions = notes.filter(n => n.type === 'decision').sort(NOTE_TIEBREAK); - const fileNotes = byFile.get(file)!.sort((a, b) => a.lineRange.start - b.lineRange.start); + const fileNotes = byFile.get(file)!.sort((a, b) => + (a.lineRange.start - b.lineRange.start) || + (a.lineRange.end - b.lineRange.end) || + a.id.localeCompare(b.id) + );Also applies to: 127-143, 158-161
🤖 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 `@src/exportGenerator.ts` around lines 116 - 118, The current sort for the "critical" array (const critical = notes.filter(...).sort(...)) and similar sorts for handoffs, decisions, and same-line file notes rely only on priority and thus are non-deterministic for equal-priority items; update each comparator to add a stable tiebreaker chain that compares filePath (string), then lineRange.start (number), then lineRange.end (number), then id (string) so equal-priority entries are deterministically ordered; locate the sort calls for the variables critical, handoffs, decisions and the same-line notes block and extend their comparator functions to return comparisons on those fields when PRIORITY_RANK values are equal.src/test/suite/exportWriter.test.ts (1)
24-59: ⚡ Quick winAdd a regression test for overlapping
regenerate()calls.Current tests validate debounce, but not concurrent regenerate overlap. Add a test that fires two
regenerate()promises concurrently and asserts noonErrorplus deterministic final output.🤖 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 `@src/test/suite/exportWriter.test.ts` around lines 24 - 59, Add a new test that verifies overlapping ExportWriter.regenerate calls don't race: create a workspace with a '.code-notes' dir and an ExportWriter(ws, '.code-notes', { debounceMs: 0 }), set writer.onError to capture any error, then fire two concurrent regenerate() promises with different note sets (use sampleNote to produce distinct IDs), await both promises, assert onError was not called, and read/parse INDEX.json to assert the final index matches the notes from the last regenerate call (deterministic final output). Use the same tmpdir() pattern as other tests and ensure you check AGENTS.md or INDEX.json contents to validate atomic, deterministic results.
🤖 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/superpowers/plans/2026-05-15-agent-integration-v0.3-schema-and-exports.md`:
- Around line 1315-1319: The fenced code block containing
".code-notes/INDEX.json" and ".code-notes/AGENTS.md" is missing a language
identifier; update the markdown in the document so the fence is typed as
gitignore (i.e., change the opening ``` to ```gitignore) so markdownlint MD040
will accept it and the entries are highlighted/treated correctly.
In `@docs/superpowers/specs/2026-05-15-agent-integration-design.md`:
- Around line 377-381: Fenced code blocks under the "Code Context Notes" rows
are missing language identifiers which may trigger markdownlint MD040; update
both code fences (the blocks containing "Code Context Notes › Agent write mode:
[direct | queue | audit ▼]" and "Code Context Notes › Agent allow-list:
[claude-code, cursor-agent]" / "Code Context Notes › Audit log retention: [last
1000 ops]") by adding an appropriate language tag (e.g., ```text) immediately
after the opening backticks so markdownlint recognizes them.
In `@README.md`:
- Around line 375-378: The fenced code block showing the two paths
(.code-notes/INDEX.json and .code-notes/AGENTS.md) lacks a language specifier;
update the markdown fenced block in README.md so the opening triple backticks
include the `gitignore` language (i.e., change the fence to ```gitignore) to
enable proper syntax highlighting for ignore-style entries.
In `@src/exportWriter.ts`:
- Around line 39-52: scheduleRegenerate can race with direct calls to
regenerate, causing concurrent atomicWrite calls (for INDEX.json and AGENTS.md
using the fixed ".tmp" suffix in atomicWrite) to clobber the same temp files;
serialize regenerate executions by adding a Promise-based queue or mutex inside
the module (e.g., a private regenerating flag or an async queue) that all
callers (scheduleRegenerate -> the delayed invoke and direct calls to
regenerate) await before performing atomicWrite, or alternatively make
atomicWrite generate unique temp paths per execution (e.g., include
timestamp/UUID in the temp suffix) so INDEX.json.tmp/AGENTS.md.tmp are never
reused concurrently; update regenerate(), scheduleRegenerate(), and
atomicWrite() to use that serialization or unique-temp strategy to prevent
concurrent fs.rename/ENOENT/data loss.
In `@src/extension.ts`:
- Around line 983-1000: The command handlers use sidebarProvider without
guarding against partial/no-workspace activation; update the callbacks
registered in filterByTypeCommand and toggleExpiredCommand to first check that
sidebarProvider is initialized (e.g., if (!sidebarProvider) {
vscode.window.showWarningMessage('Feature unavailable without workspace');
return; }) before calling sidebarProvider.setTypeFilter(...) or
sidebarProvider.toggleHideExpired(); this prevents dereferencing undefined and
provides a clear early-exit when no workspace is active.
- Around line 111-118: The event listener for noteManager ('noteChanged') must
always be registered even if exportsEnabled is false; move or add the
noteManager.on('noteChanged', ...) registration outside the exportsEnabled
conditional and change its handler to check exportsEnabled before calling
exportWriter.scheduleRegenerate(() => noteManager.getAllNotes()). Keep the
initial exportWriter.scheduleRegenerate(...) call inside the exportsEnabled
block so immediate regeneration only happens when exports are enabled, but
ensure the persistent listener exists to start auto-exports whenever
exportsEnabled becomes true later.
In `@src/noteManager.ts`:
- Around line 182-184: The updateNoteMetadata flow allows changes to
soft-deleted notes because it doesn't check the note's deleted flag; add the
same guard used in updateNote: after retrieving the note with
this.storage.loadNoteById(noteId) (the existing variable), verify the note is
not soft-deleted (e.g. existing.deleted or existing.isDeleted) and if it is,
throw the same Error(`Note ${noteId} not found`) to prevent metadata updates on
deleted notes.
- Around line 186-199: The code currently saves `updated` from storage and
writes it to cache and emits events without normalizing defaults or updating the
search index; change the flow so you first normalize the saved note (e.g., const
normalized = applyDefaults(updated)), then pass `normalized` into
this.updateNoteInCache(...) and into emits/return, and after caching call
this.searchManager.updateIndex(normalized) (mirroring other write paths) before
calling this.clearWorkspaceCache(); ensure all references to the note in this
method (cache, events, return) use `normalized` not the raw `updated`.
In `@src/notesSidebarProvider.ts`:
- Around line 191-194: The file/root nodes are currently computed from
unfiltered notes because filtering happens only when computing the local
"filtered" variable; update the code so filtering (applyDefaults + expiry/type
checks using applyDefaults, isExpired, hideExpired, and typeFilter) is applied
earlier—before constructing fileNode and any root totals—so that fileNode.notes
and any aggregates are built from the filtered list; locate where fileNode is
created in NotesSidebarProvider and replace its notes/source-array with the
filtered result (or filter the source array first) to ensure empty files and
totals reflect active filters.
In `@src/storageManager.ts`:
- Around line 321-345: The parser in storageManager.ts currently assigns raw
strings/JSON into note fields (note.type, note.scope, note.priority,
note.authorType, note.expiresAt, note.references, note.tags) which can introduce
invalid metadata; update the parsing in the block handling '**Type:**',
'**Scope:**', '**Priority:**', '**Tags:**', '**AuthorType:**', '**ExpiresAt:**'
and '**References:**' to validate and normalize values instead of using "as any"
or blind JSON.parse: map type/priority/authorType/scope against allowed
enum/string sets and only assign when matching (otherwise set undefined or
default), normalize tags to a deduped non-empty string array, parse expiresAt
into a validated ISO date (or Date) and drop/flag invalid dates, and for
References JSON validate the shape (e.g., ensure array of objects with expected
keys or array of strings) before assigning to note.references; add graceful
error handling/logging for invalid values rather than assigning raw input.
In `@web/CLAUDE.md`:
- Line 7: Replace the hardcoded absolute path string
`/Users/nahian/Projects/code-notes/src` with a portable relative path (for
example `../src` or `./src` depending on the repository layout) in the
web/CLAUDE.md content so the reference points to the extension source from the
web subproject rather than a single developer's machine; update the text that
currently mentions `/Users/nahian/Projects/code-notes/src` to use the chosen
relative path and ensure the sentence still reads clearly.
---
Outside diff comments:
In `@web/src/pages/ChangelogPage.tsx`:
- Around line 763-808: The JSX block rendering the stale "Coming in v0.3.0"
timeline card should be removed (or updated) — remove the entire section that
includes the timeline node div, the h3 with text "Coming in v0.3.0", the Search
icon usage, and the Card / CardContent that lists "Search and Filter Notes",
"Regex Pattern Matching", and "Background Indexing"; alternatively, if you
prefer to keep a future section, change the h3 to "Coming in v0.4.0" and replace
the inner card contents with the MCP server notes from the markdown "Coming
Next" section so it no longer duplicates already released v0.3.0 features.
---
Nitpick comments:
In `@docs/superpowers/specs/2026-05-15-agent-integration-design.md`:
- Around line 133-134: The spec currently describes YAML frontmatter and nested
note paths but must be updated to match the implemented v0.3 contract: replace
any "Storage:" or frontmatter descriptions that reference YAML frontmatter and
nested note paths with the canonical v0.3 description (bold-label markdown
in-document metadata and flat storage under .code-notes/<id>.md), and normalize
all occurrences (including the regions noted around lines 176-177, 317, and
483-509) so the document consistently specifies bold-label markdown metadata and
the flat .code-notes/<id>.md path contract.
In `@README.md`:
- Around line 367-368: Update the README sentence that currently reads "When
auto-exports are enabled, two files are regenerated in `.code-notes/` on every
note change:" to clarify the timing: mention that regeneration is automatic but
debounced (200ms). For example, change it to state that files are regenerated
automatically on note changes and add "(debounced by 200ms)" so users understand
rapid successive edits won't trigger immediate repeated writes; edit the same
sentence in README to include that debounced timing.
In `@src/exportGenerator.ts`:
- Around line 116-118: The current sort for the "critical" array (const critical
= notes.filter(...).sort(...)) and similar sorts for handoffs, decisions, and
same-line file notes rely only on priority and thus are non-deterministic for
equal-priority items; update each comparator to add a stable tiebreaker chain
that compares filePath (string), then lineRange.start (number), then
lineRange.end (number), then id (string) so equal-priority entries are
deterministically ordered; locate the sort calls for the variables critical,
handoffs, decisions and the same-line notes block and extend their comparator
functions to return comparisons on those fields when PRIORITY_RANK values are
equal.
In `@src/storageManager.ts`:
- Around line 214-235: The serialization currently omits structured fields by
comparing note properties to hardcoded literals (e.g., 'context', 'line',
'normal', 'human'); update those comparisons to use the centralized defaults
from NOTE_DEFAULTS so behavior follows the shared defaults. Specifically,
replace checks like note.type !== 'context', note.scope !== 'line',
note.priority !== 'normal', and note.authorType !== 'human' with comparisons
against NOTE_DEFAULTS.type, NOTE_DEFAULTS.scope, NOTE_DEFAULTS.priority, and
NOTE_DEFAULTS.authorType respectively (leave other checks like
tags/references/expiresAt unchanged) so changes to NOTE_DEFAULTS automatically
affect serialization in the functions handling note formatting.
In `@src/test/runUnitTests.ts`:
- Around line 35-39: The unitTestFiles filter uses a long boolean OR chain which
is hard to maintain; refactor the files.filter callback in runUnitTests.ts to
compute basename via path.basename(f), then test membership against an array of
allowed test file names (e.g., const allowed =
['storageManager.test.js','gitIntegration.test.js','noteDefaults.test.js','exportGenerator.test.js','exportWriter.test.js'])
and return allowed.includes(basename) inside the files.filter callback so
adding/removing tests becomes a simple array change (update references to
unitTestFiles and the files.filter callback).
In `@src/test/suite/exportWriter.test.ts`:
- Around line 24-59: Add a new test that verifies overlapping
ExportWriter.regenerate calls don't race: create a workspace with a
'.code-notes' dir and an ExportWriter(ws, '.code-notes', { debounceMs: 0 }), set
writer.onError to capture any error, then fire two concurrent regenerate()
promises with different note sets (use sampleNote to produce distinct IDs),
await both promises, assert onError was not called, and read/parse INDEX.json to
assert the final index matches the notes from the last regenerate call
(deterministic final output). Use the same tmpdir() pattern as other tests and
ensure you check AGENTS.md or INDEX.json contents to validate atomic,
deterministic results.
In `@src/test/suite/noteManager.test.ts`:
- Around line 506-537: Add a new test that writes a legacy-shaped note (omitting
type/scope/tags) to storage as in the existing test, then call
noteManager.updateNoteMetadata('legacy-1', {/* any metadata change that triggers
an update, e.g. author or updatedAt */}) and finally re-read notes via
noteManager.getNotesForFile('/abs/x.ts') and assert that the returned note still
has type === 'context', scope === 'line', and tags deep-equals [] to ensure the
updateNoteMetadata path applies defaults the same way as initial loads;
reference the existing use of storage.saveNote, noteManager.updateNoteMetadata,
and noteManager.getNotesForFile to locate where to add this case.
🪄 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: 55a9c87c-ca66-404c-98f9-a5755ff29145
📒 Files selected for processing (22)
README.mddocs/changelogs/v0.3.0.mddocs/superpowers/plans/2026-05-15-agent-integration-v0.3-schema-and-exports.mddocs/superpowers/specs/2026-05-15-agent-integration-design.mdpackage.jsonsrc/exportGenerator.tssrc/exportWriter.tssrc/extension.tssrc/noteDefaults.tssrc/noteManager.tssrc/noteTreeItem.tssrc/notesSidebarProvider.tssrc/storageManager.tssrc/test/runUnitTests.tssrc/test/suite/exportGenerator.test.tssrc/test/suite/exportWriter.test.tssrc/test/suite/noteDefaults.test.tssrc/test/suite/noteManager.test.tssrc/test/suite/storageManager.test.tssrc/types.tsweb/CLAUDE.mdweb/src/pages/ChangelogPage.tsx
There was a problem hiding this comment.
Pull request overview
Adds the v0.3.0 foundation for structured, agent-readable notes and generated workspace exports, alongside release documentation and web changelog updates.
Changes:
- Extends notes with optional schema metadata, defaults, parsing/writing support, and sidebar metadata/filter UI.
- Adds INDEX.json / AGENTS.md generation with debounced export writing and configuration.
- Adds tests and release/web documentation for v0.3.0 agent integration and search messaging.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 26 comments.
Show a summary per file
| File | Description |
|---|---|
src/types.ts |
Adds structured note metadata types and optional fields. |
src/noteDefaults.ts |
Adds defaulting and expiry helpers. |
src/storageManager.ts |
Parses and writes structured metadata fields. |
src/noteManager.ts |
Applies defaults on storage reads and adds metadata updates. |
src/exportGenerator.ts |
Builds INDEX.json and AGENTS.md contents. |
src/exportWriter.ts |
Writes generated exports with debounce and atomic rename. |
src/extension.ts |
Wires exports and registers new commands. |
src/notesSidebarProvider.ts |
Adds type/expiry filter state for sidebar notes. |
src/noteTreeItem.ts |
Displays note type and priority in sidebar items/tooltips. |
src/test/runUnitTests.ts |
Includes new unit test files in the unit runner. |
src/test/suite/storageManager.test.ts |
Adds structured metadata parser/writer tests. |
src/test/suite/noteManager.test.ts |
Adds defaulting boundary coverage. |
src/test/suite/noteDefaults.test.ts |
Adds defaults and expiry tests. |
src/test/suite/exportGenerator.test.ts |
Adds index/digest generator tests. |
src/test/suite/exportWriter.test.ts |
Adds export writer debounce/write/error tests. |
package.json |
Bumps version and contributes export commands/settings. |
README.md |
Documents generated exports and gitignore guidance. |
docs/changelogs/v0.3.0.md |
Adds v0.3.0 release notes. |
docs/superpowers/specs/2026-05-15-agent-integration-design.md |
Adds agent integration design spec. |
docs/superpowers/plans/2026-05-15-agent-integration-v0.3-schema-and-exports.md |
Adds implementation plan. |
web/src/pages/ChangelogPage.tsx |
Adds v0.3.0 entry to web changelog. |
web/CLAUDE.md |
Adds web subproject agent guidance. |
Comments suppressed due to low confidence (7)
src/exportGenerator.ts:95
generatedAtdefaults to the current time, sobuildIndex(notes, workspaceRoot)produces a different INDEX.json on every regeneration even when the notes are unchanged. This contradicts the deterministic-output behavior documented for the generated exports and will create avoidable git diffs for teams that commit them.
return {
version: 1,
generatedAt: now.toISOString(),
workspaceRoot,
notes: entries,
src/extension.ts:1000
- This command also dereferences
sidebarProviderwithout a workspace/initialization guard. In the no-workspace partial activation path, invoking it from the command palette will fail with an exception rather than a user-facing error.
const toggleExpiredCommand = vscode.commands.registerCommand(
'codeContextNotes.toggleExpired',
() => {
sidebarProvider.toggleHideExpired();
}
docs/superpowers/specs/2026-05-15-agent-integration-design.md:508
- This migration section also describes YAML/frontmatter behavior and an older-version YAML loader, which does not match the current bold-label markdown parser. Please update the design doc so rollback/compatibility guidance matches the actual on-disk format.
### 6.3 Frontmatter format
Writer omits any field equal to its default. A plain note that's still just a `context` note on a line range stays the shape it always had.
### 6.4 Backward compatibility
A v0.2.x install opening a workspace with v0.3 notes: unknown frontmatter keys are ignored by the YAML loader. Note loads with its original fields. `INDEX.json`/`AGENTS.md` ignored. No data loss either direction. Mixed v0.2.x/v0.3 installs across a team are fine without coordination.
src/exportGenerator.ts:118
- The digest ordering for instructions/warnings only sorts by priority, leaving ties in the input order returned by the filesystem-backed note scan. Because
AGENTS.mdis documented as deterministic, add a stable tie-breaker such as file path/line/id so equal-priority notes do not reorder between regenerations.
const critical = notes
.filter(n => n.type === 'instruction' || n.type === 'warning')
.sort((a, b) => (PRIORITY_RANK[a.priority!] ?? 99) - (PRIORITY_RANK[b.priority!] ?? 99));
src/exportGenerator.ts:132
- Open handoffs are emitted in the input order without any stable sort. Since the input comes from scanning note files, this can make AGENTS.md reorder unchanged handoffs across platforms/filesystems despite the deterministic-output guarantee.
const handoffs = notes.filter(n => n.type === 'handoff');
if (handoffs.length > 0) {
out.push('## Open handoffs');
for (const n of handoffs) {
const range = n.lineRange.start === n.lineRange.end ? `${n.lineRange.start + 1}` : `${n.lineRange.start + 1}-${n.lineRange.end + 1}`;
out.push(`- **\`${relativePath(n.filePath, workspaceRoot)}:${range}\`** — handoff from ${n.author}: ${preview(n.content)}`);
src/exportGenerator.ts:142
- Decisions are also emitted in raw input order. For deterministic generated output, this section should use the same kind of stable ordering as the all-notes section, such as relative file path, line number, then note id.
const decisions = notes.filter(n => n.type === 'decision');
if (decisions.length > 0) {
out.push('## Decisions worth knowing');
for (const n of decisions) {
out.push(`- **\`${relativePath(n.filePath, workspaceRoot)}:${n.lineRange.start + 1}\`** — decision: ${preview(n.content)}`);
}
src/exportGenerator.ts:161
- Within a file, notes with the same starting line are sorted only by
lineRange.start, so equal-line notes retain filesystem scan order. Add a secondary key such as end line and id to keep AGENTS.md stable when multiple notes are attached to the same line.
out.push(`### \`${file}\``);
const fileNotes = byFile.get(file)!.sort((a, b) => a.lineRange.start - b.lineRange.start);
for (const n of fileNotes) {
const tags = n.tags!.length > 0 ? ` [${n.tags!.join(', ')}]` : '';
out.push(`- L${n.lineRange.start + 1} — ${n.type} (${n.priority})${tags}: ${preview(n.content)}`);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- exportWriter: serialize regenerate() calls so the debounced timer and manual "Regenerate Exports" command can't race on the same tmp files - extension: always subscribe to noteChanged so enabling exports after activation works without a reload; guard filterByType/toggleExpired against missing sidebarProvider in no-workspace activation; surface real success/failure from the regenerateExports command instead of always showing success - noteManager: block metadata updates on soft-deleted notes, normalize updateNoteMetadata/createNote through applyDefaults before caching so cache hits can't return notes with undefined schema fields, and keep the search index in sync on metadata updates - notesSidebarProvider: apply type/expiry filters before building file nodes so the root count and per-file nodes reflect the active filters instead of showing empty files or stale totals - storageManager: validate parsed Type/Scope/Priority/AuthorType/ References against known enums instead of blind `as any`/JSON.parse; reuse NOTE_DEFAULTS instead of duplicated literals when serializing - exportGenerator: thread the configured storage directory into contentPath/digest source line (was hardcoded to .code-notes) and add stable tiebreak sorting so AGENTS.md ordering is deterministic for equal-priority/same-line notes - fix package-lock.json version drift (0.1.7 -> 0.3.0) - fix hardcoded absolute path in web/CLAUDE.md, misleading NotePriority doc comment, missing markdownlint language tags, and remove the stale "Coming in v0.3.0" changelog section duplicating shipped notes Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015NoZbQSkHz14Fo7fnaU5n7
- Reject metadata updates on soft-deleted notes (matches updateNote) - Sync the search index on metadata changes - Apply schema defaults at the cache entry points (addNoteToCache / updateNoteInCache) so cached notes always honor the NoteManager boundary guarantee, including notes inserted by createNote Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Filtering now happens once in applyNoteFilters, used by the root count, file nodes, and note leaves — files whose notes are all filtered out no longer render as empty groups, and the root total matches what the tree shows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…status - Export hooks always attached: toggling exports.enabled takes effect both ways without a reload (regenerate reads config per run) - External note-file changes (git pull, manual edits) also regenerate exports via the noteFileChanged watcher - Watcher ignores the generated AGENTS.md — regenerating exports was re-triggering the watcher in a feedback loop - Unique temp filenames so overlapping regenerations can't clobber each other's temp file - regenerate() returns written/disabled/failed; the manual command reports each state instead of always claiming success - INDEX.json contentPath honors codeContextNotes.storageDirectory; AGENTS.md header no longer claims INDEX.json as its source - Activation log reads the real extension version (was hardcoded 0.1.3) - Guard filterByType/toggleExpired for no-workspace activation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Invalid Type/Scope/Priority/AuthorType values are dropped with a
warning (schema defaults apply at the NoteManager boundary), and
References entries are filtered to valid {kind, value} shapes.
Narrows the NotePriority comment to shipped behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verification found the search UI (commands, keybinding, settings, searchUI.ts) was silently dropped in an earlier merge — only the SearchManager backend reached main. Search claims are removed from the v0.3.0 changelogs; the UI ships in a follow-up (Issue #10). Also: README determinism caveat (generatedAt) and configurable storage-directory phrasing, gitignore/text fence languages (MD040), spec frontmatter wording aligned to bold-label format, web/CLAUDE.md relative path, stale 'Coming in v0.3.0' web section now 'Coming Next' (MCP server + search), exports setting description, lockfile synced to 0.3.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both address the same CodeRabbit/Copilot findings from parallel sessions. Kept the superset: remote's typed enum validation, NOTE_DEFAULTS reuse, regenerate() serialization, and digest tiebreak sorting; local's unique tmp filenames, regenerate status returns, noteFileChanged export regeneration + AGENTS.md watcher-loop guard, search-claims removal from changelogs, and regression tests. Kept per-entry references salvage (tested) over all-or-nothing, and the digest header without an INDEX.json pointer since exports.indexJson can be disabled independently. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
All review findings from CodeRabbit and Copilot have been addressed and the threads resolved. Two parallel fix sessions landed on this branch; they were merged in d282c9a keeping the superset of both: typed enum validation + Verification on the merged tree: 🤖 Generated with Claude Code |
tsc never deletes outputs for removed sources, so out/test/suite kept stale compiled suites from old branches (searchManager, sidebarProvider, tagManager) — ~100 phantom tests failing against code that doesn't exist in this tree. pretest now cleans out/ first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- getNotesForFile cached a pre-filtered list from storage.loadNotes, hiding deleted notes from getAllNotesForFile cache hits; the cache now always holds all notes and filters at return - deleteNote removed the soft-deleted note from the cache instead of updating it, turning 'already deleted' errors into 'not found' - getNoteById no longer returns soft-deleted notes to consumers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- getCurrentlyEditingComment looked up commentThreads (keyed by file:line thread key) with a note id, so it always returned null; the thread key is now tracked alongside the editing note id - Comment labels always said 'Last updated' because the creation history entry counted as an update; fresh notes now show 'Created' Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- CodeLens: take the first line before stripping markdown (the strip collapses newlines, merging every line into the preview) and strip heading markers that appear mid-text - noteTreeItem: strip image markdown before link markdown so  doesn't leave a stray '!'; truncateText returns the ellipsis itself when maxLength <= 3 instead of a clipped fragment - Sidebar: sort note children by line number — callers may construct FileTreeItem with notes in any order Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Mock document paths used Date.now() alone — two docs created in the same millisecond collided on the same fsPath; added a sequence counter - CommentController flows that reopen the document from disk now write the file first (openTextDocument was failing on nonexistent paths) - await the now-async updateCommentThread; assert falsy instead of null for deleted-note lookup (API returns undefined) - Changelog: record the fixes under v0.3.0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: eliminate all 34 integration test failures (phantom suites + real bugs)
# Conflicts: # docs/superpowers/plans/2026-05-15-agent-integration-v0.3-schema-and-exports.md # docs/superpowers/specs/2026-05-15-agent-integration-design.md # web/CLAUDE.md
Summary
Ships v0.3.0 with two major capabilities plus the previously-drafted search feature, all merged into one release.
1. Agent integration foundation (17-task plan executed via subagent-driven development from
docs/superpowers/plans/2026-05-15-agent-integration-v0.3-schema-and-exports.md):type(context/instruction/warning/decision/todo/handoff/rationale),priority(low/normal/high/critical),scope,tags,expiresAt,authorType,references. All optional; legacy v0.2.x notes load with defaults applied at theNoteManagerboundary (single lazy-migration point)..code-notes/INDEX.json(machine-readable, deterministic, with byFile/byType/byTag lookups) and.code-notes/AGENTS.md(human-readable digest hoisting instructions/warnings/handoffs/decisions). Debounced 200ms atomic writes on every note change.Regenerate Exports,Set Note Type / Tags / Priority…,Filter Notes by Type…,Toggle Expired Notes.codeContextNotes.exports.{enabled,indexJson,agentsMarkdown}.2. Search and Filter (deferred): review verification found the search UI (commands, keybinding, settings,
searchUI.ts) was silently dropped in an earlier merge — only theSearchManagerbackend reachedmain. Rather than double this PR's surface, search claims were removed from the changelogs; the UI ships in a follow-up release (Issue #10 stays open).Architecture decisions
**Type:** …, etc.) rather than introducing YAML frontmatter. Fields equal to default are omitted, so untouched legacy notes remain byte-identical on disk.ExportWriteraccepts an injectablegetConfigso its unit tests run without the VS Code host.INDEX.json.errors[]population — both belong with the MCP server work.Test plan
npm run compile:tsc— cleannpm run compile(esbuild) — cleannpm run test:unit— 60/60 passing (5 noteDefaults + 6 exportGenerator + 3 exportWriter + 3 storage parser + 2 storage writer)npm run package:dev— producescode-context-notes-0.3.0.vsix(95 KB)cd web && npm run build:client— clean.code-notes/INDEX.json+AGENTS.mdappear within ~250msSet Note Type / Tags / Priority…→ set type=instruction, priority=high → verify hoisted to "Critical instructions and warnings" in AGENTS.mdFilter Notes by Type→ pickinstruction→ sidebar filters correctlyManual smoke tests verified 2026-07-16 by driving the extension in a real VS Code extension host (
@vscode/test-electron): exports appeared 206ms after activation and 298ms after note creation; instruction/high note hoisted to "Critical instructions and warnings"; type filter and expired-notes toggle verified against the live sidebar tree provider; legacy note loaded with defaults and its on-disk file stayed byte-identical. (Ran the dev-extension path rather than installing the vsix;npm run package:devbuilds it cleanly.)Docs
docs/changelogs/v0.3.0.md— merged changelogdocs/superpowers/specs/2026-05-15-agent-integration-design.md— full design spec across v0.3/v0.4/v0.5docs/superpowers/plans/2026-05-15-agent-integration-v0.3-schema-and-exports.md— implementation planREADME.md— exports + .gitignore guidance sectionweb/src/pages/ChangelogPage.tsx— v0.3.0 timeline entry🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
.code-notes/: INDEX.json (for integrations) and AGENTS.md (agent digest)Documentation
Review fixes (2026-07-16)
Applied after CodeRabbit/Copilot review + live verification:
updateNoteMetadata: rejects soft-deleted notes, syncs the search index, caches defaults-applied notes (cache entry points now normalize viaapplyDefaults)filterByType/toggleExpiredguarded for no-workspace modeexports.enabledtoggles both ways without reload; external note-file changes (git pull) regenerate exports;AGENTS.mdwatcher self-trigger loop guarded; unique temp names for atomic writes; manual regenerate reports disabled/failed states honestly;INDEX.json.contentPathhonorscodeContextNotes.storageDirectorypackage-lock.jsonsynced to 0.3.0; stale hardcoded version log removed; docs nits (fence languages, spec frontmatter wording, README determinism caveat, stale web "Coming in v0.3.0" section)