Overview
Core concepts
Red Pen is one idea wearing four coats. A note is the same object whether you left it in WordPress, an Express app, a static page, or Ableton - and that shared model is exactly what lets the Hub roll every project into one board. This page is the vocabulary; the surface pages show how each platform realizes it.
The three principles
Everything in Red Pen follows from three commitments, covered on the Home page and worth restating because they shape every feature:
- Dev-only. Red Pen is for the people building the site, never its visitors. Each surface is gated - a WordPress capability check, an
NODE_ENV/ localhost check in Express, a localhost guard on the static drop-in, Live's Developer Mode in Ableton - so the overlay simply does not exist in production. - Local-first. Notes live in your site's database, a file in your repo, or your browser. No SaaS, no telemetry, no phone-home, no kill switch. The only time a note leaves your machine is when you deliberately push it (for example, to your own local Hub).
- Free core. Every concept on this page is free on every surface that implements it. The optional PRO unlock buys convenience extras - chiefly the multi-project Hub - and never gates a core feature.
The note model
A note is the core object. Every surface stores it differently (a WordPress post, a line in a JSON file, a localStorage entry) but the shape is the contract. These are the fields:
| Field | Type | Meaning | Example |
|---|---|---|---|
id | string | Port-assigned, opaque, stable for the life of the note. How every other tool (and the Hub) addresses it. | n_mqnh6jofiegeo |
body | string | The note text. Plain text, stored raw, always rendered escaped. | Header logo is 4px too low |
type | enum | The kind of note - see Note types. Stored as the enum key. | bug |
priority | enum | low / normal / high - see Priorities. | high |
severity | enum, optional | The impact axis, distinct from priority (which is scheduling). blocker / critical / major / minor / trivial. Empty by default - see Severities. | critical |
status | enum | open / in_progress / resolved - see Workflow. | open |
context | string | What the note is scoped to, so a surface can show "notes here". On the web this is the page path; see Context. | /pricing |
url | string | A human-resolvable location for the note - usually the page URL. Optional. The Hub uses it for "jump to source". | http://localhost/pricing |
anchor | object or null | The element or position the note is pinned to, or null for a context-level note - see Anchors. | { sel: "#logo", x: 0.5, y: 0.5 } |
assignee | string | Who owns the note. A WordPress user id on WP, a free-text handle elsewhere. Optional - see Assignee. | lincoln |
agent | string | An AI agent slug when the note is delegated to an agent rather than a person. Empty means a human note - see Assignee & agent. | claude |
codeScope | string | An optional file or symbol reference attached to an agent note, so the agent knows where to look. | src/header.php |
replies | array | A flat, one-level thread: [{ author?, body, createdAt }] - see Replies. | [{ body: "Fixed in r482", ... }] |
screenshot | string or null | A reference to an attached region capture, or null - see Screenshots. | shot_4f2a.webp |
createdAt | string | ISO 8601 timestamp of when the note was created. | 2026-06-21T07:38:51Z |
editedAt | string | ISO 8601 timestamp of the last field edit, where a surface records it (for example Express stamps it on a body/type/priority edit). Optional. | 2026-06-21T08:02:10Z |
resolvedAt | string, optional | ISO 8601 timestamp stamped when a note is resolved, and cleared again on reopen. Feeds the Hub's Resolved column. | 2026-07-03T15:02:11Z |
resolvedBy | string, optional | Who resolved the note, where the surface has identity (WordPress records the user's display name; a Hub-originated resolve is left unattributed). | Lincoln |
Surfaces may carry a few extra platform-only fields (WordPress, for instance, stores a structured context key and template metadata), but the fields above are the portable core that the Hub normalizes against.
Note types
Every note has a type. There are four built-in types; the stored value is the enum key, and the label shown in the UI may differ (and may be localized):
| Stored key | Shown as | Use it for |
|---|---|---|
note | Note | A neutral observation or reminder. |
suggestion | Idea | A proposed change or improvement. |
bug | Problem | Something broken that needs fixing. |
question | Question | Something to clarify or decide. |
Custom note types are part of the free core: you can define your own types, each with an optional colour flag, and they merge into every type dropdown and render their colour on the note. This is shipped on WordPress today (defined as Label|#hexcolor lines in Display settings, stored with a ct_ key prefix). On the Express and static surfaces it is planned - see the Roadmap.
Priorities
A note carries one of three priorities: low, normal (the default, sometimes surfaced as "medium"), and high. Priority drives sorting and at-a-glance triage. High priority is rendered in the brand red (#D32F2F) across the surfaces and on the Hub board, so the things that matter stand out without you reading a word.
Severities
Severity is a second, optional axis that answers a different question than priority. Priority is about scheduling - when should this be done. Severity is about impact - how bad is it if it stays. The two are orthogonal: a low-priority, high-severity crash is a real thing (it will hurt badly, but not today), and so is a high-priority, low-severity typo on the launch page. Severity is unset by default and shown only when you deliberately choose one, so it never adds noise to notes that do not need it. There are five keys:
| Stored key | Shown as | Use it for |
|---|---|---|
blocker | Blocker | Stops the work or the release entirely. |
critical | Critical | Severe impact - data loss, a broken core flow. |
major | Major | A significant problem with a workaround. |
minor | Minor | A small, contained issue. |
trivial | Trivial | Cosmetic or nice-to-have. |
Only WordPress authors severity today (set in the note form, dev-only). The Hub board and the CSV export display it, and the other surfaces can adopt it later - the field is part of the shared model regardless of which surface writes it.
Status and workflow
Notes move through a small, explicit workflow rather than just existing as sticky notes:
| Status | Meaning | Marker |
|---|---|---|
open | Logged, not started. Keeps its pin on the page. | Brand red |
in_progress | Being worked on. Keeps its pin. | Amber |
resolved | Done. Drops out of the active list and its pin is removed. | (hidden from active) |
Resolving a note clears it from the working view; reopening brings it back. The overlay's panel is organized into Open / (In Progress) / Resolved tabs with live counts so you always know what is left. Resolve also offers an undo in case you closed the wrong one.
A resolve now also records when it happened - a resolvedAt timestamp, cleared again on reopen - and, on surfaces that have identity, who did it (resolvedBy; WordPress stores the user's display name). The Hub board surfaces both as its Created and Resolved columns, so you can see and sort by when each note opened and closed.
Context and scoping
Every note belongs to a context, which is how a surface answers "what notes are on this page / this thing?":
- Web (Express and static): the page path,
location.pathname. The panel shows the notes for the page you are on; the repository shows them all. - WordPress: a structured context key - for example
post:ID,term:tax:ID,pt_archive:slug, plushome,search, and404- so a note attaches to the right piece of content even across permalink changes. - Ableton: the anchored object or time - a clip, track, scene, or beat range.
Anchors and pinning
A note can be pinned to a specific spot, or left at the context level (anchor: null). Pinning is what turns "the button near the header" into a note that points at the exact button.
On the web surfaces, an anchor is { sel, x, y }: a CSS-selector path to the element (it prefers an #id, otherwise builds an nth-of-type chain), plus a relative position x / y from 0 to 1 within the element's box. To create one you click Pin, then click the element. A numbered red marker then renders over that element and repositions as the page scrolls or resizes; Locate on a note scrolls to and briefly flashes its element.
In Ableton, an anchor is platform-specific - a beat range from an arrangement selection, or the identity of a clip, track, or scene. Locating there means jumping to or selecting that object through the Live API (native cue points for time anchors).
Threaded replies
Each note can carry a flat, single-level thread of replies - [{ author?, body, createdAt }]. You add a reply from the overlay, and the thread shows on the note and in the repository. It is enough to capture a back-and-forth ("is this still happening?" / "fixed in the last deploy") without turning Red Pen into a full discussion tool.
Screenshots
A note can carry an optional region capture: drag a box around any area of the page and the snip is attached to the note (captured with html2canvas, stored as a small WebP or data URL). The capture shows on the note and in the repository, and is served gated like everything else (a capability check plus nonce on WordPress; a local file under the notes directory on Express). Screenshots ship on WordPress today; they are planned for Express v0.3.0, with the static surface tracking the Express line.
Assignee and agent
Two related fields record who a note is for, and they are how Red Pen distinguishes manual work from automated work:
- Assignee - a person. On WordPress this is a real WordPress user; elsewhere it is a free-text handle.
- Agent - an AI coding agent (a slug such as
claudeorcodex), set when you delegate a note to an agent instead of a person. An optionalcodeScopepoints the agent at a file or symbol. Agent notes are kept out of the human views and live in an "agents" queue.
An agent consumes its queue locally, with no keys and nothing leaving the machine, in one of two ways: a filtered query (for example GET /notes?agent=claude) or an auto-maintained agent-<slug>.json brief file it reads off disk. In practice "check for red pens" means: pull the open notes (optionally one agent's), fix them, and mark each resolved.
Export and import
Your notes are never trapped. JSON export and import is the universal escape hatch and is never gated - it is a free-core promise, not a PRO feature. The static drop-in ships full JSON export and import today (the way to move browser-local notes between machines or share them); WordPress exports notes as well; the Express middleware has JSON export and import planned. The point of the principle is constant even while the implementations catch up: you can always get your notes out.
How the surfaces relate
Because all five surfaces speak the same note model, a fifth piece can aggregate them: the Red Pen Hub reads each surface's store through a small adapter, normalizes every note to the fields above, and shows them on one board - tagging each with its project and surface. The Hub is also where the model's two-way side lives today: you can resolve or reopen a note from the board and it writes back to the real source. Everything the Hub does rests on this shared vocabulary.
From here, head to the page for the surface you are using:
- WordPress plugin - the flagship, the most complete surface.
- Express middleware - one line into any Node app.
- Static drop-in - one script tag onto any website.
- Ableton Live - notes while you produce (prototype).
- Red Pen Hub - all your projects on one board.