You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
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).
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/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.
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) viaforge_loop/product_vision.py:discover()(see #122). There is currentlyno 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 intoa repo gets zero effect.
Concrete example: a repo with
.forge/quality-manifesto.mdsaying"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.mdand.forge/testing-manifesto.mdat brainstormer/worker startup. Mirror theproduct_vision.discover()pattern (raw markdown + optional structuredadjunct), but with soft-default semantics (missing file is a warning,
not an error) unless
settings.manifestos.required: true.Acceptance criteria
src/forge_loop/manifestos.pyexposes:QualityManifestopydantic model:markdown: str,rules: list[Rule] | NoneTestingManifestopydantic model: same shapeManifestosbundle dataclass/model:quality: QualityManifesto,testing: TestingManifesto,warnings: list[str]discover_manifestos(repo_path: Path, *, required: bool = False) -> Manifestos.forge/quality-manifesto.md+ optional.forge/quality-rules.yaml.forge/testing-manifesto.md+ optional.forge/testing-rules.yamlmarkdown="",rules=None) + warning event appended towarningslist. Caller decides what to log.required=Trueand either markdown file is missing → raiseMissingManifestoErrorwith absolute path in message (mirrorMissingVisionErrorstyle).*-rules.yaml(YAML parse error or schema-invalid) always raises, regardless ofrequired— silent corruption is worse than absence.markdown="".__all__and re-exported fromforge_loop/__init__.pyifProductVisionis (check current convention).pathlib,pydantic,pyyaml(same asproduct_vision.py).Test matrix
Unit tests in
tests/test_manifestos_discovery.pyusing fixtures undertests/fixtures/manifestos/:full/: both markdowns + both rules.yaml present → returns populatedManifestos, no warnings.missing_quality/: only testing files → quality manifesto is empty,warningscontains 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 butquality-rules.yamlhas 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)onmissing_both→ raisesMissingManifestoError..forge/is a symlink to a non-existent dir → raise a clean error, not a stack trace frompathlib.quality-manifesto.md/) → clean error, notIsADirectoryError.Integration: at least one test that imports
discover_manifestosexactly the way the brainstormer/worker startup path will (mirror howproduct_vision.discoveris imported in existing startup code — checkcli.py/ brainstormer entry point).Out of scope
discover_manifestosinto 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.rules: list[Rule]shape (aRuleis just{id: str, description: str, severity: str | None}for now — richer schema is a separate ticket).settings.manifestos.requiredin this PR — accept it as a function kwarg only. Config wiring is a separate ticket.product_vision.pyto share code. Duplicate the small amount of yaml-loading boilerplate; dedupe later if a third manifesto appears.forge-loop manifestos showetc.) here.File pointers
src/forge_loop/manifestos.py(new) — mirror structure ofsrc/forge_loop/product_vision.py.src/forge_loop/__init__.py(investigate — re-export iffProductVisionis re-exported there).tests/test_manifestos_discovery.py(new) — mirrortests/test_product_vision.py.tests/fixtures/manifestos/(new) — one subdir per matrix case above.src/forge_loop/product_vision.pyfor the discovery/validation pattern, error-message format, andMissingVisionErrorshape.Original report