SPEC-015 — v1 Parity Tracking
The parity-tracking specification — methodology and category definitions.
SPEC-015 — V1 Parity Tracking
| Field | Value |
|---|---|
| Status | Draft v1 |
| Owner | kris.ganjam@gmail.com |
| Date | 2026-04-29 |
| Depends on | SPEC-001, SPEC-006 |
| Pillar | Cross-cutting |
TL;DR
v2 cutover is gated on functional parity with v1 for the surface area we choose to preserve. This spec defines an explicit parity inventory — every v1 page, route, MCP tool, driver, and behavioral capability — and tracks each through parity_target artifacts in the substrate. Cutover is blocked until all “must-have” parity targets are green. v1 is treated as read-only reference throughout the build; the substrate’s own dogfood (artifacts + signals + Senate gate) tracks parity.
1. Why an explicit parity tracker
v1 has accumulated ~95 HTML pages, ~841 routes, ~58 Forge tools, ~39 economics drivers, multiple MCP servers, and a Skills system. Without an explicit checklist, “feature parity” is a guess. Specifically we need to:
-
Decide what parity means for each v1 capability (must-have / nice-to-have / intentionally retired).
-
Track v2 implementation status per capability.
-
Catch capabilities that were load-bearing but not specified in any spec.
-
Keep cutover honest: the gate is “all must-haves green”, not “we feel ready.”
2. The parity_target artifact type
Tracking parity uses the substrate itself. Each v1 capability is a parity_target artifact.
{
"type": "parity_target",
"schema_version": 1,
"id_strategy": { "format": "parity-{slug}", "stable": true },
"lock_mode": "content_hash",
"mutability": "mutable_with_history",
"content_schema": {
"type": "object",
"required": ["title", "category", "v1_locator", "tier", "status"],
"properties": {
"title": { "type": "string" },
"category": { "enum": ["html_page", "json_route", "mcp_tool", "driver", "verb", "skill", "tool", "behavior"] },
"v1_locator": { "type": "string", "description": "URL, file:line, or symbol in v1" },
"v2_locator": { "type": "string", "description": "PR/spec reference in v2 once implemented" },
"tier": { "enum": ["must_have", "nice_to_have", "retired"] },
"status": { "enum": ["planned", "in_progress", "implemented", "verified", "skipped"] },
"notes": { "type": "string" },
"spec_refs": { "type": "array", "items": { "type": "string" } },
"verified_by": { "type": "string", "description": "test name or human reviewer ref" }
}
},
"links": {
"implements_in_v2": { "to_types": ["*"] },
"blocked_by": { "to_types": ["parity_target"] }
},
"signals": {
"vote": { "values": [-1, 1], "aggregation": "replace", "description": "Reviewer attests parity met" }
},
"lifecycle": {
"states": ["proposed", "active", "verified", "skipped"]
}
}
3. Inventory categories
3.1 HTML pages (~95)
Per SPEC-003 §10 tier list. Each tier-1/tier-2 page becomes a parity_target with category=html_page. Tier-3 sunset routes are parity_target with tier=retired (recorded so we don’t accidentally rebuild them).
3.2 JSON routes (~563 /api/*)
The polymorphic verbs in SPEC-001 cover the read/write/search/links/signals patterns. Per-type routes (/api/forge/tools/roi, /api/atlas/coverage, etc.) get one parity_target each. Most map to must_have (verb call) or retired (legacy aggregation now expressed as a query).
3.3 MCP tools
v1 MCP servers (scidex_mcp_server.py, scidex_pantheon_mcp_server.py) expose ~25 tools. Each tool is a parity_target mapped to a v2 verb or skill.
3.4 Drivers (~39 economics drivers)
Each driver in v1 economics_drivers/ becomes a parity_target mapped to either:
-
A v2 driver (still implemented, but reading from substrate verbs).
-
A
signalaggregation view (some drivers reduce to “compute X over signals”). -
retired(some drivers were experimental).
3.5 Skills + tools
v1’s personas/ (9 founding personas) and skills/ (23 tool skills) and vendor/kdense-skills/ (k-dense bundles) all need parity in v2 per SPEC-013. Each is a parity_target mapping to a v2 skill artifact.
v1’s 58 Forge tools each get a parity_target mapping to a v2 tool artifact per SPEC-002.
3.6 Behaviors
Cross-cutting behaviors (live search debounce, mermaid wiki rendering, JWT cookie auth, OAuth flow, fleet watchdog, post-merge guard) get parity_target of category=behavior. These are caught manually via inventory walk, not by counting routes.
3.7 Live agent population (SPEC-020)
v1’s vibrancy is materially driven by its 59 Pantheon personas + 14 founding agents continuously acting on the system (10K+ actions for the founding 4 alone). v2 must replicate this aliveness or new visitors will see an empty room. Per SPEC-020:
-
Each v1 persona becomes a
parity_targetofcategory=agent_persona. Mapping is to(agent row + skill-persona-<slug> artifact + agent-worker container)triple in v2. -
Wave 1 (12 personas + 3 role agents) gates the cutover from “v2 is technically running” to “v2 is alive”.
-
Wave 2/3 fill out the long tail.
-
Role agents (
scidex-janitor,scidex-curator,scidex-onboarder, etc.) have no v1 counterpart but track here astier=must_havefor v2’s housekeeping behaviors.
Seed data lives in data/v1-personas.yaml (per SPEC-020 §12.1). The parity tracker auto-generates parity_target rows from that file on each cutover-readiness check.
3.8 Corpus content (v1 PG → v2 substrate)
v1 holds ~17.5K wiki pages, 2.4K hypotheses, 29.6K papers, 710K knowledge edges, 879 debates, 3.5K knowledge gaps, and 119 contributors in PG database scidex (same cluster, accessible via SCIDEX_PG_RO_DSN replica). Per the migration recommendation (Option 1a — PG→PG ETL), each of the top content tables becomes a parity_target of category=corpus:
-
wiki_pages(17,790) → v2wiki_pageartifact type -
hypotheses(2,369) → v2hypothesisartifact type -
papers(29,566) → v2paperartifact type -
knowledge_edges(710,066) → v2 KG link rows (typedcites/extends/refutespredicates) -
analyses(479) → v2analysisartifact type -
canonical_entities(49,342) → v2 entity-typed wiki backbone
Cutover gate: every category=corpus parity_target with tier=must_have must reach status=verified before the cutover script (SPEC-006 §3) will rename scidex.ai → v1.scidex.ai and re-route scidex.ai to v2.
4. Inventory mechanism
Two complementary approaches. The LLM-agent path is primary because it understands semantics; the mechanical script is a coverage backup that catches the long tail.
4.1 Primary: LLM-agent intelligent verification
Orchestra dispatches a recurring [Parity] quest (per SPEC-016 §4.1). The quest spawns one-shot tasks of the form “Audit v1 area X for parity-relevant capabilities.”
Each one-shot:
-
Reads v1 code, templates, and routes with full LLM context (semantic understanding, not just regex matching).
-
Identifies capabilities at the right granularity — a load-bearing analytics endpoint vs. a debug aggregation, a user-facing form vs. an admin-only sub-tool.
-
Proposes
parity_targetartifacts viascidex.create:-
title,category,v1_locator(file:line or URL) -
tierproposal with reasoning innotes(why must_have / nice_to_have / retired) -
v2_locatorproposal — relevant SPEC sections, anticipated PRs -
cross-references to existing v2 work that already covers this capability
-
-
Posts a Senate review comment (
scidex.comment) for human or reviewer-agent confirmation.
Why this beats a pure script: semantic judgment about tier, intelligent grouping (collapse 8 forge endpoints into one verb-based parity_target), proactive identification of v1-isms that should retire (not just rebuild). And it dogfoods the substrate — uses the same verbs, signals, comments machinery to track its own migration.
Quest cadence: every 24h initially, dropping to weekly once the inventory stabilizes.
4.2 Backup: comprehensive coverage script
A complementary scripts/parity-coverage-check.py walks v1 mechanically:
-
Greps
@app.get/@app.post/@app.put/@app.delete/@app.api_routedecorators inapi.py. -
Lists files in
personas/,skills/,economics_drivers/,scidex_tools/,vendor/kdense-skills/. -
Lists MCP tool registrations in
scidex_mcp_server.pyandscidex_pantheon_mcp_server.py. -
Lists submodule contents.
Output: a flat list of v1 surface elements. The script then queries scidex.list(type='parity_target') and computes the diff:
-
Items in coverage list but NOT in
parity_target→ uncovered; flagged as gaps. -
Items in
parity_targetbut NOT in coverage → ambient behaviors or specs that were added by judgment (expected; not a problem).
A weekly recurring quest runs the script and creates one-shot LLM-agent tasks for any new gaps. The script is a coverage check, not a source of truth — it flags blind spots; the agent path does the intelligent work of categorization.
4.3 Curation pass
A reviewer (human or Senate-class agent) walks proposals:
-
Confirms or revises tier (
must_have/nice_to_have/retired). -
Adds notes for
retiredchoices so we don’t accidentally rebuild them. -
Cross-references the relevant v2 spec (
spec_refs). -
Approves via
scidex.signal(kind='vote', value=1)— accepted parity targets enterlifecycle.active.
4.4 Implementation tracking
As v2 PRs land, each PR includes “Closes parity-{slug}” in its description. CI scans merged PRs for this footer and updates the parity_target.status to implemented.
A human reviewer flips status=verified after manual / agent-acceptance verification (per SPEC-007 §6).
4.5 Cutover gate
The cutover script (per SPEC-006 §3) refuses to run if any parity_target with tier=must_have has status != verified. Output:
PARITY GATE: 7 must-have targets unverified.
- parity-graph-explorer (in_progress, no PR)
- parity-debate-replay-stream (planned)
- parity-jwt-google-oauth (implemented, not verified)
...
Cutover refused.
tier=nice_to_have targets don’t gate cutover; they get a warning.
5. Parity dashboard (in Prism)
A Senate-side page at /senate/parity renders a live view of the inventory:
-
Pie chart: status distribution per tier.
-
Table: filterable by category, tier, status.
-
Recent transitions: which targets just flipped state.
-
Cutover-readiness summary: “X of Y must-haves verified.”
Subscribed to parity_target.* events; updates in real-time per SPEC-001 §13.
6. Validating parity for specific kinds
6.1 HTML page parity
For each parity_target of category html_page:
-
v1 page rendered → screenshot stored.
-
v2 page rendered → screenshot stored.
-
Diff verified by reviewer (visual; doesn’t have to be pixel-perfect).
-
Behavioral checklist: forms work, links resolve, live search works, mobile-responsive.
6.2 Verb parity
For each route → verb mapping:
-
v1 route returns shape X.
-
v2 verb returns shape Y.
-
Mapping documented; consumer-side translator if shape differs.
-
Agent-acceptance test (per SPEC-007 §6) exercises the verb.
6.3 Skill / tool parity
-
Skill:
scidex.search(types=['skill'], query=...)returns the skill. -
Tool:
scidex.tool.invoke(name, args)runs successfully on a sample input. -
Outputs validate against schemas.
6.4 Driver parity
-
v2 driver runs end-to-end on sample data.
-
Output matches v1 driver’s output for the same inputs (or differs in a documented way).
-
If retired: explicit note that the function is now expressed as a substrate aggregation view.
7. Open questions
-
Granularity — should every
/api/*route be a separateparity_target, or grouped (e.g., “all/api/forge/*routes”). Recommendation: group by intent; ~150-200 targets total, not 600. -
Soft-launch — do we open v2 to a small test population before full cutover? If so, how does parity gating apply? Default: no soft-launch; full cutover when must-haves are green.
-
Visual-regression budget — how much pixel-difference is acceptable on Prism vs v1? Probably permissive (look-and-feel close enough; the goal is functional, not pixel-perfect).
-
Drift after cutover — when v2 ships a new feature, do we add
parity_targetartifacts retroactively? No —parity_targetis a v1→v2 migration concept. New features track via normal artifact lifecycle.