For the complete documentation index, see llms.txt. This page is also available as Markdown.

Score Computation framework

Score Computation — Feature Design Document

Project: OpenG2P Registry Gen2 Feature: Score Computation Status: Design / Pre-implementation Date: 2026-04-09


1. Overview

Score Computation is an asynchronous, extensible feature that automatically computes domain-specific scores (e.g. PMT Score, FSF Score) for records in a registry, whenever a change request that touches one or more contributing attributes is approved.

The design follows the same metadata → trigger → queue → beat/worker → interface/factory → results pattern used by deduplication and functional ID generation in the existing codebase. Core provides all infrastructure; domain extensions provide only the computation logic.


2. Scope and Constraints

  • Score computation is available only for registers with register_purpose = RegisterPurposeEnum.REGISTER. Registers with purpose PROGRAM_REGISTER or TABLE are excluded.

  • Every insert or update to a domain register table is always mediated through a Change Request — there are no direct writes. Score computation therefore triggers on Change Request approval, not on CR creation.

  • The feature is fully asynchronous: the approval API returns immediately; the score is computed in the background by a Celery worker.

  • The base registry (openg2p-registry-gen2-core and openg2p-registry-gen2-celery) provides all infrastructure. The actual computation formula lives entirely in openg2p-registry-gen2-extensions.


3. Design Across Repositories

Repository
What changes

openg2p-registry-gen2-core

New models, interface, factory, controller service

openg2p-registry-gen2-celery

New beat producer, worker, Workers constant, config keys

openg2p-registry-gen2-apis

New API endpoint on staff portal

openg2p-registry-gen2-ui-widgets

New scores-display widget

openg2p-registry-gen2-extensions

Concrete compute service implementations (per score type)


4. Data Model

4.1 G2PRegisterScoreDefinition

Stores the score types configured for a register, and which attributes contribute to each score. One register may have multiple score definitions (one per score type).

Location: openg2p-registry-gen2-core/openg2p-registry-core/src/openg2p_registry_core/models/g2p_register_score_definition.py

Constraint enforced at service layer: register_purpose must be RegisterPurposeEnum.REGISTER when creating or updating a score definition.

Unique constraint: (register_id, score_type) — a register may only have one definition per score type.


4.2 G2PScoreComputeQueue

One queue item per (internal_record_id, score_type) per trigger event. Follows the same pattern as G2PFunctionalIdGenerationQueue.

Location: openg2p-registry-gen2-core/openg2p-registry-core/src/openg2p_registry_core/models/g2p_score_compute_queue.py

Upsert behaviour: When a trigger event fires for a (internal_record_id, score_type) pair that already has a PENDING queue item, the existing row is updated (change_request_id and contributing_attribute_values are refreshed) rather than inserting a duplicate. This ensures the worker always computes on the most recent approved state.


4.3 G2PRegisterScore

Stores the latest computed score per record per score type. One row per (internal_record_id, score_type), upserted on every computation.

Location: openg2p-registry-gen2-core/openg2p-registry-core/src/openg2p_registry_core/models/g2p_register_score.py

Upsert key: (internal_record_id, score_type). The score_id remains stable across recomputations; only computed_score, computed_at, and triggered_by_cr_id are updated.


Appends an immutable snapshot on every computation. Consistent with the pattern used by G2PRegisterHistory in the existing codebase.

This table is append-only. It enables answering "how has this record's PMT score changed over time and which CR caused each change?"


5. Trigger: Change Request Approval

The queue is populated inside G2PRegisterService.approve_change_request(), after the domain record has been updated and the history snapshot written. This is the only correct trigger point — the contributing attribute values in the domain table are not updated until approval.

Trigger Logic

_enqueue_score_computations logic


6. Interface and Factory

6.1 G2PScoreComputeInterface

Location: openg2p-registry-gen2-core/.../interfaces/g2p_score_compute_interface.py

6.2 G2PScoreComputeFactory

Location: openg2p-registry-gen2-core/.../interfaces/g2p_score_compute_factory.py

Mirrors G2PPayloadEnricherFactory exactly. Uses importlib to load the implementation from the extensions namespace at runtime.


7. Celery Beat Producer and Worker

7.1 Beat Producer

Location: openg2p-registry-gen2-celery/.../tasks/score_compute_beat_producer.py

Follows the exact pattern of deduplication_register_beat_producer.py.

7.2 Worker

Location: openg2p-registry-gen2-celery/.../tasks/score_compute_worker.py

The worker is stateless — it reads everything it needs from the queue item (including the attribute snapshot and the change_request_id). The worker delegates to the factory; it never contains formula logic.

7.3 Workers constants update

Add to openg2p-registry-gen2-celery/.../utils/workers.py:

7.4 Beat schedule and config

Add to openg2p-registry-celery-beat-producers/src/.../app.py beat schedule:

Add to config.py:


8. Core Service

Location: openg2p-registry-gen2-core/.../services/g2p_score_compute_service.py

Provides the _enqueue_score_computations method (called from G2PRegisterService.approve_change_request) and manages the score definition CRUD. This is a BaseService singleton.


9. Controller Service and API Endpoint

9.1 Controller Service

Location: openg2p-registry-gen2-core/.../controller_services/g2p_score_controller_service.py

9.2 Staff Portal API Controller

Location: openg2p-registry-gen2-apis/openg2p-registry-staff-portal-api/.../controllers/g2p_score_controller.py


10. Extension Implementation

Directory structure in openg2p-registry-gen2-extensions

Example PMT Score implementation

Namespace packaging (pyproject.toml)

This ensures the factory's importlib.import_module("openg2p_registry_extensions.score_compute.services") resolves correctly when the extension package is installed alongside core.


11. UI — Scores Display

11.1 New widget: scores-display

Location: openg2p-registry-gen2-ui-widgets/src/widgets/ScoresDisplayWidget.tsx

A read-only widget that fetches and displays all computed scores for the current record.

Renders as a compact table:

Score Type
Score
Computed At
Triggered by CR

PMT_SCORE

47.32

2026-04-08 14:22

CR-00123

FSF_SCORE

82.10

2026-04-08 14:22

CR-00123

Shows a spinner/pending badge when a PENDING queue item exists for the record (polled via a secondary data source or SSE).

Register the widget in src/registry/defaultWidgets.ts:

11.2 Core Section in UI Schema

Scores are surfaced as a core section — a section managed by the registry platform, not user-configurable via SectionBuilder. Distinguish it with a section-is-core: true flag in SectionConfig:

SectionBuilder should hide or lock sections with section-is-core: true to prevent accidental editing.


12. End-to-End Data Flow


13. Migration Checklist

All new tables require Alembic migrations. Add to CoreInitializer.migrate_database():


14. Summary of All New Artifacts

openg2p-registry-gen2-core

Type
File
Description

Model

models/g2p_register_score_definition.py

Score type config per register

Model

models/g2p_score_compute_queue.py

Async compute queue

Model

models/g2p_register_score.py

Latest score per record/type

Model

models/g2p_register_score_history.py

Immutable score audit trail

Interface

interfaces/g2p_score_compute_interface.py

Abstract compute_score()

Factory

interfaces/g2p_score_compute_factory.py

Dynamic loader by score_type

Service

services/g2p_score_compute_service.py

Enqueue logic, CRUD, results

Ctrl Svc

controller_services/g2p_score_controller_service.py

HTTP layer

openg2p-registry-gen2-celery

Type
File
Description

Beat

tasks/score_compute_beat_producer.py

Polls PENDING, dispatches

Worker

tasks/score_compute_worker.py

Computes, persists, retries

Constant

utils/workers.py

SCORE_COMPUTE_WORKER

Config

config.py

score_compute_beat_producer_frequency

openg2p-registry-gen2-apis

Type
File
Description

Controller

g2p_score_controller.py

5 new endpoints

openg2p-registry-gen2-ui-widgets

Type
File
Description

Widget

widgets/ScoresDisplayWidget.tsx

scores-display widget

Types

types/index.ts

ScoresDisplayConfig type

Registry

registry/defaultWidgets.ts

Auto-register scores-display

openg2p-registry-gen2-extensions

Type
File
Description

Impl

score_compute/services/g2p_score_compute_service_pmt_score.py

PMT formula

Impl

score_compute/services/g2p_score_compute_service_fsf_score.py

FSF formula

Last updated

Was this helpful?