Skip to content

Conversation

@ahouseholder
Copy link
Contributor

This PR extends the registry object definition added in #795.

Replace generic object placeholder in object with specific defined objects

Most significantly, it removes a kludge in which the SsvcObjectRegistry used generic python object as the data type for the obj type inside a version branch. Now we explicitly define obj as a Union[DecisionPoint,DecisionTable] to enforce pydantic typing all the way down.

Side-effect: updated Registry schema.json

This change has side effects on the json schema for the registry object, notably that it now includes the definitions of both objects in the schema. Because the json schemas are entirely generated, understanding the python SsvcObjectRegistry class is important to make sense of the schema.

Incidentals

  • Eliminate direct imports of the REGISTRY object in favor of a get_registry() method that returns the object. This ensures consistent runtime-binding without risk of circular imports. Also guarantees that the registry has been initiated consistently at first use.
  • Use model_post_init() hooks instead of @model_validator decorators for side effects. Specifically, we now initiate object registration from within the model_post_init() instead of having it be set up as if it were a validator.

Copilot Summary

This pull request introduces significant refactoring and enhancements to the SSVC object registry schema and the registry access pattern throughout the codebase. The main changes include a major redesign of the JSON schema for registry objects, the introduction of a get_registry() accessor to replace direct global registry usage, and updates to the registration and validation logic for decision points and decision tables.

Key changes:

Schema Redesign and Registry Model

  • Major overhaul of Ssvc_Object_Registry-2-0-0.schema.json: introduces new definitions for DecisionPoint, DecisionPointValue, and DecisionTable objects, restructures the registry to use internal types (_Key, _Namespace, _Version, _NsType), and updates required fields and validation patterns for keys, namespaces, and versions. The schema now supports more flexible and explicit modeling of decision points, tables, and their values. [1] [2] [3] [4] [5] [6]

Registry Access and Usage

  • Replaces direct usage of the global REGISTRY variable with the get_registry() function throughout the codebase, improving encapsulation and testability. This affects src/ssvc/__init__.py, src/ssvc/doctools.py, and several internal utility functions in src/ssvc/decision_tables/base.py. [1] [2] [3] [4] [5] [6] [7]

Decision Point and Decision Table Enhancements

  • Updates the registration mechanism for decision points and decision tables to use the new model_post_init hook instead of the deprecated @model_validator(mode="after"), ensuring objects are registered immediately after initialization. [1] [2]
  • Refactors type hints and imports in decision_points/base.py and decision_tables/base.py to support the new registry pattern and improve type safety. [1] [2] [3] [4]

These changes modernize the SSVC registry infrastructure, improve schema validation, and make registry usage more robust and maintainable.

- use actual object classes instead of generic `object` inside the registry
- reduce dependency on direct import of `ssvc.registry.REGISTRY` object in favor of `get_registry()` method to improve late-binding and avoid circular imports
- use `model_post_init()` for registerable objects
@ahouseholder ahouseholder marked this pull request as ready for review August 6, 2025 17:54
@ahouseholder ahouseholder requested a review from Copilot August 6, 2025 17:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the SSVC registry construction by replacing generic object placeholders with specific typed objects, introducing a get_registry() accessor function, and modernizing the registration mechanism.

  • Replaces generic object type in registry with Union[DecisionPoint, DecisionTable] for proper type enforcement
  • Introduces get_registry() function to replace direct REGISTRY imports, preventing circular import issues
  • Updates registration mechanism from @model_validator decorators to model_post_init() hooks

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/ssvc/registry/base.py Major refactor of registry classes with proper typing and function extraction
src/ssvc/registry/__init__.py Replaces global REGISTRY with get_registry() function
src/ssvc/_mixins.py Adds _Registered mixin with model_post_init() registration
src/ssvc/decision_points/base.py Updates to use _Registered mixin and removes old registration pattern
src/ssvc/decision_tables/base.py Updates to use _Registered mixin and get_registry() function
data/schema/v2/Ssvc_Object_Registry-2-0-0.schema.json Updated JSON schema with explicit type definitions
Multiple test files Updated to use get_registry() function and new registry patterns

@ahouseholder
Copy link
Contributor Author

replying to #844 (comment)

Should we consider how to also identify potentially outcome decision points? I wasn't sure when looking at the registry if these are obvious. One could walk through Decision Trees to find some of these I suppose too.

At a DecisionPoint level, there's no difference, so they all just appear in the list. From a DecisionTable perspective, one DecisionPoint in the DecisionTable.decision_points dict is special because DecisionTable.outcome points to its ID. So in the context of that DecisionTable you can tell which DecisionPoint is intended as the outcome group.

I think this is okay because it allows us to build what we used to call "compound decision points" as just extra DecisionTable objects. Look at Utility for example, which combines Automatable and Value Density:

UTILITY_1 = DecisionTable(
namespace =NameSpace.SSVC,
key='U',
version='1.0.0',
name='Utility',
description='Utility decision table for SSVC',
decision_points={dp.id: dp for dp in [Automatable,ValueDensity,Utility]},
outcome = Utility.id,
mapping = [
{
"ssvc:A:2.0.0": "N",
"ssvc:VD:1.0.0": "D",
"ssvc:U:1.0.1": "L"
},
{
"ssvc:A:2.0.0": "N",
"ssvc:VD:1.0.0": "C",
"ssvc:U:1.0.1": "E"
},
{
"ssvc:A:2.0.0": "Y",
"ssvc:VD:1.0.0": "D",
"ssvc:U:1.0.1": "E"
},
{
"ssvc:A:2.0.0": "Y",
"ssvc:VD:1.0.0": "C",
"ssvc:U:1.0.1": "S"
}
]
)

and then how just Utility shows up in the Supplier table:

SUPPLIER_1 = DecisionTable(
namespace=NameSpace.SSVC,
key="SP",
version="1.0.0",
name="Supplier Patch Development Priority",
description="Decision table for evaluating supplier patch development priority in SSVC",
decision_points={
dp.id: dp
for dp in [Exploitation, Utility, TechnicalImpact, PublicSafetyImpact, DSOI]
},
outcome=DSOI.id,
mapping=[
{
"ssvc:E:1.1.0": "N",
"ssvc:U:1.0.1": "L",
"ssvc:TI:1.0.0": "P",
"ssvc:PSI:2.0.1": "M",
"ssvc:DSOI:1.0.0": "D",
},
{
"ssvc:E:1.1.0": "N",
"ssvc:U:1.0.1": "L",
"ssvc:TI:1.0.0": "P",
"ssvc:PSI:2.0.1": "S",
"ssvc:DSOI:1.0.0": "S",
},

We previously had thought that this was just a notational convenience. Which it is, but it's useful to be able to model it this way (with two distinct DecisionTable objects) because it lets us hide additional combinatorics that would otherwise make the Supplier table 4x larger. This has even broader impact on some of the other compound decision points, where the combinatory explosion of inputs could get quite large otherwise.

So I don't think there's really much of a reason to distinguish "outcomes" from "decision points" in the DecisionPoint section of the registry, as that distinction only really shows up in use inside a DecisionTable, and the DecisionTable has a way to highlight that (via the outcome attribute)

@ahouseholder
Copy link
Contributor Author

@sei-vsarvepalli I added a lookup_latest() method to help with getting the current version of a decision point (or table) out of the registry.

I think this is as ready to go as it needs to be unless you have any blockers.

@ahouseholder ahouseholder added enhancement New feature or request tools Software Tools python Pull requests that update Python code tech/backend Back-end tools, code, infrastructure tech/data Data implementation (content of /data, data object instances, etc.) integration Related to integration of SSVC into another framework or system labels Aug 7, 2025
@ahouseholder ahouseholder self-assigned this Aug 7, 2025
@ahouseholder ahouseholder added this to the 2025-09 milestone Aug 7, 2025
Copy link
Contributor

@sei-vsarvepalli sei-vsarvepalli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All features check out!

@ahouseholder ahouseholder merged commit 39233c2 into CERTCC:main Aug 7, 2025
1 check passed
@ahouseholder ahouseholder deleted the feature/refactor_registry_constructor branch August 7, 2025 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request integration Related to integration of SSVC into another framework or system python Pull requests that update Python code tech/backend Back-end tools, code, infrastructure tech/data Data implementation (content of /data, data object instances, etc.) tools Software Tools

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants