Imported from hraness/oompa (
app/AGENTS.md). Install upstream withnpx skills add hraness/oompa --skill app. Copyright stays with the author.
Contents
index.htmlis the only shell. It carries the mobile viewport withviewport-fit=coverand one authored module entry. The sealed build adds a same-origin appearance bootstrap before paint.src/appearance-entry.tsandsrc/appearance.tsapply shared themes and restrict preference storage to a bounded palette/mode record. The bootstrap binds static controls; each mounted app menu owns and releases its controller reference.vite.config.tsand the repository's app build script compile the shell with no runtime asset or style inlining. The sole authored exception is one canonical data-URI favicon pinned to the reviewed Oompa SVG bytes; it adds no image origin or public asset type. Every compiler foundation precedes one finalized same-origin StyleX recipe stylesheet.vercel.jsonconfigures the second Vercel project (app.oompa.app) with the F1 Content Security Policy and the no-store shell headers.src/oompa/re-exports the browser-safe repository modules the app is allowed to reach.src/auth/holds the Convex client, the in-memory token storage adapter, and the one-time-code sign-in screen.src/custody/holds device key generation, IndexedDB key storage, the enrollment flow, the account-key context that opens an enrolled browser automatically, and presence.src/data/holds the wire parsers, the session heads and one head, the compact history walk, the subscribed compact and detail stream tails, the session metadata cache, the session and device command hooks, the device, device registry, and archived session hooks, the composer attachment state, the in-memory hold of bytes this tab sent, and the manual grid arrangement bound to this browser.src/model/holds the framework-free session model reducer, the transcript derivation, the grid and interaction view models, the manual card-order reducer, the session scheduled-task selection, the settings and device command view models and builders, the composer attachment rules and the one send-payload builder, the image downscaling arithmetic, the provider switch payload builder, and time formatting.src/markdown/holds the sanitiser and the markdown renderer.src/lib/holds the class-name helper, the cancellation helper, and the canvas image wrapper the downscaler injects.src/routing/holds the hash route model and the router hook.src/components/ui/holds the interface primitives as owned source.src/components/holds the icons, the state indicator, the inline conversation panel and composer, the session card, the subagent chips, the scheduled-tasks badge, the transcript, the attachment chips, the account-login relay, and the interaction panel.src/screens/holds the grid and settings screens.fixtures/product/renders the actual screens with fictional Direct scenarios and IO-only substitutions for the isolated public website examples. It is not an app entry point or a live provider test.
Guidelines
- Import repository source only from
src/cloud/crypto,src/cloud/projection,src/cloud/payloads,src/cloud/contracts,src/cloud/client, andsrc/domain/*, and reach all of them throughapp/src/oompa/. The othersrc/cloudmodules are node-only and must never enter the bundle. - Never write an inline style attribute or a style element.
style-src 'self'blocks both. Author component-owned presentation in colocated static StyleX recipes, compiled through the public@hraness/ui/stylex-buildcontract with runtime injection disabled. Retain only tokens, resets, and document grammar insrc/index.css. - Reuse the pure native menu and recipe in
src/components/appearance-menu*for both app and site. Use the public framework-neutral design-kit palette controller; do not import the aggregate shared React barrels into this closed app graph. - Compose primitive styles in base, finite variant/size, then caller
xstyleorder; retain ordinary caller classes last. Reject dynamic StyleX output that requires inline styles. Preserve native control semantics, physical safe-area edges, finite state behavior, and reduced-motion and forced-color contracts. - Register the complete app graph and its package manifests before building, seal the generated shell, and finalize once. Load
@hraness/design-kit/compiler-palettes.css(the UI compiler foundation and palette bridge, with no fonts) before product grammar and the finalized recipe asset. Do not mix standalone package recipe stylesheets with compiler-adopter output or claim an unverified development/HMR path. - Use
bun run dev:appfor local development on127.0.0.1:5183. It serves immutable, fully compiled revisions; refresh the page to select the latest successful revision. It does not hot-replace modules or styles. Keep failed builds off the public route, retain older revisions for in-flight assets, and restart after dependency or configuration changes. Do not widen the production Content Security Policy for development. - Never render raw HTML from projection text, and never resolve a non-https URL from it. Projection text reaches the reader only through
src/markdown/, which removes zero-width and bidi characters, refuses every href that is not an absolutehttps:URL, and renders an image as its alt text. - Render an image only from bytes the tab already holds.
img-src data: blob:names no origin, so animgelement cannot fetch anything; a projected attachment is a manifest with no bytes and renders as a chip, and a thumbnail appears only for an attachment this tab sent itself. - Build an attachment send payload only in
src/model/attachments.tsand a provider switch payload only insrc/model/provider-switch.ts. Both shapes are ahead of the repository contract, both ask the repository parser whether this build accepts them, and neither is constructed anywhere else. - Offer a device command only where the machine would accept one.
src/model/device-commands.tsderives the pickers from the projected registry, so a machine with the kill switch set, an account that is signed out, or a machine with no project is never offered as a target, and the account-linking flow appears only behind that machine's local opt-in. - Offer a remote decision only where the daemon will accept one.
src/model/session-view.tsconsumes only the parser-validated projected policy;src/domain/remote-interaction-policy.tsis the sole action-membership table shared by projection and live daemon verification. Remote answers are closed-choice user answers only; free text, Other, and MCP forms stay local. - Never register a service worker, load an analytics script, or reference an origin outside the pinned Convex deployment.
- Never persist plaintext projection text, an authentication token, or an unwrapped account key. Tokens live in the in-memory storage adapter and the account key lives in the custody context only.
- Keep local storage to
app/src/data/card-order.tsfor a bounded list of opaque session public ids,app/src/appearance.tsfor the singlehraness-design-palette-v1key, andapp/src/data/automatic-effort.tsfor the singleoompa-automatic-effort-v1key containing exactlyonoroff. The automatic-effort preference never stores prompts, decisions or identifiers; unknown or unavailable storage disables it, and a failed write keeps it off for the tab. Appearance accepts at most 256 characters, parses the shared finite palette/mode contract, and serializes only those two fields; it contains no account, session, projection, or key material. Disable legacy preference reads.app/src/auth/no-persistent-storage.test.tsallowlists these modules by name; nothing else in the app may namelocalStorage,sessionStorage, ordocument.cookie. - Apply automatic effort only to a new grid-composer start, before building its one ordinary device command. Downshift only a conservatively bounded prompt under the exact Astra High/Ultra contract; explicit commands and established conversations keep their selections. Snapshot the decision with the submitted prompt, and never resubmit or change accounts on a failed or uncertain result.
- Show a schedule; never offer to change one. The scheduled-task badge and the settings list read the projected device registries and expose no create, edit, or delete anywhere.
- Show personal-session adoption only as per-machine provider aggregates with exact local CLI hints. Never add an adopted-session badge or let the browser grant access to a personal provider home.
- Persist only non-extractable
CryptoKeyobjects, and only in IndexedDB. A private key must never be exportable. - Drop the account key on
pagehideand on the first authority error from Convex. An active enrolled browser unwraps its account key automatically on load; there is no manual or idle lock. - A browser device is never the first device on an account and never approves another device.
- Keep the reducer, the custody helpers, and the wire parsers free of React. App presentation tests use
scripts/register-app-stylex-test-transform.ts; the reducer and custody tests still run without a document. - Parse every value that arrives from Convex from
unknownbefore it reaches a component. - Pin every new dependency to an exact version in the root
package.jsonand keep it indevDependencies: nothing underapp/is published.