docs: tighten specification ownership and migration (#3004)
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user