OpenPost agents
August 29, 2026 · View on GitHub
This file is always loaded. Keep it useful and compact. Code and config are the source of truth.
Identity
OpenPost is an all-in-one social publishing workspace. It helps solo founders, creators, teams, and agencies turn ideas into channel-ready content, publish it, and track what happened. The main user is a solo founder without a content team; the app removes repeat work without hiding provider rules or publishing state. The Hosted service is the primary product; self-hosting is a deployment option.
- A Publication is the user-visible post; it owns the source idea, schedule, status, and destination outputs.
- A Rendition is one destination-specific version: per-account text, media, format, timing, and provider settings.
- The composer (posts, threads, stories, short videos, videos), Social Sets, shared Media library, Image and Video Editors, calendar and durable queue, analytics/engagement/inbox, and Workspaces make up the product.
- The web app, native mobile app, HTTP API, CLI, and MCP server share the same terms, permissions, and workspace boundaries.
Product direction
- Start with the user's work: launches, updates, lessons, and ideas.
- Preserve provider truth. Show real limits, capabilities, review needs, and failures.
- Make every outcome clear: draft, scheduled, queued, published, failed, or retrying.
- UX and visual consistency is the highest product priority.
- One coherent product across app, automation, mobile, and self-hosting.
- Reuse established UI patterns. Consistency builds trust.
- Keep keyboard access, visible focus, readable contrast, reduced motion, and touch targets.
Project state and memory
- The OpenPost Vikunja project is the authority for private/internal active work, specs, priorities and execution state. Create and update those tasks through Executor. GitHub Issues remain only for public reports and contributor-facing discussion.
- Hindsight bank
rodrigois the authority for durable cross-agent product context, domain language, decisions, constraints and project history. Recall withproject:openpostbefore substantial work. Retain only verified durable facts withproject:openpostandsource:<agent>; never retain secrets, raw logs, temporary task state, completed-work reports or unverified claims. - Code, tests, generated contracts, build commands, public docs and hard engineering constraints remain versioned here. Do not create new
CONTEXT.md, mutable ADR folders, local issue stores, task boards or agent-memory files. - Treat recalled memory as historical context, not proof of current behavior. Verify it against code, Vikunja or the live system. Correct superseded memories rather than adding contradictions.
Working model
- Substantial, ambiguous, or multi-ticket work follows the
agent-workflowskill; tiny, well-scoped fixes skip the flow. - Match the work to its reference: UI/UX/visual/copy →
ux-consistency(plusimpeccablefor design critique),DESIGN.md,PRODUCT.md; interaction sounds →cuelume; Go API →huma; TypeScript client →openapi-typescript; durable domain/decision context → Hindsight plus current code; paths and seams →docs/agents/repository-map.md(confirm withrg); internal work → Vikunja; public issue/PR triage → GitHub. - Use Devenv for the environment; run verification, build, and release through the root
bun runcommands. - Do routine, non-destructive work yourself rather than delegating manual steps; production, external, and reputational mutations stay gated (see Delivery).
- Canonical backlog: the OpenPost Vikunja project. P0 = active security/privacy/data-integrity emergency or a safety gate before an external-write path; P1 = before the next broad release or paid-growth push; P2 = planned work. Treat it as an ordered queue, not one flat list.
Architecture
- SvelteKit builds the interface; a Go server embeds it into one binary. Echo serves HTTP, Huma owns OpenAPI, Bun ORM owns database access. SQLite is the default; PostgreSQL supports hosted use.
- Persistent work uses database jobs, not in-memory goroutines. Media goes through
BlobStorage. Provider code lives inbackend/internal/platform/. - Page reads use stored state; provider calls happen in explicit sync or job flows. Publications are the canonical user-visible content inventory.
- Svelte uses runes, the typed API client, and shared UI/page controls; visible form fields belong in shared primitives. Secrets stay out of code and logs; stored provider tokens are encrypted.
- Video Editor on-canvas gestures keep drafts out of undo history and commit once on release. Animated position uses versioned vector keys with temporal easing and spatial Bezier tangents; legacy scalar X/Y tracks promote lazily on the first vector edit.
- CLI, MCP, API, and product surfaces share terms, permissions, and boundaries. Public contract changes are acceptable when deliberate and UX-improving; change the source, then regenerate.
- AI features use maintained SDKs behind a small provider-neutral boundary (
backend/internal/ai/); reuse the shared model and config choices instead of adding per-feature models. - Meme Maker embeds a pinned template catalog and renders in process; captions and workspace overlay bytes stay server-side, while optional AI suggestions receive only bounded written template semantics. Refresh the audited snapshot with
scripts/sync-meme-catalog.mjs <pinned-checkout>. - Video Editor GPU effects share one WebGL2 ping-pong compositor; use point-scatter vertex passes for exact cross-texel writes that a fragment pass cannot express.
- Video Editor adjustment layers are timed, non-rendering items; preview and export apply their enabled effects, top-first, to active visual items on higher-order tracks.
- Video Editor export workers return in-memory artifacts; only the main thread writes final workspace files. Fall back only for explicit worker limits, availability, runtime, or message-clone failures, and never retry a worker render error as a second render.
- Video Editor Auto preview serializes heavy-media proxy work, keeps proxy visuals separate from original-source audio, and applies a hysteretic render-scale cap to real GPU and stacked canvases. A bounded worker cache predecodes upcoming boundary frames; stalled seeks may present exact prewarm or filmstrip fallbacks until the video element settles. Full preview and export always use the original source at full resolution.
- Non-normal Video Editor blend modes use the shared
CanvasStackCompositorin preview and export so each transformed layer blends against the finished frame below; keep the exact CPU fallback aligned with the GPU shader. - Video Editor keyframe views share item keyframe tracks and
keyframeSelectionStore; batch graph and dope-sheet edits use atomic keyframe actions so parallel metadata arrays and undo stay aligned. - Video Editor Motion parenting stores bind-space references, resolves parent chains after local animation, links, expressions, and modifiers, and converts direct world-space canvas edits back to child-local values. Controller items participate in transforms but never render. Published composition controls store their schema on the source composition and per-instance overrides on the wrapper; preview and export apply overrides to cloned source items.
- Video Editor color comparison and pickers use
colorPreviewStoreas preview-only state; export always reads the persisted full effect stack, and grade presets clone color-category GPU effects instead of sharing mutable params. - Video Editor keyboard commands use the closed catalog in
settings/keyboard-shortcuts.ts; page and timeline handlers resolve saved bindings by command ID and must not add hardcoded key comparisons. - Video Editor interface sounds use
soundPreferencesas the only Cuelume owner and semantic tokens throughsounds/editor-sounds.ts; suppress normal cues during preview playback and remove generic control cues when an outcome cue owns the same gesture. - Video Editor diagnostics use
previewDiagnosticsas the single owner for measured playback and renderer state. Reports must exclude project names, clip labels, media IDs, and file paths; keep repair and fixture tools out of the normal product UI. - Video Editor ProRes decode uses the lazy
media/prores-decoder.tsregistry in everyCanvasSinkrealm except the DOM-video prewarm worker. Browser-undecodable clips must keep their compatibility proxy even at Full preview quality; export still reads the original source.
Reference repositories
- Publishing, scheduling, automation, or provider work: inspect
references/postiz/andreferences/shoutrrr/when either has similar behavior (reposts, delays, thresholds; durable jobs, engagement gates, plateau checks, per-post overrides). - Image Editor work: inspect
references/miniPaint/for tools, raster workflows, effects, import/export, and desktop/mobile UX. - OpenPost Studio, recording, or Video Editor work: read
references/README.md, then inspect OpenScreen, Capptivo, and Cap for capture/cursor seams; FreeCut, Kdenlive, and Shotcut for editing semantics; and OpenCut, OpenVid, OpenReel, or QCut for product and interaction ideas. - Keep checkouts shallow and Git-ignored. Use them as references, never dependencies; never commit or vendor them. Audit the source and license before porting code. OpenPost's architecture, security, accessibility, provider rules, and product language win.
Delivery
- Commit directly to
mainwith Conventional Commits; commit as work progresses. Preserve unrelated changes and stay inside the requested scope. - Push, release, and deploy at the end, and only when asked. A push is not a deployment: releases run from
v*tags after passing CI, live readiness, and an exact-revision check. - Production-affecting mutations (tag push, release, deploy, destructive live actions) require explicit authorization. State the exact changes and their effect before acting.
- CI reuses Devenv/Nix/Bun/git caches and should stay fast (target under ~2 minutes where realistic).
- n8n package release work must follow
docs/agents/n8n-package-release.md; npm publication follows exact production readiness and uses its own stable SemVer.
Verify and deliver
- Tests need an independent reason to exist: reproduce a real failure through the closest stable boundary or check an externally defined contract. Each test must fail on a plausible behavior regression, not merely because the implementation was refactored.
- Remove circular tests that call a new private helper or guard and assert its own literals, branches, fields, or error text. When no meaningful behavior test exists, use focused practical verification and add no test.
- During development, run the nearest relevant check after each edit:
bun run check:frontend:typesfor Svelte/TS type checking only,bun run check:contractsfor generated contracts,bun run test:file -- <path>for a single test,bun run test:backend:pkg -- <package>for one Go package. Usebun run check -- <scope>for a full surface gate once the candidate is stable. Usebun run doctorbefore broad, browser, or release work;bun run release -- checkbefore releases;bun run verifyonly for high-risk local build proof. - UI and copy changes must pass the
ux-consistencyacceptance bar before finishing. - Behavior changes: sync docs, product copy, and contracts under
Unreleased. Write changelog entries tochanges/<issue-number>.md, not directly toCHANGELOG.md, to avoid conflicts when parallel tickets are in flight.