Skip to content

fix(analysis): require a sealed snapshot for Architecture, remove fabricated metrics - #225

Merged
parthrohit22 merged 2 commits into
Second-Origin:devfrom
parthrohit22:chore/217-architecture-truth
Jul 27, 2026
Merged

fix(analysis): require a sealed snapshot for Architecture, remove fabricated metrics#225
parthrohit22 merged 2 commits into
Second-Origin:devfrom
parthrohit22:chore/217-architecture-truth

Conversation

@parthrohit22

Copy link
Copy Markdown
Collaborator

Summary

Architecture was the one flagship product surface still bypassing the sealed
ri.v1 snapshot contract: it fell back to record.file_tree (unsealed
repository metadata) when no snapshot existed, and it invented
estimated_lines/estimated_complexity from file counts
(max(len(module.files) * 80, 20), a file-count-derived guess) rather than a
real measurement. This PR removes both violations so Architecture behaves
like every other consumer (Dependencies, Review, Insights): a sealed snapshot
or an explicit missing_snapshot/not_computed state, never a fabricated
number.

Linked issue

Closes #217

Roadmap alignment

  • Roadmap §23 workstream this advances: W0 / Truth contract — "Every
    shipped claim maps to tested fact/evidence contracts."
  • §28 market-fit criterion this moves toward: Trusted output — "users
    verify and cite PARTHA evidence; reported false certainty is rare, measured,
    and corrected."
  • Accepted evidence it is real: the 2026-07-27 audit (§7, CQ-H1) identified
    the exact violating lines (architecture.py:133,185, current dev); this
    PR's regression tests (test_architecture_relationships.py,
    test_legacy_read_model_guard.py) fail on the old behaviour and pass on the
    new one, verified locally (see Testing performed).

What changed

Backend

  • app/analysis/architecture.py: build_architecture now requires a sealed
    snapshot via SnapshotQueryService.require_sealed_snapshot_for_current_revision
    before building anything (raises NotFoundError → 404, same contract as
    Dependencies/Review/Insights). Removed the record.file_tree fallback and
    the now-dead _persisted_file_paths helper. estimated_complexity/
    estimated_lines are now always "not_computed" for every node
    (module and dependency) instead of a file-count-derived guess.
  • app/schemas/architecture.py: ArchNode.estimated_complexity/
    estimated_lines widened to accept an explicit "not_computed" state
    alongside real values, so a future real heuristic can populate them without
    another contract break.
  • app/reports/builders.py: dropped the "Size Class" export row that only
    ever printed the fabricated complexity value.
  • app/api/routes/analysis.py: updated the /architecture OpenAPI 200
    example to reflect that 200 now always means a sealed snapshot backs the
    response (no more "no sealed snapshot" diagnostic in a 200 example).
  • tests/test_legacy_read_model_guard.py: extended with a new AST guard,
    test_analytical_consumers_never_read_repository_file_tree, that fails if
    any file under app/{analysis,graph,review,insights,ai,reports} reads
    .file_tree — the contract test the issue's acceptance criteria asked for.

Frontend

  • useArchitecture.ts / ArchitecturePage.tsx: added the same noSnapshot
    empty state Dependencies already has ("No sealed snapshot yet — Run
    analysis") instead of the page silently reaching !architecture.model with
    no useful next action.
  • ArchitectureNode.tsx, NodeInspector.tsx: removed the complexity badge
    and the Complexity/Est. Lines inspector stats — they only ever displayed the
    fabricated value. The accessible name no longer claims a complexity that
    wasn't measured.
  • layout.ts, HeatmapControls.tsx, shared/types/architecture.ts: removed
    the complexity and size heatmap modes (zero real signal existed for
    either); critical is recomputed from dependents-count and file-count only,
    both real graph facts.

Acceptance criteria completed

  • Architecture returns an explicit missing-snapshot state (404, matching
    Dependencies/Review/Insights) when no sealed ri.v1 snapshot exists — no
    200 fallback graph.
  • No Architecture payload can contain a numerical estimated_lines/
    estimated_complexity value not backed by a snapshot fact — every node now
    reports "not_computed".
  • A new contract test fails if any analytical consumer reads
    RepositoryRecord.file_tree or repo_metadata for analytical output.
  • productSurfaces.test.tsx/accessibility.test.tsx still pass (verified
    — neither asserts on Architecture-specific fields, unaffected).
  • npm run build:frontend and backend pytest pass; test_openapi_contract.py
    passes unchanged (no field was removed, only widened).

Testing performed

cd apps/backend && PYTHONPATH=<worktree>/apps/backend .venv/bin/python -m pytest
  → 731 passed, 3 skipped (PostgreSQL/Redis-gated, expected locally), exit 0

npm --prefix apps/frontend run test -- --run
  → Test Files  33 passed (33) / Tests  177 passed (177)

npm run lint:frontend
  → clean, no errors

npm run build:frontend
  → tsc -b && vite build succeeded, no type errors

git diff --check
  → clean, no whitespace errors

Screenshots

Not applicable (empty-state text/badge changes only; no new visual surface —
the "No sealed snapshot yet" state reuses the existing EmptyState component
already visible on the Dependencies page).

Security and data considerations

None. No auth/owner-scoping change (the 404 path reuses the same owner-scoped
require_sealed_snapshot_for_current_revision every other consumer already
uses). No new data is stored or logged. No migration.

Dependencies and blocked work

None.

Scope changes or remaining work

None — all acceptance criteria from #217 are complete. Follow-up note: a real
size/complexity heuristic (with a named formula and truth class) could
replace not_computed in a future issue; this PR deliberately does not add
one (explicit non-goal in the issue).

Contributor checklist

  • This PR targets dev
  • I claimed the issue and had it assigned or acknowledged before starting substantial work
  • The branch was created from an up-to-date upstream/dev
  • The branch is rebased on the latest upstream/dev
  • This PR addresses one clearly scoped issue
  • This PR advances a §23 workstream toward a §28 market-fit criterion (Roadmap alignment filled)
  • Every acceptance criterion I claim as complete is actually complete
  • Relevant tests pass
  • Documentation is updated for any user-visible change (none required — no public doc described the old fallback behaviour as current)
  • No secrets, credentials, local env files, or generated artifacts are included
  • No unrelated files were changed
  • Closing syntax (Closes) is used only because the issue is fully resolved
  • Dependencies and follow-up work are linked

…ricated metrics

Architecture fell back to record.file_tree and invented estimated_lines/
estimated_complexity from file counts when no sealed ri.v1 snapshot existed.
It now 404s the same way Dependencies/Review/Insights already do, and every
node reports an explicit not_computed size/complexity state instead of a
guessed value. Extends the legacy-read-model guard to fail if any analytical
consumer reads RepositoryRecord.file_tree.
…mplexity UI

Adds the same noSnapshot empty state Dependencies already has to the
Architecture page instead of silently rendering an empty graph. Removes the
node complexity badge, the Complexity/Est. Lines inspector stats, and the
Complexity/Size heatmap modes, since no producer measures any of them today;
'critical' and 'usage' heatmap modes are recomputed from real graph facts only.
@parthrohit22
parthrohit22 marked this pull request as ready for review July 27, 2026 18:48
@parthrohit22
parthrohit22 merged commit a5dec48 into Second-Origin:dev Jul 27, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore: remove pre-snapshot Architecture fallback and fabricated line/complexity metrics (CQ-H1)

1 participant