orchestrata_ultimate.factory.detectors.models

Reference › orchestrata_ultimate › factory › detectors › models

models defines the frozen data types of the C3 lexical and structural detector set (SPEC-033 row B22, ADR-0029). Tier names the three tiers by what they do: BLOCKING, ADVISORY and QUESTION. RuleMeta is static metadata for a rule (id, tier, provenance and an optional published precision). Finding is one detector hit with its line, span, matched text, message, weight, fingerprint and an optional question; Finding.new derives the fingerprint with finding_fingerprint, which hashes the rule id and the normalised context line with sha256, so a dismissal survives line moves. RATIONALE_WEIGHT (0.5) is the weight given to findings inside a rationale clause. DetectorReport holds findings, dismissed and files_scanned. It deliberately has no field such as complete or passed: under ADR-0011 no detector issues a completeness verdict, and the caller decides what to do with the list. The engine, rules, c1_bridge and decision phase modules import it.

At a glance

Source src/orchestrata_ultimate/factory/detectors/models.py
Lines of code 124
Classes 4
Functions 1
Imports (internal) orchestrata_ultimate.factory.assumptions.models
Imported by orchestrata_ultimate.factory.decision.phase, orchestrata_ultimate.factory.detectors.c1_bridge, orchestrata_ultimate.factory.detectors.engine, orchestrata_ultimate.factory.detectors.rules, scripts.factory_detectors
Tests that import it tests/factory/test_detector_engine.py, tests/factory/test_detector_models.py, tests/factory/test_detector_rules.py

Class diagram

Class diagram orchestrata_ultimate.factory.detectors.models

Dependency diagram

Dependency diagram orchestrata_ultimate.factory.detectors.models

API

Classes

Tier (enum)

Bases: StrEnum · Line 24

The three detector tiers, named by what they DO (ADR-0029).

Member Value
BLOCKING 'blocking'
ADVISORY 'advisory'
QUESTION 'question'

RuleMeta (dataclass)

Decorators: dataclass(frozen=True) · Line 37

Static metadata for one detector rule.

Attribute Type Default
rule_id str
tier Tier
provenance str
published_precision float | None

Finding (dataclass)

Decorators: dataclass(frozen=True) · Line 66

One detector hit.

Attribute Type Default
rule_id str
tier Tier
line int
span tuple[int, int]
matched_text str
message str
weight float
fingerprint str
question str | None None
Method Signature Summary
new classmethod new(*, rule_id: str, tier: Tier, line: int, span: tuple[int, int], matched_text: str, message: str, context_line: str, weight: float = 1.0, question: str | None = None) -> Finding Build a :class:Finding, deriving its fingerprint from context_line.

DetectorReport (dataclass)

Decorators: dataclass(frozen=True) · Line 114

The result of one :func:~...engine.run_detectors call.

Attribute Type Default
findings list[Finding]
dismissed list[Finding]
files_scanned list[str]

Functions

Function Signature Summary
finding_fingerprint finding_fingerprint(rule_id: str, context_line: str) -> str sha256 hex of rule_id|normalized(context_line).

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/src/orchestrata_ultimate/factory/detectors/models.py

"""Data model for the SPEC-033 C3 lexical/structural detector set (B22, ADR-0029).

Every dataclass here is frozen. Note what is deliberately **absent**:
:class:`DetectorReport` carries no field named anything like
``complete``/``ready``/``passed`` — ADR-0011's boundary is that no detector,
in any tier, ever issues a completeness verdict. A finding is an indicator;
callers (the CLI's ``--tier1-gate``, or a human reviewer) decide what to do
with the list.
"""

from __future__ import annotations

import hashlib
from dataclasses import dataclass
from enum import StrEnum

from orchestrata_ultimate.factory.assumptions.models import normalize

# Global rule 3 (ADR-0029): findings inside a rationale clause ("so that...")
# are ~2x denser and empirically less relevant -- down-weight, never suppress.
RATIONALE_WEIGHT = 0.5


class Tier(StrEnum):
    """The three detector tiers, named by what they DO (ADR-0029)."""

    #: May fail a gate (``tier1_gate``). Every Tier-1 rule measures >=0.70
    #: published precision.
    BLOCKING = "blocking"
    #: Reported, never blocking.
    ADVISORY = "advisory"
    #: Produces questions for the ADR-0024 C1 cascade. Never a verdict.
    QUESTION = "question"


@dataclass(frozen=True)
class RuleMeta:
    """Static metadata for one detector rule.

    ``published_precision`` is ``None`` where no measurement exists in the
    literature this detector set is derived from (Femmer et al. JSS 2017 /
    ISO 29148 §5.2.7 / Berry, Kamsties & Krieger) -- never invent one.
    """

    rule_id: str
    tier: Tier
    provenance: str
    published_precision: float | None


def finding_fingerprint(rule_id: str, context_line: str) -> str:
    """sha256 hex of ``rule_id|normalized(context_line)``.

    Shares :func:`orchestrata_ultimate.factory.assumptions.models.normalize`
    with the assumption ledger so the same "what counts as the same text"
    rule applies everywhere in the factory. This is the identity a
    :class:`~orchestrata_ultimate.factory.detectors.engine.Blacklist` entry
    dismisses -- re-running the detectors over unchanged text always
    reproduces the same fingerprint.
    """
    normalized = normalize(context_line)
    return hashlib.sha256(f"{rule_id}|{normalized}".encode()).hexdigest()


@dataclass(frozen=True)
class Finding:
    """One detector hit. An indicator, never a verdict.

    ``weight`` is 1.0 normally, :data:`RATIONALE_WEIGHT` inside a rationale
    clause (global rule 3). ``question`` is populated for Tier-3 findings
    only (they feed :mod:`orchestrata_ultimate.factory.detectors.c1_bridge`);
    it is ``None`` for Tier 1/2.
    """

    rule_id: str
    tier: Tier
    line: int
    span: tuple[int, int]
    matched_text: str
    message: str
    weight: float
    fingerprint: str
    question: str | None = None

    @classmethod
    def new(
        cls,
        *,
        rule_id: str,
        tier: Tier,
        line: int,
        span: tuple[int, int],
        matched_text: str,
        message: str,
        context_line: str,
        weight: float = 1.0,
        question: str | None = None,
    ) -> Finding:
        """Build a :class:`Finding`, deriving its fingerprint from ``context_line``."""
        return cls(
            rule_id=rule_id,
            tier=tier,
            line=line,
            span=span,
            matched_text=matched_text,
            message=message,
            weight=weight,
            fingerprint=finding_fingerprint(rule_id, context_line),
            question=question,
        )


@dataclass(frozen=True)
class DetectorReport:
    """The result of one :func:`~...engine.run_detectors` call.

    ``dismissed`` holds findings a :class:`~...engine.Blacklist` recognized
    -- they are never silently dropped, only relocated out of ``findings``.
    Deliberately NO completeness/readiness field (see module docstring).
    """

    findings: list[Finding]
    dismissed: list[Finding]
    files_scanned: list[str]