diff --git a/.github/automation/badges.schema.yml b/.github/automation/badges.schema.yml new file mode 100644 index 00000000..45bd2e01 --- /dev/null +++ b/.github/automation/badges.schema.yml @@ -0,0 +1,33 @@ +# Canonical badge definitions and mapping rules +version: 1 +badges: + workflow: + front-matter-schema: + label: "Front-Matter" + icon: "🧩" + source: "github-actions" + job: "front-matter-validate" + success_text: "Schema OK" + failure_text: "Schema Fail" + meta: + license: + label: "License" + icon: "πŸ“„" + values: + mit: "MIT" + gpl-2.0: "GPL-2.0" +mapping: + # Deterministic mapping from front matter and repo state to badges + - when: + has_front_matter: true + add: + - workflow.front-matter-schema + - when: + front_matter.license: ["mit","gpl-2.0"] + add: + - meta.license +render: + order: + - title + - badges + - description diff --git a/.github/automation/emoji.schema.yml b/.github/automation/emoji.schema.yml new file mode 100644 index 00000000..892fe522 --- /dev/null +++ b/.github/automation/emoji.schema.yml @@ -0,0 +1,15 @@ +version: 1 +apply_to: ["h1","h2"] +skip: + # formal docs where emojis are not used + - "CHANGELOG.md" + - "CODE_OF_CONDUCT.md" +map: + # keyword -> emoji (first match wins) + design: "🎨" + workflow: "πŸ› " + release: "πŸš€" + governance: "πŸ›" +fallbacks: + generic: "✨" # used only if no keyword matched and page not in skip +notes: "Conservative: 0–1 emoji per heading; never on h3+." diff --git a/.github/automation/footers.yml b/.github/automation/footers.yml new file mode 100644 index 00000000..c9ec8703 --- /dev/null +++ b/.github/automation/footers.yml @@ -0,0 +1,31 @@ +version: 1 +categories: + docs: + phrases: + - "Made with ❀️ by the LightSpeed team." + - "Questions? Open an issue; we're listening." + - "Prefer a guided setup? Book a consult." + - "Clarity first, then code. Thanks for reading." + - "Improvements welcomeβ€”PRs encouraged." + prompts: + phrases: + - "Copy, adapt, and keep shipping." + - "Tweak the variables, get results fast." + - "Your feedback shapes the next iteration." + - "Reuse beats rework. Share improvements." + - "Keep prompts versioned for sanity." + saved-replies: + phrases: + - "Use responsibly; tailor to the thread." + - "Keep tone human, clear, and kind." + - "Update when guidance changes." + - "Link policies; avoid assumptions." + - "Thanks for helping contributors." +default: + phrases: + - "Made with ❀️ by the LightSpeed team." + - "Need help? Say hiβ€”work with us." +selection: + strategy: "seeded" + seed: "file.path_slug" # stable per file + # Deterministic selection algorithm not yet implemented. This section is non-functional. diff --git a/.github/automation/front-matter.schema.json b/.github/automation/front-matter.schema.json new file mode 100644 index 00000000..1f698d40 --- /dev/null +++ b/.github/automation/front-matter.schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Front Matter", + "type": "object", + "additionalProperties": true, + "properties": { + "title": { "type": "string" }, + "slug": { "type": "string" }, + "series": { "type": ["object","string"] }, + "tags": { "type": "array", "items": { "type": "string" } }, + "no_branding": { "type": "boolean" }, + "category": { "type": "string" } + } +} diff --git a/.github/metrics/branding-log.md b/.github/metrics/branding-log.md new file mode 100644 index 00000000..4e6579ab --- /dev/null +++ b/.github/metrics/branding-log.md @@ -0,0 +1,9 @@ +--- +title: 'Branding Metrics Log' +description: 'Historical log of branding automation metrics' +--- + +# Branding Metrics Log + +| timestamp | coverage | changes | errors | optouts | +|---|---:|---:|---:|---:| diff --git a/.github/metrics/branding.json b/.github/metrics/branding.json new file mode 100644 index 00000000..f9332173 --- /dev/null +++ b/.github/metrics/branding.json @@ -0,0 +1,7 @@ +{ + "ts": null, + "coverage": 0, + "changes": 0, + "errors": 0, + "optouts": 0 +} diff --git a/.github/workflows/branding.yml b/.github/workflows/branding.yml new file mode 100644 index 00000000..75a41e3b --- /dev/null +++ b/.github/workflows/branding.yml @@ -0,0 +1,142 @@ +--- +name: Branding Agent + +on: + workflow_dispatch: + pull_request: + branches: [develop] + paths: + - "**/*.md" + - ".github/automation/**" + - ".github/agents/**" + push: + branches: [develop] + paths: + - "**/*.md" + - ".github/automation/**" + - ".github/agents/**" + schedule: + - cron: "0 3 * * 1" # weekly metrics roll-up (Mon 03:00 UTC) + +concurrency: + group: "branding-${{ github.ref }}" + cancel-in-progress: true + +permissions: + contents: write + pull-requests: write + +jobs: + front-matter-validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Validate Front Matter + run: | + npm i -g ajv-cli + if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then + BASE_REF="${{ github.base_ref }}" + HEAD_REF="${{ github.sha }}" + elif [ "${GITHUB_EVENT_NAME}" = "push" ]; then + BASE_REF="${{ github.event.before }}" + HEAD_REF="${{ github.sha }}" + else + BASE_REF="HEAD~1" + HEAD_REF="${{ github.sha }}" + npm install --prefix .github/scripts js-yaml ajv + FILES=$(git diff --name-only ${{ github.sha }} ${{ github.base_ref || 'HEAD~1' }} | grep -E '\.md$' || true) + for f in $FILES; do + node .github/scripts/validate-frontmatter.js "$f" + done + + lint-and-links: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: avto-dev/markdown-lint@v1 + with: + config: 'markdownlint.config.js' + args: '**/*.md' + + - name: Check Links + uses: lycheeverse/lychee-action@v2 + with: + args: --no-progress --verbose './**/*.md' --exclude-mail + + apply-branding: + if: ${{ github.event_name != 'pull_request' }} + needs: [front-matter-validate, lint-and-links] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Run Branding Agent + run: | + node .github/agents/branding.agent.js + + - name: Commit changes (content-only) + run: | + git config user.name "lightspeed-bot" + git config user.email "ops@lightspeedwp.agency" + git add -A + if ! git diff --cached --quiet; then + git commit -m "chore(branding): apply header/badges/refs/banner/footer [skip ci]" + git push + fi + + metrics-update: + needs: [apply-branding] + if: ${{ success() || failure() }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Update Metrics Snapshot + run: | + mkdir -p .github/metrics + # Read metrics from branding agent output (branding-metrics.json) + if [ -f .github/metrics/branding-metrics.json ]; then + node -e " + const fs = require('fs'); + const m = JSON.parse(fs.readFileSync('.github/metrics/branding-metrics.json', 'utf8')); + m.ts = new Date().toISOString(); + fs.writeFileSync('.github/metrics/branding.json', JSON.stringify(m, null, 2)); + const logLine = \`| \${m.ts} | coverage:\${m.coverage} | changes:\${m.changes} | errors:\${m.errors} | optouts:\${m.optouts} |\n\`; + fs.appendFileSync('.github/metrics/branding-log.md', logLine); + " + else + echo "Warning: .github/metrics/branding-metrics.json not found. Writing zeroed metrics." + node -e " + const fs = require('fs'); + let m = { ts: new Date().toISOString(), coverage: 0, changes: 0, errors: 0, optouts: 0 }; + fs.writeFileSync('.github/metrics/branding.json', JSON.stringify(m, null, 2)); + const logLine = \`| \${m.ts} | coverage:0 | changes:0 | errors:0 | optouts:0 |\n\`; + fs.appendFileSync('.github/metrics/branding-log.md', logLine); + " + fi + - name: Commit metrics + run: | + git config user.name "lightspeed-bot" + git config user.email "ops@lightspeedwp.agency" + git add .github/metrics/branding.json .github/metrics/branding-log.md + if ! git diff --cached --quiet; then + git commit -m "chore(branding): update metrics snapshot [skip ci]" + git push + fi diff --git a/docs/BRANDING.md b/docs/BRANDING.md new file mode 100644 index 00000000..bd3715fa --- /dev/null +++ b/docs/BRANDING.md @@ -0,0 +1,232 @@ +--- +title: 'Branding Agent β€” Specification' +version: 'v1.0' +last_updated: '2025-11-13' +author: 'LightSpeed' +maintainer: 'Ash Shaw' +owners: ['ashleyshaw'] +description: 'Automated branding system for headers, badges, references, banners, footers, and emojis in documentation.' +tags: ['branding', 'automation', 'documentation', 'badges', 'emojis', 'workflows'] +file_type: 'documentation' +category: 'docs' +references: + - '../.github/automation/badges.schema.yml' + - '../.github/automation/emoji.schema.yml' + - '../.github/automation/footers.yml' + - '../.github/automation/front-matter.schema.json' + - '../.github/workflows/branding.yml' + - '../.github/agents/branding.agent.js' +--- + +# Branding Agent β€” Spec (develop) + + +![License](https://img.shields.io/badge/license-MIT-blue.svg) +![Workflow](https://img.shields.io/badge/workflow-branding-success.svg) + + +The Branding Agent ensures consistent, professional presentation across all LightSpeed documentation by automatically applying headers, badges, references, banners, footers, and emojis according to defined schemas and content categories. + +## Scope + +- **Headers:** Title with badges and short description +- **Badges:** Workflow status and metadata badges from schema +- **References:** Cross-links to related documentation +- **Banner:** Visual branding element +- **Footer:** Contextual, rotating footer phrases +- **Emojis:** Conservative emoji application to H1/H2 headings + +## Schemas + +The branding system is governed by four schema files: + +1. **[`badges.schema.yml`](../.github/automation/badges.schema.yml)** – Badge definitions, mapping rules, and render order +2. **[`emoji.schema.yml`](../.github/automation/emoji.schema.yml)** – Emoji application rules and exclusions +3. **[`footers.yml`](../.github/automation/footers.yml)** – Category-based footer phrase selection +4. **[`front-matter.schema.json`](../.github/automation/front-matter.schema.json)** – Front matter validation schema + +## Workflow + +The branding system is triggered by [`branding.yml`](../.github/workflows/branding.yml) on: + +- Pull requests to `develop` branch (validation only) +- Pushes to `develop` branch (validation + application) +- Weekly schedule (Monday 03:00 UTC for metrics) + +### Workflow Jobs + +1. **`front-matter-validate`** – Validates front matter against schema +2. **`lint-and-links`** – Runs markdown linting and link checking +3. **`apply-branding`** – Applies branding to markdown files (push only) +4. **`metrics-update`** – Updates branding metrics snapshot + +## Header Order + +Standard documentation header structure: + +```markdown +# Title + +[Badges] + +Short description paragraph. +``` + +## References β†’ Banner β†’ Footer + +The branding agent inserts three elements in order: + +1. **References Section** – Cross-links to related docs +2. **Banner Image** – Visual branding (`assets/banners/work-with-us.png`) +3. **Footer** – Contextual phrase based on category + +### Banner Variants + +- Default: `assets/banners/work-with-us.png` +- Product-specific variants: TODO + +## Emojis + +Conservative emoji application following [`emoji.schema.yml`](../.github/automation/emoji.schema.yml): + +- **Applied to:** H1 and H2 headings only +- **Never applied to:** H3+, formal docs (CHANGELOG.md, CODE_OF_CONDUCT.md) +- **Selection:** Keyword-based mapping with fallback +- **Limit:** 0–1 emoji per heading + +### Emoji Mapping + +| Keyword | Emoji | +|---------|-------| +| design | 🎨 | +| workflow | πŸ› οΈ | +| release | πŸš€ | +| governance | πŸ›οΈ | +| (fallback) | ✨ | + +## Opt-Out + +Files can opt out of branding in two ways: + +### Front Matter Opt-Out + +```yaml +--- +no_branding: true +--- +``` + +### Body Marker Opt-Out + +```markdown + +``` + +## Formal Documents (Always Skip) + +The following formal documents are always excluded: + +- `CHANGELOG.md` +- `CODE_OF_CONDUCT.md` +- Any file with `no_branding: true` in front matter + +## Examples + +### Before + +```markdown +# Documentation Guide + +This guide explains how to write documentation. +``` + +### After + +```markdown +# Documentation Guide πŸ“š + +![License](https://img.shields.io/badge/license-MIT-blue.svg) +![Front-Matter](https://img.shields.io/badge/Front--Matter-Schema%20OK-success.svg) + +This guide explains how to write documentation. + +## References + +- [Contributing Guide](../CONTRIBUTING.md) +- [Coding Standards](../.github/instructions/coding-standards.instructions.md) + +--- + +![Work with LightSpeed](../assets/banners/work-with-us.png) + +--- + +Made with ❀️ by the LightSpeed team. +``` + +## Metrics + +Branding metrics are tracked in two files: + +- **[`.github/metrics/branding.json`](../.github/metrics/branding.json)** – Latest snapshot +- **[`.github/metrics/branding-log.md`](../.github/metrics/branding-log.md)** – Historical log + +### Metrics Tracked + +| Metric | Description | +|--------|-------------| +| `coverage` | Percentage of docs with branding applied | +| `changes` | Number of files modified in last run | +| `errors` | Number of errors encountered | +| `optouts` | Number of files opted out | + +## Agent Implementation + +The branding logic is implemented in [`branding.agent.js`](../.github/agents/branding.agent.js). + +### Key Functions + +- `applyHeader()` – Applies title, badges, description +- `applyBadges()` – Inserts workflow and metadata badges +- `applyReferences()` – Adds cross-reference section +- `applyBanner()` – Inserts banner image +- `applyFooter()` – Adds contextual footer +- `applyEmojis()` – Adds emojis to H1/H2 headings + +## Testing + +Test framework: Jest (TODO: confirm) + +Test location: `.github/agents/__tests__/` (TODO: confirm) + +## Open Questions + +1. **Test Framework:** Jest, Vitest, or other? Location: `/tests` or `/__tests__`? +2. **Helper Modules:** Names and locations for `badgeUtils.js`, `footerUtils.js`, etc.? +3. **Formal Exclusions:** Any additional pages to hard-exclude beyond CHANGELOG/CODE_OF_CONDUCT? +4. **Banner Variants:** Naming convention and initial set for product-specific banners? + +_(Mark answers in relevant GitHub Issues)_ + +## Validation Checklist + +- [ ] Schemas compile (YAML/JSON) locally +- [ ] `branding.yml` dry-runs clean; no CI loops +- [ ] `[skip ci]` respected in commits +- [ ] Agent idempotence verified on sample set +- [ ] Every item validated on `develop` before promote-to-main + +## References + +- [Badges Schema](../.github/automation/badges.schema.yml) +- [Emoji Schema](../.github/automation/emoji.schema.yml) +- [Footers Configuration](../.github/automation/footers.yml) +- [Front Matter Schema](../.github/automation/front-matter.schema.json) +- [Branding Workflow](../.github/workflows/branding.yml) +- [Branding Agent](../.github/agents/branding.agent.js) +- [Agents Documentation](../AGENTS.md) +- [Custom Instructions](../.github/custom-instructions.md) + +--- + +Made with ❀️ by the LightSpeed team.