This file defines how AI agents should work in this repository, with special rules for documentation, app onboarding, and platform validation.
These rules are required for every non-trivial change (docs, config, code, infra).
- Never work directly on
main.- If current branch is
main, create and switch to a new branch before editing. - Branch name format:
docs/<topic>fix/<topic>feat/<topic>chore/<topic>
- If current branch is
- Use
stagingas the default integration target for feature work.- Merge feature branches into
stagingfirst when the goal is fast template/infrastructure testing. - Promote tested batches from
stagingtomainthrough the release workflow inRELEASING.md.
- Merge feature branches into
- Maintain
CHANGELOG.mdduring the work, not after.- Add/update an entry under
## [Unreleased]in the same branch as the change. - Keep entries concise, user-relevant, and release-shaped.
- Prefer a few broad release-note bullets over a detailed work log of small implementation changes.
- Do not add a new changelog bullet for every follow-up fix, test tweak, or implementation step within the same area of work.
- Fold iterative work into an existing broader bullet unless the later change introduces a distinct user-visible outcome, operational change, or compatibility note.
- Avoid logging low-level refactors, internal cleanups, or development-only harness work on their own unless they change user-visible behavior, operations, or compatibility.
- When in doubt, compress multiple related tweaks into one broader bullet instead of listing them separately.
- Add/update an entry under
- Release process must follow
RELEASING.md.- Do not invent ad-hoc versioning or release steps.
- Keep all release metadata files aligned when preparing a release:
- root
VERSION - root
releases/stable.json apps/suite-manager/release.json
- root
- Run
npm run release:checkwhenever a task changes release metadata or prepares a release branch.
- No direct pushes to
mainand no direct commits onmain.- Use PR workflow only.
- When a change affects compatibility contracts, call it out explicitly in changelog.
- This includes env vars, compose service/profile names, Dockerfile paths, and persistent volume semantics.
Before making edits, agents should confirm:
- Current branch is not
main. - If the change is intended for fast platform testing, target
stagingfirst rather thanmain. CHANGELOG.mdcontains or will contain anUnreleasedentry for the change.- Any needed docs split rules (MDX vs app README) are respected.
- Local git hooks are installed (
npm run hooks:install) so commits/pushes onmainare blocked. - If the work is release-related, confirm
VERSION,releases/stable.json, andapps/suite-manager/release.jsonwill stay in sync with the intended tag.
Branding in this repo uses a single-source-of-truth workflow. Agents must follow it whenever touching shared visual identity.
- Canonical project branding lives under
branding/. branding/styles/mos.cssis the canonical shared MOS brand stylesheet.- Canonical logo and favicon assets live in
branding/andbranding/favicons/. - Do not create or maintain hand-edited duplicate brand styles inside
site/,apps/suite-manager/, or other app folders when the change is meant to affect shared MOS branding. - App-local branding copies that exist for runtime isolation are generated artifacts or sync targets, not the source of truth.
- When changing shared branding, run
npm run branding:sync. - If a task changes shared branding inputs, verify the affected app-local outputs were refreshed before finalizing.
- If an app needs a one-off local style that is not part of shared MOS branding, keep it narrowly scoped and do not move shared tokens out of
branding/styles/mos.css.
Current shared-branding sync targets include:
site/src/generated/branding/mos.cssapps/suite-manager/frontend/src/styles/mos.cssapps/homepage/config/custom.cssfor the synced Homepage theme block- public brand/favicons copied into app-local runtime folders
Use a strict split:
- End-user content lives in
site/src/content/docs/apps/*.mdx - Technical/operational content lives in
apps/*/README.md
No duplicated content across these two sources.
- Technical specs must be authored in app README files only.
- App MDX pages must embed the matching README under a
Technical referencesection. - If technical content appears in MDX body text, move it to README.
- If descriptive/end-user content appears in README, move it to MDX.
Include:
- Short, plain-language app description immediately under title/logo.
- Core capabilities (what users can do).
- Optional roadmap/status notes if relevant.
- Links section (official website/repository/docs).
- Optional screenshots and explanatory narrative.
Do not include:
- Env var lists
- Volume mount details
- Healthcheck endpoints
- Container/runtime internals
- Deployment command runbooks
- Troubleshooting runbooks for operators
- Duplicated
Technical referencetitle text - Low-value filler sections (e.g. weak "Other relevant info")
Include only technical content that is operationally useful:
- Environment variables
- Volumes/persistence
- Healthcheck endpoint(s)
- Dependencies/integrations (technical only)
- Project-specific customizations/patching behavior
- Operational commands (only if real and useful)
- Troubleshooting (only if real and useful)
Do not include:
- Marketing copy
- End-user capabilities lists
- App description prose already present in MDX
- Official links (links belong in MDX)
- Empty/no-op sections that only say "not documented"
Use this structure:
- Frontmatter + app logo marker div
- Description text (no
Application descriptionheading) ## Technical reference- Embedded README content via import/render
## Links
Example:
---
title: Example App
description: Short user-facing summary.
slug: docs/apps/example
---
import * as TechSpecs from '../../../../../apps/example/README.md';
<div className="app-page app-example" aria-hidden="true"></div>
Example app helps users do X and Y in plain language.
## Technical reference
<div className="tech-specs">
<TechSpecs.Content />
</div>
## Links
- [Example website](https://example.com/)- Avoid redundant headings:
- Do not use
Application description - Do not add
Other relevant infounless it is genuinely useful and non-redundant
- Do not use
- If an
Other relevant infosection is empty or weak, remove it. - Keep docs easy to scan; prefer fewer sections over filler.
For each app page:
- MDX contains only end-user content + links + embedded references.
- README contains only technical/maintenance content.
- No duplicated sentence appears in both MDX and README unless absolutely necessary.
- No empty sections.
- No redundant titles like "
<App> Technical reference" in embedded content.
- Preserve existing facts; do not drop meaningful technical details.
- Rephrase/move content rather than delete unless it is redundant filler.
- Keep wording concise and direct.
The repo now includes real black-box Playwright tests that run against the Docker stack without test-only application bypasses.
- Prefer end-to-end validation for onboarding and app reachability changes before adding unit-test-only coverage.
- Keep E2E tooling isolated under
tests/e2eunless a shared repo-level script or config is genuinely needed. - Do not add source-code-only test hooks, fake auth shortcuts, or alternate code paths just to make tests easier.
- When changing onboarding, auth, Homepage routing, or app integration behavior, consider whether
npm run e2e:fullshould be rerun before finalizing.
Useful commands:
npm run e2e:installinstalls Playwright browser dependencies for the local harness.npm run e2e:onboardingruns the real onboarding flow headlessly.npm run e2e:onboarding:headedruns the onboarding flow in a visible browser.npm run e2e:onboarding:manualruns the onboarding flow and then pauses on Homepage so the browser stays open for manual testing.npm run e2e:appsruns Homepage-driven app verification.npm run e2e:apps:headedruns app verification in a visible browser.npm run e2e:fullruns the full local E2E suite headlessly.npm run e2e:full:headedruns the full local E2E suite in a visible browser.
These rules are mandatory for app/service onboarding and version updates.
- Do not write runtime image tags/digests directly in
deploy/vps/docker-compose.yml. deploy/vps/docker-compose.ymlmust usebuild:entries that point to Dockerfiles inapps/.- Pin base images in Dockerfiles with immutable digests (
FROM image@sha256:...). - Never use floating tags like
latestorreleasein runtime Dockerfiles.
For each app, Dockerfiles must live in the app root folder:
- Primary app service:
apps/<app>/Dockerfile - Additional services:
apps/<app>/Dockerfile.<service>
Examples:
apps/immich/Dockerfileapps/immich/Dockerfile.machine-learningapps/immich/Dockerfile.postgresapps/immich/Dockerfile.valkeyapps/seafile/Dockerfileapps/seafile/Dockerfile.mysqlapps/seafile/Dockerfile.memcached
Do not introduce new canonical Dockerfiles in nested subfolders like apps/<app>/<service>/Dockerfile.
- Dockerfile paths used by external deploy templates (Railway/Dokploy) are treated as stable API.
- If a path is already used publicly, do not remove or move it in a patch/minor change.
- If a path migration is needed:
- Keep the old Dockerfile as a compatibility stub.
- Add a deprecation comment pointing to the canonical root-level Dockerfile.
- Remove old paths only in a clearly documented breaking release.
When adding a new service to deploy/vps/docker-compose.yml:
- Use:
build.context: ../../apps/<app>build.dockerfile: DockerfileorDockerfile.<service>
- Do not use direct
image:references for services managed by this repo.
- Runtime env templates live in:
deploy/vps/.env.templatedeploy/vps/services/<service>/.env.template
- Do not reintroduce runtime env templates under
deploy/vps/apps/. deploy/vps/services/suite-manager/.env.templateis the shared control-plane input file for local/VPS setup.
When adding/changing an app service, also update:
apps/<app>/README.mdtechnical specs (env vars, volumes, healthchecks, dependencies).deploy/vps/services/<service>/.env.templatewhen relevant..github/dependabot.ymlDocker entries for the affected app root directory.