agentleFS
meyverickmeyverick/agentic/skills/hypermedia-engine-stacks/SKILL.md

hypermedia-engine-stacks

Skill0 starsChanged 13 days agoISCView on GitHub

Use when the task is a Rust-first hypermedia application or simulation shell — a Rama gateway with SSE, SQLx typed queries and LISTEN/NOTIFY fan-out, Maud server-rendered HTML, Datastar signals and morphing, a Bevy WASM canvas, or binary rama-ws telemetry. Task-shaped triggers include an SSE dashboard, a WASM canvas embedded in server-rendered HTML, PostgreSQL notify fan-out, a graceful drain of open streams, a stream connection budget, and a SQLx offline metadata drift gate. Do not use for SvelteKit, React, or any JS SPA stack; do not use for Axum-based hypermedia, which has its own extractor and SSE surface.

The file

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Zero-JS server runtime: every byte of HTML comes from Maud through Rama.

    line 37
  2. Very specific:

    Prefer the re-anchor over retry: 'always': the ladder is 1 s → 30 s over ten attempts (~181 s) before retries-failed, while a replaced node …

    line 83

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Hypermedia & Engine Stacks

Rust-first architecture for server-rendered applications: Rama is the gateway, SQLx the typed persistence layer, Maud the template compiler, Datastar the client engine, and Bevy WASM plus rama-ws the conditional simulation tier. Five per-pillar archives carry the signatures; this syllabus carries the rules.

Fidelity command of record: bun scripts/verify-archives.ts (rule helpers in scripts/verify-archives-lib.ts). It asserts both Markdown ceilings, archive-link resolution, the provenance lock, the unpinned sweep, the frontmatter description cap and content, the transport-table parity, the archive section order, the stream-lifecycle spine with resolvable owner archives, and the retired-token sweep.

Six-Pillar Matrix

PillarOwnsArchive
Gateway and transportrouting, extraction, SSE framing, WSS upgrade, limits, CSRF, drainreferences/rama.md
Persistence and fan-outcompile-time SQL, pools, migrations, LISTEN/NOTIFY to broadcastreferences/sqlx.md
Templatingcompile-time HTML, escaping, single-buffer renderingreferences/maud.md
Client reactivitysignals, actions, CQRS commands, Fat Morphing, retryreferences/datastar.md
SimulationECS schedules, messages versus events, WASM build, canvas mountreferences/bevy.md
High-frequency transportbinary/text frames over rama-ws, consumed by Bevyreferences/rama.md, references/bevy.md

Selection boundary: SvelteKit, React, and Axum work is out of scope. Those are different stack classes — an Axum hypermedia service has its own extractor and SSE surface, and a JS SPA owns rendering that this skill hands to Maud. This skill is the Rust hypermedia stack and does not choose between them.

Adjacent concernShipped owner
Queue topology, worker pools, backpressure, push pipelinesscalability
HTML/DOM sinks, secret hygiene, dependency and container hardeningguardrails
Rust lint and unsafe policy, async cancellation contracts, verification lanesrust-systems
Interface craft, motion, accessibility reviewdesign-craft
Whole-system auditssystems-auditor

This skill carries only the SSE/WSS transport contract and the five pillar archives; it does not restate those owners' rules.

Non-Negotiable Hypermedia & Simulation Invariants

  • Zero-JS server runtime: every byte of HTML comes from Maud through Rama. No Node.js, Bun, or external SSR runtime in the serving path.
  • Compile-time PostgreSQL lock-in: query!/query_as! against PostgreSQL, .sqlx/ committed, cargo sqlx prepare --check in CI. No dual-engine trait, no #[cfg] database gate. The isolated SQLite fallback publishes to the same in-process broadcast channel and never emulates LISTEN/NOTIFY in SQL.
  • Event-driven reactivity only: push state over SSE or WSS. Polling, setInterval refetches, and Datastar interval actions are prohibited as state transport.
  • Hybrid simulation boundary: high-frequency inputs and ticks go directly over rama-ws into Bevy; low-frequency HUD, inventory, and menus cross via CustomEvent or Datastar signals. A 60 Hz transform stream never touches the DOM.
  • No shared memory: SharedArrayBuffer and cross-origin memory locks are prohibited between WASM and the DOM. Bound the wasm-bindgen queue instead; a hidden tab pauses requestAnimationFrame while WSS keeps writing.
  • Canvas morph boundary: data-ignore-morph on the <canvas> and its mount container. data-ignore suppresses plugin processing and is not a morph boundary. Any data-on:* listener that must survive a patch sits on an ancestor outside the morphed region.
  • Open streams are bounded: ConcurrentCounter::new(max) with the guard moved into the stream, released on Drop; over budget answers 503 with Retry-After, never 429 and never 204. That 503 is the route's own mapping — LimitReached implements no IntoResponse — while LimitLayer/ConcurrentPolicy cap in-flight serve() inputs only and answer 429 on the request lane, because an SSE response completes at handoff.
  • Streams end by decision: drain broadcasts an explicit exit, answers new streams 410 Gone with a proxy-facing Retry-After, and re-anchors the @get node with a final PatchElements. A clean 200 close is not re-requested by the engine.
  • Authorization is bounded, not re-checked: an established SSE response cannot re-authorize. Cap the stream lifetime; at expiry re-anchor under a fresh principal check, or answer 410 when the session is gone.
  • Lag is a resync signal: RecvError::Lagged(n) means drop buffered state, re-query a snapshot, render it, and count it. It is not an error to log and ignore.
  • Extensions isolate by fork, not by clone: Extensions::fork() gives a child store whose inserts the parent never sees; a clone shares its store, so an insert through a clone is visible to the original handle. A request-local overlay starts with fork().
  • Routing is case-insensitive unless told otherwise: PathMatchPolicy defaults to AsciiInsensitive + PercentDecoded, so a security-sensitive path opts into PathMatchPolicy::STRICT; specificity breaks a tie, static over capture over catch-all.
  • CSRF is fetch metadata, not an allow-list: CsrfLayer allows the safe methods, Sec-Fetch-Site same-origin or none, a trusted Origin, absent fetch metadata, or an Origin authority matching the effective host. TRACE and QUERY are not exempt, and a proxy that rewrites Host or strips Origin silently degrades the check. Datastar ships no CSRF rule, so an application-invented origin check is not a substitute.

Transport Status

StateAnswerDatastar consequence
Draining, stream route410 Gone (+ Retry-After, proxy-facing)datastar-fetch error; retry: 'auto' does not retry a 4xx, so the client needs the re-anchor patch
Draining, command route503 + Retry-Aftererror event, no retry
Over stream budget503 + Retry-Aftererror event, no retry; pair with the same re-anchor patch
Stream ended on purpose410, never 204204 applies no patch and emits no error or retrying event; started and finished still fire
Command accepted204 No Contentcommand routes only; never a stream

410 is stream-only: it means permanently gone, and a deploy is not. Retry-After is proxy, load-balancer, and browser-facing; the Datastar engine never reads it. 429 belongs to neither stream state: it is the request-rate lane's answer, from RatePolicy or KeyedRatePolicy in the gateway archive, and never the budget or drain answer.

Stream Lifecycle

One ordered sequence, each step carrying the mechanism that enforces it. The owner archive holds the detail; this is the spine, not a second copy of it.

#StepEnforcing mechanismOwner
1Admit under a stream-owned guardConcurrentTracker::try_access() on a ConcurrentCounter; the guard moves into the stream and Drop releases itreferences/rama.md
2Subscribe, then snapshotPgListener::listen before the snapshot query, merged by id — the reverse order loses any commit landing between the tworeferences/sqlx.md
3Render the shell and first patcha Maud page opened with maud::DOCTYPE, then a PatchElements against a deterministic idreferences/maud.md
4Stream patches and keep-aliveSse::new(KeepAliveStream::new(..)) over the broadcast receiver, one guard held for the body's lifereferences/rama.md
5Detect lag and resyncRecvError::Lagged(n) → drop buffered state, re-query a snapshot, re-render it, count itreferences/sqlx.md
6Re-anchor the subscription nodea final PatchElements replacing the @get node, so the engine re-subscribes immediately instead of waiting on the retry ladderreferences/datastar.md
7Drain, then answer 410broadcast an explicit exit; the stream ends on that message, not on a poll of the shutdown guardreferences/rama.md
8Treat a closed stream as closeda cleanly closed 200 is never re-requested — only a replaced node or retry: 'always' brings it backreferences/datastar.md

Bounded Streams and Principal Reverification

  • Admit with the ConcurrentCounter guard inside the stream; the guard is the budget, not a request-layer permit.
  • Cap the stream lifetime (a value inside the proxy idle timeout) and treat expiry as a re-subscription, not a mid-stream check: emit a final PatchElements that replaces the @get node, or answer 410 when the session is gone.
  • openWhenHidden: false on @get already forces one rebuild per tab on visibilitychange; the lifetime bound rides that re-subscribe instead of inventing a second one.
  • Prefer the re-anchor over retry: 'always': the ladder is 1 s → 30 s over ten attempts (~181 s) before retries-failed, while a replaced node re-subscribes immediately.

Retained and Retired Invariants

Shipped invariantStatusHome
Zero-JS server runtimecarriedthis syllabus
PostgreSQL compile-time lock-incarriedreferences/sqlx.md
Polling prohibitioncarriedthis syllabus
Hybrid simulation boundarycarried and sharpened to the Message/Event lane rulereferences/bevy.md
SharedArrayBuffer prohibitioncarried, with the bounded-queue replacementreferences/bevy.md
Canvas morph boundaryrewritten: data-ignore → data-ignore-morph on canvas and containerreferences/datastar.md, references/bevy.md

Telemetry

Server signals beside the client rule, each with the condition it detects. All four ride the wide event emitted by TraceLayer::on_eos, the hook that fires when the response body ends — on_response fires at handoff and would report every stream as zero-duration.

Failure modeMetricCondition it detects
Streams outlive their budgetopen_streamscurrent ConcurrentCounter occupancy; at or above the cap with a flat request rate, streams are not ending — a permit leak or a client that never disconnects
Subscriber falls behind the channelbroadcast_lagged_totalRecvError::Lagged occurrences, i.e. resyncs; a capacity problem, not a database one
Shutdown strands a streamdrain_stateaccepting versus draining; a stream still open past the drain ceiling means the window is shorter than the stream's own keep-alive
Admission refuses a streamstream_rejections_totalover-budget and drain-time answers, counted by status, so a 503 spike is distinguishable from a 410 deploy

The client datastar-fetch event is the only lifecycle signal; it fires on document with detail.type = started, finished, error, retrying, or retries-failed. No per-phase event name exists.

Workflow

  1. Scope the tier. Pick the matrix row; write the one-line boundary (hypermedia only, or canvas plus WSS).
  2. Shape the data. Append expand-contract migrations; write query!/query_as!; run cargo sqlx prepare --workspace -- --all-targets --all-features in the same commit.
  3. Wire the gateway. Router::new_with_state, ReadSignals per the method rule, CsrfLayer, a request-concurrency LimitLayer, and the ConcurrentCounter stream budget. Commands answer 204; streams answer 410 at drain.
  4. Render the shell. Maud components with deterministic ids, serialized data-signals JSON spliced through escaping (never PreEscaped), and a single pre-sized buffer with Render::render_to for row lists.
  5. Fan out events. One PgListener::connect(url) per replica into tokio::sync::broadcast; one ConcurrentCounter-guarded SSE route per view; Lagged triggers a snapshot resync.
  6. Mount simulation when required. Bevy canvas with data-ignore-morph, Message for frame telemetry and On<E> for input reactions, postcard over rama-ws for high-frequency traffic, CustomEvent for the HUD.
  7. Verify. Consumer lane, available wherever this skill is installed: bun scripts/verify-archives.ts, run from the skill directory. Repository-only: the package gate bun scripts/gates.ts invokes that verifier, bun scripts/check.ts runs the whole lane, and bun scripts/docs-corpus.ts --lock refreshes the pin record. None of those three ship in the package. Run cargo sqlx prepare --check in CI.

Verification

Five application tests, each the smallest thing that fails if its layer breaks. They assert the transport, not the framework; keep them beside the code they exercise.

LayerTestCommand
Gateway#[tokio::test] calling the stream route, asserting text/event-stream and at least one datastar-patch-elements frame before the body endscargo test --test sse
Persistence#[sqlx::test] for a migration plus a query_as! round trip, against a test database created from DATABASE_URLcargo test --test db
Templatinga snapshot assertion on the rendered Markup, deterministic ids includedcargo test --test render
Simulationa headless App with MinimalPlugins, one app.update(), asserting the queue drain wrote the messagecargo test --test sim
Transporta rama WSS client against the upgrade route, asserting a postcard round trip and a clean closecargo test --test wss

cargo test --workspace runs all five. The archive gate is the fidelity lane, not an application test; cargo sqlx prepare --check belongs in CI beside both.

Anti-Pattern Matrix

Anti-patternFailureInstead
LimitLayer as the open-stream cappermit released at response handoffConcurrentCounter guard owned by the stream
204 ending a streamno patch and no error, but started/finished still fire — a silent dead stream410 plus a re-anchor patch
Relying on Retry-After in the clientthe engine never reads itre-anchor, or retry: 'always'
data-ignore on the canvasplugin suppression, not a morph boundarydata-ignore-morph on canvas and container
60 Hz coordinates through signals or HTMLDOM thrashingbinary frames over rama-ws
Buffered reader on the cross-frame telemetry lanea two-update buffer used as an immediate oneMessageReader with #[derive(Message)]
Nested html! inside a loopone allocation per rowRender::render_to into one buffer
Hand-concatenated data-signals JSONescaping bypassedserialize, then splice through Maud
PgListener::connect_with(&app_pool)the listener holds an application-pool connection for its lifePgListener::connect(url)
test_before_acquire(true) everywherea round trip on every checkouttest_before_acquire(false) plus before_acquire on meta.idle_for
Aliased Datastar bundleevery data-* attribute is silently deadserve the embedded unaliased bundle
SQL state without offline metadataCI cannot build without a databasecommit .sqlx/ and gate prepare --check

Provenance

scripts/provenance.lock is the single pin record: the five repositories, their commits, their verification dates, and the local-corpus digest. The digest is local-corpus scoped because it also folds the cargo-registry crates, so the commit pins — not the digest — are the durable anchor; the digest mirrors the recorded corpus snapshot, and the corpus and lock are refreshed together when a claim is re-verified. This syllabus and its archives stay version-free so a stale release number cannot be mistaken for an observed claim; each archive keeps only its Re-verify when: trigger. Read the lock before trusting a claim, and move the pin when you re-verify one.

Reference Archives

  • references/rama.md — read before touching routing, SSE framing, the stream budget, CSRF, WSS upgrades, compression, or drain.
  • references/sqlx.md — read before adding SQL, tuning the pool, writing a migration, or wiring PgListener.
  • references/maud.md — read before changing templates, escaping, JSON attributes, or Markup conversion.
  • references/datastar.md — read before changing attributes, signals, actions, morph boundaries, CSP, or retry behaviour.
  • references/bevy.md — read before touching ECS schedules, WASM builds, the canvas mount, or the shell bridge.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.