Skip to content

feat(bookmarks): the sidebar page is a bookmark list, and stops shipping as evidence - #32

Merged
guan4tou2 merged 1 commit into
mainfrom
feat/bookmarks-ruling
Sep 4, 2026
Merged

guan4tou2 merged 1 commit into
mainfrom
feat/bookmarks-ruling

Conversation

@guan4tou2

Copy link
Copy Markdown
Owner

The ruling 8b left open, decided against the code rather than against the word on the sidebar. The answer is not "make quickmarks append-only too" — it is that the table was never a record, and the product has been claiming it was.

Six columns, no operator_id, no engagement_id, no hash. Every feature on its page is a bookmark feature: a CDP poll for the focused tab, title/URL autofill, open-in-browser, a dot whose colour is a hash of the title. Its own shipped copy reads "Click + Capture to bookmark the current browser state", and ARCHITECTURE.md already classified it "Bookmarks. Not chained."

So mutability was never the false part. These three were:

  1. The sidebar called it 標記 while every gesture an operator would call dropping a mark — ⌘⇧M, the title-bar button, the tray, both HUD buttons, the timeline right-click — writes a chained marker event and never touches this table. The page's empty state told them to press ⌘⇧M, a key that cannot produce a row on it.
  2. It shipped as evidence: written into the bundle unconditionally beside events.jsonl, in a bundle whose README classifies two files and never names it, and which the bundled verifier never opens. Sharpest case: the scope-filtered export ran events through the filter and these rows around it, so a file labelled scope-filtered carried URLs for hosts the operator had deliberately excluded.
  3. 8b's shipped ruling says url is an immutable marker field, and no marker had a url.

Markers now carry url — filled once from the browser connector, editable, and deliberately not amendable, because it is where the mark points (the web analogue of atTimestamp). That makes 8b true; 8b moves from partial to done.

Why not append-only. Triggers on a table with no hash, no signature, no place in the chain or the anchor, and no verifier that reads it buy the feeling of immutability and none of the checking — DELETE FROM quickmarks still works. And they would remove the only removal path there is: createQuickMark runs no redaction and sanitize reads events only, so a password pasted into a note is unmaskable in both directions and the hard delete is the operator's sole remedy. Append-only would keep that credential forever — and until this PR it also shipped it.

Deferred, recorded: the internal identifiers (findings.ts, FindingsView, quickmarks:* IPC, the REST route, the CLI verb, the read:findings plugin capability) keep their old names. Renaming them needs aliases so third-party plugin manifests and installed integrations survive — a rename-only PR. The SQL table name stays for the same reason.

Verification: npm run typecheck clean · npm test 1055 passed (98 files) · npm run build · playwright 89 passed, 1 skipped.

🤖 Generated with Claude Code

…ing as evidence

The open ruling from 8b, decided against the code rather than against the word
on the sidebar. The answer is not "make quickmarks append-only too" — it is that
the table was never a record, and the product has been claiming it was.

Six columns, no operator_id, no engagement_id, no hash. Every feature on its page
is a bookmark feature: a CDP poll for the focused tab, title and URL autofill,
open-in-browser, and a dot whose colour is a hash of the title. Its own shipped
copy says "Click + Capture to bookmark the current browser state", and
ARCHITECTURE.md already classified it as "Bookmarks. Not chained."

So mutability was never the false part. These three were, and all three are
fixed here:

The sidebar called it 標記 while every gesture an operator would call dropping a
mark — ⌘⇧M, the title-bar button, the tray, both HUD buttons, the timeline
right-click — writes a chained `marker` event and never touches this table. The
page's own empty state told the operator to press ⌘⇧M, a key that cannot produce
a row on it.

It shipped as evidence: written into the bundle unconditionally, beside
events.jsonl, in a bundle whose README classifies two files and never names it,
and which the bundled verifier never opens. Worse, the scope-filtered export ran
events through the scope filter and these rows around it — so a file labelled
scope-filtered carried URLs for hosts the operator had deliberately excluded.

And 8b's shipped ruling says `url` is an immutable marker field, while no marker
had a `url` at all.

Markers now carry `url`, filled once from the browser connector and editable,
deliberately NOT amendable — it is where the mark points, the web analogue of
`atTimestamp`. That makes 8b true, so 8b moves from partial to done.

Append-only was rejected on the evidence: triggers on a table with no hash, no
signature, no place in the chain or the anchor, and no verifier that reads it
buy the feeling of immutability and none of the checking — `DELETE FROM
quickmarks` still works. And they would remove the only removal path that exists:
`createQuickMark` runs no redaction and `sanitize` reads `events` only, so a
password pasted into a note is unmaskable in both directions and the hard delete
is the operator's sole remedy. Append-only would keep that credential forever,
and until this commit it also shipped.

Deferred, recorded: the internal identifiers (findings.ts, FindingsView,
`quickmarks:*` IPC, the REST route, the CLI verb, the `read:findings` plugin
capability) keep their old names. Renaming those needs aliases so third-party
plugin manifests and installed integrations do not break, and that belongs in a
rename-only PR. The SQL table name stays too.

npm run typecheck clean · npm test 1055 passed (98 files) · npm run build ·
playwright 89 passed, 1 skipped

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: b8608703-ab39-4fce-9c0b-9852b38751c9


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.

@guan4tou2
guan4tou2 merged commit 952e968 into main Sep 4, 2026
5 checks passed
@guan4tou2
guan4tou2 deleted the feat/bookmarks-ruling branch September 4, 2026 04:50
guan4tou2 added a commit that referenced this pull request Oct 8, 2026
Seven P1 findings from the UI/UX audit, batched:

  • #15 HUD auto-collapse now pauses while the pointer is on the overlay
    (audit was: hard 8s timer, no way to keep it open while reading the
    topology chain / Reveal button).
  • #16 Terminal tab labels ("Shell 1", "Shell 2" …) used tabs.length + 1
    → duplicated after any close. Now uses a monotonic counter.
  • #17 Closing a terminal tab kills the PTY silently. Now confirms via
    ConfirmDialog when the tab is still alive (matches ProjectPicker
    delete pattern).
  • #21 Settings CDP test callback had a `const tab = ...` shadowing the
    outer `tab` state (settings tab name). Renamed to `cdpTab`.
  • #28-29 EventMarker showed raw enum strings ("info" / "initial_access")
    for severity + category — now i18n'd (marker.severity.*, marker.
    category.*). Also added an opt-out "Capture screenshot too" checkbox
    (default on) so a mark taken over sensitive UI doesn't force a capture.
  • #30 EventMarker gained role="dialog", aria-modal, aria-label, and an
    Escape-to-close handler on the backdrop. Autofocus lands on the dialog
    body so keyboard users have a hook.
  • #32 App title-bar Mark button title="Ctrl+Shift+M" was hardcoded —
    Mac users saw the wrong hint. Uses isMac to pick ⌘⇧M vs Ctrl+Shift+M.

Deferred (need bigger refactors): #9-14/#49-50 shared IPC store to stop
per-event refetches across StatusBar/Sidebar/Dashboard/LootPanel/TargetView/
ScopeStatus; #22-24 sidebar keyboard drag; #25-27 FindingsView/SearchPanel/
LootPanel interactive; #33 uptime should be engagement-scoped not window-
scoped; #34 StatusBar record toggle button styling; #37-38 timeline layout
constraints. All queued for the next batch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
guan4tou2 added a commit that referenced this pull request Oct 8, 2026
…line ↑/↓ nav

- LootPanel: chip-based type filter with per-type counts + dedup toggle
  collapses same (type, preview) match across events (audit #18)
- ScreenshotsView: trigger-type chip filter (periodic/manual/mark) so grid
  isn't a mixed pile (audit #32)
- Timeline: ↑/↓ walks the selected event across visible (hidden-lane-aware)
  events; skips inputs so search field still types normally (audit #5)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
guan4tou2 added a commit that referenced this pull request Oct 8, 2026
feat(bookmarks): the sidebar page is a bookmark list, and stops shipping as evidence
guan4tou2 added a commit that referenced this pull request Oct 8, 2026
… part A)

PR #32 renamed the feature to bookmarks in the UI but left the code as
QuickMark. This is the INTERNAL half — no external contract changes:

- db/findings.ts → db/bookmarks.ts; QuickMark(Context) → Bookmark(Context);
  create/list/get/update/delete/deleteOlderThan QuickMark(s) → …Bookmark(s).
- SQL table quickmarks → bookmarks, with a migration that renames an existing
  table in place (ALTER TABLE RENAME) so an older project's rows carry over —
  the SQL-table-rename decision, accepted.
- IPC channels quickmarks:* → bookmarks:*, the preload namespace and env.d.ts
  contract, and every renderer caller.
- FindingsView.tsx → BookmarksView.tsx.
- visibility signal, retention, bundle, tray, browser-launcher call sites.

Deliberately KEPT for part B (external contracts, need an alias window): the
REST route /api/quickmarks and its `quickmarks` response key, the CLI
quickmark/quickmarks verbs, and the plugin read:findings capability /
findings.list method.

Validated: typecheck clean, unit 1152 passed, e2e 16 passed (incl. cli-smoke
hitting the kept REST route and the bookmark visibility flow).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
guan4tou2 added a commit that referenced this pull request Oct 8, 2026
Seven P1 findings from the UI/UX audit, batched:

  • #15 HUD auto-collapse now pauses while the pointer is on the overlay
    (audit was: hard 8s timer, no way to keep it open while reading the
    topology chain / Reveal button).
  • #16 Terminal tab labels ("Shell 1", "Shell 2" …) used tabs.length + 1
    → duplicated after any close. Now uses a monotonic counter.
  • #17 Closing a terminal tab kills the PTY silently. Now confirms via
    ConfirmDialog when the tab is still alive (matches ProjectPicker
    delete pattern).
  • #21 Settings CDP test callback had a `const tab = ...` shadowing the
    outer `tab` state (settings tab name). Renamed to `cdpTab`.
  • #28-29 EventMarker showed raw enum strings ("info" / "initial_access")
    for severity + category — now i18n'd (marker.severity.*, marker.
    category.*). Also added an opt-out "Capture screenshot too" checkbox
    (default on) so a mark taken over sensitive UI doesn't force a capture.
  • #30 EventMarker gained role="dialog", aria-modal, aria-label, and an
    Escape-to-close handler on the backdrop. Autofocus lands on the dialog
    body so keyboard users have a hook.
  • #32 App title-bar Mark button title="Ctrl+Shift+M" was hardcoded —
    Mac users saw the wrong hint. Uses isMac to pick ⌘⇧M vs Ctrl+Shift+M.

Deferred (need bigger refactors): #9-14/#49-50 shared IPC store to stop
per-event refetches across StatusBar/Sidebar/Dashboard/LootPanel/TargetView/
ScopeStatus; #22-24 sidebar keyboard drag; #25-27 FindingsView/SearchPanel/
LootPanel interactive; #33 uptime should be engagement-scoped not window-
scoped; #34 StatusBar record toggle button styling; #37-38 timeline layout
constraints. All queued for the next batch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
guan4tou2 added a commit that referenced this pull request Oct 8, 2026
…line ↑/↓ nav

- LootPanel: chip-based type filter with per-type counts + dedup toggle
  collapses same (type, preview) match across events (audit #18)
- ScreenshotsView: trigger-type chip filter (periodic/manual/mark) so grid
  isn't a mixed pile (audit #32)
- Timeline: ↑/↓ walks the selected event across visible (hidden-lane-aware)
  events; skips inputs so search field still types normally (audit #5)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
guan4tou2 added a commit that referenced this pull request Oct 8, 2026
feat(bookmarks): the sidebar page is a bookmark list, and stops shipping as evidence
guan4tou2 added a commit that referenced this pull request Oct 8, 2026
… part A)

PR #32 renamed the feature to bookmarks in the UI but left the code as
QuickMark. This is the INTERNAL half — no external contract changes:

- db/findings.ts → db/bookmarks.ts; QuickMark(Context) → Bookmark(Context);
  create/list/get/update/delete/deleteOlderThan QuickMark(s) → …Bookmark(s).
- SQL table quickmarks → bookmarks, with a migration that renames an existing
  table in place (ALTER TABLE RENAME) so an older project's rows carry over —
  the SQL-table-rename decision, accepted.
- IPC channels quickmarks:* → bookmarks:*, the preload namespace and env.d.ts
  contract, and every renderer caller.
- FindingsView.tsx → BookmarksView.tsx.
- visibility signal, retention, bundle, tray, browser-launcher call sites.

Deliberately KEPT for part B (external contracts, need an alias window): the
REST route /api/quickmarks and its `quickmarks` response key, the CLI
quickmark/quickmarks verbs, and the plugin read:findings capability /
findings.list method.

Validated: typecheck clean, unit 1152 passed, e2e 16 passed (incl. cli-smoke
hitting the kept REST route and the bookmark visibility flow).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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