Meridian: the harness, inhabited
The Repo page shows you the structure. This shows you the structure lived in — a complete ProductHarness repository for a fictitious product team, with every steering document filled in and one feature carried end-to-end through the Double Loop.
A blank template tells you the shape. It doesn't tell you the job.
Not an engineer? This page gets technical on purpose — the detail is the proof it's real. For the same example in plain language, with no file trees, read In Plain Terms →
The ProductHarness template gives you the right folders and a prompt in each file. That's the correct starting point — but on its own it can feel like an empty house. You can see where the kitchen goes; you can't see anyone cooking. The hard part isn't the directory layout. It's knowing what a real steering standard says, what an acceptance criterion authored from a validated prototype actually looks like, and how a single product decision travels from a support ticket to a production component without a translation tax.
So this is the same repository, inhabited. A fictitious company — Meridian — has filled in its standards with real engineering decisions, run one feature through discovery and delivery, and grown the repo across all three adoption stages. Nothing here is a placeholder. It's the artifact you'd point a new PM, designer, or engineer at and say: this is how we work.
The template answers "what goes where." The worked example answers "what does good look like." You need both — but only the second one teaches the framework.
Meridian, Inc. — and the stack the harness encodes
Meridian is a fictitious B2B SaaS company building spend & expense management for mid-market organizations. The team you're looking at is its Product group. Because ProductHarness only matters against a concrete stack, the example commits to one — and every steering standard reflects it, so generated work comes out in Meridian's conventions, not generic AI defaults.
| Layer | Choice |
|---|---|
| Frontend | React 18 + TypeScript, Vite, Tailwind, Storybook |
| Backend | Node.js + NestJS (TypeScript), REST |
| Database | PostgreSQL 15 (RDS) via Prisma; Redis (ElastiCache) |
| Infrastructure | AWS, Terraform, account-per-environment |
| Auth | Auth0 (OIDC), RBAC, org-scoped |
| CI/CD | GitLab CI/CD |
| Observability | Datadog |
| Testing | Vitest (unit/integration), Playwright (E2E) |
That stack isn't decoration. Here's a slice of what steering/standards/database.md actually encodes — the kind of decision the agent reads before it writes a single line of data code:
- Money: monetary amounts are stored as integer minor units (cents) with a
separate ISO-4217 currency column. Never floats for money.
- Migrations: Prisma Migrate, backward-compatible (expand/contract). No
destructive migration ships in the same release as the code that stops
using the column.
- Naming: tables plural snake_case; PK id (UUID v7); FK {singular}_id;
created_at / updated_at on every table.
The same structure — but every file carries a real answer
The layout matches the template exactly. What changes is that the steering documents are filled in and the docs/ folder holds a real body of work instead of a sample stub.
PRODUCTHARNESS.md ← master context — grounded in Meridian + the stack
steering/standards/ ← all seven FILLED with real decisions:
infrastructure.md ← Terraform / AWS naming, account-per-env
database.md ← integer-cents money, expand/contract migrations
authentication.md ← Auth0 RBAC, mandatory org_id scoping
api.md ← REST error envelope, cursor pagination
security.md ← SOC 2, never persist PAN, data classes
cicd.md ← GitLab gates, separate-approver prod deploy
testing.md ← 90% on changed lines, AC → test traceability
steering/templates/ ← Meridian's work-product shapes
steering/loops/ ← definition of done per Double Loop node
docs/work/<feature>/ ← ACTIVE features — a feature's whole Double Loop trail
e.g. receipt-auto-match/ — full trail in one folder
docs/decisions/ ← architecture decisions (cross-feature ADRs)
docs/_shipped/ ← ARCHIVE — shipped features move here, by period
e.g. 2025-Q3/bulk-receipt-upload/ — its Impact looped back to start auto-match
The point of the tour: open any standard and you find a decision a real team would defend — not a TODO. And the work is organized for the long run — one folder per active feature, shipped work moved to _shipped/ so the working set stays small. That is what makes the agent's output production-aligned, and the repo navigable after the fiftieth feature instead of the first.
In plain terms: every team's work lives in one place, written once — so nothing gets re-typed across tools and no decision (or the reason behind it) gets lost. The plain-language walkthrough → shows what that feels like in practice.
One feature, end to end: Receipt Auto-Match
The heart of the example. A single feature — automatically matching uploaded receipts to card transactions — is walked through the entire Double Loop, so you can read the evidence trail from the first signal to a production-shaped component. Each step below is a real file in the repo.
docs/work/receipt-auto-match/problem.md
A churn review and rising support volume converge on one truth: the bottleneck isn't uploading receipts, it's matching them. The problem is framed independent of any solution, quantified (41% monthly report abandonment, 6.2-day median time-to-submit), and tied to who it hurts.
…-solution-discovery.md
Three approaches are weighed against the binding constraint: a wrong match misroutes money, so trust wins over coverage. The team picks a weighted-confidence matcher with a tunable threshold and builds a thin prototype to test it.
docs/work/receipt-auto-match/validation.md
An offline run on anonymized data finds the threshold: at 0.92, the matcher auto-matches 87% of receipts with a 0.6% false-match rate — under the 1% trust bar. FX and split transactions match poorly and are made explicit non-goals for v1. Evidence, not intuition, sets the spec.
docs/work/receipt-auto-match/requirements.md
Seven numbered ACs authored from the validated prototype, in Given/When/Then, each testable and each tied downstream. The threshold question becomes an ADR (org-configurable, default 0.92) in the same feature folder (decision-confidence-threshold.md).
docs/work/receipt-auto-match/stories.md
The agent derives Jira stories (MER-1841…1845) from the ACs — full criteria, standards-aware technical notes, test cases, dependencies, definition of done. Authored once in the repo; published to the backlog. The Translation Tax disappears.
handoff.md + src/
A completed handoff checklist with sign-off marks the crossing into Delivery. On the production-connection branch, the prototype reappears in src/ as a production-shaped component — same shape engineering ships, tests referencing the AC numbers. The demo and the build no longer diverge.
Two artifacts from that trail, side by side — an acceptance criterion, and the production component built to satisfy it:
Given a receipt whose OCR'd amount/currency match the org currency,
and exactly one candidate transaction scores >= the org threshold,
when the matcher runs,
then the receipt is auto-matched and shown in "Matched" with a one-line
explanation (merchant, amount, date).
const canMatch = useHasScope('receipt:match'); // scope, not role
// ...
<ConfidenceBadge value={match.confidence} /> // AC1
<p>{match.explanation}</p>
<Button onClick={onConfirm} disabled={!canMatch}>Confirm</Button>
Read top to bottom, the feature is its own argument for the framework: a decision that began as a support ticket arrives in production carrying its full reasoning — and a human signed off at every crossing.
The whole adoption arc — across branches
Because moving between stages is additive and non-destructive, the example shows all three as a branch progression. You can git diff between them to see exactly what each stage introduces — nothing is removed, only added.
| Branch | Stage | What it adds |
|---|---|---|
main | Starting Out | The full design workspace — standards, templates, requirements, prototypes. Active day one. |
stage/production-connection | Production Connection | production-patterns.md, stack-context.md, and a src/ mirror so components match what ships. Closes the Demo Gap. |
stage/full-integration | Full Integration | sync-manifest.json and an illustrative sync-to-prod.sh — the pipeline that opens a merge request for engineering to own. |
Even the sync pipeline honors the first principle: it opens a merge request; it never merges to production on its own. Accountability stays with people, all the way to the last step.
Where it lives
The example is a Git repository — the same thing you'd clone and adapt. It pairs with the unopinionated template repo: start from the template when you want a blank harness to fill with your own standards; read the Meridian example when you want to see what "filled in" should look like first.
Browse the worked example on GitLab: productharness-meridian-example →
Its three adoption stages — Starting Out, Production Connection, and Full Integration — live on separate branches, so you can compare them and see exactly what each stage adds.
The reference repositories are public — browse or clone them freely. A training engagement is about installing and adapting the framework for your organization, not about access. Engagements →