Pulse
v0.21.3Early accessMIT
Reading as

One spec per page. Nothing to guess.

Each page is one plain JavaScript object: server data, state, mutations, view. Pulse validates it against a schema before it runs and tells you exactly what to fix. There is one correct way to build a page, and the validator knows it.

Install, then run pulse
$ npm install -g @invisibleloop/pulse
01The spec

One object holds everything a page needs.

Server fetchers, client state, mutations and view, co-located. No split files, no folder conventions to infer, no hidden config.

src/pages/dashboard.js JavaScript
export default {
route: '/dashboard',
meta: { title: 'Dashboard — My App', styles: ['/app.css'] },
server: {
data: async (ctx) => {
const user = await db.users.find(ctx.cookies.userId)
return { user, stats: await db.stats.forUser(user.id) }
},
},
state: { filter: 'all' },
mutations: {
setFilter: (state, event) => ({ filter: event.target.value }),
},
view: (state, server) => `
<main id="main-content">
<h1>Hello, ${server.data.user.name}</h1>
<select data-event="change:setFilter">
<option value="all">All time</option>
<option value="week">This week</option>
</select>
<p>${server.data.stats[state.filter].total} requests</p>
</main>
`,
}
route · metaWhere the page lives and what goes in its head.
serverFetched on the server. HTML streams before slow data resolves.
stateClient state, with optional min/max constraints.
mutationsSynchronous, pure functions. State in, state out.
viewA function that returns HTML. Any markup, any CSS.
02Constraints

Rules that can’t be skipped.

Each rule is enforced by the schema, the runtime or the server. Break one and validation fails with the rule’s name and the fix.

Every page is exactly one plain objectData can’t be split from view or spread across files.
schema
Mutations are synchronous and pureState changes are predictable and testable.
runtime
Dialogs always renderOpened with data-dialog-open, never conditionally mounted.
runtime
Constraints hold after every mutationState can’t leave its declared min/max bounds.
runtime
Forms carry CSRF tokensAdded automatically. Unprotected submissions are rejected.
server
03pulse_review

A real check, catching a real mistake.

A common agent-shaped mistake: a modal whose visibility is driven by state instead of always being in the DOM. This is the actual output of pulse_review against it.

src/pages/settings.js JavaScript
export default {
route: '/settings',
state: { modalOpen: false },
mutations: {
openModal: (state) => ({ modalOpen: true }),
closeModal: (state) => ({ modalOpen: false }),
},
view: (state) => `
<main id="main-content">
<button data-event="openModal">Edit profile</button>
${state.modalOpen ? `
<dialog open>
<p>Edit your profile</p>
<button data-event="closeModal">Close</button>
</dialog>
` : ''}
</main>
`,
}
pulse_review output 7 passed · 1 failed
✓No positive tabindex
✓No data-event on text inputs
✓No React patterns (className/htmlFor/onClick)
✓No emoji in view HTML
✓<main id="main-content"> present
✓No obvious hex colours in view
✗modalOpen-style state found — never conditionally render a <dialog>; always render it unconditionally and open it with data-dialog-open
✓No malformed _storeUpdate found
Fix before proceeding, then run pulse validate again.
04Structure and design

Strict about structure. Silent about style.

The spec tells an agent exactly how to wire up data, state and behaviour. Layout, typography and CSS remain real decisions, and the agent makes them.

The structure is fixed
One spec format. No ambiguity about how a page should be built.
Schema validation at startup. Bad output is rejected before it ships.
Security, SSR and caching are part of the architecture.
Consistent, reviewable output across every agent and every session.
The design is not
The view is a plain JS function. The agent writes whatever HTML it wants.
CSS, layout and typography are entirely up to the agent.
A component library is there when needed. Custom HTML when it isn’t.
The result looks considered because the agent had room to make it so.
05Measured

Not claimed. Measured.

Report generated 6 Oct 2026, 21:26 from a build on this deploy. Recomputed on every server start.
0 kB Static page. No JS shipped.
< 6 kB Single-page app. Runtime and page, brotli.
0.00 Cumulative Layout Shift.
0 Runtime dependencies. No production build step.
100 Lighthouse Accessibility.
100 Lighthouse Best Practices.
100 Lighthouse SEO.
< 2 kB Multi-page shared runtime, cached, brotli.
06Get started

Your first page in two minutes.

Pulse is in early access. The goal is not to compete on features. It is to eliminate the class of problems that come from having too many of them.

1
Install the CLI$ npm install -g @invisibleloop/pulse
2
Scaffold in an empty directory$ pulse
3
Run it again to open your agent$ pulseClaude Code or GitHub Copilot CLI, with the Pulse MCP server already connected.
Describe what you want“Create a contact form with name, email, and message fields. Validate the email format before submitting.”