Skip to content

feat(notes): add workspace notes and guided AI setup - #3

Merged
yattdev merged 14 commits into
mainfrom
feature/per-workspace-notes-ac1
Aug 18, 2026
Merged

feat(notes): add workspace notes and guided AI setup#3
yattdev merged 14 commits into
mainfrom
feature/per-workspace-notes-ac1

Conversation

@yattdev

@yattdev yattdev commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Overview

This updates kandev-plugin-notes for workspace-scoped notes and fixes the dead-end Enhance with AI setup path. It reuses the existing notes editor surface, preserves task-note behavior, and makes configuration failures point to the correct settings page.

Changes

  • Generalize the note store to work against arbitrary (scope, scopeId) pairs while preserving task-note callers.
  • Key the note-indicator cache by scope so task and workspace note state stay isolated and update live.
  • Add workspace-note modal wiring and a sidebar workspace-notes button registration for the new host slot.
  • Classify utility-agent precondition failures into stable codes and surface guided setup actions in the modal.
  • Bump the plugin to 0.3.0 and update the README and changelog for workspace notes and AI setup.

Motivation

Users need a workspace-level scratchpad that does not require creating a task, and the existing AI enhance error collapsed distinct setup failures into one wrong remedy. These changes add the missing workspace scope and make the AI setup path diagnosable and actionable.

Testing

  • go test ./...
  • node --test "ui/**/*.test.mjs"
  • Manual verification on a live host in earlier QA: workspace note modal title, autosave, reload persistence, live icon flip, and guided AI setup actions
  • Verified this pass that no shipped bytes changed since the last full live verification: git diff 777992c..HEAD -- manifest.yaml ui/ server/ Makefile is empty

Breaking Changes

None.

QA handoff

QA fixes worth reviewer attention

None this pass. No defect was found in either PR's changes, so nothing was fixed and no commit was added. Both worktrees are unchanged and clean (plugin 3044930, host 0f8c8f4cb).

Earlier QA passes contributed two documentation commits that remain relevant to reviewers:

  • Redacted an unpatched upstream security finding from the PR-bound notes. .kandev/review-notes.md carried the file, line, mechanism and blamed author for an authentication weakness that is still unpatched in kdlbs/kandev. All three repos involved are public, so publishing it in a PR description would have disclosed it and would also have notified the blamed author by @-mention on a public thread — the wrong channel for a security issue. The entry now keeps the finding's existence, its irrelevance to this branch, and a pointer to its tracking task, and directs the disclosure upstream privately. Commit 3044930.
  • Corrected a false "tracked nowhere" claim in the same file. An earlier pass recorded the finding as untracked after its original tracking task was deleted. A replacement task exists and is live, so the claim would have shipped as a falsehood in both PR descriptions. Commit 9681dd3.

Assumptions made during QA

  • Assumed the unpatched upstream security finding should not be described in full in a public PR description. Both PRs land on public repositories and the issue is unpatched, so QA withheld the location and mechanism and pointed at the tracking task instead. This was QA's call, not one you made explicitly — if you would rather the PR carry the full description, revert the single hunk in commit 3044930. Confirm?
  • Assumed the finding should be disclosed to upstream through a private channel (GitHub security advisory on kdlbs/kandev, or direct contact with the maintainers) rather than by @-mentioning the blamed author in a public PR. Confirm?
  • Assumed this release will be cut by pushing a v0.3.0 tag, not by dispatching the release workflow manually. The branch's hand-set 0.3.0 in manifest.yaml/Makefile and the hand-written ## [0.3.0] CHANGELOG section are correct for the tag-push path (prepare is skipped; publish builds straight from this metadata). A manual workflow_dispatch instead computes max(manifest, latest tag) and bumps past it, so the default patch choice publishes 0.3.1, rewrites the version in manifest.yaml, Makefile and the README.md tarball name, and prepends a generated ## [0.3.1] section above the hand-written ## [0.3.0] one — two changelog entries for one set of changes, and no 0.3.0 release at all. Note prepare also hard-requires refs/heads/main, so a dispatch cannot run from this branch anyway. Nothing in the code depends on the number, so this is a release-time choice, not a defect. Confirm the tag-push path?

Pre-existing issues found (out of scope, tasks created)

  • A pre-existing authentication weakness in the host's plugin-webhook routing, confirmed live during an earlier QA pass. Pre-existing and not introduced by either PR; the code it lives in is untouched by this branch. Tracked in task 51781b28-0580-48e7-ac31-a732b07e3ddb, which holds the location, mechanism, reproduction, blame and three suggested remedies. That task is now the only record of the detail, so it should not be closed without action. Deliberately not described further here — see the assumptions above.

  • One e2e test in the host repo fails before this branch touches anything. apps/web/e2e/tests/plugins/plugin-task-panel.spec.ts:247 ("useNotesValue discards a stale storage read that resolves after a newer one") fails deterministically — 5 runs, 0 passes. It fails at the panel-mount step, before reaching the stale-read behavior it is named for, which points at a timing assumption that does not hold on a slower machine rather than a product defect. Proven not to be this branch's doing by reverting each of the branch's two shipped host changes independently and re-running: it fails identically in all three configurations. Tracked in task d059af5a-73a6-41c9-afeb-a8a6a7a96ac4. cc @yattdev — introduced in 345ea1216 ("feat(plugins): add task panel, menu, and storage hooks", #2152).

  • Enhance with AI accepts an empty result and lets Accept wipe the note. enhanceNote validates that the returned content is a string but not that it is non-empty, so a utility agent returning "" (or whitespace only) produces an empty preview whose Accept button replaces the user's note with nothing, autosaved, with no undo. Pre-existing — both lines predate this branch's base commit — and out of scope here, but directly adjacent to subtask C's work. One-line fix suggested in the task. Tracked in task 410a7934-52a4-4752-9cf9-c636a3bf485a. cc @yattdev — introduced in 4c84eb9 ("feat: markdown toolbar, AI-assisted proofreading, and a fixed-size note modal").

QA verification performed this pass

No shipped byte has moved since the last full live verification (git diff 777992c..HEAD -- manifest.yaml ui/ server/ Makefile is empty; host unchanged at 0f8c8f4cb; both trees clean). This pass therefore targeted surface the earlier rounds had not attacked, rather than re-running identical binaries:

  • Re-ran the real e2e against a live backend and Chromium, not just the automated unit gates: plugin-sidebar-workspace-actions.spec.ts 3/3, and the full e2e/tests/plugins/ directory 31 passed / 1 failed / 2 skipped (the one failure is the pre-existing test above). The e2e fixture package was rebuilt from source for this run.
  • Confirmed both C4 guided-setup routes actually resolve, rather than merely existing as files. The plan cited apps/web/app/settings/ file existence, which stopped being sufficient when this app migrated from Next.js to a Vite SPA. /settings/utility-agents is a static route (apps/web/src/settings-routes.tsx:166); /settings/plugins/kandev-plugin-notes matches the dynamic /^\/settings\/plugins\/([^/]+)$/ branch and renders PluginDetailPage. Neither button navigates to a dead page.
  • Boundary-probed the changed JS exports against adversarial input the committed tests do not cover: a 412 body carrying a non-string code (number, null, object, array) degrades to undefined and renders no action button; enhanceErrorAction returns null for junk, wrong case, and trailing whitespace; odd scopeId values thread through to the right storage calls. C5's robustness holds well beyond what is asserted in the suite.
  • Established that packaging cannot leak .kandev/ by construction, not by inspecting one build: make package/package-host build a fresh $(STAGE) from explicit cp of manifest.yaml and ui/bundle.js only, so the result is independent of what the working tree contains. That matters because .kandev/review-notes.md has changed content since it was last checked with tar tzf.
  • Full suites, all first-hand this pass: plugin go test -count=1 ./... ok (cache bypassed), plugin node --test 96/96, host go test ./internal/plugins/... ok across all 9 packages, host full vitest 10416 passed / 4 skipped / 0 failed across 1299 files.
  • Environment restored after the revert experiments: fixture source restored and its tarball rebuilt, apps/web/dist rebuilt from restored source, branch e2e re-verified 3/3, and both worktrees confirmed clean.
  • Prior pass (still valid, same bytes): full five-platform make package cross-compile; host app-sidebar-new-task-item.test.tsx 24/24; version strings agree across manifest.yaml, Makefile and CHANGELOG.md (all 0.3.0), which is the release job's own consistency check.

One caveat stated plainly: this pass did not repeat the manual browser walkthrough of B3-B5 (typing a workspace note, watching the icon flip live, cross-tab edit). Those were verified live in an earlier pass against an artifact this pass proved byte-identical, so the result carries over — but it is inherited evidence, not something re-observed today.

ayattara-sfl and others added 14 commits August 11, 2026 14:21
createNoteStore, the card-indicator cache, and the modal factory take a
(scope, scopeId) pair instead of a hardcoded "task" scope (taskId remains
a working alias, so every existing task surface is unaffected). Registers
a new sidebar-workspace-actions button that opens the same note modal
scoped to the active workspace instead of a task, inert on hosts that
don't carry that slot yet.
The enhance webhook's 412 body now carries a stable, machine-readable code
(agent_unset/agent_missing/agent_disabled/agent_unavailable), classified
from the host's real FailedPrecondition message, plus the raw message as
detail. The UI maps each code to its own guided-setup action button
(Settings > Plugins > Notes for unset/missing, Settings > Utility Agents
for disabled) instead of one message pointing everyone at the same page
regardless of which setting is actually missing. An older server that
omits code still gets today's plain-message behavior.
README documents the new sidebar workspace-notes button and the two-step
Enhance with AI setup (select an agent, then enable it with a model under
Settings > Utility Agents), including the Enabled asymmetry between
kandev's own prompt enhancement and plugin utility-agent calls.
agent_unavailable is by definition a FailedPrecondition the plugin could
not classify, but its message named Settings > Plugins > Notes anyway.
That is reachable today, not hypothetically: host_utility.go has a fourth
wording, "configured utility agent %q has no usable agent profile", fired
when an agent is selected and enabled but its model/profile is still
unbound - the state every built-in utility agent ships in. Following the
README's own two-step setup lands there, and the message sent the user
back to the page they had already used correctly, with no action button.

The message now quotes the host's own wording instead of guessing a
remedy, which is the degradation the classifier was designed for: a
missing button, not a wrong instruction. Routing that case to its own
code and action button is left to the author - C1 fixes the code set at
four, so adding a fifth is a scope decision, not a QA fix.

Also fixes the workspace note editor showing "Jot a note about this
task..." under a modal titled "Workspace notes - <label>".

Verified against a real host (isolated instance, plugin 0.3.0 installed
via the real install API): all four states reproduced before and after,
the three classified ones unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
host_utility.go has a fourth FailedPrecondition wording, "configured
utility agent %q has no usable agent profile", fired when an agent is
selected and enabled but its model/agent profile is still unbound. That
is not an edge case: every built-in utility agent ships that way, so it
is the state a user lands in immediately after completing the README's
two documented setup steps.

It previously fell through to agent_unavailable, which rendered no
guided-setup button at all - the one case most likely to need it. It now
has its own agent_unconfigured_profile code and a "Finish setting up the
agent" action pointing at Settings > Utility Agents. Kept separate from
agent_disabled deliberately: same page, different control, and telling
someone to enable an agent they just enabled is the dead end this
mapping exists to remove.

agent_unavailable now means only "a cause this plugin could not
identify" and keeps quoting the host's own wording.

This widens C1's four-code enumeration to five, approved by the author
after QA confirmed the gap live.

Verified end to end on an isolated instance with the real 0.3.0 package:
the repro now returns agent_unconfigured_profile with the correct
message, the served bundle carries the new action, and unset/missing/
disabled/400 are unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The "Added" bullet still enumerated four codes
(unset/missing/disabled/unavailable) after bbb3160 added
agent_unconfigured_profile in the same release.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The QA pass recorded the plugin-webhook auth finding as "tracked
nowhere" after the original subtask was deleted. A replacement task
(51781b28-0580-48e7-ac31-a732b07e3ddb) exists and is in progress, so the
claim is false and would have shipped verbatim into both PR
descriptions. Restore the citation and correct the release-trigger note
to list all three files the prepare job rewrites.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All three repos (kdlbs/kandev, yattdev/kandev, yattdev/kandev-plugin-notes)
are public and the webhook auth issue is unpatched upstream, so the
PR-bound notes no longer carry its file, line, mechanism or blame
attribution. The entry keeps the finding's existence, its irrelevance to
this branch, and a pointer to tracking task 51781b28; it now directs the
disclosure to a private upstream channel instead of an @-mention in a
public PR. Flagged in the file as a QA call the author can veto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@yattdev
yattdev marked this pull request as ready for review August 18, 2026 01:19
@yattdev
yattdev merged commit 5d53207 into main Aug 18, 2026
3 checks passed
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.

2 participants