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
22 changes: 12 additions & 10 deletions .fleet/design-review.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "fleet.design-review.v1",
"version": 1,
"project": "saas-maker",
"target": "shared workshop frame and /projects alignment",
"target": "homepage and complete-directory content boundary",
"mode": "preserve",
"register": "product",
"context": {
Expand All @@ -14,33 +14,33 @@
"probes": [],
"selected": "existing-design",
"approval": "not-required",
"before": "artifacts/design/live-projects-1440.png"
"before": "artifacts/design/home-boundary-before-1440.png"
},
"evidence": {
"screenshots": [
{
"width": 390,
"path": "artifacts/design/after-projects-390.png"
"path": "artifacts/design/home-boundary-after-390.png"
},
{
"width": 768,
"path": "artifacts/design/after-projects-768.png"
"path": "artifacts/design/home-boundary-after-768.png"
},
{
"width": 1440,
"path": "artifacts/design/after-projects-1440.png"
"path": "artifacts/design/home-boundary-after-1440.png"
}
],
"projectCheck": {
"command": "pnpm catalog:validate-public && pnpm check:docs && pnpm test && pnpm typecheck && pnpm build:widget && pnpm check:shared-packages && pnpm build:showcase && pnpm build:docs && pnpm build:cockpit && git diff --check",
"command": "pnpm catalog:validate-public && FLEET_PUBLIC_PRODUCTS_PATH=/Users/sarthak/Desktop/fleet/site-health/apps/backend/config/projects.json pnpm catalog:check-public && pnpm check:docs && pnpm lint && pnpm test && pnpm typecheck && pnpm build:widget && pnpm check:shared-packages && pnpm build:showcase && pnpm build:docs && pnpm build:cockpit && git diff --check",
"status": "pass"
},
"critique": {
"score": 36,
"score": 38,
"maximum": 40
},
"audit": {
"score": 18,
"score": 19,
"maximum": 20
},
"unresolved": {
Expand All @@ -49,11 +49,13 @@
},
"detector": {
"posture": "advisory",
"findings": []
"findings": [
"single-font warning in Layout.astro is an intentional Schibsted Grotesk system documented in DESIGN.md"
]
}
},
"ownerFeedback": {
"decision": "delegated",
"note": "Owner explicitly delegated correcting the public frame mismatch and unnecessary gutter while preserving the established landing-page direction."
"note": "Owner explicitly directed separating the curated homepage from the complete /projects register while preserving the established landing-page direction."
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
target: homepage and /projects content boundary
total_score: 27
max_score: 28
na_heuristics: 7,9,10
p0_count: 0
p1_count: 0
timestamp: 2026-08-22T13-35-27Z
slug: apps-showcase-src-pages-index-astro
---
Method: dual-agent (A: directory_design_review · B: directory_detector_review)

## Design Health Score

| # | Heuristic | Score | Key issue |
|---|---|---:|---|
| 1 | Visibility of system status | 4 | Current-state and verification language is visible. |
| 2 | Match system / real world | 4 | Final directory copy is direct and visitor-facing. |
| 3 | User control and freedom | 4 | Standard links and navigation provide direct exits. |
| 4 | Consistency and standards | 4 | The workshop system remains coherent. |
| 5 | Error prevention | 4 | No risky interactions; external destinations are marked. |
| 6 | Recognition rather than recall | 4 | Four focused products and one complete-directory gateway are explicit. |
| 7 | Flexibility and efficiency | n/a | No repeated workflow requires accelerators. |
| 8 | Aesthetic and minimalist design | 3 | Token World remains intentionally expansive on mobile. |
| 9 | Error recovery | n/a | No user-input or error workflow exists on this surface. |
| 10 | Help and documentation | n/a | The landing experience needs no instructional layer. |
| **Total** | | **27/28** | **Excellent** |

## Design Specificity Verdict

The glazier workshop, steel mullions, saturated product panes, and Token World make the homepage recognizably SaaS Maker. The final information architecture is now authored around two distinct jobs: the homepage curates four products and studio evidence; `/projects` owns complete enumeration.

The deterministic scan returned one `single-font` warning in `Layout.astro`. This is a false positive because `DESIGN.md` explicitly defines Schibsted Grotesk as the single-family system and the page establishes hierarchy through scale, weight, proportion, and color.

## Overall Impression

The page is materially clearer and shorter. It presents a studio, not a second directory, and leaves one unambiguous route to all 58 projects.

## What's Working

- Exactly four focused product links appear on the homepage at all reviewed widths.
- One directory gateway replaces the repeated maintained and past catalogs and the self-embedded project strip.
- Latest learning is now a sibling section with its own heading, not semantically part of the directory invitation.

## Priority Issues

- **P3 — Token World remains long on narrow screens.** It is the page's intentional spectacle, but it delays later sections. If the mobile homepage needs further shortening, compact its stage and metrics without removing the proof surface.
- **P3 — Overflow clipping can conceal future regressions.** Current browser measurements show no overflow at 390, 768, or 1440, but keep the viewport assertion in future visual checks.

## Persona Red Flags

- **Jordan:** No current red flag; the four products and one directory gateway now describe distinct choices.
- **Riley:** No duplicate directory promise remains; the 58-row destination matches the gateway claim.
- **Casey:** Token World is still a long mobile interlude before learning and package content.

## Minor Observations

- The homepage title now foregrounds the product studio rather than the directory.
- The agent-readable homepage uses one H1, H2 section headings, and H3 entry headings.

## Questions to Consider

- Should Token World remain the homepage's deliberate spectacle, or become a shorter mobile proof panel in a later pass?
8 changes: 8 additions & 0 deletions PROJECT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ Cockpit, CodeVetter, and App Health remain independent repositories.

## Timeline

- **2026-08-22 — Homepage and directory roles separated:** Reduced the public
homepage to four products in focus, one complete-directory gateway, the
current learning entry, and SaaS Maker's package surfaces. Removed the
repeated maintained/past catalogs and SaaS Maker's self-embedded portfolio
strip; `/projects` remains the sole complete 58-identity register, with the
same boundary reflected in the agent-readable homepage.
- **2026-08-22 — Complete directory distilled:** Replaced 58 repeated,
full-height anatomy panels with compact specimen rows. Purpose, form,
platforms, prominent tools, and destination remain visible; public links,
Expand Down Expand Up @@ -103,6 +109,8 @@ Cockpit, CodeVetter, and App Health remain independent repositories.

## Features (shipped)

- Curated public homepage with four products in focus and a single gateway to
the complete register, without duplicating the 58-project directory.
- Deterministic 58-identity public projection consumed without private Fleet
runtime access, with deny-by-default field validation.
- Searchable `/projects` register grouped by current, supporting/parked, and
Expand Down
5 changes: 3 additions & 2 deletions apps/showcase/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ long prose near 70 characters.
Compose public product discovery as a wall elevation. Matte-black mullions
separate unevenly proportioned panes; each pane carries one statement or
destination. The homepage first viewport pairs one large studio pane with four
featured product panes. Supporting products become disciplined specimen rows
rather than cards.
featured product panes. It does not repeat the catalog: one directory gateway
points to `/projects`, where supporting and past work become disciplined
specimen rows rather than cards.

Product detail pages inherit the same workshop world: one clear identity pane
shares a steel frame with a saturated canonical-product action, while public
Expand Down
1 change: 1 addition & 0 deletions apps/showcase/PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ local machine state, schedules, private repositories, or operational telemetry.
- Lead with products, not infrastructure.
- Show evidence through canonical links rather than invented metrics.
- Keep the spotlight concise while preserving the complete public catalog.
- Curate on the homepage; enumerate only in the complete `/projects` register.
- Let one visual identity carry the directory without merging product brands.
- Make the same public facts legible to people, search engines, and AI agents.

Expand Down
76 changes: 17 additions & 59 deletions apps/showcase/src/components/Fleet.astro
Original file line number Diff line number Diff line change
@@ -1,43 +1,23 @@
---
import { ACTIVE_GROUPS, PAST_PROJECTS, PROJECT_COUNT } from '../data/projects';
import { LEARNINGS } from '../data/learnings';
import { DIRECTORY_COUNT } from '../data/directory';
import { LEARNINGS } from '../data/learnings';

const latestLearning = LEARNINGS[0];
---
<section class="studio-index" id="products" aria-labelledby="catalog-title">
<header class="studio-index-head">
<p class="studio-kicker">The maintained catalog</p>
<h2 id="catalog-title">{PROJECT_COUNT} products.<br />One accountable maker.</h2>
<div class="studio-index-context">
<p>
Each product keeps its own domain, roadmap, changelog, and release
boundary. Nothing here is a concept render.
</p>
<a href="/projects">Browse the complete {DIRECTORY_COUNT}-project directory <span aria-hidden="true">→</span></a>
</div>
</header>

<div class="catalog-wall">
<div class="catalog-groups">
{ACTIVE_GROUPS.map((group) => (
<section class="catalog-group" aria-labelledby={`group-${group.id}`}>
<h3 id={`group-${group.id}`}>{group.label}</h3>
<ul>
{group.products.map((project) => (
<li>
<a href={project.href} class="catalog-row">
<strong>{project.name}</strong>
<span>{project.desc}</span>
<span aria-hidden="true">→</span>
</a>
</li>
))}
</ul>
</section>
))}
</div>
</div>
<div class="studio-index">
<section id="directory" aria-labelledby="directory-title">
<header class="studio-index-head">
<p class="studio-kicker">The complete directory</p>
<h2 id="directory-title">{DIRECTORY_COUNT} projects.<br />{' '}One directory.</h2>
<div class="studio-index-context">
<p>
Browse current, supporting, parked, and past work in one place, with
public links, deployment details, prominent tools, and retained Git dates.
</p>
<a href="/projects">Open the complete directory <span aria-hidden="true">→</span></a>
</div>
</header>
</section>

<article class="learning-ledger" id="latest-learning" aria-labelledby="learning-title">
<header class="learning-ledger-meta">
Expand All @@ -48,34 +28,12 @@ const latestLearning = LEARNINGS[0];
</div>
</header>
<div class="learning-ledger-story">
<h3 id="learning-title">{latestLearning.title}</h3>
<h2 id="learning-title">{latestLearning.title}</h2>
<p>{latestLearning.description}</p>
<a href={latestLearning.href}>
Read the learning
<span aria-hidden="true">→</span>
</a>
</div>
</article>

<section class="archive-wall" id="past-projects" aria-labelledby="archive-title">
<header>
<p class="studio-kicker">Public repository history</p>
<h2 id="archive-title">Past work, kept legible.</h2>
<p>
{PAST_PROJECTS.length} public repositories preserved for source and
attribution, without pretending they are still maintained.
</p>
</header>
<ul>
{PAST_PROJECTS.map((project) => (
<li>
<a href={project.repositoryUrl} target="_blank" rel="noopener noreferrer">
<strong>{project.name}</strong>
<span>{project.desc}</span>
<span aria-hidden="true">↗</span>
</a>
</li>
))}
</ul>
</section>
</section>
</div>
10 changes: 3 additions & 7 deletions apps/showcase/src/components/Hero.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
import { CORE, PROJECT_COUNT } from '../data/projects';
import { CORE } from '../data/projects';

const PANE_TONES = ['cobalt', 'seeded', 'amber', 'oxblood'];
---
Expand All @@ -11,13 +11,9 @@ const PANE_TONES = ['cobalt', 'seeded', 'amber', 'oxblood'];
<p class="studio-kicker">Software as a specialized service</p>
<h1 id="studio-title">A living index of things I build.</h1>
<p class="studio-intro">
{PROJECT_COUNT} maintained products, with their real domains, roadmaps,
release history, and someone accountable for every one.
Four products in focus, with the complete portfolio accounted for in one
separate register.
</p>
<a href="#products" class="studio-action">
Explore the work
<span aria-hidden="true">↓</span>
</a>
<p class="studio-status">
<span aria-hidden="true"></span>
Built in public. Kept current.
Expand Down
55 changes: 8 additions & 47 deletions apps/showcase/src/data/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,6 @@ export interface CoreProject {
href: string;
}

export interface ActiveProject {
name: string;
desc: string;
href: string;
}

export interface ActiveProjectGroup {
id: string;
label: string;
products: ActiveProject[];
}

export interface PastProject {
name: string;
desc: string;
repositoryUrl: string;
}

interface PublicProduct {
id: string;
name: string;
Expand Down Expand Up @@ -52,35 +34,14 @@ function toCore(product: PublicProduct): CoreProject {
};
}

const spotlight = spotlightOrder
.map((id) => products.find((product) => product.id === id))
.filter((product): product is PublicProduct => Boolean(product?.spotlight));
const spotlight = spotlightOrder.map((id) => {
const product = products.find((candidate) => candidate.id === id);

export const CORE = spotlight.map(toCore);
if (!product?.spotlight) {
throw new Error(`Homepage spotlight is missing the public catalog entry for ${id}`);
}

const GROUP_LABELS: Record<string, string> = {
product: 'Products & research',
helper: 'Maker tools',
personal: 'Personal utilities',
};
return product;
});

export const ACTIVE_GROUPS: ActiveProjectGroup[] = ['product', 'helper', 'personal']
.map((category) => ({
id: category,
label: GROUP_LABELS[category],
products: products
.filter((product) => !product.spotlight && product.category === category)
.sort((left, right) => left.name.localeCompare(right.name))
.map((product) => ({
name: product.name,
desc: product.description,
href: `/p/${product.id}`,
})),
}))
.filter((group) => group.products.length > 0);
export const PROJECT_COUNT = products.length;
export const PAST_PROJECTS: PastProject[] = publicCatalog.pastProjects.map((project) => ({
name: project.name,
desc: project.description,
repositoryUrl: project.repositoryUrl,
}));
export const CORE = spotlight.map(toCore);
Loading