scripts.factory_c4

Reference › scripts › factory_c4

The command-line interface for the SPEC-033 C4 declared-formalization detectors (B26, REQ-033-C4). It builds on c4 and the bridge in dor_bridge; the factory narrative describes the subsystem. No module or test imports it.

Invocation: main has two subcommands. template prints a skeleton formalization for a requirement id (_skeleton_formalization, _cmd_template). check runs the C4 detectors on a formalization and prints a C4Report, converted to a dictionary by _report_to_dict (_cmd_check). With no subcommand it prints help and exits 1, and an exception prints an error and exits 1.

At a glance

Source scripts/factory_c4.py
Lines of code 188
Classes 0
Functions 6
Imports (internal) orchestrata_ultimate.factory.formalization.c4, orchestrata_ultimate.factory.formalization.dor_bridge, orchestrata_ultimate.factory.formalization.models
Imported by none
Tests that import it none

Class diagram

No classes; see Functions.

Dependency diagram

Dependency diagram scripts.factory_c4

API

Functions

Function Signature Summary
_skeleton_formalization _skeleton_formalization(requirement_id: str) -> Formalization Build a fresh skeleton: one observable variable, zero constraints.
_cmd_template _cmd_template(args: argparse.Namespace) -> int
_report_to_dict _report_to_dict(report: C4Report) -> dict[str, Any]
_cmd_check _cmd_check(args: argparse.Namespace) -> int
_build_parser _build_parser() -> argparse.ArgumentParser
main main(argv: list[str] | None = None) -> None Parse argv, dispatch to the matching subcommand, and sys.exit.

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/scripts/factory_c4.py

#!/usr/bin/env python3
"""CLI for the SPEC-033 C4 declared-formalization detectors (B26, REQ-033-C4).

Subcommands
-----------
template  Emit a skeleton :class:`Formalization` JSON for a requirement --
          one example observable variable with two domain values and ZERO
          constraints, so the skeleton honestly reports under-constrained
          (0 constraints on 2 observable values admits both) until it is
          actually authored.
check     Load a formalization, run both C4 detectors, print the findings
          (witnesses/cores, never a bare boolean -- ADR-0026), and exit 1 if
          either detector fired.

Examples
--------
    python scripts/factory_c4.py template --requirement REQ-033-C4 \\
        --out formalization.json
    python scripts/factory_c4.py check formalization.json
    python scripts/factory_c4.py check formalization.json --json
"""

from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path
from typing import Any

# Ensure repo src is on path when run directly (mirrors factory_dor_procedure.py
# / kg_schema.py; also lets static analysis resolve the import without the
# editable-install .pth machinery).
_repo_src = Path(__file__).resolve().parent.parent / "src"
if str(_repo_src) not in sys.path:
    sys.path.insert(0, str(_repo_src))

from orchestrata_ultimate.factory.formalization.c4 import (  # noqa: E402
    C4Report,
    run_c4,
)
from orchestrata_ultimate.factory.formalization.dor_bridge import (  # noqa: E402
    c4_blocking_findings,
)
from orchestrata_ultimate.factory.formalization.models import (  # noqa: E402
    Domain,
    Formalization,
    Variable,
    load_formalization,
    save_formalization,
)


def _skeleton_formalization(requirement_id: str) -> Formalization:
    """Build a fresh skeleton: one observable variable, zero constraints.

    Two domain values and no constraints admits both -- honestly
    under-constrained until real variables/constraints are authored, the
    same "present-but-empty" discipline as ``factory_dor_procedure.py
    template``.
    """
    example_variable = Variable(
        name="example_var",
        domain=Domain(values=("value_a", "value_b")),
        observable=True,
    )
    return Formalization(
        requirement_id=requirement_id,
        variables=(example_variable,),
        constraints=(),
    )


def _cmd_template(args: argparse.Namespace) -> int:
    formalization = _skeleton_formalization(args.requirement)
    save_formalization(formalization, Path(args.out), overwrite=args.overwrite)
    print(
        f"Wrote skeleton C4 formalization for {args.requirement} to {args.out} "
        f"(0 constraints on 2 observable values -- honestly under-constrained "
        f"until authored; see the Constraint docstring for REQUIRE/FORBID/IMPLIES "
        f"shapes)"
    )
    return 0


def _report_to_dict(report: C4Report) -> dict[str, Any]:
    contradiction = report.contradiction
    under_constraint = report.under_constraint
    return {
        "requirement_id": report.requirement_id,
        "contradiction": (
            None
            if contradiction is None
            else {
                "conflicting_constraint_ids": list(
                    contradiction.conflicting_constraint_ids
                ),
                "conflicting_sources": list(contradiction.conflicting_sources),
            }
        ),
        "under_constraint": (
            None
            if under_constraint is None
            else {
                "differing_observable_vars": list(
                    under_constraint.differing_observable_vars
                ),
                "witness_a": under_constraint.witness_a,
                "witness_b": under_constraint.witness_b,
            }
        ),
        "solution_count_observable": report.solution_count_observable,
    }


def _cmd_check(args: argparse.Namespace) -> int:
    formalization = load_formalization(Path(args.file))
    report = run_c4(formalization)
    findings = c4_blocking_findings(report)

    if args.json:
        print(json.dumps(_report_to_dict(report), indent=2))
        return 1 if findings else 0

    if not findings:
        print(
            f"requirement={report.requirement_id}: no C4 finding "
            f"(solution_count_observable={report.solution_count_observable})"
        )
        return 0

    for line in findings:
        print(line)
    return 1


def _build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="factory_c4",
        description="SPEC-033 C4 declared-formalization detectors (B26, REQ-033-C4).",
    )
    sub = parser.add_subparsers(dest="command", required=True)

    template_p = sub.add_parser(
        "template", help="Emit a skeleton formalization JSON for a requirement."
    )
    template_p.add_argument("--requirement", required=True, help="Requirement id.")
    template_p.add_argument("--out", required=True, help="Output JSON path.")
    template_p.add_argument(
        "--overwrite",
        action="store_true",
        help="Allow overwriting an existing file at --out.",
    )

    check_p = sub.add_parser(
        "check", help="Run both C4 detectors and print findings; exit 1 if any fired."
    )
    check_p.add_argument("file", help="Formalization JSON path.")
    check_p.add_argument("--json", action="store_true", help="Print findings as JSON.")

    return parser


def main(argv: list[str] | None = None) -> None:
    """Parse ``argv``, dispatch to the matching subcommand, and ``sys.exit``."""
    parser = _build_parser()
    args = parser.parse_args(argv)

    dispatch = {
        "template": _cmd_template,
        "check": _cmd_check,
    }
    handler = dispatch.get(args.command)
    if handler is None:
        parser.print_help()
        sys.exit(1)

    try:
        exit_code = handler(args)
    except Exception as exc:  # noqa: BLE001
        print(f"ERROR: {exc}", file=sys.stderr)
        sys.exit(1)

    sys.exit(exit_code)


if __name__ == "__main__":
    main()