From 2260588b705902dee8072e1b2f2a9b4491a8c3ce Mon Sep 17 00:00:00 2001 From: welsberr Date: Mon, 27 Apr 2026 12:41:29 -0400 Subject: [PATCH] Support GroundRecall query bundles in draft packs --- src/didactopus/pack_emitter.py | 13 ++++++++++++- tests/test_pack_emitter.py | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/didactopus/pack_emitter.py b/src/didactopus/pack_emitter.py index 3f6c76a..eedf503 100644 --- a/src/didactopus/pack_emitter.py +++ b/src/didactopus/pack_emitter.py @@ -71,8 +71,12 @@ def build_draft_pack( license_name: str, review_flags: list[str], conflicts: list[str] | None = None, + groundrecall_query_bundle: dict | None = None, ) -> DraftPack: pack_name = course.title.lower().replace(" ", "-") + supporting_artifacts = ["source_corpus.json", "knowledge_graph.json"] + if groundrecall_query_bundle is not None: + supporting_artifacts.append("groundrecall_query_bundle.json") pack = { "name": pack_name, "display_name": course.title, @@ -87,7 +91,7 @@ def build_draft_pack( "overrides": [], "profile_templates": {}, "cross_pack_links": [], - "supporting_artifacts": ["source_corpus.json", "knowledge_graph.json"], + "supporting_artifacts": supporting_artifacts, } concepts_yaml = { "concepts": [ @@ -134,6 +138,8 @@ def build_draft_pack( for src in course.source_records ], } + if groundrecall_query_bundle is not None: + attribution["groundrecall_query_bundle"] = groundrecall_query_bundle return DraftPack( pack=pack, concepts=concepts_yaml, @@ -159,6 +165,11 @@ def write_draft_pack(pack: DraftPack, outdir: str | Path) -> None: conflict_lines = ["# Conflict Report", ""] + [f"- {flag}" for flag in pack.conflicts] if pack.conflicts else ["# Conflict Report", "", "- none"] (out / "conflict_report.md").write_text("\n".join(conflict_lines), encoding="utf-8") (out / "license_attribution.json").write_text(json.dumps(pack.attribution, indent=2), encoding="utf-8") + if isinstance(pack.attribution.get("groundrecall_query_bundle"), dict): + (out / "groundrecall_query_bundle.json").write_text( + json.dumps(pack.attribution["groundrecall_query_bundle"], indent=2), + encoding="utf-8", + ) def write_source_corpus(course: NormalizedCourse, outdir: str | Path) -> None: diff --git a/tests/test_pack_emitter.py b/tests/test_pack_emitter.py index b1ff8b0..aa08cb7 100644 --- a/tests/test_pack_emitter.py +++ b/tests/test_pack_emitter.py @@ -27,3 +27,29 @@ def test_emit_pack(tmp_path: Path) -> None: assert (tmp_path / "review_report.md").exists() assert (tmp_path / "source_corpus.json").exists() assert (tmp_path / "knowledge_graph.json").exists() + + +def test_emit_pack_can_write_groundrecall_query_bundle(tmp_path: Path) -> None: + course = parse_markdown_course(SAMPLE, "Sample Course") + concepts = extract_concept_candidates(course) + ctx = RuleContext(course=course, concepts=concepts) + run_rules(ctx, build_default_rules()) + groundrecall_bundle = { + "bundle_kind": "groundrecall_query_bundle", + "concept": {"concept_id": "concept::topic-a", "title": "Topic A"}, + "review_candidates": [{"candidate_id": "concept::topic-a", "rationale": "Topic A | lane=knowledge_capture | priority=20"}], + } + draft = build_draft_pack( + course, + ctx.concepts, + "Tester", + "REVIEW", + ctx.review_flags, + groundrecall_query_bundle=groundrecall_bundle, + ) + write_draft_pack(draft, tmp_path) + + pack_yaml = (tmp_path / "pack.yaml").read_text(encoding="utf-8") + bundle_payload = (tmp_path / "groundrecall_query_bundle.json").read_text(encoding="utf-8") + assert "groundrecall_query_bundle.json" in pack_yaml + assert '"bundle_kind": "groundrecall_query_bundle"' in bundle_payload