{
  "product": "Parity",
  "title": "Parity (the ProveSpec workbench)",
  "summary": "The application that measures whether one implementation has what another declares — specified against itself.",
  "category": "apps",
  "version": 1,
  "source": "D:\\Dev\\Lab\\Parity",
  "commit": "83fd0aa",
  "extractionMethod": "Read Parity.AI/src (engine, data, mcp, cli) and Parity.UI/src (renderer) at 83fd0aa, one capability per exported behaviour a user or caller would name. Every entry was checked against the code before it was written; nothing was taken from documentation or from memory of what the app used to do. Deliberately excluded: the published packages (mcp-public, action) and the site generator, which are separate distributions rather than capabilities of this application; and internal helpers with no observable consequence. The AI/UI split is this catalog's own convention, kept here so layer parity can be asked of Parity itself — which engine capabilities does the workbench actually reach?",
  "entries": [
    {
      "key": "E01",
      "path": ["AI", "Model", "Comparison as products x capability tree x cells"],
      "status": "yes",
      "note": "The whole product rests on this one shape: a tree of capabilities, a set of products, and a verdict at each intersection. Everything else is a projection of it.",
      "sourceRef": "Parity.AI/src/engine/types.ts (ComparisonData, Product, Attribute, Cell)"
    },
    {
      "key": "E02",
      "path": ["AI", "Model", "Capability tree of arbitrary depth"],
      "status": "yes",
      "note": "Groups nest without limit; only leaves carry verdicts. Depth is the unresolved granularity question — two honest extractions at different depths score the same code differently.",
      "sourceRef": "Parity.AI/src/engine/matrix.ts (buildTree, isLeaf, leafAttributes)"
    },
    {
      "key": "E03",
      "path": ["AI", "Model", "Four verdicts per cell"],
      "value": "yes | partial | no | na",
      "status": "yes",
      "note": "\"partial\" is what a has-it checkbox cannot express, and it is where a port quietly diverges from its reference.",
      "sourceRef": "Parity.AI/src/data/apply.ts:24"
    },
    {
      "key": "E04",
      "path": ["AI", "Relation", "Parity as fully-has over declares"],
      "value": "parity = fullyHas(target) / has(baseline)",
      "status": "yes",
      "note": "Directional by construction. The same tree answers \"is the port done?\" and \"what does the port add?\" without a second model.",
      "sourceRef": "Parity.AI/src/engine/report.ts (buildParityReport)"
    },
    {
      "key": "E05",
      "path": ["AI", "Relation", "Not-applicable leaves the denominator on both sides"],
      "status": "yes",
      "note": "Subtle and load-bearing: an \"na\" that stayed in the denominator would penalise a target for something nobody asked of it.",
      "sourceRef": "Parity.AI/src/engine/report.ts; Parity.AI/src/engine/card.ts:65"
    },
    {
      "key": "E06",
      "path": ["AI", "Relation", "Ungraded counts as missing"],
      "status": "yes",
      "note": "Silence is not credit. Without this rule a spec gets a good score by leaving hard capabilities unanswered.",
      "sourceRef": "Parity.AI/src/engine/report.ts"
    },
    {
      "key": "E07",
      "path": ["AI", "Relation", "Target-only capabilities reported, never scored"],
      "status": "yes",
      "note": "What the target has beyond the baseline is information, not a defect. Getting this backwards once turned a 100% reading into 95%.",
      "sourceRef": "Parity.AI/src/engine/report.ts (ReportItem)"
    },
    {
      "key": "E08",
      "path": ["AI", "Relation", "Parity of a subtree, not only the whole"],
      "status": "yes",
      "note": "The differentiated question. Scoping to one branch is what lets a UI be graded against its own engine.",
      "sourceRef": "Parity.AI/src/engine/matrix.ts (subtreeAttributes); Parity.AI/src/cli/provespec.ts:116"
    },
    {
      "key": "E09",
      "path": ["AI", "Output", "Markdown report of the gap"],
      "status": "yes",
      "note": "The artifact a porting agent actually works from — not the percentage, the list.",
      "sourceRef": "Parity.AI/src/engine/report.ts (reportToMarkdown)"
    },
    {
      "key": "E10",
      "path": ["AI", "Output", "Spec card: coverage rolled up per section"],
      "status": "yes",
      "sourceRef": "Parity.AI/src/engine/card.ts (buildSpecCard, cardCoverage)"
    },
    {
      "key": "E11",
      "path": ["AI", "Output", "SVG badge coloured by percentage"],
      "status": "yes",
      "sourceRef": "Parity.AI/src/engine/badge.ts (badgeSvg, parityColor)"
    },
    {
      "key": "E12",
      "path": ["AI", "Output", "CI gate against a threshold"],
      "status": "yes",
      "note": "Turns the number into something that can fail a build, which is the only way a parity figure changes behaviour.",
      "sourceRef": "Parity.AI/src/engine/badge.ts (evaluateGate, GateResult)"
    },
    {
      "key": "E13",
      "path": ["AI", "Output", "Difference detection across products"],
      "status": "yes",
      "sourceRef": "Parity.AI/src/engine/matrix.ts (rowDiffers)"
    },
    {
      "key": "E14",
      "path": ["AI", "History", "Append-only assessment record"],
      "status": "yes",
      "note": "A parity number is only true of a commit. Overwriting the last reading is how a stale number gets presented confidently.",
      "sourceRef": "Parity.AI/src/data/db.ts:45 (assessments table)"
    },
    {
      "key": "E15",
      "path": ["AI", "Import", "Apply a JSON spec into a comparison"],
      "status": "yes",
      "note": "The way agents write to the app. Values are normalised so \"REST\" and \"rest \" do not read as a difference.",
      "sourceRef": "Parity.AI/src/data/apply.ts (applyEntries, parseImportPayload); Parity.AI/src/engine/matrix.ts (normalizeValue)"
    },
    {
      "key": "E16",
      "path": ["AI", "Storage", "Local store with no native dependency"],
      "value": "node:sqlite",
      "status": "yes",
      "note": "Chosen over better-sqlite3 specifically to avoid Electron native rebuilds on Windows.",
      "sourceRef": "Parity.AI/src/data/db.ts"
    },
    {
      "key": "E17",
      "path": ["AI", "Storage", "Carries a pre-rename database forward on first run"],
      "status": "yes",
      "note": "Copies rather than moves, and never overwrites an existing file. The catalog is the asset; a rename must not cost it.",
      "sourceRef": "Parity.AI/src/data/paths.ts (resolveDbPath, legacyDbPath)"
    },
    {
      "key": "E18",
      "path": ["AI", "Headless", "MCP server over the local store"],
      "value": "comparisons_list, comparison_get, parity_report, spec_import, parity_history",
      "status": "yes",
      "note": "How an agent works with Parity without a screen.",
      "sourceRef": "Parity.AI/src/mcp/server.ts"
    },
    {
      "key": "E19",
      "path": ["AI", "Headless", "CLI for CI"],
      "value": "check, badge, history, record",
      "status": "yes",
      "sourceRef": "Parity.AI/src/cli/provespec.ts"
    },
    {
      "key": "E20",
      "path": ["AI", "Headless", "Engine is browser-safe, storage is not"],
      "status": "yes",
      "note": "@parity/ai is pure and bundles for a browser; @parity/ai/data reaches node:sqlite and does not. This split is what makes a web shell an adapter rather than a rewrite.",
      "sourceRef": "Parity.AI/src/index.ts; Parity.AI/src/data/index.ts"
    },
    {
      "key": "U01",
      "path": ["UI", "Shell", "VS Code-style workbench"],
      "status": "yes",
      "note": "Activity bar, primary and secondary sidebars, status bar, bottom panel — from the shared Shell library, not reimplemented.",
      "sourceRef": "Parity.UI/src/renderer/src/app/initShell.ts; registerActivities.ts"
    },
    {
      "key": "U02",
      "path": ["UI", "Views", "Matrix: capability rows against product columns"],
      "status": "yes",
      "sourceRef": "Parity.UI/src/renderer/src/components/Matrix.tsx"
    },
    {
      "key": "U03",
      "path": ["UI", "Views", "Spec card view"],
      "status": "yes",
      "note": "The second reading of the same data. It does not yet render the dense square grid used as the product's signature image on the site — the two surfaces draw the same artifact differently.",
      "sourceRef": "Parity.UI/src/renderer/src/components/SpecCard.tsx"
    },
    {
      "key": "U04",
      "path": ["UI", "Views", "Differences-only filter"],
      "status": "yes",
      "note": "The most-used reading in practice: rows where the products actually disagree.",
      "sourceRef": "Parity.UI/src/renderer/src/components/Matrix.tsx:26"
    },
    {
      "key": "U05",
      "path": ["UI", "Views", "Scope the view to one branch"],
      "status": "yes",
      "sourceRef": "Parity.UI/src/renderer/src/activities/ComparisonsActivity.tsx (Clear scope)"
    },
    {
      "key": "U06",
      "path": ["UI", "Editing", "Cycle a cell's verdict by clicking it"],
      "status": "yes",
      "note": "Grading is the highest-frequency action in the app, so it costs one click and no dialog.",
      "sourceRef": "Parity.UI/src/renderer/src/components/Matrix.tsx:314 (cycleStatus)"
    },
    {
      "key": "U07",
      "path": ["UI", "Editing", "Create and delete comparisons"],
      "status": "yes",
      "sourceRef": "Parity.UI/src/renderer/src/activities/ComparisonsActivity.tsx"
    },
    {
      "key": "U08",
      "path": ["UI", "Editing", "Add, rename, delete and source a product"],
      "status": "yes",
      "note": "A product's source folder is what lets a grade cite where it came from.",
      "sourceRef": "Parity.UI/src/renderer/src/components/SourceDialog.tsx; store.ts"
    },
    {
      "key": "U09",
      "path": ["UI", "Editing", "Add, rename and delete capabilities"],
      "status": "yes",
      "sourceRef": "Parity.UI/src/renderer/src/store.ts (addAttribute, renameAttribute, deleteAttribute)"
    },
    {
      "key": "U10",
      "path": ["UI", "Editing", "Import a JSON spec from the interface"],
      "status": "yes",
      "sourceRef": "Parity.UI/src/renderer/src/store.ts (importJson)"
    },
    {
      "key": "U11",
      "path": ["UI", "Reading", "Report dialog with markdown export"],
      "status": "yes",
      "sourceRef": "Parity.UI/src/renderer/src/components/ReportDialog.tsx"
    },
    {
      "key": "U12",
      "path": ["UI", "Reading", "Assessment history panel"],
      "status": "yes",
      "note": "Reads the append-only record. Without it the app shows only the present, which is the state a stale number hides in.",
      "sourceRef": "Parity.UI/src/renderer/src/activities/ComparisonsActivity.tsx (HISTORY)"
    },
    {
      "key": "U13",
      "path": ["UI", "Reading", "Running version shown in the status bar"],
      "status": "yes",
      "note": "package.json is the single authority; the running app exposes exactly which build you are looking at.",
      "sourceRef": "Parity.UI/src/renderer/src/app/registerActivities.ts"
    },
    {
      "key": "U14",
      "path": ["UI", "Freshness", "Re-reads the store when the window regains focus"],
      "status": "yes",
      "note": "Agents write to the database from outside the app. Without this the interface shows a stale picture and never says so.",
      "sourceRef": "Parity.UI/src/renderer/src/activities/ComparisonsActivity.tsx (onFocus)"
    }
  ]
}
