inspections.organizationId/siteId for scope readsinspections.organizationId / inspections.siteId now exist and are backfilled, but many read paths still infer scope via inspections.assetId -> assets.Switch inspection-scope read paths to canonical inspection columns:
i."organizationId"i."siteId"Keep behavior/functionality equivalent for dashboard, ET, compare, reports, BAM, and deep links.
inspection.asset.*Inspection type update to include top-level organizationId and siteIdinspections.organizationId / inspections.siteId.inspections.organizationId and inspections.siteId are now canonical and backfilled.inspection.assetId -> assets or inspection.asset.*.inspection.organizationId, inspection.siteIdinspection.assetIdGET /v1/inspections (including compare=true candidate mode)InspectionsService.getInspectionsInspectionsRepository.findComparableInspectionsbackend/src/modules/inspections/inspections.service.tsbackend/src/modules/inspections/inspections.repository.tsbackend/src/modules/inspections/sql/find-comparable-inspections.sqlGET /v1/inspections/findingsInspectionsService.listFindingsInspectionsService.getFindingsInspectionsRepository.resolveScopedInspectionIdsbackend/src/modules/inspections/inspections.service.tsbackend/src/modules/inspections/inspections.repository.tsbackend/src/modules/inspections/sql/resolve-scoped-inspection-ids.sqlGET /v1/inspections/:id/findings/summary (legacy + scoped ET mode)InspectionsService.getFindingsSummaryInspectionsRepository.resolveScopedInspectionIdsbackend/src/modules/inspections/inspections.service.tsbackend/src/modules/inspections/inspections.repository.tsbackend/src/modules/inspections/sql/resolve-scoped-inspection-ids.sqlGET /v1/inspections/compareInspectionsService.compareInspectionsInspectionsService.validateInspectionAccessInspectionsRepository.compareInspectionSummaryAggregatesInspectionsRepository.findComparableInspectionsbackend/src/modules/inspections/inspections.service.tsbackend/src/modules/inspections/inspections.repository.tsbackend/src/modules/inspections/sql/compare-summary-aggregates.sqlbackend/src/modules/inspections/sql/find-comparable-inspections.sqlGET /v1/inspections/compare/findingsInspectionsService.compareInspectionFindingsInspectionsService.validateInspectionAccessInspectionsRepository.compareInspectionFindingsPagebackend/src/modules/inspections/inspections.service.tsbackend/src/modules/inspections/inspections.repository.tsbackend/src/modules/inspections/sql/compare-findings-page.sqlGET /v1/organizations/:orgId/dashboard/widget/:widgetIdDashboardRepository.getChartPowerLossTrendgetKpiPowerLossPct, getKpiRevenueAtRisk, getKpiFireRisk, getKpiDefectDensity, getChartAnomalyPareto, getRankSites)backend/src/modules/dashboard/sql/resolve-dashboard-inspection-scope.sqlbackend/src/modules/dashboard/sql/chart-power-loss-trend.sql/v1/inspections/:id/reports*, /v1/reports)ReportsService.validateInspectionAccessReportsService.getAllReportsReportsService.getReportsForInspectionReportsService.getReportByIdReportsService.streamReportFilebackend/src/modules/reports/reports.service.tsGET /v1/admin/bam/inspection-pollersAdminBamService.getInspectionPollersbackend/src/modules/admin-bam/admin-bam.service.tsPOST /v1/ai-workflow/detect-anomalyAiWorkflowService.detectAnomalyDetectAnomalyHandler.handlebackend/src/modules/ai-workflow/ai-workflow.service.tsbackend/src/modules/jobs/handlers/detect-anomaly.handler.tsPOST /v1/inspections/:id/import-anomaliesInspectionsService.buildImportComputeContextInspectionsService.importAnomaliesImportAnomaliesHandler.handleImportAnomaliesHandler.handleDataplaneImportbackend/src/modules/inspections/inspections.service.tsbackend/src/modules/jobs/handlers/import-anomalies.handler.tsPATCH /v1/ai-agent/threads/:threadId/context (inspection access path)assertThreadContextAccessassertInspectionAccessbackend/src/modules/ai-agent/context/context-access.validation.tsGET /v1/organizations/:orgId/assets/hierarchy (inspection-scoped rollup path)AssetService.validateInspectionInOrganizationAssetRepository.findInspectionOrgScopesiteInspectionSelectForSitesiteInspectionSelectForSiteRootsAssetRepository.getRolledUpFindingsCountsForSiteRootsbackend/src/modules/asset/asset.service.tsbackend/src/modules/asset/asset.repository.tsInspection contract in API clientfrontend/src/services/api.tsfrontend/src/pages/inspections/InspectionDetailsPage.tsxfrontend/src/pages/findings/AIFindingReviewPage.tsxfrontend/src/hooks/digital-twin-v2/useDigitalTwinInspection.tsfrontend/src/pages/digital-twin-v2/DigitalTwinPage.tsxbackend/src/modules/inspections/inspections.repository.spec.tsbackend/src/modules/inspections/inspections.service.spec.tsbackend/src/modules/inspections/inspection-compare.integration.spec.tsbackend/src/modules/inspections/inspections.scope-summary.provided-ids.integration.spec.tsbackend/src/modules/dashboard/dashboard.repository.spec.tsbackend/src/modules/dashboard/dashboard.service.spec.tsbackend/src/modules/dashboard/dashboard.response-contract.spec.tsbackend/src/modules/reports/reports.service.spec.tsbackend/src/modules/admin-bam/admin-bam.service.spec.tsbackend/src/modules/ai-workflow/ai-workflow.service.spec.tsbackend/src/modules/ai-agent/context/context-access.validation.spec.tsbackend/src/modules/asset/asset.repository.spec.tsfrontend/src/services/api.spec.tsInspectionDetailsPage.tsxAIFindingReviewPage.tsxuseDigitalTwinInspection.tsDigitalTwinPage.tsxImplementation appendix. Use the API map above as the primary checklist; keep this section for low-level execution details.
i."assetId" AS site_idassets and filtering a."organizationId" / a.idi."organizationId" = $1::text($2::text IS NULL OR i."siteId" = $2::text)site_id from i."siteId".i."assetId" for site scoping.-- __DASHBOARD_SCOPE_CTES__ and are expected to inherit behavior after resolver fix:
kpi-power-loss-pct.sqlkpi-revenue-at-risk.sqlkpi-fire-risk.sqlkpi-defect-density.sqlchart-anomaly-pareto.sqlrank-sites.sqlbackend/src/modules/dashboard/sql/kpi-power-loss-pct.sqlbackend/src/modules/dashboard/sql/kpi-revenue-at-risk.sqlbackend/src/modules/dashboard/sql/kpi-fire-risk.sqlbackend/src/modules/dashboard/sql/kpi-defect-density.sqlbackend/src/modules/dashboard/sql/chart-anomaly-pareto.sqlbackend/src/modules/dashboard/sql/rank-sites.sqlresolved_inspections / resolved_sites from shared CTE marker.horizongrid.inspections i for scope.i."assetId" site scoping logic.JOIN inspections i ON i."assetId" = ss.site_id.i."organizationId" = $1i."siteId"i."siteId".i."organizationId" / i."siteId".fi."assetId".$3/$4) where needed.fi."siteId" + categoryfi."assetId" + categoryfindInspectionOrgScopesiteInspectionSelectForSitei."assetId" = siteId with i."siteId" = siteId.siteInspectionSelectForSiteRootsi."assetId" = ANY(siteRootIds) with i."siteId" = ANY(siteRootIds).i."siteId", c.category (site-based selection).getRolledUpFindingsCountsForSiteRootssite_inspections CTE, join/filter using i."siteId" instead of i."assetId".inspection.asset.organizationId/siteId.inspection.asset.* only for asset identity/navigation concerns.validateInspectionAccess depends on inspection.asset.organizationId.inspection.organizationId (and inspection.siteId where needed).siteId = inspection.assetId with inspection.siteId.inspection.organizationId.handle(...), update inspection load + scope derivation to use inspection columns for scope:
const siteId = inspection.assetId;const organizationId = inspection.asset.organizationId;const siteId = inspection.siteId;const organizationId = inspection.organizationId;inspection.siteId (should not happen after backfill, but fail fast with clear error for safety).loadSiteAIConfig(...) is called with (organizationId, siteId) from inspection columns only.inspection.assetId usage only if explicitly needed for asset identity (not for scope).inspection.asset.organizationId / inspection.asset.siteId.inspection.organizationId / inspection.siteId.inspection.assetId argument passed to processDamageGroup(...) as-is where it represents the inspected asset identity for finding writes.inspection.siteId/organizationId => scope, inspection.assetId => finding target asset identity.assertInspectionAccess(...) inspection select to include top-level fields:
organizationId, siteId, and (only if needed) assetId.inspection.organizationId (remove dependency on inspection.asset.organizationId).inspection.siteId first:
if (siteId && inspection.siteId !== siteId) -> Forbidden.assertUserSiteAccess(...) with siteId ?? inspection.siteId.Inspection type with:
organizationId: stringsiteId?: string | nullinspection.organizationId / inspection.siteId.backend/src/modules/inspections/inspection-redis-sync.service.ts
inspection.asset.* with inspection top-level columns for scope-sensitive sync payloads.backend/src/modules/inspections/inspection-image.service.ts
image.inspection.asset.organizationId with inspection-level org/site fields where scope is intended.backend/src/modules/inspections/inspection.anomaly.service.ts
inspection.asset.* with inspection-level fields.backend/src/common/pixel-to-asset/module-derivation.cli.ts
finding.inspection.organizationId/siteId instead of nested asset scope fields.backend/src/modules/inspections/inspections.service.ts (importAnomaly path)
inspection.assetId is used as the finding asset target (CreateFindingDto.assetId), not site scope.backend/src/modules/jobs/handlers/import-anomalies.handler.ts
inspection.assetId passed to processDamageGroup is the inspected-asset identity for finding writes.backend/src/modules/jobs/handlers/database-restore.handler.ts
assetId linkage is not part of read-scope migration.inspection.organizationId / inspection.siteId for scope, access, and context resolution.inspection.assetId for inspected-asset identity and asset-linked write semantics.resolve-scoped-inspection-ids.sql latest partition key:
assetId + category if ET semantics are per inspected asset.siteId + category only if product confirms site-level latest semantics.find-comparable-inspections.sql:
assetId (inspected asset identity).resolve-scoped-inspection-ids, resolve-dashboard-inspection-scope, chart-power-loss-trend, find-comparable-inspections).asset.repository.ts scope selectors.inspections.service.ts, reports.service.ts, admin-bam.service.ts.organizationId/siteId to Inspection type.inspection-redis-sync.service.tsinspection-image.service.tsinspection.anomaly.service.tscommon/pixel-to-asset/module-derivation.cli.tspnpm formatpnpm buildpnpm lintpnpm testpnpm buildpnpm lintpnpm type-checkpnpm test:coverageassetId.assetId inspections included correctly)i."siteId", not i."assetId"
comments (0)