Skip to content

T-1PII-shared-sdlc-yaml-loader-lib

Status: closed/obsoleted · Impact: low · Complexity: small

Auto-generated from a /sdlc:task-work post-mortem. Review and promote to open/ready before picking up.

Two consumers (plugin/scripts/count_inflight_tasks.py and plugin/scripts/run_quality_checks.py) now route sdlc.yaml through the shared validator before reading keys, but each subprocesses plugin/validators/validate_sdlc_yaml.py to do so — robust, but adds ~real-world subprocess cost per invocation and duplicates the schema-path / load logic. A small shared library under plugin/lib/ would let both consumers import load_and_validate(sdlc_yaml_path) directly.

LocationRole today
plugin/validators/validate_sdlc_yaml.pyexposes
Both consumers settle for subprocess.run([validator, …]) which<migrated from v2 — no role recorded>
There is no plugin/lib/ directory today; shared helpers live<migrated from v2 — no role recorded>

A new plugin/lib/sdlc_yaml.py (regular Python module, not a uv- script) exposing:

  • default_schema_path() -> Path
  • load_schema(path: Path) -> dict
  • load_yaml(path: Path) -> Any
  • validate_loaded(data, schema) -> list[str]
  • validate_file(path, schema=None) -> tuple[bool, list[str]]
  • load_and_validate(path) -> tuple[dict | None, list[str]] — the convenience entry point: returns (data, []) on pass, (None, errors) on fail.

The validator CLI (plugin/validators/validate_sdlc_yaml.py) becomes a thin wrapper around this module. Both consumers (and any future ones) from plugin.lib.sdlc_yaml import load_and_validate (or equivalent importlib boilerplate for non-package imports) instead of subprocessing.

  1. Extract the pure-Python functions from plugin/validators/validate_sdlc_yaml.py into plugin/lib/sdlc_yaml.py. Keep the CLI script and re-export from the new module so external callers (CI, humans) see no change.
  2. Refactor count_inflight_tasks.py and run_quality_checks.py to import the helper directly. Decide on the import strategy (importlib.util.spec_from_file_location is the precedent elsewhere in the plugin; adopt that).
  3. Confirm the per-tick cost saved is real on the orchestrator hot path (which calls count_inflight_tasks.py per tick).
LocationKindChange
plugin/lib/sdlc_yaml.pynewthe shared module.
plugin/validators/validate_sdlc_yaml.pymodifyslim down to a CLI
plugin/scripts/count_inflight_tasks.pymodifyreplace subprocess
plugin/scripts/run_quality_checks.pymodifyreplace subprocess
plugin/validators/tests/test_validate_sdlc_yaml.pymodifyextend or
  • AC-1: plugin/lib/sdlc_yaml.py exists and exposes load_and_validate, validate_loaded, default_schema_path.
  • AC-2: count_inflight_tasks.py and run_quality_checks.py no longer invoke the validator as a subprocess (verify with command grep -n "subprocess.*validate_sdlc_yaml" plugin/scripts/).
  • AC-3: All existing tests (plugin/validators/tests/test_validate_sdlc_yaml.py and any new module-level tests) pass.
  • AC-4: The validator CLI still passes a smoke run against the project’s own sdlc.yaml.
  • Generalising to a plugin/lib/ for other shared helpers. This task creates exactly one module; broader refactors (e.g. moving _schema_patterns.py) belong in their own task.
  • Packaging the plugin as an installable Python package. The plugin is a flat checkout; this task respects that and uses importlib for the cross-script import.

Spawned by /sdlc:task-work post-mortem of T-J1M3-sdlc-yaml-json-schema-and-validator on 2026-05-21. The originating task settled for subprocess.run to call the validator from sibling scripts because every script in this plugin is a PEP-723 uv-script and a clean cross-script import surface doesn’t exist. This task adds one.


← Back to Tasks