Docs · Reference
Launch Control Operations Manual
Oct 2, 2026 · Tristan Rogers
Purpose and scope
This manual is how TRMG builds, ships and runs Launch Control, and how a dealer's month moves through it. It is the reference for anyone working on the product or using it, and it replaces memory: if a step is not written here or in the repo, it is not yet a process.
Launch Control turns one offer brief into compliant creative for every channel a dealer runs: Meta, Google Display, TikTok, website banners, live HTML email, print and out of home. Every frame is checked against OEM, dealer, channel and agency rules before it can go out for approval. The example client throughout is Beck & Beck Buick GMC; all launch content in the system today is example data.
Who it serves
| Person | What they use it for |
|---|---|
| Dealer operations staff | Bring a monthly offer to market quickly without breaking OEM or co-op rules |
| TRMG marketing | Build, adjust and deliver creative across channels from one brief |
| Approvers (dealer GM, GM co-op reviewer, TRMG creative lead) | Sign off on their part of each version |
| TRMG leadership | Decide what gets built next and approve every change to the live product |
What this manual covers: the system and its environments, who does what, the change process from request to deploy, quality gates, releases and rollbacks, the database, the monthly launch cycle, brand kits, assets, access, integrations, security, accounts and costs, working with Claude, current status, troubleshooting and a glossary.
Where the source of truth lives: the GitHub repository trmg1/launch-control. This manual summarizes it; when they disagree, the repo wins and this manual gets corrected.
System overview
One Cloudflare Worker serves the app, the login page and the API from launchcontrol.trmg.cloud; GitHub holds everything it is built from, and only builds from main reach production data.
[Embedded content in the artifact: Launch Control architecture · from repository to live data. A drawn diagram in the source; it does not translate to markdown.]
A push goes to GitHub, where Actions runs the checks, and to Cloudflare, which builds main as production and every other branch as a preview. The lane switch inside the Worker decides which database and storage a build may use.
| Part | Lives in | Does |
|---|---|---|
| App | prototype/src, compiled by prototype/build.py | Every screen: Launches, workspace, Approvals, Brand kits, Library, Integrations, Team |
| Login page | prototype/src/login.html | Sign-in by link, code or first-time setup |
| Frame templates | prototype/src/templates.js, email.js | One layout per shape builds every size; email is live HTML |
| Compliance engine | prototype/src/checks.js, rules.js | Evaluates every launch against the brand kits as it is typed |
| Worker | worker/index.js, wrangler.jsonc | Serves the app, requires sign-in, mounts the API, applies the lane |
| API | api/src | Sign-in, roles, launches, versions, comments, team, webhooks, assets, Mailchimp |
| Brand kits | brand-kits/ | Rules as data beside their source guidelines |
| Asset registry | assets/manifest.json | Every image, logo and font with rights and CDN key |
| Database structure | migrations/ | Numbered changes to D1 |
| Checks | .github/workflows/ci.yml, scripts/ | Kit validation, tests, build on every push |
Environments and lanes
There are four places Launch Control runs, and only one of them is real. Production is the only environment that may touch real launches, people or approvals.
| Environment | Where | Built from | Data | Who uses it |
|---|---|---|---|---|
| Production | launchcontrol.trmg.cloud | The main branch, automatically on every merge | Production database (DB), once connected | The team and dealers |
| Preview | A Cloudflare workers.dev link per branch, shown on its pull request | Any branch other than main | Preview database (DB_PREVIEW) or none; never production | Whoever reviews that pull request |
| Local | This Mac, through wrangler dev | Whatever is checked out | A throwaway local database | Claude, for end-to-end tests before pushing |
| Demo artifact | The claude.ai artifact link | A copy published by hand | Each viewer's own browser only | Sales demos and quick looks |
How a build knows its lane. Every build writes build.json with the branch and commit it came from. The Worker reads it: a build from main is production; anything else, including an unknown branch, is a preview. A preview swaps in the preview database and storage, and shows an orange banner naming its branch. /api/health on any environment reports its lane, branch and commit, so anyone can check which version they are looking at.
Rules that follow from this
- Never point a preview at the production database, even temporarily.
- Never share a preview link outside the team; it is public, though it holds only test data.
- The demo artifact never signs in and never shares data. Use it to show the product, not to run a launch.
Roles and responsibilities
Tristan owns the product and approves every change that reaches production; Claude builds, tests and documents. Inside the app, five roles decide what each signed-in person can do.
Building the product
| Responsibility | Tristan (owner) | Claude (builder) |
|---|---|---|
| Decide what gets built and in what order | Decides | Proposes, keeps docs/ROADMAP.md current |
| Write the code, tests and docs | Reviews in the preview | Does |
| Run checks before pushing | Does: kit validation, tests, build, browser run | |
| Open the pull request | Clicks Create on the prefilled link | Pushes the branch and sends the link |
Approve and merge to main | Only Tristan | Never merges, never commits to main |
| Confirm the deploy | Checks /api/health after each merge | |
| Roll back a bad deploy | Can do it in one click | Can do it, then fixes on a new branch |
| Accounts, payment, API keys | Owns them; pastes keys into Cloudflare himself | Gives the exact screen and field |
| Invite the team and set roles | Does, on the Team screen | Explains, never sees passwords or keys |
Using the product (roles in the app)
| Role | Can do | Typical person |
|---|---|---|
| Admin | Everything, including the Team screen, integrations and webhooks | Tristan, a TRMG lead |
| Marketing | Build, edit, send and deliver creative; edit disclaimers; preview brand kit changes; upload assets | TRMG marketing staff |
| Dealer ops | Build, edit and send creative; disclaimers stay as the GM library has them | Dealer operations staff |
| Approver | Approve or request changes in their own slot only | Dealer GM, GM co-op reviewer, TRMG creative lead |
| Viewer | See everything and comment | Anyone who needs visibility |
The server enforces these on every request. Hiding a button is only a convenience; it is never the protection.
The change process
Every change, however small, travels the same path: branch, pull request, checks, preview, merge, deploy, verify. Nothing reaches launchcontrol.trmg.cloud any other way.
Step by step
- Request. Tristan names the goal in a sentence or two: who it is for and what done looks like. Screenshots and client examples help. Or he says "next on the roadmap".
- Plan. For anything larger than a small fix, Claude replies with what will be built and anything needed from Tristan (an account, a decision, a key).
- Branch. Claude starts a branch from the latest
main. - Build and test. Claude writes the change, its tests and its docs, then runs the full checks locally plus a real browser run for anything on screen.
- Push and link. Claude pushes the branch and sends one prefilled pull request link.
- Open the pull request. Tristan clicks the link, then Create pull request. The title and description are already written.
- Checks. GitHub runs the checks (about two minutes). Red means Claude fixes it on the same branch; the pull request updates itself.
- Preview. Cloudflare builds the branch and adds a preview link to the pull request. Tristan opens it and tries the change.
- Merge. Tristan clicks Merge pull request, Confirm merge, then Delete branch.
- Deploy and verify. Cloudflare deploys
mainin about two minutes. Tristan says "merged"; Claude confirms/api/healthshows production at the new commit, deletes any leftover branch, and updates the roadmap.
Branch names
| Kind of change | Prefix | Example |
|---|---|---|
| New capability | feature/ | feature/email-codes |
| Bug fix | fix/ | fix/story-headline-wrap |
| Documentation only | docs/ | docs/onboarding-guide |
| Tooling or setup | setup/ | setup/branch-workflow |
Lowercase, words joined by hyphens, one purpose per branch.
Every pull request answers four things (the template asks for them): what changes, why, how to check it in the preview, and whether it changes the database.
Definition of done
- Checks are green on the pull request
- Tristan has looked at it in the preview
- Any database change is a new numbered migration, applied to the preview database first
- Docs are updated in the same pull request (README, WORKFLOW, ROADMAP, or this manual)
- After merge,
/api/healthshows production at the new commit
Pace. One branch at a time while the team learns the routine. Later, two or three can run side by side, each with its own preview.
Quality gates
A change must pass three automatic checks on GitHub and a human look at the preview before it can merge. A red check means stop.
| Gate | What it proves | Runs | Command |
|---|---|---|---|
| Brand kits | Every kit file is valid: known checks, correct parameters, no misspelled placeholders, no dashes as separators. Every example launch in brand-kits/fixtures/launches.json still gets its expected results (11 cases today) | Every push and pull request | node scripts/validate-kits.mjs |
| Tests | The API, sign-in, roles, approvals, comments, webhooks, Mailchimp drafts, asset uploads, the Worker and the lanes (35 tests today) | Every push and pull request | node --test api/test/*.test.mjs |
| Build | The app and login page compile | Every push and pull request | python3 prototype/build.py |
| Deploy dry run | Cloudflare would accept the Worker and its settings | Claude, before pushing config changes | npx wrangler deploy --dry-run |
| Browser run | The feature works in a real browser, signed in as the roles it affects | Claude, for anything on screen | Local Worker + scripted Chrome |
| Preview review | It does what was asked, and nothing else changed | Tristan, on every pull request | The Cloudflare preview link |
House rules every change follows
- No spaced dashes as separators anywhere: in creative, in the app, in docs, in commit messages. Use a colon, a semicolon, a middle dot, or let weight and size carry the break. The kit validator enforces this on kit files.
- Every fix to a misfiring rule starts with a new fixture that reproduces it, so it stays fixed.
- Tests ship in the same pull request as the code they cover.
- Secrets never enter the repository or a chat.
Releases, verification and rollback
Every merge to main is a release, live in about two minutes; if it goes wrong, roll back first and fix second.
How a release happens
- A pull request merges into
main. - Cloudflare's Git integration for the Worker
launch-controlruns the build commandpython3 prototype/build.py, thennpx wrangler deployusingwrangler.jsonc. - The new version takes all traffic at launchcontrol.trmg.cloud. Cloudflare keeps every earlier version.
How to verify a release
Open https://launchcontrol.trmg.cloud/api/health. It must show "lane":"production", "branch":"main", and a commit matching the merge. Then open the app and check the thing that changed. Claude does both after every "merged".
How to roll back (one minute)
- Open the Cloudflare dashboard, then Workers & Pages, then launch-control.
- Open Deployments.
- Find the last version that worked, open its menu, choose Rollback, and confirm.
- Check
/api/health: the commit now shows the older version. - Tell Claude what went wrong. The fix comes on a
fix/branch through the normal process; the next merge replaces the rollback.
What a rollback does not undo: database migrations, secrets and data written since the release. That is why migrations only ever add things (see Database operations).
Hotfixes. An urgent fix still uses a branch and a pull request, just a small one with only the fix. Skipping the process is how a fix breaks something else.
Release record. GitHub keeps the history: every merged pull request is a numbered release note (what, why, how it was checked). Pull request #1, the branch workflow, merged on Oct 2, 2026.
Database operations
The database is Cloudflare D1, two of them: launch-control for production and launch-control-preview for branch previews. Neither is connected yet; that is the next roadmap item.
What it holds
| Table | Holds |
|---|---|
users, sessions, login_codes, login_links | People, their roles and approval slots, and sign-in (secrets stored only as keyed hashes) |
launches | Each launch's brief, approval state and version number, with a revision counter |
launch_versions | Every version exactly as it was sent for approval, with the kit versions and check results at that moment |
comments | Pinned feedback: frame, position, version, author, resolved or open |
webhooks, events, deliveries | Endpoints, every event sent, and each delivery's attempts |
assets | Uploaded files: content hash, CDN key, size |
How the structure changes: migrations
The structure lives in numbered files in migrations/. 0001_initial.sql is the first. Each runs once per database, in order, and the database records which have run.
- A change is a new file, for example
0002_add_frame_adjustments.sql. Never edit one that has been applied. - Add first, remove later. A new table or column ships with the code that uses it; anything old is removed in a later release, once nothing uses it. Rolling back code never undoes a migration, so this keeps rollbacks safe.
- Order: Claude applies the migration to the preview database while the pull request is open (
npx wrangler d1 migrations apply launch-control-preview --remote), then to production right before Tristan merges (npx wrangler d1 migrations apply launch-control --remote). - The pull request template's Database box says whether a migration is included.
How saves stay safe with several people
Every save of a brief carries the revision it started from. If a teammate saved first, the save is refused, the app loads their version and says so. Approval state only changes through dedicated actions (send, approve, request changes, next version), so editing a brief can never approve anything.
Backups and recovery. D1's Time Travel can restore a database to an earlier point in time. Confirm the retention window for the account's plan when the database is connected, and record it here.
Monthly launch operations
Each month runs on one loop: new OEM incentives arrive, each dealer's launches are briefed, checked, approved by three people, then delivered and reported. The Launches screen shows the month as a timeline of programs with every launch beneath them.
The cycle
- Incentives land. When GM publishes a program (for example GMC Truck Month, Oct 1 to Oct 31), marketing updates
brand-kits/gm-offers/programs.jsonthrough a pull request: name, OEM, start, end, and the chip wording. This is the one change made every month. - Launches are created. Marketing or dealer ops starts a launch per offer: program, vehicle, offer type (price stack, APR or brand USP), copy, end date, disclaimer and channels. Duplicating last month's launch is the fast path.
- Compliance runs live. As the brief is typed, every frame is checked against the dealer kit, the GM offer rules, the channel specs and TRMG delivery rules. Blocking issues must be fixed before sending; warnings need a person's judgement.
- Send for approval. Sending freezes the version (v1, v2, ...) with the exact brief, kit versions and check results. Three approvers are asked: the dealer general manager, the GM co-op reviewer and the TRMG creative lead.
- Review and feedback. Each approver opens the frames, pins comments to exact spots, and either approves their slot or requests changes.
- Iterate. Changes requested, or any edit after sending, starts the next version. Version compare shows the previous version beside the current one. Comments are resolved as they are addressed.
- Approved. When all three slots approve, the launch is approved and the delivery actions unlock.
- Deliver. Approved creative goes to its channels: PNG frames for Meta, Google and print, live HTML for email (a Mailchimp draft once connected), website banners for the dealer CMS. A delivery event tells connected systems.
- Report. Links carry the launch slug as their UTM campaign, so reporting in AgencyAnalytics groups results by launch.
- Close the month. Expired offers block automatically on their end date. Rights on vehicle photos that end before an offer are flagged in the Library.
Approval rules
| Rule | Why |
|---|---|
| A version can't be sent with blocking issues | Nothing non-compliant reaches an approver |
| Approvers act only in their own slot | A GM can't sign for co-op, and the reverse |
| Any edit after sending starts a new version | What was approved is exactly what ships |
| Only approved launches can be delivered or exported to a send platform | Unapproved creative never reaches a customer |
| Each version records the brand kit versions it was checked against | The audit trail a co-op claim needs |
Suggested rhythm, to confirm with dealers: incentives entered within a day of release, first versions sent within two days, approvals turned around within one business day.
Brand kit management
Brand kits are the rules Launch Control checks against, kept as files in GitHub next to the guidelines they came from. Six kits hold 27 rules today.
| Kit | Folder | Tier | Holds | Rules |
|---|---|---|---|---|
| GM offer rules | brand-kits/gm-offers | OEM compliance | Program match, offer dates, disclosures, APR qualifier, price math, photo rights; plus programs.json | 6 |
| GMC | brand-kits/gmc | OEM brand | Nothing yet: no GMC guidelines received | 0 |
| Buick | brand-kits/buick | OEM brand | Colors and type from the Buick tokens; no written rules yet | 0 |
| Beck & Beck | brand-kits/beck-beck | Dealer brand | Headlines, separators, length, Envista USP, pill buttons, legal plates, vehicle placement | 7 |
| Channel specs | brand-kits/channels | Channel | Every format's sizes, plus safe zones, email size and subject limits, bulletin word count | 12 |
| TRMG | brand-kits/trmg | Agency | Board surface and export naming | 2 |
How a rule is written. A rule names a check from the check library (prototype/src/checks.js), gives it parameters and its own pass and fail wording, and has a severity: block, warn or off. Locked rules are guaranteed by the templates and can't be switched off. Brand managers change wording, limits and severity; a new kind of check is an engineering change.
How a kit changes
- Edit
kit.jsonand bump its version, or change a severity on the Brand kits screen, which shows the change as a pull request preview. - Open a pull request. The validator fails it if anything is malformed or any example launch changes outcome.
- Review the preview to see exactly what the new rules block.
- Merge. Every version sent for approval afterwards records the new kit version.
Cadence
| When | What |
|---|---|
| Monthly | New incentive programs in programs.json |
| When guidelines change | Update the source markdown and the rules together, in one pull request |
| When a rule misfires | Add a fixture that reproduces it first, then fix the rule |
| New dealer | Copy beck-beck/, replace its source files, have Claude draft rules from the new guidelines, review them as a pull request, then add the folder to KIT_ORDER in prototype/build.py |
| New OEM guidelines | Same as a new dealer, in the OEM kit's folder |
Asset management
Every image, logo and font the templates use is registered in assets/manifest.json, with its rights and where it is used; git keeps the record, and the files themselves are meant to live on the CDN. Eight assets are registered today.
What each record carries: a stable id, a human name, kind (vehicle, backdrop, logo, font), the kit it belongs to, tags, and rights (source and end date). The build adds the file's size, dimensions, SHA-256 content hash, CDN key and URL.
Content-hashed names. A file's CDN key includes the first 10 characters of its content hash, for example kits/beck-beck/vehicles/veh-sierra-1500-at4.688e661541.png. The same bytes always get the same key, so duplicates are caught and files can be cached forever. A changed image gets a new URL, so nothing already published changes underneath anyone.
Rights are a compliance check. Rule GM-06 compares a vehicle photo's license end date with the offer's end date and flags the launch when the license ends first. The example Yukon XL photo is set to end Oct 15, 2026, so the Yukon launch shows that warning.
Adding or replacing an asset
- Upload it on the Library screen (today it is hashed and measured in the browser; once storage is connected it uploads to R2), or send it to Claude.
- Record its rights: where it came from and when the license ends. An asset with no rights record is flagged.
- Claude adds it to the manifest in a pull request; the build computes its key.
- After merge,
node scripts/sync-assets.mjsuploads it to the bucket. Re-running is safe.
Open item: the Futura font. It came with the client's files under a desktop license. Confirm the license covers web and email embedding, or switch to a licensed web font, before it is served from a public CDN.
Planned CDN: an R2 bucket with cdn.trmg.cloud as its custom domain (roadmap item 5). Until then, email exports carry {{CDN}} placeholders and the app embeds images directly.
Access management
Once the database is connected, every page except /login needs a signed-in person (switched on Oct 2, 2026), and admins manage who that is on the Team screen.
Organizations. Everyone belongs to one organization. TRMG sees every client. Each client, in automotive or any other industry, sees only its own launches and people; anything else answers as if it didn't exist. TRMG admins add clients on the Team screen. A Client admin at each dealer group adds, changes and disables their own people, and can't touch anyone else. This ships with feature/client-workspaces.
Dealers and groups. Below a client sit dealer groups and dealers (sub-clients outside automotive). Hi Tech Motorcars, for example, has the Hi Tech Luxury Imports group (Aston Martin Austin, Bentley Austin, Lotus of Austin, Rolls-Royce Motor Cars Austin, Hi Tech Luxury Collection) and Georgetown INFINITI on its own. Platinum Land has Highland Branch, The Estates at Highland Branch and Platinum Land. A launch is for the whole client, a group, or one dealer. Each person's Access on the Team screen is every dealer, or chosen groups and dealers, and a group covers every dealer in it, including ones added later. Someone with Georgetown INFINITI sees only that store's launches; someone with the Luxury Imports group sees its five stores and its group campaigns; someone with every dealer sees everything at that client. Changes apply on the person's next click.
Client sign-in, planned. Dealer staff will use Sign in with Microsoft with their Outlook work login, and TRMG will use Sign in with Google. Either way a session lasts 30 days and every visit restarts it. Some dealer groups' IT must approve a new app once before their staff can use it.
Ways to sign in
| Method | How it works | Available |
|---|---|---|
| Setup link | /login?setup=<token> creates the first admin while there are no people yet, then stops working | When the database connects |
| Sign-in link | An admin creates it on the Team screen and sends it by any channel. Works once, on one device, for 7 days | When the database connects |
| Email code | A 6-digit code by email, valid 10 minutes, at most 5 per hour per address | After Resend is connected (roadmap item 4) |
Sessions last 30 days on a device. Signing out ends that session; disabling a person ends all of theirs at once.
What each role can do
| Permission | Admin | Marketing | Dealer ops | Approver | Viewer |
|---|---|---|---|---|---|
| See launches | Yes | Yes | Yes | Yes | Yes |
| Comment | Yes | Yes | Yes | Yes | Yes |
| Edit briefs | Yes | Yes | Yes | ||
| Edit disclaimers | Yes | Yes | |||
| Send for approval | Yes | Yes | Yes | ||
| Approve | Any slot | Own slot | |||
| Deliver and export | Yes | Yes | |||
| Brand kit rules | Yes | Yes | |||
| Upload assets | Yes | Yes | |||
| Integrations and webhooks | Yes | ||||
| Team | Yes |
Onboarding a person
- Team screen, then Add a person: email, name, role, and for approvers their slot (general manager, co-op reviewer or creative lead).
- Click Add and get link, then Copy link.
- Send it to them directly. They open it and click Sign in to Launch Control.
Offboarding: on the Team screen, click Disable next to the person. Their sessions end immediately and their links stop working. Their comments and history stay.
Guard rails: there must always be at least one active admin; the system refuses to demote or disable the last one. Review the Team list monthly and disable anyone who has left.
Integrations and webhooks
Launch Control tells other systems what happened through signed webhooks, and pushes approved creative to the platforms each channel runs on. Webhooks go live with the database; platform connectors arrive one roadmap item at a time.
Events sent
| Event | Sent when |
|---|---|
launch.created | A launch is created or duplicated |
launch.version_started | An edit or a change request starts a new version |
launch.submitted | A version is sent for approval |
approval.recorded | One approver signs off |
launch.changes_requested | An approver requests changes |
launch.approved | All three slots have approved |
comment.created | Feedback is pinned to a frame |
comment.resolved | Feedback is resolved or reopened |
compliance.blocked | A launch picks up a blocking issue |
delivery.requested | Approved creative is pushed to a channel |
ping | An admin sends a test event |
How delivery works. Each event is a JSON POST with an LC-Signature header: an HMAC SHA-256 of the timestamp and body, keyed with that endpoint's secret. Receivers verify it and reject anything older than five minutes; the Integrations screen has copy-ready code. The first attempt runs immediately; failures retry on the minute cron after 1, 2, 4, 8, 16, 32 and 64 minutes, then are marked failed. A receiver answering 410 Gone pauses its endpoint.
Managing endpoints (admins): Integrations screen, then Add an endpoint: a name, an https:// URL, and the events it should receive. Copy the signing secret when it is shown; it is never shown again. Pause, test or remove endpoints from the same screen, and read every event's delivery status in the event log.
Inbound webhooks. Other systems post to /api/v1/inbound/<source>, signed the same way or with a token in the URL. They become inbound.<source> events that endpoints can subscribe to. Planned uses: Stripo saving an edited email, an OEM incentive feed, monday.com status changes.
Connections
| System | Does | Status | Needs from TRMG |
|---|---|---|---|
| monday.com | Mirrors each launch as an item; status follows the events | Ready through webhooks | The board's webhook or automation URL |
| Stratos CRM | Starts lead routing when a launch is approved | Ready through webhooks | The receiving URL |
| Slack | Posts comments, change requests and approvals | Ready through webhooks | An incoming webhook URL |
| AgencyAnalytics | Groups results by launch through UTM tags | Tags already on every link | Nothing |
| Mailchimp | Creates a draft campaign from an approved email, never sends | Built and tested; not connected | API key and audience |
| Stripo | Hand edits in the Stripo editor, returning as a new version | Planned | Plugin ID and secret |
| Meta, Google Ads, Duda | Push approved frames to ad accounts and the dealer site | Planned | Account access |
Security and data handling
The rule of thumb: secrets live only in Cloudflare, permissions are checked on the server, and test data never mixes with real data.
Built into the system
| Protection | How |
|---|---|
| Sign-in secrets are never stored readable | Session cookies, sign-in links and email codes are stored only as HMAC hashes keyed with AUTH_SECRET, so a copy of the database can't be used to sign in |
| Cookies | HttpOnly, Secure, SameSite=Lax, 30 days |
| Permissions | Checked by the API on every request; approvers limited to their own slot |
| Approval integrity | A plain save can't change approval state; sent versions are frozen |
| Cross-site requests | Writes from any other website are refused |
| No account discovery | The email code form answers the same way whether or not an address has access |
| Rate limits | 5 email codes per address per hour; 5 tries per code |
| Previews isolated | Non-main builds never get the production database or storage |
| Not indexed | Every page sends noindex; framing by other sites is blocked |
| Webhook authenticity | Every delivery signed; inbound webhooks verified |
People rules
- Keys, tokens and passwords are never pasted into chat, email or the repository. They go straight into the Worker's Variables and Secrets in Cloudflare.
- The GitHub repository stays private. It holds client brand files and a licensed font.
- Preview links stay inside the team.
- Disable people the day they leave.
- Real customer data (names, emails) is not in the system and should not be added until it is needed and reviewed.
Known gaps, tracked on the roadmap
- Compliance results on submit come from the browser; the server will re-run the checks itself (roadmap item 3).
- Sign-in routes have per-address limits but no wider rate limiting yet.
maincan't be protected by GitHub rules on the free plan; for now it is a habit that only Tristan merges.
Accounts, domains, credentials and costs
Everything runs on accounts TRMG owns, on free tiers today.
Accounts
| Service | Account | Used for | Plan |
|---|---|---|---|
| GitHub | trmg1 (personal account) | The private repository trmg1/launch-control, checks on every push | Free |
| Cloudflare | Tristan@trmg.io's account | The Worker launch-control, its Git builds, DNS for trmg.cloud, and soon D1 and R2 | Free tier today |
| Resend | Not created yet | Email sign-in codes | Free tier planned |
| Mailchimp | To be decided: TRMG's or each dealer's | Draft campaigns from approved emails | Open question |
Domains
| Domain | DNS host | Role |
|---|---|---|
| launchcontrol.trmg.cloud | Cloudflare | Production app, login and API |
| cdn.trmg.cloud | Cloudflare | Planned asset CDN |
| trmg.io | Google, with the site on Duda | TRMG's main site and Google Workspace email. Not used by Launch Control; moving its DNS would mean copying every website, email and CRM record exactly |
Credentials and where they live
| Credential | Lives in | Who can change it |
|---|---|---|
| Deploy key for pushing to GitHub | This Mac (~/.ssh/id_ed25519_launch-control), registered on the repo with write access | Tristan, on the repo's Deploy keys page |
| Wrangler (Cloudflare command line) authorization | This Mac, after Tristan clicks Allow | Tristan; npx wrangler logout revokes it |
AUTH_SECRET, BOOTSTRAP_TOKEN | Worker secrets in Cloudflare | Set during database connection |
RESEND_API_KEY, MAILCHIMP_API_KEY | Worker secrets in Cloudflare | Tristan pastes them in himself |
| Webhook signing secrets | The database; shown once when an endpoint is added | Admins, on the Integrations screen |
Costs to expect
- Today: none beyond existing subscriptions.
- Optional: GitHub Pro (a few dollars a month) to make GitHub enforce pull requests and green checks on
main. - Later: Cloudflare's paid Workers plan only if traffic or storage outgrows the free tier; Resend's paid tier only above its free sending limit.
Working with Claude
Claude is the builder on this project: it writes, tests and documents every change, and Tristan decides and approves. Conversations end and get compressed, so the project's memory lives in files, not in any one chat.
Asking for work
- Say the goal, who it is for, and what done looks like. "Dealers need to duplicate last month's launches in one click" beats "add a button".
- Send screenshots of anything confusing or broken, and client examples of what good looks like.
- "Next on the roadmap" is always a valid request.
- Expect a short plan back for anything bigger than a small fix, naming anything Claude needs from you.
What Claude always does
- Works on a branch, never on
main; Tristan merges. - Runs every check, and a real browser test for anything on screen, before sending a link.
- Says plainly what was verified and what wasn't.
- Updates the docs in the same pull request.
- Asks before anything hard to undo or outward facing: deleting, sending, sharing, spending.
What Claude never does
- Asks for passwords or keys in chat.
- Merges its own pull requests or changes production data directly.
- Guesses at facts it can't check; it marks them as open questions instead.
Where context lives
| Place | Holds |
|---|---|
The repository (README.md, docs/WORKFLOW.md, docs/ROADMAP.md, api/README.md) | How the system works, how we work, what's next |
| This manual | The operating picture in one place |
| Claude's memory notes on this Mac | Account details, where things live, the agreed workflow, what's pending |
| Pull requests | Every change, why it was made, and how it was checked |
Sessions. A fresh session per roadmap item keeps conversations focused. Automatic compression can be turned off in Claude Code with /config; if it is off and a conversation fills, start a new session and point Claude at this manual and the roadmap.
Approvals that happen on this Mac. Cloudflare authorization opens a browser page on the Mac where Claude's commands run, with a 2-minute window. Before Claude starts one, it says so; be at this Mac to click Allow.
Current status and roadmap
The product, sign-in, roles, shared data and webhooks are built and tested; they switch on when the databases are connected, which is waiting on one Cloudflare authorization click at this Mac.
Shipped
| Date | What | Where |
|---|---|---|
| Oct 2, 2026 | Production and preview databases connected; sign-in switched on | Pull request #2 |
| Oct 2, 2026 | Branch workflow: guide, roadmap, pull request template, migrations, preview isolation | Pull request #1 |
| Oct 2, 2026 | Sign-in, roles, Team screen, shared launches and comments, live webhooks, one Worker for app and API | Commit 7cfc129 |
| Oct 2, 2026 | Live at launchcontrol.trmg.cloud from GitHub through Cloudflare | Worker launch-control |
| Oct 1, 2026 | Brand kits as files with a validator; asset registry; integrations screen; email as live HTML; out of home layout; vehicles on one ground line; feedback, versions and approvals | Prototype releases |
Next, in order
| # | Branch | Unlocks | Needs from Tristan |
|---|---|---|---|
| 1 | feature/client-workspaces | Clients as separate organizations; Client admin role | Review the preview, merge (pull request open Oct 2) |
| 2 | feature/microsoft-signin | Sign in with Microsoft for dealer staff on Outlook | Register the app in Microsoft's developer portal |
| 3 | feature/google-signin | Sign in with Google for the TRMG team | Create a sign-in client in Google Cloud |
| 4 | kits/hi-tech-motorcars | Hi Tech's own brand kit | Hi Tech's guidelines, logo and fonts |
| 5 | feature/server-compliance | The server re-runs every brand kit check on submit | Nothing |
| 6 | feature/email-codes | Sign in by emailed code, for anyone without Microsoft or Google | A free Resend account with trmg.cloud verified |
| 7 | feature/cdn | Assets at cdn.trmg.cloud; real image links in emails; Library uploads | Nothing |
| 8 | feature/export | PNG at 2x per frame and a ZIP per channel | Nothing |
| 9 | feature/frame-adjustments | Per-frame tweaks that survive brief edits | Nothing |
| 10 | feature/mailchimp | The Create Mailchimp draft button works | API key and audience |
| 11 | feature/trmg-webhooks | monday.com, Stratos and Slack receive events | Their receiving URLs |
| 12 | fix/header-wrap | The header fits on one line at laptop widths | Nothing |
Later: incentive programs from an OEM feed through the inbound webhook; a GMC brand kit when guidelines arrive; Stripo editing; Meta, Google Ads and Duda delivery.
Open questions for Tristan
- Whose Mailchimp account receives drafts: TRMG's, or each dealer's?
- Does the Futura license cover web and email embedding?
- Who are the first five people to invite, and in which roles?
- Is GitHub Pro worth adding now so GitHub enforces the merge rules?
Troubleshooting
Most problems have a known cause and a one-step fix; when in doubt, check /api/health first, then send Claude a screenshot.
| Symptom | Likely cause | Fix |
|---|---|---|
| The live site looks wrong right after a merge | The new release has a bug | Roll back in Cloudflare Deployments, then report it |
| A check on the pull request is red | A test, a kit rule or the build failed | Tell Claude; the fix goes on the same branch |
| No Cloudflare preview on a pull request | Builds for non-production branches are off | Cloudflare, Workers & Pages, launch-control, Settings, Build: turn on non-production branch builds |
| Cloudflare can't find the repository when connecting | The Cloudflare GitHub app can't see it | github.com/settings/installations, Cloudflare Workers and Pages, Configure, add launch-control |
| Header says "Not connected: changes stay in this browser" | The database isn't connected (expected until roadmap item 2) | None needed; finish item 2 |
| Orange "Preview" banner | You are on a branch preview, not production | Use launchcontrol.trmg.cloud for real work |
| Sign-in link says it has expired | Links work once and for 7 days | An admin makes a new one on the Team screen |
| "Email codes aren't switched on yet" | Resend isn't connected | Use a sign-in link from an admin |
| Too many codes | 5 codes per hour per address | Wait an hour, or use a sign-in link |
| "Your role can't do that" | The action needs another role | Ask an admin to change the role on the Team screen |
| A teammate saved at the same moment | Two people edited the same launch | The app shows their version; redo your edit |
| "Send for approval" is greyed out | Blocking compliance issues | Fix the items marked Blocked in the Compliance panel |
| Approve buttons missing for an approver | Wrong or missing approval slot | Admin sets the slot on the Team screen |
| Webhook deliveries failing | The receiver is down or rejecting the signature | Event log shows status and last error; retries run for about two hours |
| Cloudflare authorization timed out | The Allow page opened on the Mac and wasn't clicked within 2 minutes | Be at this Mac when Claude starts it, then click Allow |
| Mailchimp draft fails | Key not set, or the launch isn't approved | Add the key in Worker secrets; approve the launch first |
Glossary
| Term | Meaning here |
|---|---|
| Branch | A separate line of work in the repository; changes on it don't affect main until merged |
main | The branch that is live at launchcontrol.trmg.cloud |
| Pull request (PR) | A GitHub page proposing a branch for main, with its changes, checks and preview |
| Merge | Accepting a pull request into main, which deploys it |
| Checks (CI) | The automatic kit validation, tests and build GitHub runs on every push |
| Preview | A temporary copy of the app built from a branch, with an orange banner and the preview database |
| Lane | Production or preview; decided by the branch a build came from |
| Rollback | Switching the live site back to an earlier version in Cloudflare |
| Worker | The Cloudflare program launch-control that serves the app, the login page and the API |
| Wrangler | Cloudflare's command line tool, used from this Mac to set up and inspect the Worker |
| D1 | Cloudflare's database; Launch Control uses two, production and preview |
| R2 | Cloudflare's file storage, planned for assets behind cdn.trmg.cloud |
| Migration | A numbered file in migrations/ that changes the database structure once |
| Brand kit | A folder of rules and source guidelines (OEM, dealer, channel or agency) that creative is checked against |
| Check library | The code (checks.js) that kit rules point to by name |
| Fixture | An example launch with expected results that every kit change must still produce |
| Severity | Block (stops sending), warn (flag for a person) or off |
| Launch | One offer brought to market: its brief, frames, versions, approvals and comments |
| Version | A frozen copy of a launch as sent for approval (v1, v2, ...) |
| Approval slot | One of the three sign-offs: general manager, co-op reviewer, creative lead |
| Frame | One ad size in one channel, such as the Meta square feed at 1080×1080 |
| Webhook | A signed message Launch Control sends to another system when something happens |
| HMAC | A keyed hash used to sign webhooks and protect stored sign-in secrets |
| Secret | A key or token kept only in Cloudflare's Variables and Secrets, never in the repo or chat |
| Deploy key | The key on this Mac that lets Claude push branches to the one repository |
| Content hash | A fingerprint of a file's bytes, part of every CDN file name |