diff --git a/.claude/assets/sc-rust/quality-mgr/templates/rust-best-practices-assignment.json.j2 b/.claude/assets/sc-rust/quality-mgr/templates/rust-best-practices-assignment.json.j2 index 83f38d53..93deeaca 100644 --- a/.claude/assets/sc-rust/quality-mgr/templates/rust-best-practices-assignment.json.j2 +++ b/.claude/assets/sc-rust/quality-mgr/templates/rust-best-practices-assignment.json.j2 @@ -1,6 +1,6 @@ --- name: rust-best-practices-assignment -version: 1.0.0 +version: 1.0.1 description: Render a fenced-JSON assignment for rust-best-practices-agent. format: json required_variables: @@ -41,7 +41,7 @@ defaults: "round_limit": {% if round_limit %}true{% else %}false{% endif %}, "changed_files": [{% for file in changed_files %}{{ file }}{% if not loop.last %}, {% endif %}{% endfor %}], "triage_records": [{% for record in triage_records %}{{ record }}{% if not loop.last %}, {% endif %}{% endfor %}], - "carry_forward_findings": {{ carry_forward_findings_json }}, + "carry_forward_findings": {{ carry_forward_findings_json | safe }}, "findings_scope_locked": {% if carry_forward_findings_json != "[]" %}true{% else %}false{% endif %}, "notes": {{ notes ~ (" SCOPE LOCK: this is a fix-round verification dispatch. Report a disposition (fixed | open | regressed) for each id in carry_forward_findings ONLY. Do not add any new finding to the `findings` array beyond those ids, even if you notice something real and unrelated -- put anything else observed under `notes`, not `findings`." if carry_forward_findings_json != "[]" else "") }} } diff --git a/.claude/assets/sc-rust/quality-mgr/templates/rust-qa-assignment.json.j2 b/.claude/assets/sc-rust/quality-mgr/templates/rust-qa-assignment.json.j2 index dc991461..b8d4ee86 100644 --- a/.claude/assets/sc-rust/quality-mgr/templates/rust-qa-assignment.json.j2 +++ b/.claude/assets/sc-rust/quality-mgr/templates/rust-qa-assignment.json.j2 @@ -1,6 +1,6 @@ --- name: rust-qa-assignment -version: 1.0.0 +version: 1.0.1 description: Render a fenced-JSON assignment for rust-qa-agent. format: json required_variables: @@ -50,6 +50,6 @@ defaults: "round_limit": {% if round_limit %}true{% else %}false{% endif %}, "changed_files": [{% for file in changed_files %}{{ file }}{% if not loop.last %}, {% endif %}{% endfor %}], "triage_records": [{% for record in triage_records %}{{ record }}{% if not loop.last %}, {% endif %}{% endfor %}], - "carry_forward_findings": {{ carry_forward_findings_json }}, + "carry_forward_findings": {{ carry_forward_findings_json | safe }}, "notes": {{ "Checklist: scan for infallible Result shapes; check for Ok(x) as the sole return path in Result-returning functions; for deletion sprints, search the whole workspace for every removed legacy construct family named in the sprint doc, not just touched files." ~ (" " ~ notes if notes else "") }} } diff --git a/.claude/assets/sc-rust/quality-mgr/templates/rust-service-hardening-assignment.json.j2 b/.claude/assets/sc-rust/quality-mgr/templates/rust-service-hardening-assignment.json.j2 index c17b981f..6a8c075f 100644 --- a/.claude/assets/sc-rust/quality-mgr/templates/rust-service-hardening-assignment.json.j2 +++ b/.claude/assets/sc-rust/quality-mgr/templates/rust-service-hardening-assignment.json.j2 @@ -1,6 +1,6 @@ --- name: rust-service-hardening-assignment -version: 1.0.0 +version: 1.0.1 description: Render a fenced-JSON assignment for rust-service-hardening-agent. format: json required_variables: @@ -42,6 +42,6 @@ defaults: "round_limit": {% if round_limit %}true{% else %}false{% endif %}, "changed_files": [{% for file in changed_files %}{{ file }}{% if not loop.last %}, {% endif %}{% endfor %}], "triage_records": [{% for record in triage_records %}{{ record }}{% if not loop.last %}, {% endif %}{% endfor %}], - "carry_forward_findings": {{ carry_forward_findings_json }}, + "carry_forward_findings": {{ carry_forward_findings_json | safe }}, "notes": {{ notes }} } diff --git a/.claude/skills/codex-orchestration/arch-qa-assignment.json.j2 b/.claude/skills/codex-orchestration/arch-qa-assignment.json.j2 index 181ea377..fe2c9746 100644 --- a/.claude/skills/codex-orchestration/arch-qa-assignment.json.j2 +++ b/.claude/skills/codex-orchestration/arch-qa-assignment.json.j2 @@ -1,6 +1,6 @@ --- name: arch-qa-assignment -version: 1.0.0 +version: 1.0.1 description: Render a fenced-JSON assignment for arch-qa. format: json required_variables: @@ -45,6 +45,6 @@ defaults: "round_limit": {% if round_limit %}true{% else %}false{% endif %}, "changed_files": [{% for file in changed_files %}{{ file }}{% if not loop.last %}, {% endif %}{% endfor %}], "triage_records": [{% for record in triage_records %}{{ record }}{% if not loop.last %}, {% endif %}{% endfor %}], - "carry_forward_findings": {{ carry_forward_findings_json }}, + "carry_forward_findings": {{ carry_forward_findings_json | safe }}, "notes": {{ notes }} } diff --git a/.claude/skills/codex-orchestration/flaky-test-qa-assignment.json.j2 b/.claude/skills/codex-orchestration/flaky-test-qa-assignment.json.j2 index a0876e5a..3599ee2e 100644 --- a/.claude/skills/codex-orchestration/flaky-test-qa-assignment.json.j2 +++ b/.claude/skills/codex-orchestration/flaky-test-qa-assignment.json.j2 @@ -1,6 +1,6 @@ --- name: flaky-test-qa-assignment -version: 1.0.0 +version: 1.0.1 description: Render a fenced-JSON assignment for flaky-test-qa. format: json required_variables: @@ -33,6 +33,6 @@ defaults: "round_limit": {% if round_limit %}true{% else %}false{% endif %}, "changed_files": [{% for file in changed_files %}{{ file }}{% if not loop.last %}, {% endif %}{% endfor %}], "triage_records": [{% for record in triage_records %}{{ record }}{% if not loop.last %}, {% endif %}{% endfor %}], - "carry_forward_findings": {{ carry_forward_findings_json }}, + "carry_forward_findings": {{ carry_forward_findings_json | safe }}, "notes": {{ notes }} } diff --git a/.claude/skills/codex-orchestration/req-qa-assignment.json.j2 b/.claude/skills/codex-orchestration/req-qa-assignment.json.j2 index 5ed9730c..e01dab97 100644 --- a/.claude/skills/codex-orchestration/req-qa-assignment.json.j2 +++ b/.claude/skills/codex-orchestration/req-qa-assignment.json.j2 @@ -1,6 +1,6 @@ --- name: req-qa-assignment -version: 1.1.0 +version: 1.1.1 description: Render a fenced-JSON assignment for req-qa. format: json required_variables: @@ -46,6 +46,6 @@ defaults: "round_limit": {% if round_limit %}true{% else %}false{% endif %}, "changed_files": [{% for file in changed_files %}{{ file }}{% if not loop.last %}, {% endif %}{% endfor %}], "triage_records": [{% for record in triage_records %}{{ record }}{% if not loop.last %}, {% endif %}{% endfor %}], - "carry_forward_findings": {{ carry_forward_findings_json }}, + "carry_forward_findings": {{ carry_forward_findings_json | safe }}, "notes": {{ notes }} } diff --git a/.claude/skills/codex-orchestration/ruthless-boundary-qa-assignment.json.j2 b/.claude/skills/codex-orchestration/ruthless-boundary-qa-assignment.json.j2 index d16e9d49..662be848 100644 --- a/.claude/skills/codex-orchestration/ruthless-boundary-qa-assignment.json.j2 +++ b/.claude/skills/codex-orchestration/ruthless-boundary-qa-assignment.json.j2 @@ -1,6 +1,6 @@ --- name: ruthless-boundary-qa-assignment -version: 1.0.0 +version: 1.0.1 description: Render a fenced-JSON assignment for ruthless-boundary-qa. format: json required_variables: @@ -33,7 +33,7 @@ defaults: "changed_files": [{% for file in changed_files %}{{ file }}{% if not loop.last %}, {% endif %}{% endfor %}], "duplicate_sweep_symbols": [{% for symbol in duplicate_sweep_symbols %}{{ symbol }}{% if not loop.last %}, {% endif %}{% endfor %}], "triage_records": [{% for record in triage_records %}{{ record }}{% if not loop.last %}, {% endif %}{% endfor %}], - "carry_forward_findings": {{ carry_forward_findings_json }}, + "carry_forward_findings": {{ carry_forward_findings_json | safe }}, "findings_scope_locked": {% if carry_forward_findings_json != "[]" %}true{% else %}false{% endif %}, "notes": {{ notes ~ (" SCOPE LOCK: this is a fix-round verification dispatch. Report a disposition (fixed | open | regressed) for each id in carry_forward_findings ONLY. Do not add any new finding to the `findings` array beyond those ids, even if you notice something real and unrelated -- put anything else observed under `notes`, not `findings`." if carry_forward_findings_json != "[]" else "") }} } diff --git a/bindings/sc-lint-py/python/sc_lint/tests/test_orchestration_contracts.py b/bindings/sc-lint-py/python/sc_lint/tests/test_orchestration_contracts.py index ae492c5c..d89f2396 100644 --- a/bindings/sc-lint-py/python/sc_lint/tests/test_orchestration_contracts.py +++ b/bindings/sc-lint-py/python/sc_lint/tests/test_orchestration_contracts.py @@ -267,3 +267,21 @@ def test_missing_sample_var_fails_composition(self) -> None: else: with self.assertRaisesRegex(Exception, "undefined|Undefined"): self.compose(template, bad_vars) + + def test_carry_forward_findings_render_as_json_arrays(self) -> None: + expected = [{"id": "F-1", "severity": "important"}] + for template_name in self.AGENT_CONTRACTS: + template = next( + directory / template_name + for directory in self.TEMPLATE_DIRS + if (directory / template_name).is_file() + ) + sample_name = template.name.removesuffix(".j2") + sample = template.parent / "vars" / sample_name + variables = json.loads(sample.read_text(encoding="utf-8")) + variables["carry_forward_findings_json"] = json.dumps(expected) + with self.subTest(template=template.name), tempfile.TemporaryDirectory() as directory: + vars_path = Path(directory) / "vars.json" + vars_path.write_text(json.dumps(variables), encoding="utf-8") + payload = json.loads(self.compose(template, vars_path)) + self.assertEqual(payload["carry_forward_findings"], expected)