Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx agentmods add instructions/alekkowalczyk/noteback/claude-mdgit clone --depth 1 https://github.com/alekkowalczyk/notebackWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/instructions/alekkowalczyk/noteback/claude-md)<a href="https://agentmods.dev/instructions/alekkowalczyk/noteback/claude-md"><img src="https://agentmods.dev/badge/instructions/alekkowalczyk/noteback/claude-md.svg" alt="Measured on agentmods" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5 | $0.05375 | $0.05375 |
| Opus 5 | $0.02687 | $0.02687 |
| Sonnet 5 | $0.01075 | $0.01075 |
| Haiku 4.5 | $0.00537 | $0.00537 |
Grade A, and why
noteback CLAUDE.md scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 4d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 282 lines — stays where its author put it; the contents beside it link to each section on GitHub.
CLAUDE.md — Noteback engineering notes
Project-local guidance for working in this repo. Read alongside README.md
(what/why), CONTRACTS.md (the runtime module API + behavioral invariants), and
docs/design.md (the original design).
This file records the non-obvious gotchas — things you can't infer by reading
the code, that have already bitten us once.
Hard constraints (do not break)
- Zero RUNTIME dependencies, no build step, no TypeScript. The shipped code
(
bin,src,skills) loads unpacked exactly as written — never add a bundler, a framework, or adependenciesentry, and neverrequirea package fromsrc/. Tests run on the Node built-in runner (npm test→node --test). The one allowed exception isdevDependencies: Playwright backs the browser e2e (test/e2e/,npm run test:e2e) that covers overlay DOM behaviour the Node suite can't. It is test-only and never reaches users (filesshipsbin/src/skillsonly). Needs the browser binary once:npx playwright install chromium. - One runtime, two modes. The annotation engine in
src/runtime/runs both as the extension content script (ChromeStorageAdapter) and inlined into a saved canvas file (InFileStateAdapter). Anything insrc/runtime/must work in both — nochrome.*access, no extension-only globals. Mode-specific code lives insrc/content/,src/adapters/,src/canvas/. - Pure-logic modules (
anchor,state,markdown) must run under Node and the browser (UMD-lite dual export) so they stay unit-testable. Keep them DOM-free.
Gotchas that already bit us
- CSS transition out of
display:nonedoes not reliably fire. The comment chip's entrance is a keyframe animation restarted by a forced reflow:el.classList.remove('nb-in'); void el.offsetWidth; el.classList.add('nb-in'). Don't "simplify" it back to atransition— it'll snap in with no animation. - The comment chip is debounced (~340 ms). A
setTimeoutis re-armed on eachselectionchangeand the anchor is re-resolved onmouseup. Two consequences: (1) live/Playwright tests must wait ~380 ms after selecting before the chip is clickable; (2)commitPopoveris async (await persist) — a test that creates two comments synchronously will have the second reuse the first anchor (becauseonSelectionChangeearly-returns while a popover is open). Await the first commit. - Composer vs. sidebar outside-click are opposite on purpose. The composer
closes only via Cancel / Save / Escape (never outside-click); the sidebar
does close on outside-click (guarded). See
CONTRACTS.md§3.5. Don't "unify" them. - Markdown line refs are computed from the document markup, not the DOM. The
full (uncondensed) quote is located in
docHtml; long quotes are condensed for display only. If a line ref and the quote ever disagree, the quote wins — it's the anchor; the line number is a convenience. - Line-number semantics differ by mode. Embedded canvas → doc-content-relative
(
#noteback-doc-rootinnerHTML, line 1 = first body line). Extension →documentElement.outerHTML(file-absolute, tracks the opened file). SametoMarkdown, differentdocHtmlorigin. This is a deliberate, documented tradeoff — don't try to "fix" one to match the other. - Doc identity is the BAKED doc-id; a version is hashed from the CLEAN, pre-paint
content root. A draft's identity is the explicit
data-noteback-doc-idbaked on#noteback-doc-root(extension pages Noteback didn't author fall back to a per-URL minted id undernb:url:<href>). Within that doc-id, a version is keyed by a content hash over#noteback-doc-roottextContent(createHistoryStateAdapter'scontentText), read before highlights are painted — never recompute it from the live DOM after<mark>wrappers are added, or the hash shifts (and the draft splinters into a new version). When the text is too short to hash, the version key falls back toh0:<docId>. window.localStorageaccess can THROW (not just be absent) onfile://or when storage is blocked — andfile://is the primary canvas use case. TheEMBEDDED_BOOTbuilds the localStorage-backed kv store (lsStore) inside atry/catch; on failurelsStoreisnullandcreateHistoryStateAdapterdegrades to the in-fileInFileStateAdapter(comments still work, just no version history). Never referencewindow.localStorageraw in the boot guard, or a blocked store crashes the whole canvas mount (it did once — the overlay never appeared).file://localStorage is one shared bucket across all local canvases (Chrome). Keys are namespaced and keyed by the explicit doc-id (nb:doc:<docId>) / content-hashed version key (nb:ver:<versionKey>), withnb:url:<href>for per-URL minted ids (extension only), precisely so distinct documents don't collide in that shared bucket.- History snapshots the WHOLE clean document ONCE, at a version's first comment —
there is no per-comment fragment/"section" extraction.
snapshot-capture.jscaptureCleanDocclonesdocumentElement, strips[data-noteback-ui], unwraps every<mark class="noteback-highlight">, and drops#noteback-state+ the inline runtime<script>, then stores the result gzipped (makeCodec). Because the marks are stripped, the snapshot is paint-independent: it does NOT matter whether highlights are painted whensave/persistruns (the old "paint before persist" bug class and itshistory-popup.e2e.test.jsguard are gone —commitPopover'srepaintHighlights()is now just a visual refresh with no ordering requirement vs.persist).history-state-adapter.jscaptures the snapshot only when the version has no snapshot yet (needSnapshot = comments.length>0 && !r.hasSnapshot);hasSnapshotis seeded from the real stored snapshot, never the comment count. - Version viewing is IN-TAB and read-only. Clicking a version row opens
overlay.openVersionInline— a read-only<iframe srcdoc>side panel (.nb-hist-view) beside the sidebar (NOT a new tab, NOT a centered modal). It reuses the snapshot painter (paintHighlights+ re-injectedHIGHLIGHT_CSS+buildPeekPopoverScript). An in-tabviewingKeydrives the timeline: the viewed row is the activenb-ver-viewingrow (active dot + highlight — there is no "you are here" text label), a "Back to current" bar (renderBackToCurrentBar→closeVersionInline) returns to the live draft, and other rows switch. There is NO new-tab "checkout" (openVersionTab/ thedata-noteback-checkoutmarker were removed) because awindow.open(blob:)tab from afile://canvas gets an opaque origin whoselocalStorageis denied, leaving the opened tab's history sidebar empty (the bug). The.nb-hist-frameiframe fills the panel as a column-flex child (flex:1;min-height:0), not via absoluteheight:calc(...). - The diff view diffs THIS version against the NEXT one, not the previous.
overlay.openVersionDiffresolves the target viaresolveTargetSnapshot: the most-recent earlier version (index 0 ofgetHistory, newest-first) diffs against the LIVE current draft (snapshotCapture.captureCleanDoc(document), labelled "now"); any older version diffs against the next-newer stored snapshot. The pure diff brain issrc/runtime/diff.js(DOM-free, Node-tested); the DOM renderer issrc/runtime/diff-render.js(browser-only, e2e-tested, likehighlight.js). BOTH new files must be registered in the parity-locked runtime lists (bin/noteback.js,examples/build-canvas.js,src/background/service-worker.js— guarded bytest/canvas-runtime-parity.test.js) AND inmanifest.json(itscontent_scriptsANDweb_accessible_resourcesruntime-file arrays), ordereddiff.jsaftermarkdown.jsanddiff-render.jsafterhighlight.js(beforeoverlay.js). Comment highlights are painted AFTER the diff wraps words, so a comment whose quote straddles a changed region may not re-anchor — unchanged-region highlights always do. - The diff's Prev/Next change navigator runs INSIDE the iframe, not from the
overlay. The legend (and its
‹ Prev · n/N · Next ›cluster) lives in the diff<iframe srcdoc>, a separate document — so its buttons CANNOT be wired with overlayaddEventListener; the click handling,.nb-diff-focusstepping (ring + intensified fill, wrapping both ends), scroll-to-centre, and counter are all an injected static<script>(buildDiffNavScript, same pattern as the peek script). It replaced the old one-shot "scroll to first change" script and keeps its no-changes fallback (scroll the first highlight into view). The separate "Show diff" shortcut on the livenowtimeline row IS overlay-side (renderNowRow): it setsdiffMode=trueand callsopenVersionInline(latestKey)— shown only off the live draft and only when a latest earlier version exists. - The version chevron menu SAVES via download, not a tab. A version row's
▾menu has Copy feedback + Save HTML with comments + Save clean HTML (both saves disabled when the version's snapshot is pruned). "Save HTML with comments" rebuilds a re-openable canvas of that version withbuildVersionCanvasHtml(re-added — clones the live shell, swaps in the snapshot content, re-seeds#noteback-statewith the version's comments, escaping</script>; it does NOT bake a checkout marker); "Save clean HTML" saves the rawv.htmlsnapshot. Both route through a newexporter.onSaveHtml(html, name)hook (embedded:saveCanvasInPlace→downloadCanvas). Because the result is downloaded (a freshfile://canvas when reopened, with its own storage), it sidesteps the opaque-originlocalStorageproblem that retired the new-tab open — this is whybuildVersionCanvasHtmlis back butopenVersionTabis not. - The Versions timeline docks at the BOTTOM of the sidebar, not inside the comment
list.
renderVersions()renders into.nb-versions-dock(aflex:0 0 autoband withmax-height:34vh, its own scroll, collapsing via:emptywhen there are no earlier versions), a sibling between.nb-list(flex:1) and.nb-foot. So the current draft's notes (or the "No notes yet" empty state) keep the available room and the timeline stays put above the action buttons. - "Save · with comments and history" embeds the timeline in the FILE; the block is
stripped everywhere else.
onSaveCanvasWithHistory→adapter.exportHistory()(coreexportDoc) gathersnb:doc:<id>+ everynb:ver:<key>(snapshots included) into a<script id="noteback-history" type="application/json">block (escaping</script>→<\/script>, valid JSON). On reopen theEMBEDDED_BOOTsynchronously seedslocalStoragefrom it BEFORE the adapter resolves, and only for keys not already present (never clobber newer local data — so two machines with diverged history don't merge theirnb:docversion lists; a fresh machine rehydrates fully). The block must be excluded from snapshots (captureCleanDoc), clean copies (rebuildCleanHtml), and plain "with comments" saves (rebuildHtmlviabuildCanvasClone) — else it nests/recurses. Do NOT mark itdata-noteback-uito get free stripping: the cross-world stand-down keys off[data-noteback-ui], so a CSP-blocked canvas carrying the block would make the extension stand down and mount nothing. Covered bytest/e2e/history-embed.e2e.test.js. - A
hiddenmenu item needs.nb-menu-item[hidden]{display:none}— thehiddenattribute alone does NOTHING here..nb-menu-item{display:block}is an author rule of equal specificity to the UA[hidden]{display:none}, and author wins — so an item with thehiddenattribute still renders (it bit the "with comments and history" visibility toggle: the property was set, the item stayed visible). The explicit.nb-menu-item[hidden]{display:none}(specificity class+attr) restores it. wrapPRESERVES an existing doc-id — don't make it re-mint. The version history follows the bakeddata-noteback-doc-id, so re-wrapping a canvas must keep the same id or the history orphans.bin/noteback.js's precedence is: explicit--id→ the id already baked in the-otarget file → the id baked in the input HTML (#noteback-doc-root[data-noteback-doc-id]OR a source<!-- noteback-doc-id: … -->marker, viareadBakedDocId/readMarkerDocId) → mint a fresh one (mintDocId). The-o-target reuse is the easy one to drop — it's howwrapin place keeps history across re-exports.--bake-idanchors the id in the SOURCE so a deleted-ocanvas can't orphan history. With a SEPARATE-otarget (e.g.examples/spec.html -o examples/spec.canvas.html), the resolved id lives ONLY in the gitignored canvas;rmit and the nextwrapre-mints, splintering history.--bake-idstampsbakeDocIdIntoSourceinto the tracked source as a<!-- noteback-doc-id: … -->comment (after the doctype, so it can't trigger quirks mode; prepended for fragments; idempotent — re-bake replaces, never duplicates). The marker is SOURCE-ONLY:wrapFilerunsstripDocIdMarkeron the doc content before building, so it never leaks into the canvas (which carries the authoritative id on#noteback-doc-root). In-place wrap ignores--bake-id(the canvas already carries the id and would clobber the marker).examples/spec.htmlis anchored this way (dmq41se03tm5q0nu8bh).- Extension history is GATED per-site (
historyAllowed), decided at first mount.origin-policy.jshistoryAllowed(info, settings)is default-on forfile/localhost/127.0.0.1and opt-in viahistorySitesfor any other origin. When it's false the content script keeps the comments-onlyChromeStorageAdapter(no version timeline). The gate is read once at firstmount(), so toggling a per-site history opt-in takes effect on reload, not live (unlike the activate/deactivate transition, which is live onchrome.storage.onChanged). The embedded canvas has no settings and always runs history (subject tolsStore).createChromeKvStoreTHROWS ifchrome.storage.localis missing — the content script catches it (not.catch()) and degrades. - The click-to-activate injection list is sourced from the manifest, never
copied.
popup.jsactivates unsupported-origin pages by readingchrome.runtime.getManifest().content_scripts[0].jsandexecuteScript-ing that exact list. Don't hard-code the file list in the popup — it would silently drift the next time a runtime file is added to the manifest, and the injected page would boot an incomplete runtime. - The extension and an embedded canvas run in SEPARATE JS worlds — the
single-mount guard can't cross. Open a saved canvas while the extension is
installed and BOTH want to annotate the page: the canvas's inlined runtime boots
in the page's MAIN world, the content script in an ISOLATED world.
boot.js'swindow.__notebackBootedis a per-world global, so the content script never sees the canvas's flag — without help both mount (two launchers) and the extension routes comments to chrome.storage while the canvas's localStorage history stays empty (comments appear, but no version is ever recorded — the symptom that bit us). The hand-off rides the DOM, the only shared channel:boot.jsstamps a synchronous<div data-noteback-ui="mount">(before its firstawait, so it's in place by the extension'sdocument_idle), andcontent-script.jsstands down viaoriginPolicy.overlayMounted(document). The marker rides[data-noteback-ui], so every export strip drops it anddestroy()removes it. Don't "simplify" the guard back to the JS flag alone — it silently does nothing across worlds. Covered bytest/e2e/extension-standdown.e2e.test.js, which loads the real unpacked extension (channel: 'chromium') and reproduces the double-mount. extractBodyMarkupdrops the WHOLE<head>— so the canvas re-carries the doc's styling separately. A styled source (inline<style>in<head>) wrapped naively rendered as raw unstyled HTML in the canvas (the body markup survived, the<head><style>didn't).exporter.extractHeadStylespulls the original head's inline<style>blocks and<link rel="stylesheet">refs (EXCLUDING any[data-noteback-ui]style — the runtime re-injects its own), andbuildCanvasHtmlsubstitutes them into the template's{{DOC_STYLE}}head token. Two constraints: (1) the<title>is deliberately NOT carried — the template owns the canvas title (<title>… — Noteback feedback canvas</title>), and a test asserts the original<title>never lands in the output; (2){{DOC_STYLE}}is replaced last of all tokens, so a CSS rule likecontent:"{{x}}"in the carried stylesheet isn't eaten by an earlier token pass. Covered by the head-carry tests intest/exporter.test.js.- History opt-out is a SUBTRACT layer, and the two surfaces go live differently.
origin-policy.historyAllowedsubtractshistoryDisabledGlobal/historyDisabledSites/historyDisabledDocs(keyed on the resolved history doc-id, passed asinfo.docKey) above the base rule. The extension can't gate in place (it picks adapter TYPE at mount), so a live opt-out re-mounts (content-script.jsapplySettingscompareslastHistoryOkand unmount+mounts on a flip; the gate is computed once viahistoryOkFor) — the "gate read once at mount" invariant still holds (a re-mount is a new mount). The embedded canvas always builds the history adapter, so it gates in place viacreateHistoryStateAdapter'sisEnabled()(fed byhistoryControlovernb:nohist:global/nb:nohist:doc:<docId>, read/written with guarded raw localStorage). Opt-out HIDES the timeline and stops recording but KEEPS stored snapshots; re-enabling the gear re-saves the live draft (persist(getState())) so the now-enabled version adopts comments added while off.
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 4d ago First seen · 282 lines · 5,375 tokens per session scan A 99c5907277ec
noteback CLAUDE.md is an instructions file published in the GitHub repository alekkowalczyk/noteback (10 stars, last pushed 2mo ago), licensed MIT. It adds 5,375 tokens to every session, about $0.0269 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.
Other instructions, from other repositories
vscode buildNext.instructions.md
Working notes and architecture documentation for the new esbuild-based build system in build/next. Use when making changes to the new build pipeline (transpile/bundle commands, NLS plugin, source-map handling, resource copying, or self-hosting watch tasks).
spec-kit AGENTS.md
AGENTS.md instructions for github/spec-kit, covering agents.md, about spec kit and specify, quickstart — add a new integration in 5 steps, integration architecture and integrationmanifest — file tracking.
codex AGENTS.md
AGENTS.md instructions for openai/codex, covering rust/codex-rs, the codex-core crate, code review rules, crate api surface and model visible context.
langchain AGENTS.md
AGENTS.md instructions for langchain-ai/langchain, covering global development guidelines for the langchain monorepo, corridor security analysis, project architecture and context, monorepo structure and development tools & commands.
vscode oss-third-party-notices.instructions.md
Instructions for microsoft/vscode, covering vs code oss third-party-notices pipeline, architecture, pipeline flow in ci, applying the notice (cutover) and fallback chain (never fail the build).
next.js AGENTS.md
Instructions for vercel/next.js, covering next.js development guide, codebase structure, monorepo overview, core package: packages/next and other important packages.