@smurfy92/product-graph
v0.7.0
Published
Product graph analyzer CLI — extract product facts (user stories, domains, pages, API endpoints, migrations, i18n keys, tests) and enforce Datalog rules on them. Inspired by codegraph-toolkit but for the product layer.
Maintainers
Readme
@smurfy92/product-graph
Product graph analyzer for software projects. Extracts product facts (user stories, domains, pages, API endpoints, DB tables, i18n keys, tests) from a codebase and runs Datalog rules over them, with a ratchet baseline that blocks new violations.
Inspired by codegraph-toolkit, but for the product layer instead of the code layer.
Install
npm install --save-dev @smurfy92/product-graph @smurfy92/product-invariantsThen create a product-graph.config.json at the project root:
{
"project": "Acme",
"sourceRoot": ".",
"paths": {
"docs": "docs/product",
"appRouter": "src/app",
"migrations": "supabase/migrations",
"tests": ["src", "e2e"],
"i18n": "src/i18n"
},
"domains": [
{ "id": "auth", "name": "Auth", "status": "critical" },
{ "id": "billing", "name": "Billing", "status": "production" }
]
}Usage
# Extract product facts to .productgraph/facts/
npx product-graph analyze --config product-graph.config.json
# Run Datalog rules + ratchet baseline check (exits 1 if new violations)
npx product-graph check --config product-graph.config.json
# Lock current violations as the new baseline floor
npx product-graph baseline --config product-graph.config.json
# Generate a 3-level markdown brief
npx product-graph synopsis --config product-graph.config.jsonConcepts
| Concept | What it means |
|---|---|
| Fact | A tuple destined for a Datalog relation. Stored as TSV in .productgraph/facts/<Relation>.facts. |
| Rule | A Datalog statement (.dl) that derives Violation(...) tuples from facts. |
| Baseline | JSON file pinning current violations as the new floor. New violations break CI. |
| Synopsis | Auto-generated 3-level Markdown brief: Domain → Stories → Source files. |
User-story binding
Write user stories in docs/product/<domain>.md with frontmatter:
---
domain: auth
stories:
- id: auth.signup
text: "Visitor can create an account"
path: /auth/signup
- id: auth.delete-account
text: "User can delete account (RGPD)"
api: /api/account/delete
---The path: and api: fields are what no-orphan-story and no-untested-critical rules join on. Stories without either are reported as orphans (legitimate transverse concerns can be OrphanStoryGrandfathered'd in a local .dl file).
Architecture
extractors → facts NDJSON (TSV) → @smurfy92/datalog → Violations → ratchet baseline
↘ synopsis Markdown 3-levelSeven extractors ship by default:
markdown-frontmatter→UserStory(id, domain, text, file, line)story-routes-binding→StoryRefsPage(...),StoryRefsApi(...)nextjs-routes→Page(path, file)api-routes→ApiEndpoint(method, path, file)supabase-migrations→Table(...),RlsPolicy(...),Migration(...)test-coverage→Test(file, kind, target)i18n-keys→TranslationKey(locale, key, file)
Rules
See @smurfy92/product-invariants for the rule catalogue.
Programmatic API
import { analyze, loadConfig, buildSynopsis } from "@smurfy92/product-graph";
const { config, projectRoot } = loadConfig("./product-graph.config.json");
const result = await analyze(projectRoot, config);
console.log(result.emitted);License
MIT. See LICENSE.
