Skip to content
The PDaC product graph

Define the product once. Let every spec cite it.

Product Definition as Code keeps the agreed product definition in versioned Markdown that delivery work cites instead of restating. Each citation carries a stable identifier and a content digest of the exact text it relies on; when that text changes, one command flags every recorded citation for a person to review.

Three specs restate one refund rule. The rule is defined once, cited by stable ID and content digest, then changed — and prodshape citations verify flags every recorded citation, file and line.

Terminal recording. grep shows three specs paraphrasing one refund rule three different ways. The rule is defined once as BR-REFUND-001 in a small Markdown file. Each spec cites it by ID and content digest; verification reports three current citations. The rule changes from 30 to 14 days, and verification reports three stale citations with PRODUCT061 warnings naming each file and line.

The recording is real output, not an animation: its end card names the exact CLI version it captured, and the same story is runnable below.

  • pdac-lint is the conformance runner, versioned separately from the spec, that runs the published conformance tests against an implementation.
  • @prodshape/cli: npm version
  • v0.1 RFC, comment here: the spec is open for comment on GitHub Discussions.

Run this on any repository that has been doing Spec-Driven Development for a few weeks:

Terminal window
$ grep -rn -i "refund" openspec/specs
billing/spec.md:4: Refunds are issued for purchases made in the last 30 days.
checkout/spec.md:4: Customers can request a refund within 30 days of delivery.
support/spec.md:4: If the order is less than a month old, offer a refund.

Same rule, three specs, three wordings — and two meanings: 30 days from purchase is not 30 days from delivery. Nobody decided that fork. A coding agent paraphrased it into existence, and no human caught it, because specs are words only agents read. PDaC applies the oldest rule in engineering: don’t repeat yourself.

1. Define the rule once. One small versioned file with an ID.

---
id: BR-REFUND-001
type: business-rule
title: Refund window
status: active
---
## Rule
Refunds are accepted within 30 days of delivery.

2. Every spec cites it instead of restating it. prodshape cite emits the reference, carrying a content digest of the rule at the moment it was cited:

## Returns
Refunds follow BR-REFUND-001. {pdac:cite id="BR-REFUND-001" digest="sha256:b5c58067…"}

There is nothing left to fork: the rule has one home, and the specs get shorter.

3. The rule changes — in this sandbox, by editing the file directly. A governed repository moves the accepted definition only through a Product Change; this demo skips that on purpose to trigger drift quickly, and the catch works the same either way. One command flags every citing spec — file and line, no grep:

Terminal window
$ prodshape citations verify
stale BR-REFUND-001 openspec/specs/billing/spec.md:4
stale BR-REFUND-001 openspec/specs/checkout/spec.md:4
stale BR-REFUND-001 openspec/specs/support/spec.md:4
warning PRODUCT061 openspec/specs/billing/spec.md [BR-REFUND-001]: Citation of 'BR-REFUND-001' is stale: canonical content changed since the citation was recorded
3 citation(s): 0 current, 3 stale, 0 tampered, 0 unresolved

Put that command in CI and stale product knowledge stops being invisible. PDaC does not rewrite the specs for you: a human reviews each flagged reference and updates the citation. citations verify reports one of four states for every citation:

  • current - the cited text is unchanged.
  • stale - the cited text changed since the citation was recorded.
  • tampered - an embedded copy of the text no longer matches the original.
  • unresolved - the cited artifact or anchor cannot be found.

4. The whole script. Verified against @prodshape/cli@0.14.0 and served at pdac.dev/demo.sh: the fork, the fix, the catch. It is a non-governed sandbox — it runs in a temporary directory via npx, no global install, no git, and it is re-runnable as many times as you like.

The whole demo, copy-paste (3 minutes)
Terminal window
cd "$(mktemp -d)"
npm init -y >/dev/null 2>&1
npm install --save-dev --save-exact @prodshape/cli@0.14.0
prodshape() { npx --no-install prodshape "$@"; }
mkdir -p docs/product/model/business-rules openspec/specs/checkout openspec/specs/billing openspec/specs/support
# ACT 1: the same rule, restated by agents in three specs, three wordings
cat > openspec/specs/checkout/spec.md <<'EOF'
# Checkout
## Returns
Customers can request a refund within 30 days of delivery.
EOF
cat > openspec/specs/billing/spec.md <<'EOF'
# Billing
## Credits
Refunds are issued for purchases made in the last 30 days.
EOF
cat > openspec/specs/support/spec.md <<'EOF'
# Support playbook
## Refunds
If the order is less than a month old, offer a refund.
EOF
grep -rn -i "refund" openspec/specs
# purchase vs delivery vs "a month" — nobody decided that fork
# ACT 2: define once, cite everywhere
cat > docs/product/model/business-rules/br-refund-001.md <<'EOF'
---
id: BR-REFUND-001
type: business-rule
title: Refund window
status: active
---
## Rule
Refunds are accepted within 30 days of delivery.
## Rationale
Customers need a predictable window; finance needs a bounded liability.
## Examples
A delivery on March 1 may be refunded through March 31.
## Exceptions
None.
EOF
CITE=$(prodshape cite --id BR-REFUND-001 \
--file docs/product/model/business-rules/br-refund-001.md --form inline)
cat > openspec/specs/checkout/spec.md <<EOF
# Checkout
## Returns
Refunds follow BR-REFUND-001. $CITE
EOF
cat > openspec/specs/billing/spec.md <<EOF
# Billing
## Credits
Refund eligibility is BR-REFUND-001. $CITE
EOF
cat > openspec/specs/support/spec.md <<EOF
# Support playbook
## Refunds
Apply BR-REFUND-001 as written. $CITE
EOF
prodshape citations verify
# current x3 — every spec agrees with the rule
# ACT 3: the rule changes (sandbox shortcut: a direct edit; a governed repository uses a Product Change)
node --input-type=module -e "
import { readFileSync, writeFileSync } from 'node:fs';
const p = 'docs/product/model/business-rules/br-refund-001.md';
writeFileSync(p, readFileSync(p,'utf8').replace('30 days','14 days'));
"
prodshape citations verify
# stale x3 — every citing spec, file:line. No grep.

On Windows, run it in Git Bash or WSL; the heredocs above are POSIX-shell constructs.

Adopting for real, on a repository that already runs OpenSpec, is the 15 minute walkthrough: first rule, first citation, first caught drift, through a full Product Change.

The full rules are in the Citation Contract. The rest of this page explains the model that makes this possible.

Product Definition as Code keeps the agreed product definition in versioned Markdown that delivery work cites instead of restating.

The definition lives as small, related Markdown files — actors, journeys, use cases, business rules, domain terms, requirements — compiled into a validated product graph that humans and AI agents read alike. It changes only through an explicit Product Change, reviewed and accepted by a human. Consumer documents (SDD specs, tasks, agent prompts) cite the exact product text they rely on by stable ID and content digest, and when cited text changes, tools flag every recorded citation for review: documentation drift is detected instead of silent. Deterministic tools check structure and references, never truth; people decide what is true and what should change.

Two zones. On the left, product thinking: what the product means, holding the accepted product definition, versioned, related and reviewable. On the right, software delivery: how the product is built, holding SDD specs, AI agents, developers, backlog and code. An arrow labelled "cited by" runs from the definition to delivery, and a dashed amber arrow returns, labelled "delivery evidence reveals new product questions or changes".

PDaC-0, the one-minute map. Eight more diagrams cover the boundary, the model, change, citation and verification.

Spec-Driven Development tools own one implementation increment. PDaC owns accepted product intent: the graph that outlives every spec. Delivery owns its implementation choices within that intent, and what it learns flows back as evidence and proposed Product Changes. One-way authority, two-way learning. The citation contract at the boundary is delivery-neutral: the same citations brief an SDD framework, an AI coding agent, or a human team working from the backlog.

The specification is a v0.1 draft under public RFC, weeks old and labeled accordingly, extracted from one working reference implementation. Conformance of a second, independent implementation, and external pilots, are the next proof points, and they are release gates for v1, not assumed achievements. The known limits page names every gap we know about.

Nine diagrams

What PDaC protects, what a product definition contains, how it changes, and where it stops. One question per diagram. See all nine.

The manifesto

Four values, ten principles, the position in full. Signed by pull request. Read and sign it.

The specification

Nine normative chapters, RFC 2119 language, stable diagnostic codes. Currently v0.1, an early draft under request for comments. Start with the overview.

The reference implementation

ProductShape, the reference implementation of Product Definition as Code. It implements the citation contract and passes the published conformance tests: npm install -g @prodshape/cli@0.14.0. ProductShape on GitHub.

PDaC was introduced in Product Definition as Code for the AI-SDLC (July 2026) and extracted into an open specification.