Skip to content

feat(manifestos): quality + testing manifesto discovery + schema #131

@hadamrd

Description

@hadamrd

Parent

Part of #130 — Quality + Testing manifestos epic.

Problem

The brainstormer and worker pipelines already discover a "north star"
(.forge/product-vision.md + .forge/axes.yaml) via
forge_loop/product_vision.py:discover() (see #122). There is currently
no equivalent loader for the quality and testing manifestos the team
intends to ship in this epic. Without auto-discovery, downstream prompts
have no way to receive the contents of .forge/quality-manifesto.md /
.forge/testing-manifesto.md, and an operator dropping these files into
a repo gets zero effect.

Concrete example: a repo with .forge/quality-manifesto.md saying
"never silently swallow exceptions" is invisible to the worker — the
worker still ships PRs that swallow exceptions because the manifesto is
never loaded into context.

What

Auto-discover .forge/quality-manifesto.md and
.forge/testing-manifesto.md at brainstormer/worker startup. Mirror the
product_vision.discover() pattern (raw markdown + optional structured
adjunct), but with soft-default semantics (missing file is a warning,
not an error) unless settings.manifestos.required: true.

Acceptance criteria

  • A new module src/forge_loop/manifestos.py exposes:
    • QualityManifesto pydantic model: markdown: str, rules: list[Rule] | None
    • TestingManifesto pydantic model: same shape
    • Manifestos bundle dataclass/model: quality: QualityManifesto, testing: TestingManifesto, warnings: list[str]
    • discover_manifestos(repo_path: Path, *, required: bool = False) -> Manifestos
  • File layout discovered:
    • .forge/quality-manifesto.md + optional .forge/quality-rules.yaml
    • .forge/testing-manifesto.md + optional .forge/testing-rules.yaml
  • Missing markdown file → empty manifesto (markdown="", rules=None) + warning event appended to warnings list. Caller decides what to log.
  • If required=True and either markdown file is missing → raise MissingManifestoError with absolute path in message (mirror MissingVisionError style).
  • Malformed *-rules.yaml (YAML parse error or schema-invalid) always raises, regardless of required — silent corruption is worse than absence.
  • Empty markdown file is treated as "present but empty": no warning, markdown="".
  • Public API exported via __all__ and re-exported from forge_loop/__init__.py if ProductVision is (check current convention).
  • Module is dependency-light: only pathlib, pydantic, pyyaml (same as product_vision.py).

Test matrix

Unit tests in tests/test_manifestos_discovery.py using fixtures under tests/fixtures/manifestos/:

  • full/: both markdowns + both rules.yaml present → returns populated Manifestos, no warnings.
  • missing_quality/: only testing files → quality manifesto is empty, warnings contains a quality-specific entry.
  • missing_testing/: only quality files → mirror.
  • missing_both/: empty .forge/ → both manifestos empty, two warnings.
  • markdown_only/: markdowns present, no rules.yaml → rules is None, no warning.
  • rules_yaml_malformed/: well-formed markdown but quality-rules.yaml has invalid YAML → raises with absolute path in message.
  • rules_yaml_schema_invalid/: YAML parses but doesn't match schema → raises with pydantic-formatted error.
  • required_flag/: discover_manifestos(path, required=True) on missing_both → raises MissingManifestoError.
  • Adversarial: .forge/ is a symlink to a non-existent dir → raise a clean error, not a stack trace from pathlib.
  • Adversarial: markdown file is a directory (e.g. someone made quality-manifesto.md/) → clean error, not IsADirectoryError.

Integration: at least one test that imports discover_manifestos exactly the way the brainstormer/worker startup path will (mirror how product_vision.discover is imported in existing startup code — check cli.py / brainstormer entry point).

Out of scope

  • Do not wire discover_manifestos into the brainstormer or worker prompts in this PR. That belongs to a follow-up issue in the epic: Quality + Testing manifestos — customer-tunable, drive worker workflow, feedback-loop on bugs #130 epic — this PR only delivers the loader and tests.
  • Do not define the content schema of the rules YAML beyond a minimal rules: list[Rule] shape (a Rule is just {id: str, description: str, severity: str | None} for now — richer schema is a separate ticket).
  • Do not add settings.toml / config plumbing for settings.manifestos.required in this PR — accept it as a function kwarg only. Config wiring is a separate ticket.
  • Do not refactor product_vision.py to share code. Duplicate the small amount of yaml-loading boilerplate; dedupe later if a third manifesto appears.
  • Do not add CLI commands (forge-loop manifestos show etc.) here.

File pointers

  • src/forge_loop/manifestos.py (new) — mirror structure of src/forge_loop/product_vision.py.
  • src/forge_loop/__init__.py (investigate — re-export iff ProductVision is re-exported there).
  • tests/test_manifestos_discovery.py (new) — mirror tests/test_product_vision.py.
  • tests/fixtures/manifestos/ (new) — one subdir per matrix case above.
  • Reference (read, do not modify): src/forge_loop/product_vision.py for the discovery/validation pattern, error-message format, and MissingVisionError shape.

Original report

Auto-discover .forge/quality-manifesto.md and .forge/testing-manifesto.md at brainstormer/worker startup. Mirror the product-vision.md pattern from #122.

Acceptance:

  • Extend forge_loop/product_vision.py (or new forge_loop/manifestos.py) to load both new files.
  • New pydantic models: QualityManifesto, TestingManifesto. Both accept the markdown content + an optional structured rules.yaml adjunct.
  • discover_manifestos(repo_path) -> Manifestos returns the bundle.
  • Missing files = soft-default (empty manifesto, doesn't block) WITH a warning event. Hard-fail only if settings.manifestos.required: true.
  • Tests: full / missing-one / missing-both / structured-rules-yaml.

Metadata

Metadata

Assignees

No one assigned

    Labels

    po:expandedPO subagent expanded the issue bodypriority:p1Important, near-term

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions