Prefill world-class submission artifact hashes

This commit is contained in:
yaojingang
2026-06-16 03:58:05 +08:00
parent 7b21b9f633
commit fa4067771b
6 changed files with 152 additions and 10 deletions
+109 -9
View File
@@ -3,7 +3,6 @@ import argparse
import hashlib
import json
import shlex
import shutil
from datetime import date
from pathlib import Path
from typing import Any
@@ -59,12 +58,60 @@ def template_result_by_key(intake: dict[str, Any]) -> dict[str, dict[str, Any]]:
return {str(item.get("evidence_key")): item for item in intake.get("templates", [])}
def artifact_rows_by_key_and_path(rows: list[dict[str, Any]]) -> dict[tuple[str, str], dict[str, Any]]:
ready: dict[tuple[str, str], dict[str, Any]] = {}
for row in rows:
key = str(row.get("evidence_key", ""))
path = str(row.get("path", ""))
if key and path and row.get("artifact_ref_ready"):
ready[(key, path)] = row
return ready
def load_template_payload(path: Path, errors: list[str]) -> dict[str, Any] | None:
try:
payload = json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError:
errors.append("template file is not valid JSON")
return None
return payload if isinstance(payload, dict) else None
def prefill_artifact_refs(
payload: dict[str, Any],
evidence_key: str,
ready_rows: dict[tuple[str, str], dict[str, Any]],
) -> dict[str, int]:
refs = payload.get("artifact_refs", [])
stats = {
"prefilled_artifact_ref_count": 0,
"unfilled_artifact_ref_count": 0,
}
if not isinstance(refs, list):
return stats
for ref in refs:
if not isinstance(ref, dict):
stats["unfilled_artifact_ref_count"] += 1
continue
path = str(ref.get("path", "")).strip()
row = ready_rows.get((evidence_key, path))
if row and row.get("sha256"):
ref["sha256"] = row["sha256"]
ref["contains_raw_content"] = False
stats["prefilled_artifact_ref_count"] += 1
else:
stats["unfilled_artifact_ref_count"] += 1
return stats
def copy_template(
skill_dir: Path,
output_dir: Path,
item: dict[str, Any],
template_results: dict[str, dict[str, Any]],
ready_rows: dict[tuple[str, str], dict[str, Any]],
overwrite: bool,
prefill_artifacts: bool,
) -> dict[str, Any]:
key = str(item.get("evidence_key", ""))
template_result = template_results.get(key, {})
@@ -83,6 +130,8 @@ def copy_template(
"status": "skipped",
"template_path": rel_path(template_path, skill_dir),
"output_path": rel_path(output_path, skill_dir),
"prefilled_artifact_ref_count": 0,
"unfilled_artifact_ref_count": 0,
"errors": errors,
}
@@ -92,16 +141,36 @@ def copy_template(
"status": "exists",
"template_path": rel_path(template_path, skill_dir),
"output_path": rel_path(output_path, skill_dir),
"prefilled_artifact_ref_count": 0,
"unfilled_artifact_ref_count": 0,
"errors": [],
}
payload = load_template_payload(template_path, errors)
if errors or payload is None:
return {
"evidence_key": key,
"status": "skipped",
"template_path": rel_path(template_path, skill_dir),
"output_path": rel_path(output_path, skill_dir),
"prefilled_artifact_ref_count": 0,
"unfilled_artifact_ref_count": 0,
"errors": errors or ["template file is not a JSON object"],
}
prefill_stats = (
prefill_artifact_refs(payload, key, ready_rows)
if prefill_artifacts
else {"prefilled_artifact_ref_count": 0, "unfilled_artifact_ref_count": 0}
)
output_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(template_path, output_path)
write_json(output_path, payload)
return {
"evidence_key": key,
"status": "written",
"template_path": rel_path(template_path, skill_dir),
"output_path": rel_path(output_path, skill_dir),
**prefill_stats,
"errors": [],
}
@@ -210,6 +279,7 @@ def render_readme(report: dict[str, Any]) -> str:
"3. Set `template_only` to `false` only after real evidence exists.",
"4. Set attestation booleans truthfully; do not include credentials, raw prompts, raw outputs, transcripts, notes, or private user content.",
"5. Validate the packet before asking the ledger reviewer to accept it.",
"6. Optional artifact prefill only inserts SHA-256 digests for current local aggregate artifacts; it does not mark a draft as real evidence.",
"",
"## Commands",
"",
@@ -219,11 +289,13 @@ def render_readme(report: dict[str, Any]) -> str:
"",
"## Drafts",
"",
"| Evidence | Draft | Status |",
"| --- | --- | --- |",
"| Evidence | Draft | Status | Prefilled refs |",
"| --- | --- | --- | ---: |",
]
for item in report["files"]:
lines.append(f"| `{item['evidence_key']}` | `{item['output_path']}` | `{item['status']}` |")
lines.append(
f"| `{item['evidence_key']}` | `{item['output_path']}` | `{item['status']}` | `{item.get('prefilled_artifact_ref_count', 0)}` |"
)
lines.extend(["", "## Execution Runbook", ""])
for item in report.get("evidence_items", []):
must_collect = item.get("must_collect", {}) if isinstance(item.get("must_collect", {}), dict) else {}
@@ -304,6 +376,7 @@ def render_html_files(files: list[dict[str, Any]]) -> str:
<dl>
<dt>Template</dt><dd><code>{template}</code></dd>
<dt>Draft</dt><dd><code>{output}</code></dd>
<dt>Prefill</dt><dd>{prefill} artifact refs</dd>
</dl>
{errors}
</article>
@@ -312,6 +385,7 @@ def render_html_files(files: list[dict[str, Any]]) -> str:
key=html_text(item.get("evidence_key", "")),
template=html_text(item.get("template_path", "")),
output=html_text(item.get("output_path", "")),
prefill=html_text(item.get("prefilled_artifact_ref_count", 0)),
errors=(
"<ul class=\"errors\">"
+ render_html_list(item.get("errors", []), "No errors.")
@@ -428,6 +502,7 @@ def render_html(report: dict[str, Any]) -> str:
("Existing", summary["existing_count"]),
("Skipped", summary["skipped_count"]),
("Artifacts", f"{artifact_ready}/{artifact_total}"),
("Prefilled", summary.get("artifact_ref_prefill_count", 0)),
]
stat_html = "".join(f"<article><span>{html_text(label)}</span><strong>{html_text(value)}</strong></article>" for label, value in stats)
evidence_html = "".join(render_html_item(item) for item in report.get("evidence_items", []))
@@ -457,7 +532,7 @@ def render_html(report: dict[str, Any]) -> str:
h3 {{ margin:4px 0 10px; font-size:22px; }}
h4 {{ margin:0 0 8px; font-size:16px; }}
.lede {{ max-width:800px; color:var(--muted); font-size:20px; }}
.stats {{ display:grid; grid-template-columns:repeat(5, minmax(0,1fr)); gap:12px; margin:26px 0 0; }}
.stats {{ display:grid; grid-template-columns:repeat(6, minmax(0,1fr)); gap:12px; margin:26px 0 0; }}
.stats article, .panel, .draft-card, .evidence-card {{ border:1px solid var(--line); border-radius:8px; background:#fff; }}
.stats article {{ padding:16px; }}
.stats span, .draft-card span, .evidence-card span, .muted {{ color:var(--muted); }}
@@ -495,11 +570,11 @@ def render_html(report: dict[str, Any]) -> str:
<section class="hero">
<span class="eyebrow">Evidence Intake</span>
<h1>World-Class Evidence Submission Kit</h1>
<p class="lede">Use this cockpit to prepare human and external evidence packets. Drafts are not accepted evidence, and this page never changes the ledger result.</p>
<p class="lede">Use this cockpit to prepare human and external evidence packets. Drafts are not accepted evidence, artifact prefill only inserts local SHA-256 digests, and this page never changes the ledger result.</p>
<div class="stats">{stat_html}</div>
</section>
<section class="section two-col" id="workflow">
<article class="panel"><h2>Workflow</h2><ol><li>Run the real provider, human review, native permission, or native client telemetry work first.</li><li>Edit the matching JSON draft with aggregate artifact references and provenance metadata.</li><li>Set template_only to false only after real evidence exists.</li><li>Validate intake, refresh the ledger, then guard public claims.</li></ol></article>
<article class="panel"><h2>Workflow</h2><ol><li>Run the real provider, human review, native permission, or native client telemetry work first.</li><li>Edit the matching JSON draft with aggregate artifact references and provenance metadata.</li><li>Set template_only to false only after real evidence exists.</li><li>Use prefilled SHA-256 values as convenience data, not evidence acceptance.</li><li>Validate intake, refresh the ledger, then guard public claims.</li></ol></article>
<aside class="panel"><h2>Commands</h2><ul class="commands">{render_html_commands(report['commands'])}</ul></aside>
</section>
<section class="section" id="drafts"><h2>Drafts</h2><div class="draft-grid">{render_html_files(report['files'])}</div></section>
@@ -519,6 +594,7 @@ def build_submission_kit(
generated_at: str,
evidence_keys: list[str] | None = None,
overwrite: bool = False,
prefill_artifacts: bool = False,
output_html: Path | None = None,
) -> dict[str, Any]:
intake = build_intake(skill_dir, generated_at, submissions_dir=output_dir)
@@ -526,8 +602,20 @@ def build_submission_kit(
valid_keys = {str(item.get("evidence_key")) for item in intake.get("operator_checklist", [])}
unknown_keys = sorted(set(evidence_keys or []) - valid_keys)
template_results = template_result_by_key(intake)
files = [copy_template(skill_dir, output_dir, item, template_results, overwrite) for item in items]
artifact_checklist = build_artifact_checklist(skill_dir, items)
ready_rows = artifact_rows_by_key_and_path(artifact_checklist)
files = [
copy_template(
skill_dir,
output_dir,
item,
template_results,
ready_rows,
overwrite,
prefill_artifacts,
)
for item in items
]
source_checklist = build_source_checklist(items)
manifest_path = output_dir / "submission_manifest.json"
readme_path = output_dir / "README.md"
@@ -535,6 +623,8 @@ def build_submission_kit(
written_count = sum(1 for item in files if item["status"] == "written")
existing_count = sum(1 for item in files if item["status"] == "exists")
skipped_count = sum(1 for item in files if item["status"] == "skipped")
prefilled_artifact_ref_count = sum(item.get("prefilled_artifact_ref_count", 0) for item in files)
unfilled_artifact_ref_count = sum(item.get("unfilled_artifact_ref_count", 0) for item in files)
artifact_ready_count = sum(1 for item in artifact_checklist if item.get("artifact_ref_ready"))
artifact_missing_count = sum(1 for item in artifact_checklist if not item.get("artifact_ref_ready"))
artifact_glob_count = sum(1 for item in artifact_checklist if item.get("concrete_reference_required"))
@@ -557,6 +647,9 @@ def build_submission_kit(
"artifact_ready_count": artifact_ready_count,
"artifact_missing_count": artifact_missing_count,
"artifact_glob_expansion_count": artifact_glob_count,
"artifact_prefill_enabled": prefill_artifacts,
"artifact_ref_prefill_count": prefilled_artifact_ref_count,
"artifact_ref_unfilled_count": unfilled_artifact_ref_count,
**source_summary,
"drafts_count_as_evidence": False,
"ledger_counts_submission_as_completion": False,
@@ -574,6 +667,7 @@ def build_submission_kit(
},
"safety": {
"template_only_drafts": True,
"artifact_prefill_counts_as_evidence": False,
"real_evidence_required_before_template_only_false": True,
"raw_content_allowed": False,
"credentials_allowed": False,
@@ -599,6 +693,11 @@ def main() -> None:
parser.add_argument("--output-dir", default="evidence/world_class/submission-kit")
parser.add_argument("--evidence-key", action="append", default=[])
parser.add_argument("--overwrite", action="store_true")
parser.add_argument(
"--prefill-artifacts",
action="store_true",
help="Insert SHA-256 digests for currently available aggregate artifacts while keeping drafts template-only.",
)
parser.add_argument("--generated-at", default=date.today().isoformat())
parser.add_argument("--output-html")
args = parser.parse_args()
@@ -613,6 +712,7 @@ def main() -> None:
args.generated_at,
evidence_keys=args.evidence_key,
overwrite=args.overwrite,
prefill_artifacts=args.prefill_artifacts,
output_html=Path(args.output_html).resolve() if args.output_html else None,
)
print(json.dumps(report, ensure_ascii=False, indent=2))
+5
View File
@@ -237,6 +237,11 @@ def build_parser(command_handlers: dict[str, Callable[[argparse.Namespace], int]
world_class_submission_kit_cmd.add_argument("--output-dir")
world_class_submission_kit_cmd.add_argument("--evidence-key", action="append", default=[])
world_class_submission_kit_cmd.add_argument("--overwrite", action="store_true")
world_class_submission_kit_cmd.add_argument(
"--prefill-artifacts",
action="store_true",
help="Insert SHA-256 digests for currently available aggregate artifacts while keeping drafts template-only.",
)
world_class_submission_kit_cmd.add_argument("--generated-at")
world_class_submission_kit_cmd.add_argument("--output-html")
world_class_submission_kit_cmd.set_defaults(func=_handler(command_handlers, "command_world_class_submission_kit"))
+2
View File
@@ -196,6 +196,8 @@ def command_world_class_submission_kit(args: argparse.Namespace) -> int:
cmd.extend(["--evidence-key", key])
if args.overwrite:
cmd.append("--overwrite")
if args.prefill_artifacts:
cmd.append("--prefill-artifacts")
if args.generated_at:
cmd.extend(["--generated-at", args.generated_at])
if args.output_html: