From a30559c219bb1a88cb9775d9ec76f5bd8bb0af26 Mon Sep 17 00:00:00 2001
From: Jude Thomas Zeng <98306986+1613358894@users.noreply.github.com>
Date: Mon, 3 Aug 2026 23:08:46 +0800
Subject: [PATCH] Suggest execute validation for missing ZIP smoke
---
docs/guide.md | 4 ++++
docs/reference.md | 2 +-
tests/test_cli_commands.py | 4 ++++
tests/test_cli_download_smoke.py | 33 ++++++++++++++++++++++++++++++
typetreeflow/commands_cli.py | 4 ++++
typetreeflow/download_smoke_cli.py | 26 +++++++++++++++++++++++
6 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/docs/guide.md b/docs/guide.md
index f735826..be82471 100644
--- a/docs/guide.md
+++ b/docs/guide.md
@@ -1524,6 +1524,10 @@ point to the next local `coverage-pipeline server-validation-result
review-queue` handoff for the written inspection directory, using
`download_smoke_review_queue.tsv` inside that inspection directory as the
default output path.
+When ZIP outputs are missing and the sibling
+`bounded_download_smoke_commands.tsv` is present, the summary also includes a
+validation-only `recommended_execution_validation_request` for
+`download-smoke execute`; it does not include `--execute` and does not download.
The inspection does not run `datasets`, extract ZIPs, write raw sequence text,
access the network, contact providers, or mutate workflow outputs.
diff --git a/docs/reference.md b/docs/reference.md
index b39d8b5..869d08d 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -1559,7 +1559,7 @@ Recommended layout:
- `bounded_download_smoke_execution_summary.json`: read-only JSON summary for `download-smoke execute`. Stable fields include `schema_version`, `command`, `source_commands_manifest_path`, `limit`, `selected_row_count`, `command_valid_count`, `command_invalid_count`, `execute_requested`, `executed_command_count`, `datasets_zip_ready_for_inspection_count`, `status_counts`, `ready`, `blockers`, `execution_boundary`, `safe_for_unattended_download`, `recommended_inspection_request_target`, `recommended_inspection_request`, `recommended_inspection_request_blockers`, `recommended_inspection_next_command`, `recommended_inspection_command`, `downloads_triggered`, `providers_contacted`, `network_access`, `external_tools`, `manifest_mutated`, `strict_scientific_deliverable`, and legacy `recommended_next_command`. When execution audit outputs are written and the sibling `bounded_download_smoke_plan.tsv` is present, the structured `recommended_inspection_*` fields point to the matching local `download-smoke inspect` handoff. If the sibling plan is absent, the request fields stay empty and `recommended_inspection_request_blockers` includes `bounded_download_smoke_plan_missing`. Dry-run validation leaves `downloads_triggered=0`, `network_access=false`, and `external_tools=false`. Explicit execution may set those fields when commands run, but it still writes only isolated audit outputs and requires later ZIP/FASTA inspection before any bounded-smoke quality decision.
`--download-smoke-execution-dir
` is an explicit read-only report/package surface for these two files. Report-only mode may show `## Bounded Download Smoke Execution`; `package-results --include reports` and `--include all` may copy validated members under `download_smoke/` with `evidence_policy=download_smoke_execution_audit` and `strict_scientific_deliverable=false`. Missing input is omitted; partial or malformed input copies only valid members and emits a compact warning. Failed-handoff packages exclude the execution files.
- `bounded_download_smoke_inspection.tsv`: local inspection rows for a bounded smoke plan after a separately authorized download attempt. Fields are `record_id`, `assembly_accession`, `assembly_level`, `refseq_category`, `quality_tier`, `zip_path`, `zip_exists`, `zip_valid`, `unsafe_zip_member_count`, `genome_fasta_present`, `genome_fasta_member_count`, `genomic_named_fasta_member_count`, `genome_fasta_install_selection_status`, `fasta_record_count`, `fasta_total_bases`, `fasta_longest_record_bases`, `fasta_n50_bases`, `fasta_ambiguous_bases`, `fasta_header_wgs_keyword_count`, `fasta_header_scaffold_keyword_count`, `fasta_header_contig_keyword_count`, `empty_genome_fasta_count`, `multiple_genome_fasta_members_count`, `fasta_fragmentation_signal`, `fasta_quality_gate_blockers`, `installable_genome_fasta_ready`, `installable_genome_fasta_not_ready_reasons`, and `status`. `assembly_level`, `refseq_category`, and `quality_tier` are copied from annotated bounded smoke plans when available and otherwise use `unknown`. `unsafe_zip_member_count` is a count-only fail-closed signal for absolute, path-traversing, Windows-drive-prefixed, or symlink-like ZIP members; row status becomes `zip_unsafe_members` and blocker `unsafe_zip_member_paths` is reported before FASTA inspection. `genome_fasta_install_selection_status` is `selected`, `ambiguous`, or `not_evaluated` and mirrors the later reference-genome installer selection rule: one FASTA member is selectable, more than one member is selectable only when exactly one member is named `genomic.fna` or `*_genomic.fna`. When the source member is uniquely selectable, FASTA quality statistics describe that installable member; member-count fields still describe all FASTA members in the ZIP. `fasta_quality_gate_blockers` is empty unless explicit local quality gates are supplied, then contains semicolon-separated controlled blocker codes for that row. `installable_genome_fasta_ready` is a row-level local smoke signal derived from ZIP validity, unsafe-member checks, genome FASTA presence, install-selection status, empty-FASTA checks, and enabled quality-gate blockers; `installable_genome_fasta_not_ready_reasons` contains semicolon-separated controlled reason codes when the row is not ready. The FASTA statistics, N50 value, header keyword counts, unsafe-member count, empty-FASTA count, multiple-member count, install-selection status, fragmentation signal, row-level readiness, and row-level blocker/reason codes are counts-only audit visibility; raw sequence text and raw FASTA headers are not copied into the inspection files. Readers accept earlier inspection TSVs that lack `assembly_level`, `refseq_category`, `quality_tier`, `unsafe_zip_member_count`, `genome_fasta_install_selection_status`, `genomic_named_fasta_member_count`, `multiple_genome_fasta_members_count`, `empty_genome_fasta_count`, `fasta_quality_gate_blockers`, `installable_genome_fasta_ready`, or `installable_genome_fasta_not_ready_reasons`.
-- `bounded_download_smoke_inspection_summary.json`: read-only JSON summary for `download-smoke inspect`. Stable fields include `schema_version`, `command`, `source_download_plan_path`, `selected_row_count`, `zip_exists_count`, `zip_valid_count`, `unsafe_zip_member_count`, `genome_fasta_present_count`, `genome_fasta_member_count`, `genomic_named_fasta_member_count`, `genome_fasta_install_selection_status_counts`, `genome_fasta_install_selection_ambiguous_count`, `installable_genome_fasta_ready_count`, `installable_genome_fasta_not_ready_count`, `installable_genome_fasta_not_ready_reason_counts`, `installable_genome_fasta_ready_preview`, `installable_genome_fasta_ready_preview_truncated`, `installable_genome_fasta_not_ready_preview`, `installable_genome_fasta_not_ready_preview_truncated`, `assembly_metadata_high_quality_row_count`, `assembly_metadata_high_quality_installable_ready_count`, `assembly_metadata_high_quality_fasta_quality_blocked_count`, `assembly_metadata_high_quality_fasta_quality_blocker_counts`, `assembly_metadata_high_quality_fasta_quality_blocked_preview`, `assembly_metadata_high_quality_fasta_quality_blocked_preview_truncated`, `fasta_record_count`, `fasta_total_bases`, `fasta_longest_record_bases`, `fasta_max_n50_bases`, `fasta_ambiguous_bases`, `fasta_header_wgs_keyword_count`, `fasta_header_scaffold_keyword_count`, `fasta_header_contig_keyword_count`, `empty_genome_fasta_count`, `multiple_genome_fasta_members_count`, `fasta_fragmentation_signal_counts`, `installable_genome_fasta_fragmentation_signal_counts`, `installable_genome_fasta_header_fragment_keyword_row_count`, `min_fasta_n50_bases`, `max_fasta_record_count`, `max_fasta_ambiguous_bases`, `min_fasta_total_bases`, `min_fasta_longest_record_bases`, `quality_profile`, `block_fragmented_fasta`, `block_fasta_header_keywords`, `fasta_n50_below_minimum_count`, `fasta_record_count_above_maximum_count`, `fasta_ambiguous_bases_above_maximum_count`, `fasta_total_bases_below_minimum_count`, `fasta_longest_record_below_minimum_count`, `fragmented_fasta_signal_count`, `fasta_header_fragment_keyword_row_count`, `fasta_quality_gate_passed_row_count`, `fasta_quality_gate_blocked_row_count`, `fasta_quality_gate_blocker_counts`, `quality_gate_recommendation`, `quality_gate_recommendation_reasons`, `recommended_quality_gate_request_target`, `recommended_quality_gate_request`, `recommended_quality_gate_next_command`, `recommended_quality_gate_command`, `recommended_review_queue_request_target`, `recommended_review_queue_request`, `recommended_review_queue_next_command`, `bounded_smoke_next_action`, `bounded_smoke_next_action_reasons`, `status_counts`, `ready`, `blockers`, `execution_boundary`, `safe_for_unattended_download`, `downloads_triggered`, `providers_contacted`, `network_access`, `external_tools`, `manifest_mutated`, and `strict_scientific_deliverable`. `installable_genome_fasta_ready_count` counts rows with a uniquely selectable non-empty genome FASTA source and no enabled FASTA quality-gate blockers; `installable_genome_fasta_not_ready_count` counts the remaining selected rows, including missing, invalid, unsafe, empty, ambiguous, missing-FASTA, or quality-gate-blocked rows. `installable_genome_fasta_not_ready_reason_counts` maps those non-ready rows to controlled blocker-style reason counts such as missing ZIPs, invalid ZIPs, unsafe ZIP members, missing or empty genome FASTA, ambiguous install selection, and enabled FASTA quality-gate blockers. The ready and not-ready previews list at most five rows each using only record IDs, accessions, assembly metadata, row status, controlled not-ready reasons, controlled quality blocker codes, and fragmentation signal; they do not include ZIP paths, raw FASTA headers, or sequence text. `assembly_metadata_high_quality_*` fields separately summarize rows selected from high-quality assembly metadata, including how many remain locally FASTA-installable and how many are blocked by local FASTA quality gates; this prevents Complete Genome or Chromosome metadata from being mistaken for automatic local FASTA acceptance. The high-quality blocked preview lists at most five affected rows using only record IDs, accessions, assembly metadata, row status, and controlled blocker codes; it does not include raw FASTA headers or sequence text. The `recommended_quality_gate_*` fields are populated when the inspection observed fragmentation/header signals without the matching active gates; controllers can pass `recommended_quality_gate_request` to `commands render`, while `recommended_quality_gate_command` remains a compatibility argv list. The `recommended_review_queue_*` fields are populated only when `inspect --write` has written an isolated inspection directory with bounded rows, and point to the local `coverage-pipeline server-validation-result review-queue` handoff. `bounded_smoke_next_action` is a controlled AI/operator routing hint such as `rerun_with_fragmentation_quality_gates`, `review_high_quality_metadata_fasta_quality_blockers`, `review_or_rerun_bounded_download_outputs`, `prepare_bounded_download_smoke_plan`, or `review_bounded_smoke_outputs`; it does not authorize downloads, provider access, manifest mutation, installation, or strict deliverable changes. The default `quality_profile` is `fragmentation`, which enables fragmented FASTA and WGS/scaffold/contig header keyword blockers so obvious scaffold/contig/WGS-like outputs fail bounded-smoke readiness by default. `--quality-profile none` preserves compatibility/visibility-only behavior and may return `quality_gate_recommendation=rerun_with_fragmentation_quality_gates` when those signals are observed. `installable_genome_fasta_fragmentation_signal_counts` and `installable_genome_fasta_header_fragment_keyword_row_count` summarize fragmentation/header keyword signals only among rows that remain locally installable under the active quality gates. `fasta_n50_bases`, header keyword counts, and `fasta_fragmentation_signal` are descriptive fields (`single_record`, `multi_record_single_dominant`, `multi_record_fragmented`, or `not_evaluated`). Unsafe ZIP member paths are blocked by default as `unsafe_zip_member_paths`, with row status `zip_unsafe_members`; no FASTA member content is inspected for that row. A genome FASTA member with zero records or zero bases is blocked by default as `empty_genome_fasta_outputs`, with row status `genome_fasta_empty`. A bounded row with more than one genome FASTA member remains visible through `multiple_genome_fasta_members_count`; it is blocked by default only when the installer selection rule cannot uniquely choose the source member, reported as `genome_fasta_install_selection_ambiguous` with row status `genome_fasta_multiple_members`. Optional flags `--min-fasta-n50-bases`, `--max-fasta-record-count`, `--max-fasta-ambiguous-bases`, `--min-fasta-total-bases`, `--min-fasta-longest-record-bases`, `--block-fragmented-fasta`, and `--block-fasta-header-keywords` can add or repeat local bounded smoke blockers for obvious short, ambiguous-base-heavy, scaffold/contig, or WGS-style outputs, and the quality-gate passed/blocked row counts plus blocker-count map summarize those controlled row blockers. `--quality-profile fragmentation` is shorthand for enabling the fragmented FASTA and WGS/scaffold/contig header keyword blockers. These readiness blockers are local smoke acceptance criteria only; they do not change strict status, completion, or final genome usability. The inspection only checks local ZIP existence, ZIP validity, unsafe member paths, whether genome FASTA members are present, install-selection ambiguity, and bounded FASTA count statistics; it does not run `datasets`, extract ZIPs, write raw sequences, access the network, contact providers, mutate manifests, or create strict deliverables.
+- `bounded_download_smoke_inspection_summary.json`: read-only JSON summary for `download-smoke inspect`. Stable fields include `schema_version`, `command`, `source_download_plan_path`, `selected_row_count`, `zip_exists_count`, `zip_valid_count`, `unsafe_zip_member_count`, `genome_fasta_present_count`, `genome_fasta_member_count`, `genomic_named_fasta_member_count`, `genome_fasta_install_selection_status_counts`, `genome_fasta_install_selection_ambiguous_count`, `installable_genome_fasta_ready_count`, `installable_genome_fasta_not_ready_count`, `installable_genome_fasta_not_ready_reason_counts`, `installable_genome_fasta_ready_preview`, `installable_genome_fasta_ready_preview_truncated`, `installable_genome_fasta_not_ready_preview`, `installable_genome_fasta_not_ready_preview_truncated`, `assembly_metadata_high_quality_row_count`, `assembly_metadata_high_quality_installable_ready_count`, `assembly_metadata_high_quality_fasta_quality_blocked_count`, `assembly_metadata_high_quality_fasta_quality_blocker_counts`, `assembly_metadata_high_quality_fasta_quality_blocked_preview`, `assembly_metadata_high_quality_fasta_quality_blocked_preview_truncated`, `fasta_record_count`, `fasta_total_bases`, `fasta_longest_record_bases`, `fasta_max_n50_bases`, `fasta_ambiguous_bases`, `fasta_header_wgs_keyword_count`, `fasta_header_scaffold_keyword_count`, `fasta_header_contig_keyword_count`, `empty_genome_fasta_count`, `multiple_genome_fasta_members_count`, `fasta_fragmentation_signal_counts`, `installable_genome_fasta_fragmentation_signal_counts`, `installable_genome_fasta_header_fragment_keyword_row_count`, `min_fasta_n50_bases`, `max_fasta_record_count`, `max_fasta_ambiguous_bases`, `min_fasta_total_bases`, `min_fasta_longest_record_bases`, `quality_profile`, `block_fragmented_fasta`, `block_fasta_header_keywords`, `fasta_n50_below_minimum_count`, `fasta_record_count_above_maximum_count`, `fasta_ambiguous_bases_above_maximum_count`, `fasta_total_bases_below_minimum_count`, `fasta_longest_record_below_minimum_count`, `fragmented_fasta_signal_count`, `fasta_header_fragment_keyword_row_count`, `fasta_quality_gate_passed_row_count`, `fasta_quality_gate_blocked_row_count`, `fasta_quality_gate_blocker_counts`, `quality_gate_recommendation`, `quality_gate_recommendation_reasons`, `recommended_quality_gate_request_target`, `recommended_quality_gate_request`, `recommended_quality_gate_next_command`, `recommended_quality_gate_command`, `recommended_review_queue_request_target`, `recommended_review_queue_request`, `recommended_review_queue_next_command`, `recommended_execution_validation_request_target`, `recommended_execution_validation_request`, `recommended_execution_validation_next_command`, `recommended_execution_validation_command`, `bounded_smoke_next_action`, `bounded_smoke_next_action_reasons`, `status_counts`, `ready`, `blockers`, `execution_boundary`, `safe_for_unattended_download`, `downloads_triggered`, `providers_contacted`, `network_access`, `external_tools`, `manifest_mutated`, and `strict_scientific_deliverable`. `installable_genome_fasta_ready_count` counts rows with a uniquely selectable non-empty genome FASTA source and no enabled FASTA quality-gate blockers; `installable_genome_fasta_not_ready_count` counts the remaining selected rows, including missing, invalid, unsafe, empty, ambiguous, missing-FASTA, or quality-gate-blocked rows. `installable_genome_fasta_not_ready_reason_counts` maps those non-ready rows to controlled blocker-style reason counts such as missing ZIPs, invalid ZIPs, unsafe ZIP members, missing or empty genome FASTA, ambiguous install selection, and enabled FASTA quality-gate blockers. The ready and not-ready previews list at most five rows each using only record IDs, accessions, assembly metadata, row status, controlled not-ready reasons, controlled quality blocker codes, and fragmentation signal; they do not include ZIP paths, raw FASTA headers, or sequence text. `assembly_metadata_high_quality_*` fields separately summarize rows selected from high-quality assembly metadata, including how many remain locally FASTA-installable and how many are blocked by local FASTA quality gates; this prevents Complete Genome or Chromosome metadata from being mistaken for automatic local FASTA acceptance. The high-quality blocked preview lists at most five affected rows using only record IDs, accessions, assembly metadata, row status, and controlled blocker codes; it does not include raw FASTA headers or sequence text. The `recommended_quality_gate_*` fields are populated when the inspection observed fragmentation/header signals without the matching active gates; controllers can pass `recommended_quality_gate_request` to `commands render`, while `recommended_quality_gate_command` remains a compatibility argv list. The `recommended_review_queue_*` fields are populated only when `inspect --write` has written an isolated inspection directory with bounded rows, and point to the local `coverage-pipeline server-validation-result review-queue` handoff. The `recommended_execution_validation_*` fields are populated when ZIP outputs are missing and the sibling `bounded_download_smoke_commands.tsv` exists, and point to validation-only `download-smoke execute` without `--execute`; that handoff validates the pinned command manifest and does not download. `bounded_smoke_next_action` is a controlled AI/operator routing hint such as `rerun_with_fragmentation_quality_gates`, `review_high_quality_metadata_fasta_quality_blockers`, `review_or_rerun_bounded_download_outputs`, `prepare_bounded_download_smoke_plan`, or `review_bounded_smoke_outputs`; it does not authorize downloads, provider access, manifest mutation, installation, or strict deliverable changes. The default `quality_profile` is `fragmentation`, which enables fragmented FASTA and WGS/scaffold/contig header keyword blockers so obvious scaffold/contig/WGS-like outputs fail bounded-smoke readiness by default. `--quality-profile none` preserves compatibility/visibility-only behavior and may return `quality_gate_recommendation=rerun_with_fragmentation_quality_gates` when those signals are observed. `installable_genome_fasta_fragmentation_signal_counts` and `installable_genome_fasta_header_fragment_keyword_row_count` summarize fragmentation/header keyword signals only among rows that remain locally installable under the active quality gates. `fasta_n50_bases`, header keyword counts, and `fasta_fragmentation_signal` are descriptive fields (`single_record`, `multi_record_single_dominant`, `multi_record_fragmented`, or `not_evaluated`). Unsafe ZIP member paths are blocked by default as `unsafe_zip_member_paths`, with row status `zip_unsafe_members`; no FASTA member content is inspected for that row. A genome FASTA member with zero records or zero bases is blocked by default as `empty_genome_fasta_outputs`, with row status `genome_fasta_empty`. A bounded row with more than one genome FASTA member remains visible through `multiple_genome_fasta_members_count`; it is blocked by default only when the installer selection rule cannot uniquely choose the source member, reported as `genome_fasta_install_selection_ambiguous` with row status `genome_fasta_multiple_members`. Optional flags `--min-fasta-n50-bases`, `--max-fasta-record-count`, `--max-fasta-ambiguous-bases`, `--min-fasta-total-bases`, `--min-fasta-longest-record-bases`, `--block-fragmented-fasta`, and `--block-fasta-header-keywords` can add or repeat local bounded smoke blockers for obvious short, ambiguous-base-heavy, scaffold/contig, or WGS-style outputs, and the quality-gate passed/blocked row counts plus blocker-count map summarize those controlled row blockers. `--quality-profile fragmentation` is shorthand for enabling the fragmented FASTA and WGS/scaffold/contig header keyword blockers. These readiness blockers are local smoke acceptance criteria only; they do not change strict status, completion, or final genome usability. The inspection only checks local ZIP existence, ZIP validity, unsafe member paths, whether genome FASTA members are present, install-selection ambiguity, and bounded FASTA count statistics; it does not run `datasets`, extract ZIPs, write raw sequences, access the network, contact providers, mutate manifests, or create strict deliverables.
`--download-smoke-inspection-dir ` is an explicit read-only report/package surface for these two files. Report-only mode may show `## Bounded Download Smoke Inspection`; `package-results --include reports` and `--include all` may copy validated members under `download_smoke/` with `evidence_policy=download_smoke_inspection_audit` and `strict_scientific_deliverable=false`. Missing input is omitted; partial or malformed input copies only valid members and emits a compact warning. Failed-handoff packages exclude the inspection files.
- `download_smoke_quality_review_decisions.tsv`: optional local decision template written by `coverage-pipeline server-validation-result quality-review-template --write --out `. It copies `record_id` and `assembly_accession` from `download_smoke_review_queue_triage.tsv` and leaves `quality_review_decision`, `decision_reason_code`, `reviewer_id`, and `reviewed_at` empty. The template is not a completed review and is rejected by `quality-review` until every row is filled with controlled decision/reason, reviewer, and date values.
- `download_smoke_quality_review.tsv`, `download_smoke_quality_review_summary.json`, and `download_smoke_quality_review_diagnostics.tsv`: isolated audit triplet written by `coverage-pipeline server-validation-result quality-review --write --outdir `. The TSV records exact triage-to-decision linkage, controlled `quality_review_decision` and `decision_reason_code`, reviewer id, review date, and fixed false boundaries for final-use acceptance, strict upgrade, and manifest mutation. The summary repeats controlled decision/reason counts and fixed boundaries: `audit_only=true`, `accepted_for_final_use=false`, `strict_upgrade_applied=false`, `manifest_mutated=false`, `downloads_triggered=0`, `providers_contacted=0`, `network_access=false`, and `external_tools=false`. The diagnostics TSV uses controlled component/code/severity/accession fields only. `--download-smoke-quality-review-dir ` is the explicit report/package surface for this triplet. Report-only mode may show `## Bounded Download Smoke Quality Review`; `package-results --include reports` and `--include all` may copy validated members under `download_smoke/` with `evidence_policy=download_smoke_quality_review_audit` and `strict_scientific_deliverable=false`. Missing input is omitted; partial or malformed input copies only valid members and emits a compact warning. Failed-handoff packages exclude the quality-review files. A bounded-smoke quality acceptance is not final genome acceptance, not download authorization, and not strict deliverable promotion.
diff --git a/tests/test_cli_commands.py b/tests/test_cli_commands.py
index 4acbfe9..cba116b 100644
--- a/tests/test_cli_commands.py
+++ b/tests/test_cli_commands.py
@@ -2666,6 +2666,10 @@ def test_commands_render_emits_download_smoke_quality_gate_argv(capsys):
)
assert "recommended_quality_gate_request" in summary_fields
assert "recommended_review_queue_request" in summary_fields
+ assert "recommended_execution_validation_request_target" in summary_fields
+ assert "recommended_execution_validation_request" in summary_fields
+ assert "recommended_execution_validation_next_command" in summary_fields
+ assert "recommended_execution_validation_command" in summary_fields
assert "bounded_smoke_next_action" in summary_fields
assert "bounded_smoke_next_action_reasons" in summary_fields
diff --git a/tests/test_cli_download_smoke.py b/tests/test_cli_download_smoke.py
index 0412dfe..d8ab02d 100644
--- a/tests/test_cli_download_smoke.py
+++ b/tests/test_cli_download_smoke.py
@@ -712,6 +712,39 @@ def test_download_smoke_inspect_accepts_annotated_prepare_plan(capsys, tmp_path)
assert rows[0]["quality_tier"] == "high"
assert summary["selected_row_count"] == 1
assert summary["blockers"] == ["missing_zip_outputs"]
+ execution_outdir = outdir / "execution"
+ assert summary["recommended_execution_validation_request_target"] == (
+ "download-smoke execute"
+ )
+ assert summary["recommended_execution_validation_request"] == {
+ "command": "download-smoke",
+ "subcommand": "execute",
+ "commands_manifest": str(outdir / "bounded_download_smoke_commands.tsv"),
+ "limit": 1,
+ "write": True,
+ "outdir": str(execution_outdir),
+ }
+ assert summary["recommended_execution_validation_next_command"] == (
+ "typetreeflow download-smoke execute --commands-manifest "
+ f"{outdir / 'bounded_download_smoke_commands.tsv'} "
+ f"--limit 1 --write --outdir {execution_outdir}"
+ )
+ assert "--execute" not in summary["recommended_execution_validation_command"]
+ assert (
+ main(
+ [
+ "commands",
+ "render",
+ "--request-json",
+ json.dumps(summary["recommended_execution_validation_request"]),
+ ]
+ )
+ == 0
+ )
+ rendered = json.loads(capsys.readouterr().out)
+ assert rendered["target_argv"] == (
+ summary["recommended_execution_validation_command"][1:]
+ )
def test_download_smoke_prepare_write_carries_inspection_quality_gates(
diff --git a/typetreeflow/commands_cli.py b/typetreeflow/commands_cli.py
index 8811604..75882d9 100644
--- a/typetreeflow/commands_cli.py
+++ b/typetreeflow/commands_cli.py
@@ -1171,6 +1171,10 @@
"recommended_review_queue_request_target",
"recommended_review_queue_request",
"recommended_review_queue_next_command",
+ "recommended_execution_validation_request_target",
+ "recommended_execution_validation_request",
+ "recommended_execution_validation_next_command",
+ "recommended_execution_validation_command",
"bounded_smoke_next_action",
"bounded_smoke_next_action_reasons",
"status_counts",
diff --git a/typetreeflow/download_smoke_cli.py b/typetreeflow/download_smoke_cli.py
index eed003b..8208a92 100644
--- a/typetreeflow/download_smoke_cli.py
+++ b/typetreeflow/download_smoke_cli.py
@@ -719,6 +719,10 @@ def _bounded_plan_path_for_commands_manifest(commands_manifest_path: str | Path)
return Path(commands_manifest_path).parent / OUTPUT_PLAN_NAME
+def _bounded_commands_path_for_plan(bounded_plan_path: str | Path) -> Path:
+ return Path(bounded_plan_path).parent / OUTPUT_COMMANDS_NAME
+
+
def _recommended_inspection_command(
bounded_plan_path: str | Path,
*,
@@ -1050,6 +1054,16 @@ def inspect_bounded_download_smoke_outputs(
recommended_quality_gate_command = _recommended_inspection_command_from_request(
recommended_quality_gate_request
)
+ recommended_execution_validation_request: dict[str, object] = {}
+ if "missing_zip_outputs" in blockers:
+ commands_manifest_path = _bounded_commands_path_for_plan(plan_path)
+ if commands_manifest_path.exists():
+ recommended_execution_validation_request = (
+ _recommended_execution_validation_request(
+ commands_manifest_path,
+ limit=len(rows),
+ )
+ )
ready = not blockers
next_action, next_action_reasons = _bounded_smoke_next_action(
@@ -1207,6 +1221,18 @@ def inspect_bounded_download_smoke_outputs(
"recommended_review_queue_request_target": "",
"recommended_review_queue_request": {},
"recommended_review_queue_next_command": "",
+ "recommended_execution_validation_request_target": (
+ EXECUTE_COMMAND if recommended_execution_validation_request else ""
+ ),
+ "recommended_execution_validation_request": (
+ recommended_execution_validation_request
+ ),
+ "recommended_execution_validation_next_command": _recommended_next_command(
+ recommended_execution_validation_request
+ ),
+ "recommended_execution_validation_command": _recommended_command_from_request(
+ recommended_execution_validation_request
+ ),
"bounded_smoke_next_action": next_action,
"bounded_smoke_next_action_reasons": next_action_reasons,
"status_counts": dict(sorted(status_counts.items())),