Skip to content
Ritesh FirodiyaGet in touch

Work / Chitragupt / Wiki / Decisions

2026-07-09-recovery-and-compute-correctness

Decisioncanonicalverified 2026-07-09

DECISION.2026-07-09.RECOVERY-AND-COMPUTE-CORRECTNESS

Decision

Close two more findings from the 2026-07-09 fresh-perspective audit of production-readiness-audit-v2:

Recovery paths — add the missing "undo the accidental deny" and "navigate to the newer version" affordances so no confirmed doc sits in a dead-end state:

  • restoreOwnership callable + client button — bounces a rejected_owner_denied doc back to pending_owner_confirmation before the 7-day sweep deletes it.
  • superseded docs get a "View the newer version →" link in the fallback view.
  • needs_ocr copy tells the user the truth ("V1 doesn't OCR scans — please re-upload as a text-based PDF") and offers Delete.

Compute correctness — three real tax bugs surface:

  • CG surcharge cap wired. applySurcharge now takes the tax split into a CG portion (§111A + §112A, capped at capital_gains_cap_pct — 15 %) and a non-CG portion (slab + VDA, full band rate). Previously the full band rate applied to everything, over-taxing wealthy filers with big CG.
  • VDA + cess rates read from the AY table. §115BBH crypto rate and the 4 % health-and-education cess are no longer hardcoded in tax-computer.ts; both come from capital_gains.rules[virtual_digital_asset].ltcg_rate and cess.health_education_pct on the verified tax table.
  • Portfolio LTCG / STCG numbers become server-computed and AY-scoped. PortfolioReview gains stcg_tax_paise, ltcg_tax_paise, ltcg_exemption_paise, stcg_rate_pct, ltcg_rate_pct; the engine reads all five off the AY table. The client-side RealisedCgSection on PortfolioPage deletes its hardcoded LTCG_EXEMPTION_PAISE = 12_500_000 / LTCG_RATE = 0.125 / STCG_RATE = 0.2 and renders the server values verbatim.

Predicate consolidation — useTaxViewModel was still using a URL-string compare (ayParam !== ACTIVE_AY) for isPastAy, which labelled the future AY as "past" and let it slip past the paywall. Now imports the shared date-based predicate.

Why

The 2026-07-09 audit flagged three dead-end statuses that let paid users lose an upload with no user-visible recovery: rejected_owner_denied (mis-click), superseded (no link to newer), needs_ocr (lied about running OCR).

Same audit flagged four hardcoded compute paths that would drift from statute the moment a Finance Act changed a rate: 0.3 VDA, 0.04 cess, 12_500_000 LTCG exemption, 0.2 STCG / 0.125 LTCG rates in the client, and the missing CG surcharge cap in the computer.

Prior 2026-07-06-v1-launch-blockers fixes closed 28 blockers + 24 HIGH. These items were the follow-on cleanup that survived that round.

Impact

Backend

  • apps/functions/src/inbox/ownership.ts — new restoreOwnership callable, registered in apps/functions/src/index.ts.
  • packages/shared/src/contracts/inbox.ts — new RestoreOwnershipInputSchema / RestoreOwnershipOutputSchema; registry codegen picked it up (143 → 144 callables).
  • packages/shared/src/rules/v1/tax-computer.ts — applySurcharge signature changes to (cgTax, otherTax, totalIncome, regime); new pickVdaRate(table) helper; cess pct read from table.cess.health_education_pct.
  • apps/functions/src/portfolio/portfolio-review-engine.ts — reads the verified AY table for STCG / LTCG rates + exemption; persists the derived stcg_tax_paise, ltcg_tax_paise, stcg_rate_pct, ltcg_rate_pct, ltcg_exemption_paise fields.
  • packages/shared/src/schemas/portfolio-review.ts — five new fields added under PortfolioReviewSchema.

Frontend

  • apps/website/src/components/inbox/InboxDocFallbackView.tsx — restore button on rejected_owner_denied, "View the newer version →" link on superseded, Delete button on the remaining dead-end statuses.
  • apps/website/src/components/inbox/StatusBanner.tsx — honest needs_ocr copy; rejected_owner_denied mentions Restore.
  • apps/website/src/app/(app)/portfolio/components/PortfolioPage.tsx — the RealisedCgSection reads all CG numbers off the server review; no more client-side tax math with hardcoded rates.
  • apps/website/src/app/(app)/tax/use-tax-view-model.ts — isPastAy uses the shared date-based predicate.
  • apps/website/src/store/documents.ts — new superseded_by field on DocumentDetail.
  • apps/website/src/api/documents.ts — new restoreOwnership wrapper.

Tests

  • yarn typecheck — 7 workspaces green.
  • yarn test — 424 functions + 122 shared + 185 mobile = 731 tests pass. All existing tax-computer snapshot tests survive the surcharge refactor (rebate → surcharge decomposition is algebraically equivalent for CG-free filers, and the CG cap only kicks in above ₹50L income where the previous test fixtures don't reach).
  • yarn lint — 0 errors.
  • yarn format:check — clean.

Status

Active.

Sources

Every project of mine is written down like this.

Read the résumé