Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/lib/orchestration_test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ def require_dev_cli(name: str, minimum: str, install: str) -> None:
if shutil.which(name):
return
if os.getenv("CI"):
pytest.skip(f"dev-host-only check: {name} not installed in CI")
return pytest.skip(f"dev-host-only check: {name} not installed in CI")
pytest.fail(f"{name} CLI is required ({minimum}); install it with: {install}")
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ def test_missing_directory_is_error_result(tmp_path):
assert "findings directory does not exist" in result.diagnostics[0]


def test_empty_directory_is_error_result(tmp_path):
validator = _validator()
findings = tmp_path / "findings"
findings.mkdir()
result = validator.run_validation(findings_dir=findings)
assert result.kind == "error"
assert "contains no Turtle files" in result.diagnostics[0]


def test_missing_structure_input_is_error_result(tmp_path):
validator = _validator()
findings = tmp_path / "findings"
Expand Down Expand Up @@ -245,6 +254,9 @@ def test_broken_sparql_is_error_result(tmp_path):
validator = _validator()
findings = tmp_path / "findings"
findings.mkdir()
(findings / "F-1.ttl").write_text(
PREFIX + "triage:f1 a triage:Finding ; triage:findingId \"F-1\" .\n"
)
broken_scripts = tmp_path / "scripts"
broken_scripts.mkdir()
(broken_scripts / "validate-findings.sparql").write_text("SELECT definitely broken")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ def _load_graph(
"persisted path must be repository-relative"
)

if parsed_files == 0 and not diagnostics:
diagnostics.append(
f"#error: {findings_dir}: findings directory contains no Turtle files"
)
for triple in finding_graph:
graph.add(triple)
return (
Expand Down
8 changes: 7 additions & 1 deletion .claude/skills/team-lead/scripts/roster_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ def find_problems(


def run_json(command: list[str]) -> dict:
completed = subprocess.run(command, capture_output=True, text=True, check=False)
try:
completed = subprocess.run(command, capture_output=True, text=True, check=False)
except OSError as error:
raise RuntimeError(f"{' '.join(command)} could not be executed: {error}") from error
if completed.returncode != 0:
raise RuntimeError(f"{' '.join(command)} failed: {completed.stderr.strip()}")
return json.loads(completed.stdout)
Expand All @@ -79,6 +82,9 @@ def main() -> int:
config = config_aliases(tomllib.loads(Path(args.atm_toml).read_text("utf-8")), args.team)
roster = run_json(["atm", "members", "--team", args.team, "--json"])["members"]
agents = run_json(["herdr", "agent", "list"])["result"]["agents"]
except tomllib.TOMLDecodeError as error:
print(f"roster_check: {args.atm_toml}: malformed TOML: {error}", file=sys.stderr)
return 2
except (OSError, RuntimeError, KeyError, ValueError) as error:
print(f"roster_check: {error}", file=sys.stderr)
return 2
Expand Down
17 changes: 17 additions & 0 deletions .just/tests/test_team_lead_roster_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import importlib.util
from pathlib import Path
import tomllib
import tempfile
import unittest
from unittest import mock

Expand Down Expand Up @@ -108,5 +109,21 @@ def test_long_alias_keeps_every_row_as_five_whitespace_fields(self) -> None:
self.assertEqual(len(rows[0].split()), 5)


class FailureDiagnosticsTests(unittest.TestCase):
def test_missing_executable_is_actionable(self) -> None:
with mock.patch.object(roster_check.subprocess, "run", side_effect=FileNotFoundError("atm")):
with self.assertRaisesRegex(RuntimeError, "atm members.*could not be executed"):
roster_check.run_json(["atm", "members"])

def test_malformed_toml_names_the_input_path(self) -> None:
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "broken.toml"
path.write_text("[broken", encoding="utf-8")
with mock.patch("sys.argv", ["roster_check.py", "--team", "sc-lint", "--atm-toml", str(path)]), \
mock.patch("sys.stderr") as stderr:
self.assertEqual(roster_check.main(), 2)
self.assertIn(str(path), "".join(call.args[0] for call in stderr.write.call_args_list))


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
sprint: lint-spx.24
bead: lint-spx.24
epic: lint-spx
status: complete
branch: chore/orchestration-verification-fixes
worktree: /Users/randlee/github/sc-lint-worktrees/chore/orchestration-verification-fixes
pr_target: chore/orchestration-script-tests
closure_type: contract
adrs: []
requirements: []
---

# lint-spx.24 — Orchestration stack: mechanics defects from verification lint-spx.6

Layer 4 of stack #172 (#166 <- #167 <- #170), stacked on PR #170 @ ee1bee9.
Source: quality-mgr targeted verification `lint-spx.6`, questions Q1-Q5.
Full report: `atm read --task lint-spx.6 --all`. Every file:line below comes
from that report; re-verify each before editing.

No ADR or requirement governs the orchestration prompts, so `adrs` and
`requirements` are empty by design.

## Deliverables

1. **Q2** — test_orchestration_contracts.py:208-209 skips every key comparison
when atm is absent and CI has no atm -> render with Jinja and compare keys
always; derive req-qa/arch-qa keys from agent docs; delete dead
EXPECTED_JSON_KEYS.
2. **Q1** — triage-record.ttl.j2 has no sample vars and is not
in TEMPLATE_DIRS; task_id supplied-but-ignored in 4 templates; unused notes
(rust-qa) and lead (review-template); SKILL.md templates list omits
ruthless-boundary-qa-assignment and sprint-plan.
3. **Q5** — 'atm gh' does not exist
(quality-mgr.md:182-187); schema-reviewer agent does not exist but is
mandatory (quality-mgr.md:257,272,291) and cites atm-core ADR-061; --watch
used (SKILL.md:278, quality-mgr.md:185) but forbidden by qa-template step j;
'CLAUDE.md section 0' does not exist (SKILL.md:205,209); queue-parallelism
contradiction (qa-template a1 vs review-template d vs quality-mgr.md:54);
atm send --template vs atm task close --template for verdicts (quality-
mgr.md:347 vs :72,200,306); review-template lacks bd claim/close and refused
path; QA-FAIL bead outcome unspecified in templates; AGENTS.md duplicates
its Beads section (72,127); rust-best-practices-agent listed twice in
SKILL.md.
4. **Q3** — validate-findings.py empty dir exits 0 silently;
roster_check.py malformed .atm.toml message lacks file path and missing atm
gives raw Errno; check_dependencies.py ignores argv; tests for each.
Overlaps lint-spx.23 where the line is atm-core residue: .23 owns reviewer
content, this bead owns mechanics.



PARENT
↑ ○ lint-spx: (EPIC) Release 0.6.0 (Phase G adoption kit + boundary fixes + beads-driven orchestration) P1

BLOCKS
← ○ lint-spx.7: Merge orchestration stack to develop P2

Where two documents contradict each other, the rule is: templates are
authoritative for task steps, `SKILL.md` for the contract, agent files for
reviewer behaviour. Fix the non-authoritative side. For the QA queue
contradiction the intended behaviour is: QA tasks for different stacks run in
parallel; tasks on the same stack run in order. For verdict delivery the
intended mechanism is `atm task close --template`. For CI waiting, `--watch`
is forbidden (qa-template step j is right).

## Acceptance criteria

- The contract test compares rendered keys to agent-documented keys with and
without `atm` on PATH (prove it: run once with `PATH=/usr/bin:/bin`).
- `grep -rn "atm gh\|schema-reviewer\|ADR-061\|CLAUDE.md §0" .claude` returns nothing.
- Every script corner case listed under Q3 has a test, and the test fails
against the pre-fix script.
- `just lint` and `just test` pass; `git diff --check origin/develop..HEAD` clean.
- CLAUDE.md and AGENTS.md stay mirrored.
- Frontmatter `status: complete` with a `## Closeout`; bead `lint-spx.24`
claimed with task start and closed with task close.

## Out of scope

- Reviewer rule content, atm-core exemptions and examples, ADR/REQ
enforcement design (`lint-spx.23`). If a line is both a mechanics defect and
atm-core residue (e.g. `schema-reviewer`), delete it here and note it in the
closeout.
- Documentation of ADRs and requirements (`lint-spx.25`).

## Closeout

Completed in the restacked layer: empty-findings validator regression coverage,
actionable roster-check diagnostics, and the Windows-safe malformed-TOML
fixture. The latter fixes the Windows CI failure where an open
`NamedTemporaryFile` produced `Permission denied` rather than TOML parsing.

Cut to `lint-spx.33`: remaining contract-test expansion, dead-command removal,
template/skill contradiction cleanup, and the rest of the verification-script
hardening scope from this plan.
Loading