| name | edit-product-bricks |
| description | Create or edit product bricks for a product domain: root groups, subgroups, bricks, their layered modules (ui/interfaces/worker/stateless-service/service/integration), brick-to-brick dependencies, data dependencies, and external-system dependencies in _config/product-domains/<domain>/product-bricks/product-bricks.json. Use when adding a brick, restructuring brick groups, adding/moving modules across layers, wiring module or brick dependencies, or linking bricks to data assets. Keeps brick and module IDs consistent across products, deployment, streams, teams, evidence, and data assets. |
Edit Product Bricks
Authoring skill for product-bricks/product-bricks.json — the implementation-facing
architecture and the most cross-referenced artifact in the model. Combines the
brick-architecture methodology with this repo's exact schema and the
validate→regenerate loop. Read .claude/skills/_references/domain-model.md first, and
read the existing file plus the reference domain ride-sharing-marketplace
(20 bricks across 3 levels) before editing — match its shape and depth.
The product-bricks validator is strict (see validate-domain): get the schema exactly
right or generation/validation fails.
Method (what good looks like)
- Bricks are buildable, ownable units that connect customer value → roadmap →
delivery → systems → data → teams. Not vague aspirations, not tiny tasks; they
sound like product/platform building blocks.
- Three meaningful levels: root group (durable product/platform area) → subgroup
(related workflows/systems) → brick. A mature domain has 20+ bricks.
- Every brick must be able to have an owning team and trace to a customer or
business outcome. A brick referenced by no product/stream/team is an orphan — drop
or wire it.
- Modules live under
layers by architectural responsibility, not as a flat list.
Only include a layer when it has modules.
- Keep module and brick IDs stable — dependencies refer to them by ID.
- Name external systems by role or provider category where exact vendor isn't
warranted. Don't invent dependencies to fill fields.
Exact schema
Top level
{ "metadata": { … }, "rootGroups": [ … ] }
metadata (governs rendering, filtering, and validation)
{
"title": "Product Bricks & Streams",
"description": "…",
"rendering": { … },
"brickTypes": [ { "id": "full-stack", "name": "full-stack brick", "color": "#b6d7a8ff" }, … ],
"brickStatuses":[ { "id": "invest", "name": "invest", "color": "blue" },
{ "id": "sustaining", … }, { "id": "sunset", … } ],
"modulesConfig": {
"layerTypes": [ { "id": "ui", "name": "ui", "description": "…",
"hostsModules": ["web-component","mobile-component"] }, … ],
"moduleTypes": [ { "id": "web-component", "name": "web component",
"description": "…", "color": "#dbeafe" }, … ]
}
}
brickTypes/brickStatuses/modulesConfig normally come from the shared model
_config/_shared/product-brick-model.json — OMIT them in domain files unless the
domain genuinely diverges (a local value overrides the shared one). Never use
legacy types/statuses.
modulesConfig.layerTypes must cover exactly the six layers and moduleTypes the
twelve module types below, each moduleType with a color. The validator rejects
mismatched sets or missing colors.
rootGroups → subGroups → bricks
{
"rootGroups": [
{
"name": "Rider Demand and Trip Experience",
"description": "…",
"subGroups": [ { "name": "…", "description": "…", "bricks": [ … ] } ],
"bricks": [ … ]
}
]
}
brick object
{
"id": "trip",
"name": "Trip Request and Intent Capture",
"type": "full-stack",
"status": "sustaining",
"description": "…",
"links": [
{ "group": "Evidence", "description": "…",
"links": [ { "label": "Evidence Explorer",
"link": "../../../../evidence-explorer/index.html",
"description": "…",
"embedLink": "../../../../evidence-explorer/index.html?embed=1",
"embedHeight": 400 } ] }
],
"layers": [ … ],
"brickDependencies": [ … ],
"dataDependencies": [ … ],
"externalSystemsThisBrickDependsOn": [ … ],
"externalSystemsDependingOnThisBrick": [ … ]
}
layers[] and modules[]
{
"layer": "ui",
"description": "…",
"modules": [
{
"id": "module-request-ux-surface",
"name": "Request UX Surface",
"type": "web-component",
"description": "…",
"dependencies": {
"modules": [
{ "moduleId": "module-quote-api", "type": "uses", "description": "…" }
]
}
}
]
}
Layer → allowed module types (fixed order ui → interfaces → worker → stateless-service → service → integration):
| layer | hostsModules |
|---|
ui | web-component, mobile-component |
interfaces | bff, api, backoffice-interface |
worker | message-queue, message-consumer, daemon |
stateless-service | stateless-service (orchestration only; aggregates services, owns no durable state) |
service | stateful-service, service |
integration | integration |
A dependencies.modules[] entry that targets a module in another brick adds
"targetBrickId": "<brick>" alongside moduleId. Same-brick deps omit it.
brickDependencies[] (brick → brick)
{
"targetBrickId": "trip",
"moduleId": "module-trip-request-api",
"sourceModuleId": "module-support-workflow-api",
"type": "context",
"description": "…"
}
Do NOT use the legacy interface field. If targetBrickId is set, moduleId is
required and must exist in the target brick; sourceModuleId must exist in this brick.
dataDependencies[] (brick → data asset)
{
"assetId": "trip-request-and-intent-capture",
"moduleIds": [ "module-issue-intake-and-classification", … ],
"role": "own",
"description": "…"
}
Required moduleIds when assetId is set; each must be a real module in this brick.
Do NOT use storeIds here — stores live in the data-asset catalog.
external system dependencies
"externalSystemsThisBrickDependsOn": [
{ "system": "Maps and geocoding providers", "type": "data",
"interface": "Location API", "description": "…",
"sourceModuleId": "module-request-capture-and-validation" }
],
"externalSystemsDependingOnThisBrick": [
{ "system": "Rider applications", "type": "channel-consumer",
"interface": "Trip Request API", "description": "…",
"moduleId": "module-trip-request-api" }
]
Cross-file rules to keep intact
When you add/rename a brick id, update every referencing file:
product-deployments/products.json → portfolio.products[].neededBricks[].brickId
(and brickName)
product-deployments/deployment.json → …deployedBricks[].brickId (and brickName)
product-bricks/product-stream.json → …brickDependencies[].targetBrickId and
flow steps[].dependencies[] of type:"brick"
teams/teams.json → …teams[].brickDependencies[].brickId
When you add/rename a module id, fix any brickDependencies[].moduleId/sourceModuleId
and dependencies.modules[].moduleId (in this and other bricks) that point at it, plus
dataDependencies[].moduleIds and external-system sourceModuleId/moduleId. When you
add a dataDependencies.assetId, it must exist in data-assets.json (add it with
edit-data-assets if not).
After editing
python3 .claude/skills/scripts/validate-domain-model.py <domain-id> --strict-ids
— this validator deeply checks bricks: layers/module-types, module- prefix,
duplicate brick/module IDs, resolvable module/brick/data dependencies, legacy
fields, and modulesConfig correctness. Fix every error before proceeding.
(It does NOT check products/deployment/stream/team back-references to bricks —
verify those by hand or with audit-domain-balance.)
- Regenerate: from
_wiring/product-domains/,
python3 generate-product-bricks-docs.py <domain-id> "<Domain Name>" "<Domain description>"
(also rebuilds stream/data-asset pages; name/description from run.sh
the domain's start/config.json; or use ./run-one.sh <domain-id>). Generator wipes the product-bricks docs folder — ensure that area's
worktree is clean first.
- Report bricks/modules added or changed, dependency wiring, and which referencing
files you updated.
Avoid
- A flat brick list where the model expects root groups + subgroups; duplicating one
brick across groups.
- Root-level
interfaces or internalModules on a brick (legacy — use layers).
- Module IDs not starting with
module-; module types not valid for their layer.
storeIds in dataDependencies; legacy interface field in brickDependencies.
- Architecture jargon with no customer/operating relevance; orphan bricks no
product/stream/team uses; inventing external/data references to fill fields.