docs: tighten specification ownership and migration (#3004)

This commit is contained in:
Carlos Florêncio
2026-08-24 21:30:53 +00:00
committed by GitHub
parent 0057e4cb7d
commit 1e05cf9201
42 changed files with 873 additions and 752 deletions
+16 -3
View File
@@ -62,7 +62,7 @@ def lint_specs(root: Path, config: dict | None = None) -> list[Violation]:
violations: list[Violation] = []
requirement_definitions: dict[str, list[tuple[Path, int]]] = {}
acceptance_definitions: dict[str, list[tuple[Path, int]]] = {}
design_references: list[tuple[str, Path, int]] = []
design_references: list[tuple[str, Path, int, str | None]] = []
systems_with_new_artifacts: set[str] = set()
system_indexes: set[str] = set()
@@ -157,7 +157,7 @@ def lint_specs(root: Path, config: dict | None = None) -> list[Violation]:
references = metadata.get("requirements", [])
if isinstance(references, list):
for reference in references:
design_references.append((str(reference), path, 1))
design_references.append((str(reference), path, 1, system))
for system in sorted(systems_with_new_artifacts - system_indexes):
violations.append(
@@ -172,7 +172,7 @@ def lint_specs(root: Path, config: dict | None = None) -> list[Violation]:
violations.extend(check_duplicate_ids("requirement", requirement_definitions))
violations.extend(check_duplicate_ids("acceptance", acceptance_definitions))
known_requirements = set(requirement_definitions)
for requirement, path, line in design_references:
for requirement, path, line, design_system in design_references:
if requirement not in known_requirements:
violations.append(
Violation(
@@ -182,6 +182,19 @@ def lint_specs(root: Path, config: dict | None = None) -> list[Violation]:
f"system design references unknown requirement {requirement}",
)
)
continue
definition_path, _ = requirement_definitions[requirement][0]
_, requirement_system = classify_path(definition_path.relative_to(root))
if requirement_system != design_system:
violations.append(
Violation(
"cross-system-requirement-reference",
path,
line,
f"system design in `{design_system}` cannot claim requirement {requirement} "
f"owned by `{requirement_system}`; link to the owning system instead",
)
)
violations.extend(check_size_exception_catalog(root, config))
violations.extend(check_legacy_size_ratchet(root, config, previous_config))
+16
View File
@@ -227,6 +227,22 @@ migration: in_progress
self.assertIn("unknown-requirement-reference", self.rules(violations))
def test_rejects_a_system_design_reference_owned_by_another_system(self) -> None:
self.write("docs/specs/task-system/README.md", self.valid_system_index())
self.write("docs/specs/task-system/requirements/dependencies.md", self.valid_requirement())
self.write(
"docs/specs/ui/README.md",
self.valid_system_index().replace("task-system", "ui"),
)
self.write(
"docs/specs/ui/system-design/dependencies.md",
self.valid_design().replace("system: task-system", "system: ui"),
)
violations = load_linter().lint_specs(self.root, self.config)
self.assertIn("cross-system-requirement-reference", self.rules(violations))
def test_rejects_a_requirement_without_acceptance_criteria(self) -> None:
content = self.valid_requirement().split("#### Acceptance criteria", 1)[0]
self.write("docs/specs/task-system/requirements/dependencies.md", content)