Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 30 additions & 34 deletions STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,39 @@ Date: 2026-04-19

## Ticket

- Active ticket: `SF-21`
- Branch: `feature/SF-21-linear-github-process`
- Goal: end-to-end Linear-first engineering workflow with PR guards and automation.
- Active ticket: `SF-23`
- Parent ticket: `SF-22` (Leopard Look redesign)
- Branch: `feature/SF-23-brand-foundation`
- Goal: establish the shared Leopard AI brand foundation consumed by follow-up platform tickets.

## Done

- Added PR governance:
- `.github/workflows/pr-ticket-guard.yml`
- `.github/PULL_REQUEST_TEMPLATE.md`
- Added Linear sync automation:
- `.github/workflows/linear-pr-sync.yml`
- `scripts/linear/pr_state_sync.mjs`
- Added auto-merge by Linear `Done`:
- `.github/workflows/linear-done-automerge.yml`
- `scripts/linear/automerge_done_tickets.mjs`
- Added process docs:
- `docs/guides/linear-github-process.md`
- `docs/guides/codex-automation-prompts.md`
- Updated docs index + README links.
- Updated CI branch pattern toward `feature/**`.

## Open

- Ensure repo secret `LINEAR_API_TOKEN` is set in GitHub Actions.
- Add `Preview` workflow state in Linear team (fallback currently uses `In Review`).
- Merge workflow PR and validate first real ticket through the full state machine.

## Verification

- `node scripts/linear/pr_state_sync.mjs` (graceful skip without token): ✅
- `node scripts/linear/automerge_done_tickets.mjs` (graceful skip without token/context): ✅
- `make test-core-js`: ✅
- `make test-core-swift`: ✅
- `make verify`: ✅ (including Android build in this environment)
### SF-23 — brand foundation
- `brand/tokens.json` — canonical source of truth (chakra palette, mode-to-chakra mapping, neutral surfaces, accents, typography, radius, spacing, elevation, leopard parameters).
- `brand/BRAND.md` — human-readable guidelines (identity, mode↔chakra, leopard pattern rules, logo/clearspace, glow formula, do/don't).
- `brand/generated/` — deterministic per-platform outputs:
- `tokens.css` (CSS custom properties)
- `BrandTokens.swift` (Swift enum tree)
- `BrandTokens.kt` (Kotlin object tree)
- `README.md` (do-not-edit notice, regen command)
- `scripts/brand/generate-tokens.mjs` — idempotent generator, verified byte-identical on repeated runs.
- `docs/guides/codex-automation-prompts.md` — worker prompt hardened to re-check active `Todo` / `In Progress` / `In Review` issues directly from Linear issue details.

## Verification (this session)

- `node scripts/brand/generate-tokens.mjs` → byte-identical twice. ✅
- `cd sonicflow_app/core-js && npm test` → 5/5 pass. ✅
- `cd sonicflow_app/chrome-extension && npm test` → 10/10 pass. ✅
- `./scripts/check_warnings.sh` → blocked in `core_swift` by sandboxed Swift toolchain cache access (`sandbox_apply: Operation not permitted`), not by an SF-23 code failure.

## Open / Flags

- `SF-24` through `SF-29` remain follow-up tickets that consume this foundation and should be split into their own branches/PRs after SF-23 is pushed.
- GitHub PR listing/comment inspection is currently blocked from this sandbox (`api.github.com` unreachable).
- Prefer moving `SF-23` to `Preview` if that Linear state exists; otherwise use `In Review`.

## Next Step

- Commit + push latest workflow hardening changes.
- Keep/update PR #21 (`[SF-21] ...`) until checks pass.
- Keep ticket in `Preview` when PR is ready; merge path then drives `Done`.
- Commit and push SF-23 branch contents.
- Open PR titled `[SF-23] ...` with the Linear link and the verification notes above.
- Move the ticket to `Preview` / `In Review`, then start the next platform slice on its own branch.
121 changes: 121 additions & 0 deletions brand/BRAND.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Leopard AI — Brand Guidelines

Version: 0.1.0 · Ticket: SF-23 · Parent: SF-22

## Identity

- **Company:** Leopard AI
- **Flagship product:** SonicFlow
- **Legacy codename in paths / schemes / packages:** FlowTones (do not surface in UI)
- **Positioning:** Chakra-tuned audio layers that match the user's intended mind state — focus, flow, meditation, sleep.

The brand is dark, warm, and quietly premium. Leopard pattern is a texture, never a character. Chakra colors carry meaning (mode semantics); gold carries identity.

## Source of truth

All tokens live in `brand/tokens.json`. Everything else (CSS variables, Swift struct, Kotlin object) is **generated** from that file — see `brand/generated/README.md`. No hex literal should appear outside of `brand/` after SF-24…SF-28 land.

If a new color is needed, add it to `tokens.json` first, regenerate, then use the generated token. Direct hex usage is reviewable as a blocker.

## Chakra palette

Seven tokens, four currently load-bearing as mode accents:

| Token | Chakra | Hex | Used for |
|------------|---------------------|-----------|-----------------------------------|
| `root` | Muladhara | `#C73D3D` | Warnings, deep-bass visualization |
| `sacral` | Svadhisthana | `#E67E22` | Secondary brand surfaces |
| `solar` | Manipura | `#F5C518` | Highlights, premium markers |
| `heart` | Anahata | `#1D9E75` | **Meditation** mode |
| `throat` | Vishuddha | `#378ADD` | **Focus** mode |
| `thirdEye` | Ajna | `#7F77DD` | **Flow** mode |
| `crown` | Sahasrara | `#534AB7` | **Sleep** mode |

Rationale: the existing four shipped mode colors map cleanly to throat, third-eye, heart, and crown — the four chakras most associated with cognitive and rest bands. We keep them unchanged to avoid visual regression, and expose the remaining three for brand / non-mode surfaces.

## Mode ↔ chakra mapping

| Mode | Beat | Band | Chakra | Color |
|------------|------|-------|-----------|-----------|
| Focus | 40Hz | gamma | throat | `#378ADD` |
| Flow | 10Hz | alpha | thirdEye | `#7F77DD` |
| Meditation | 6Hz | theta | heart | `#1D9E75` |
| Sleep | 2Hz | delta | crown | `#534AB7` |

## Accent

- `gold` `#D4A24C` — primary brand accent (logo, leopard spot highlights).
- `spotRing` `#6B4B1F` — leopard spot ring, darker accent.
- `success` `#3CCF91` — active/playing state only.
- `danger` `#E0484D` — stop/error state only.

## Neutral surfaces

Dark first. Light mode is out of scope for the Leopard Look — the brand's identity requires the warm-on-dark contrast.

| Token | Value | Use |
|--------|---------------------------------|---------------------------------|
| `ink` | `#0A0A0B` | deepest surface, pattern base |
| `bg` | `#0F0F12` | app canvas |
| `panel`| `rgba(18, 18, 22, 0.78)` | blurred surface behind content |
| `border`| `#2A2434` | panel/card borders |
| `fg` | `#F5F7FB` | primary text |
| `muted`| `#8E97A8` | secondary text |

## Typography

- **Display:** SF Pro Display / Inter → system fallback.
- **Body:** system stack (`-apple-system`, `Inter`, `Segoe UI`).
- **Mono:** JetBrains Mono → ui-monospace.

Scale: `caption 11pt`, `body 14pt`, `title 17pt`, `display 24pt`. Tracking is slightly negative at display sizes.

## Leopard pattern

The Leopard Look is built on a procedural leopard-print layer behind a blurred panel. Until SF-29 ships real photographic/illustrative assets, every platform uses a generated pattern with:

- Base: `ink` (#0A0A0B)
- Spot: `gold` (#D4A24C) with ~14% opacity
- Ring: `spotRing` (#6B4B1F)
- 2–4px blur on the pattern layer

**Rules:**

1. Pattern ALWAYS sits behind a blurred panel — never directly under body text.
2. Opacity stays between 10–18%; never above 25%.
3. No motion on the pattern (the user is listening, the eye is resting).
4. The pattern must be obviously a texture at glance distance — not readable as a photograph.

## Logo

- Clearspace: equal to the cap height of the mark on all sides.
- Minimum width: 24px (menu-bar icon), 16px (favicon).
- On dark: gold `#D4A24C`; subtle glow allowed on hero surfaces only.
- On light: ink `#0A0A0B`. Never gold on a light surface.

The SF-22 icon upload has a cropping issue ("der muss immer abgeschnitten werden") — SF-29 re-exports with added safe-area padding before slicing to platform sizes.

## Glow

Active mode cards glow with their chakra color:

```
shadow = 0 0 24px modeColor + 0 0 64px (modeColor @ 30% alpha)
```

On native platforms this compiles to layered shadows or blur effects. See `brand/generated/*` for the per-platform translation.

## Do / Don't

**Do**

- Use mode color only for that mode's surfaces (don't paint the Focus button in Heart green).
- Let the leopard pattern be quiet.
- Keep gold for identity, not for states.

**Don't**

- Don't mix chakra colors on a single card.
- Don't place gold on a light background.
- Don't animate the leopard pattern.
- Don't introduce a hex literal outside `brand/tokens.json`.
69 changes: 69 additions & 0 deletions brand/generated/BrandTokens.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// AUTOGENERATED from brand/tokens.json — do not edit by hand.
// Regenerate: node scripts/brand/generate-tokens.mjs
// Version: 0.1.0

package com.sonicflow.app.brand

import androidx.compose.ui.graphics.Color
import com.sonicflow.app.ui.fromHex

object BrandTokens {
object Chakra {
val root = Color.fromHex("#C73D3D")
val sacral = Color.fromHex("#E67E22")
val solar = Color.fromHex("#F5C518")
val heart = Color.fromHex("#1D9E75")
val throat = Color.fromHex("#378ADD")
val thirdEye = Color.fromHex("#7F77DD")
val crown = Color.fromHex("#534AB7")
}

object Neutral {
val ink = Color.fromHex("#0A0A0B")
val bg = Color.fromHex("#0F0F12")
val border = Color.fromHex("#2A2434")
val fg = Color.fromHex("#F5F7FB")
val muted = Color.fromHex("#8E97A8")
val panel = Color(0f, 0f, 0f, 0.78f)
}

object Accent {
val gold = Color.fromHex("#D4A24C")
val spotRing = Color.fromHex("#6B4B1F")
val success = Color.fromHex("#3CCF91")
val danger = Color.fromHex("#E0484D")
}

object Mode {
val focus = Color.fromHex("#378ADD")
val flow = Color.fromHex("#7F77DD")
val meditation = Color.fromHex("#1D9E75")
val sleep = Color.fromHex("#534AB7")
}

object Radius {
const val sm: Int = 8
const val md: Int = 14
const val lg: Int = 20
const val pill: Int = 999
}

object Spacing {
const val xs: Int = 4
const val sm: Int = 8
const val md: Int = 14
const val lg: Int = 20
const val xl: Int = 32
}

object Leopard {
val base = Color.fromHex("#0F0F12")
val spot = Color.fromHex("#D4A24C")
val ring = Color.fromHex("#6B4B1F")
const val opacity: Float = 0.14f
const val blurPx: Int = 2
const val spotCount: Int = 24
const val spotMinPx: Int = 14
const val spotMaxPx: Int = 44
}
}
66 changes: 66 additions & 0 deletions brand/generated/BrandTokens.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// AUTOGENERATED from brand/tokens.json — do not edit by hand.
// Regenerate: node scripts/brand/generate-tokens.mjs
// Version: 0.1.0

import SwiftUI

public enum BrandTokens {
public enum Chakra {
public static let root = Color(hex: "#C73D3D")
public static let sacral = Color(hex: "#E67E22")
public static let solar = Color(hex: "#F5C518")
public static let heart = Color(hex: "#1D9E75")
public static let throat = Color(hex: "#378ADD")
public static let thirdEye = Color(hex: "#7F77DD")
public static let crown = Color(hex: "#534AB7")
}

public enum Neutral {
public static let ink = Color(hex: "#0A0A0B")
public static let bg = Color(hex: "#0F0F12")
public static let border = Color(hex: "#2A2434")
public static let fg = Color(hex: "#F5F7FB")
public static let muted = Color(hex: "#8E97A8")
public static let panel = Color.black.opacity(0.78)
}

public enum Accent {
public static let gold = Color(hex: "#D4A24C")
public static let spotRing = Color(hex: "#6B4B1F")
public static let success = Color(hex: "#3CCF91")
public static let danger = Color(hex: "#E0484D")
}

public enum Mode {
public static let focus = Color(hex: "#378ADD")
public static let flow = Color(hex: "#7F77DD")
public static let meditation = Color(hex: "#1D9E75")
public static let sleep = Color(hex: "#534AB7")
}

public enum Radius {
public static let sm: CGFloat = 8
public static let md: CGFloat = 14
public static let lg: CGFloat = 20
public static let pill: CGFloat = 999
}

public enum Spacing {
public static let xs: CGFloat = 4
public static let sm: CGFloat = 8
public static let md: CGFloat = 14
public static let lg: CGFloat = 20
public static let xl: CGFloat = 32
}

public enum Leopard {
public static let base = Color(hex: "#0F0F12")
public static let spot = Color(hex: "#D4A24C")
public static let ring = Color(hex: "#6B4B1F")
public static let opacity: Double = 0.14
public static let blurRadius: CGFloat = 2
public static let spotCount: Int = 24
public static let spotMinPx: CGFloat = 14
public static let spotMaxPx: CGFloat = 44
}
}
19 changes: 19 additions & 0 deletions brand/generated/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# brand/generated

These files are **generated from `brand/tokens.json`** by `scripts/brand/generate-tokens.mjs`.

Do not hand-edit. Edit `tokens.json`, then run:

```bash
node scripts/brand/generate-tokens.mjs
```

Generated outputs:

- `tokens.css` — CSS custom properties, consumed by Chrome + Safari extensions.
- `BrandTokens.swift` — Swift struct with `Color` fields, consumed by iOS + macOS + Safari macOS app.
- `BrandTokens.kt` — Kotlin object with `Color.fromHex` fields, consumed by Android Compose theme.

The generator is deterministic — running it twice on the same input produces byte-identical output.

CI runs the generator and diffs the result against checked-in files. A non-empty diff fails the build (SF-24 introduces this check).
Loading
Loading