Appends the ## Architecture section to .convoys/unify-glass-panel-surfaces.md
ratifying the 4 open Decisions, and writes 7 implementer-brief files under
.convoys/unify-glass-panel-surfaces/.
Decisions ratified:
D1. <GlassSurface> grows a cornerLights='subtle'|'chrome'|'none' prop,
default 'subtle' (4-layer gradient-border per PR #118 recipe).
D2. Retire .card entirely; migrate all 8 consumers to .glass-panel.
D3. Mobile drawer uses subtle tier (.glass-panel-strong).
D4. CI gate scopes to JSX inline-style usage only; 3-entry allowlist
for documented chrome exceptions.
Briefs (1-7, all <100 LOC each, disjoint files):
1. Upgrade <GlassSurface> primitive (no deps, blocks 3+4)
2. Migrate auth form cards (parallel-safe)
3. Migrate floating popovers (sidebar dropdown, mobile drawer,
TopSearchBar UserMenu)
4. Migrate BulkSelectionToolbar + interior token sweep
5. Retire .card class; migrate 8 consumers
6. Migrate landing nav bar to .page-header-glass
7. forbidden-bespoke-glass-surface CI gate (runs LAST)
Boot-the-brief check: all 4-layer recipes verified against post-PR-#118
styles/globals.css; all box-shadow preservation chains verified against
current Layout.js + TopSearchBar.js; .card deletion verified clean
(single rule at L729-733); CI gate shape modeled on existing
forbidden-modal-shell-without-primitive job.
Architecture is read-only output — no application code touched.
Awaiting human gate 1 (plan approval) before implementers run.
Co-authored-by: Cursor <cursoragent@cursor.com>
123 lines
6.8 KiB
Markdown
123 lines
6.8 KiB
Markdown
---
|
|
convoy: unify-glass-panel-surfaces
|
|
brief_number: 7
|
|
depends_on: [1, 2, 3, 4, 5, 6]
|
|
files:
|
|
- .github/workflows/ci.yml
|
|
---
|
|
|
|
# Brief 7: `forbidden-bespoke-glass-surface` CI gate
|
|
|
|
## Goal (1 sentence)
|
|
|
|
Add a `forbidden-bespoke-glass-surface` job to `.github/workflows/ci.yml` that fails the build if `var(--glass-surface-(low|mid|high))` appears in JSX inline-style usage under `components/**` or `pages/**`, with a curated allowlist for the documented chrome exceptions (Layout sidebar nav-chip block, TopSearchBar `<header>` block, and the `<GlassSurface>` primitive itself).
|
|
|
|
## Files in scope (do not edit anything else)
|
|
|
|
- `.github/workflows/ci.yml` — append a new job after the existing `forbidden-modal-shell-without-primitive` job (around L200) or alongside it in the `forbidden-*` cluster. Don't reorder existing jobs.
|
|
|
|
**Out of scope:** every other file in the repo. This brief is pure CI surface; no application code changes.
|
|
|
|
## Conventions to follow
|
|
|
|
- **Model after the existing `forbidden-modal-shell-without-primitive` job** (`.github/workflows/ci.yml` L200-226). Same shape:
|
|
- Single `runs-on: ubuntu-latest` step that runs a `grep` command.
|
|
- Collects matches into a bash array.
|
|
- Iterates and emits `::error file=${f}::<reason>` for each match.
|
|
- Exits non-zero if any match remains.
|
|
- The grep pattern is bounded to `pages/ components/ -r --include='*.js'`.
|
|
|
|
- **Verbatim job shape** (drop into `ci.yml` at the bottom of the forbidden-* cluster):
|
|
```yaml
|
|
forbidden-bespoke-glass-surface:
|
|
name: No bespoke var(--glass-surface-*) inline styles
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Fail if JSX inline styles handroll glass surfaces
|
|
run: |
|
|
# unify-glass-panel-surfaces convoy (PR sequence after #118 +
|
|
# #117). Once Briefs 1-6 land, every panel-shaped surface in
|
|
# the app composes via .glass-panel / .glass-panel-strong /
|
|
# .page-header-glass / <GlassSurface>. Inline-style usage of
|
|
# var(--glass-surface-low|mid|high) under pages/ or
|
|
# components/ JSX is the regression vector this gate prevents.
|
|
#
|
|
# Allowlist (explicit, documented exceptions):
|
|
# - components/ui/GlassSurface.js # the primitive itself
|
|
# - components/Layout.js # sidebar nav-chip chrome block (L858 area)
|
|
# - components/ui/TopSearchBar.js # <header> chrome block
|
|
# These three files intentionally compose handrolled chrome
|
|
# surfaces ratified in PR #116 (gradient borders) + the
|
|
# unify-glass-panel-surfaces architect decision D4. If you
|
|
# need to add a fourth allowlist entry, that's a design-system
|
|
# decision — open a new convoy.
|
|
ALLOWLIST=(
|
|
"components/ui/GlassSurface.js"
|
|
"components/Layout.js"
|
|
"components/ui/TopSearchBar.js"
|
|
)
|
|
FOUND=()
|
|
while IFS= read -r file; do
|
|
skip=false
|
|
for allowed in "${ALLOWLIST[@]}"; do
|
|
if [ "$file" = "$allowed" ]; then
|
|
skip=true
|
|
break
|
|
fi
|
|
done
|
|
if [ "$skip" = true ]; then
|
|
continue
|
|
fi
|
|
FOUND+=("$file")
|
|
done < <(grep -lE "var\\(--glass-surface-(low|mid|high)\\)" \
|
|
pages components -r --include='*.js' 2>/dev/null \
|
|
| sort -u || true)
|
|
if [ ${#FOUND[@]} -gt 0 ]; then
|
|
echo "::error::Bespoke var(--glass-surface-*) inline styles detected outside the documented allowlist."
|
|
echo "Use .glass-panel / .glass-panel-strong / .page-header-glass or compose <GlassSurface> from components/ui/ instead."
|
|
for f in "${FOUND[@]}"; do
|
|
echo "::error file=${f}::Replace inline var(--glass-surface-*) with the appropriate class or primitive."
|
|
done
|
|
exit 1
|
|
fi
|
|
echo "OK: no bespoke glass-surface inline styles outside the allowlist."
|
|
```
|
|
|
|
- **Allowlist hygiene:** the 3 allowlist entries are exact paths. If
|
|
the relevant chrome block in `components/Layout.js` is later
|
|
refactored into its own sub-component (e.g. `<SidebarNavChip>`),
|
|
the new file path replaces `components/Layout.js` in the
|
|
allowlist — that's the kind of edit a future PR would carry.
|
|
|
|
- **Pre-merge negative test:** before opening this PR, run the
|
|
allowlist locally — verify that adding a scratch `style={{
|
|
background: 'var(--glass-surface-low)' }}` to a non-allowlisted
|
|
file (e.g. `pages/profile.js`) and re-running the grep produces a
|
|
match. Then revert the scratch change. Document the negative test
|
|
in the PR description (don't commit the scratch change).
|
|
|
|
- **CI ordering:** Brief 7 depends on Briefs 1-6 ALL landing first.
|
|
If this job is added before any of the prior briefs ships, the
|
|
build will fail on the in-flight migrations (every site this
|
|
convoy is migrating IS currently a bespoke `var(--glass-surface-*)`
|
|
inline-style usage). Conductor MUST hold dispatch until 1-6 are
|
|
all on `main`.
|
|
|
|
- **Boundaries:** do NOT touch any other CI job. Do NOT edit any
|
|
application file. Do NOT update AGENTS.md or other docs in this
|
|
PR (a separate AGENTS.md update can land alongside Brief 1 if
|
|
the architect wants — Brief 7 is pure CI).
|
|
|
|
## Acceptance criteria
|
|
|
|
- [ ] `.github/workflows/ci.yml` contains a new `forbidden-bespoke-glass-surface` job matching the verbatim shape above (job name, `runs-on`, checkout step, grep-and-allowlist shell block).
|
|
- [ ] The allowlist has exactly 3 entries: `components/ui/GlassSurface.js`, `components/Layout.js`, `components/ui/TopSearchBar.js`.
|
|
- [ ] On a clean post-Briefs-1-through-6 `main`, the new CI job is GREEN — no false positives. (Verify by running the grep locally before opening the PR.)
|
|
- [ ] Negative test: adding a scratch `style={{ background: 'var(--glass-surface-low)' }}` to e.g. `pages/profile.js` and re-running the same grep command produces a match. (Documented in PR description; not committed.)
|
|
- [ ] No other CI jobs are reordered, renamed, or modified.
|
|
- [ ] PR description links back to this brief and to the convoy file (`.convoys/unify-glass-panel-surfaces.md`).
|
|
|
|
## Rationale (≤3 sentences)
|
|
|
|
The convoy's success metric is unification, but unification without a regression gate is half a fix — a future PR can re-introduce a bespoke `var(--glass-surface-*)` inline style and undo the work. Modeling the gate on the existing `forbidden-modal-shell-without-primitive` job keeps it consistent with the repo's CI vocabulary and makes the failure message actionable. The 3-entry allowlist is small and intentional; growing it requires an explicit design-system decision, which is the right friction for a convention-enforcing gate.
|