Clarify world-class submission artifact roles
This commit is contained in:
@@ -69,6 +69,25 @@ def artifact_rows_by_key_and_path(rows: list[dict[str, Any]]) -> dict[tuple[str,
|
||||
return ready
|
||||
|
||||
|
||||
def template_artifact_ref_paths(skill_dir: Path, item: dict[str, Any]) -> set[str]:
|
||||
template_path = skill_dir / str(item.get("template_path", ""))
|
||||
errors: list[str] = []
|
||||
payload = load_template_payload(template_path, errors)
|
||||
if payload is None:
|
||||
return set()
|
||||
refs = payload.get("artifact_refs", [])
|
||||
if not isinstance(refs, list):
|
||||
return set()
|
||||
paths: set[str] = set()
|
||||
for ref in refs:
|
||||
if not isinstance(ref, dict):
|
||||
continue
|
||||
path = str(ref.get("path", "")).strip()
|
||||
if path:
|
||||
paths.add(path)
|
||||
return paths
|
||||
|
||||
|
||||
def load_template_payload(path: Path, errors: list[str]) -> dict[str, Any] | None:
|
||||
try:
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
@@ -201,12 +220,22 @@ def artifact_row(skill_dir: Path, evidence_key: str, pattern: str, path: Path, s
|
||||
}
|
||||
|
||||
|
||||
def with_artifact_role(row: dict[str, Any], submission_ref_paths: set[str]) -> dict[str, Any]:
|
||||
is_submission_ref = str(row.get("path", "")) in submission_ref_paths
|
||||
return {
|
||||
**row,
|
||||
"artifact_role": "submission-ref" if is_submission_ref else "supporting-evidence",
|
||||
"submission_ref_required": is_submission_ref,
|
||||
}
|
||||
|
||||
|
||||
def artifact_checklist_for_item(skill_dir: Path, item: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
key = str(item.get("evidence_key", ""))
|
||||
must_collect = item.get("must_collect", {}) if isinstance(item.get("must_collect", {}), dict) else {}
|
||||
artifacts = must_collect.get("evidence_artifacts", [])
|
||||
if not isinstance(artifacts, list):
|
||||
return []
|
||||
submission_ref_paths = template_artifact_ref_paths(skill_dir, item)
|
||||
rows: list[dict[str, Any]] = []
|
||||
for artifact in artifacts:
|
||||
pattern = str(artifact or "").strip()
|
||||
@@ -214,31 +243,12 @@ def artifact_checklist_for_item(skill_dir: Path, item: dict[str, Any]) -> list[d
|
||||
continue
|
||||
if Path(pattern).is_absolute() or ".." in Path(pattern).parts:
|
||||
rows.append(
|
||||
{
|
||||
"evidence_key": key,
|
||||
"source_pattern": pattern,
|
||||
"path": pattern,
|
||||
"status": "unsafe-path",
|
||||
"exists": False,
|
||||
"is_file": False,
|
||||
"sha256": "",
|
||||
"artifact_ref_ready": False,
|
||||
"copy_path": "",
|
||||
"copy_sha256": "",
|
||||
"contains_raw_content": False,
|
||||
"concrete_reference_required": True,
|
||||
}
|
||||
)
|
||||
continue
|
||||
if has_glob_pattern(pattern):
|
||||
matches = sorted(path for path in skill_dir.glob(pattern) if path.is_file())
|
||||
if not matches:
|
||||
rows.append(
|
||||
with_artifact_role(
|
||||
{
|
||||
"evidence_key": key,
|
||||
"source_pattern": pattern,
|
||||
"path": pattern,
|
||||
"status": "glob-no-match",
|
||||
"status": "unsafe-path",
|
||||
"exists": False,
|
||||
"is_file": False,
|
||||
"sha256": "",
|
||||
@@ -247,13 +257,38 @@ def artifact_checklist_for_item(skill_dir: Path, item: dict[str, Any]) -> list[d
|
||||
"copy_sha256": "",
|
||||
"contains_raw_content": False,
|
||||
"concrete_reference_required": True,
|
||||
}
|
||||
},
|
||||
submission_ref_paths,
|
||||
)
|
||||
)
|
||||
continue
|
||||
if has_glob_pattern(pattern):
|
||||
matches = sorted(path for path in skill_dir.glob(pattern) if path.is_file())
|
||||
if not matches:
|
||||
rows.append(
|
||||
with_artifact_role(
|
||||
{
|
||||
"evidence_key": key,
|
||||
"source_pattern": pattern,
|
||||
"path": pattern,
|
||||
"status": "glob-no-match",
|
||||
"exists": False,
|
||||
"is_file": False,
|
||||
"sha256": "",
|
||||
"artifact_ref_ready": False,
|
||||
"copy_path": "",
|
||||
"copy_sha256": "",
|
||||
"contains_raw_content": False,
|
||||
"concrete_reference_required": True,
|
||||
},
|
||||
submission_ref_paths,
|
||||
)
|
||||
)
|
||||
continue
|
||||
for match in matches:
|
||||
rows.append(artifact_row(skill_dir, key, pattern, match, "ready"))
|
||||
rows.append(with_artifact_role(artifact_row(skill_dir, key, pattern, match, "ready"), submission_ref_paths))
|
||||
continue
|
||||
rows.append(artifact_row(skill_dir, key, pattern, skill_dir / pattern, ""))
|
||||
rows.append(with_artifact_role(artifact_row(skill_dir, key, pattern, skill_dir / pattern, ""), submission_ref_paths))
|
||||
return rows
|
||||
|
||||
|
||||
@@ -305,6 +340,10 @@ def build_submission_kit(
|
||||
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"))
|
||||
submission_ref_rows = [item for item in artifact_checklist if item.get("submission_ref_required")]
|
||||
supporting_rows = [item for item in artifact_checklist if not item.get("submission_ref_required")]
|
||||
submission_ref_ready_count = sum(1 for item in submission_ref_rows if item.get("artifact_ref_ready"))
|
||||
supporting_artifact_ready_count = sum(1 for item in supporting_rows if item.get("artifact_ref_ready"))
|
||||
source_summary = summarize_source_checklist(source_checklist)
|
||||
matrix_summary = summarize_evidence_matrix(evidence_matrix)
|
||||
ok = not unknown_keys and skipped_count == 0
|
||||
@@ -325,6 +364,12 @@ def build_submission_kit(
|
||||
"artifact_ready_count": artifact_ready_count,
|
||||
"artifact_missing_count": artifact_missing_count,
|
||||
"artifact_glob_expansion_count": artifact_glob_count,
|
||||
"submission_ref_count": len(submission_ref_rows),
|
||||
"submission_ref_ready_count": submission_ref_ready_count,
|
||||
"submission_ref_missing_count": len(submission_ref_rows) - submission_ref_ready_count,
|
||||
"supporting_artifact_count": len(supporting_rows),
|
||||
"supporting_artifact_ready_count": supporting_artifact_ready_count,
|
||||
"supporting_artifact_missing_count": len(supporting_rows) - supporting_artifact_ready_count,
|
||||
"artifact_prefill_enabled": prefill_artifacts,
|
||||
"artifact_ref_prefill_count": prefilled_artifact_ref_count,
|
||||
"artifact_ref_unfilled_count": unfilled_artifact_ref_count,
|
||||
|
||||
@@ -50,14 +50,15 @@ def render_readme(report: dict[str, Any]) -> str:
|
||||
"",
|
||||
"This matrix combines draft, artifact, and source-check readiness into one operator action list. Matrix rows are guidance only; they do not count as completion evidence.",
|
||||
"",
|
||||
"| Evidence | Stage | Draft | Artifacts | Source checks | Next action |",
|
||||
"| --- | --- | --- | ---: | ---: | --- |",
|
||||
"| Evidence | Stage | Draft | Submission refs | Supporting assets | Source checks | Next action |",
|
||||
"| --- | --- | --- | ---: | ---: | ---: | --- |",
|
||||
]
|
||||
)
|
||||
for item in report.get("evidence_matrix", []):
|
||||
lines.append(
|
||||
f"| `{item['evidence_key']}` | `{item['stage']}` | `{item['draft_status']}` | "
|
||||
f"`{item['artifact_ready_count']}/{item['artifact_total_count']}` | "
|
||||
f"`{item.get('submission_ref_ready_count', 0)}/{item.get('submission_ref_total_count', 0)}` | "
|
||||
f"`{item.get('supporting_artifact_ready_count', 0)}/{item.get('supporting_artifact_total_count', 0)}` | "
|
||||
f"`{item['source_pass_count']}/{item['source_check_count']}` | {item['next_action']} |"
|
||||
)
|
||||
lines.extend(["", "## Execution Runbook", ""])
|
||||
@@ -77,13 +78,16 @@ def render_readme(report: dict[str, Any]) -> str:
|
||||
"",
|
||||
"Use these paths and SHA-256 digests when filling `artifact_refs`. Glob patterns are expanded into concrete files; submissions must reference concrete paths, not globs.",
|
||||
"",
|
||||
"| Evidence | Path | Status | SHA-256 |",
|
||||
"| --- | --- | --- | --- |",
|
||||
"| Evidence | Role | Path | Status | SHA-256 |",
|
||||
"| --- | --- | --- | --- | --- |",
|
||||
]
|
||||
)
|
||||
for item in report.get("artifact_checklist", []):
|
||||
digest = item.get("sha256") or "n/a"
|
||||
lines.append(f"| `{item['evidence_key']}` | `{item['path']}` | `{item['status']}` | `{digest}` |")
|
||||
lines.append(
|
||||
f"| `{item['evidence_key']}` | `{item.get('artifact_role', 'supporting-evidence')}` | "
|
||||
f"`{item['path']}` | `{item['status']}` | `{digest}` |"
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
@@ -172,6 +176,7 @@ def render_html_artifact_checklist(items: list[dict[str, Any]]) -> str:
|
||||
</div>
|
||||
<dl>
|
||||
<dt>Pattern</dt><dd><code>{pattern}</code></dd>
|
||||
<dt>Role</dt><dd>{role}</dd>
|
||||
<dt>Status</dt><dd>{status}</dd>
|
||||
<dt>SHA-256</dt><dd><code>{sha}</code></dd>
|
||||
</dl>
|
||||
@@ -181,6 +186,7 @@ def render_html_artifact_checklist(items: list[dict[str, Any]]) -> str:
|
||||
key=html_text(item.get("evidence_key", "")),
|
||||
path=html_text(item.get("path", "")),
|
||||
pattern=html_text(item.get("source_pattern", "")),
|
||||
role=html_text(item.get("artifact_role", "supporting-evidence")),
|
||||
sha=html_text(item.get("sha256") or "n/a"),
|
||||
)
|
||||
for item in items
|
||||
@@ -229,7 +235,8 @@ def render_html_matrix(items: list[dict[str, Any]]) -> str:
|
||||
</header>
|
||||
<dl>
|
||||
<dt>Draft</dt><dd>{draft}</dd>
|
||||
<dt>Artifacts</dt><dd>{artifact_ready}/{artifact_total} ready</dd>
|
||||
<dt>Submission refs</dt><dd>{submission_ref_ready}/{submission_ref_total} ready</dd>
|
||||
<dt>Supporting assets</dt><dd>{supporting_ready}/{supporting_total} ready</dd>
|
||||
<dt>Source</dt><dd>{source_pass}/{source_total} pass</dd>
|
||||
<dt>Owner</dt><dd>{owner}</dd>
|
||||
</dl>
|
||||
@@ -239,8 +246,10 @@ def render_html_matrix(items: list[dict[str, Any]]) -> str:
|
||||
stage=html_text(item.get("stage", "")),
|
||||
key=html_text(item.get("evidence_key", "")),
|
||||
draft=html_text(item.get("draft_status", "")),
|
||||
artifact_ready=html_text(item.get("artifact_ready_count", 0)),
|
||||
artifact_total=html_text(item.get("artifact_total_count", 0)),
|
||||
submission_ref_ready=html_text(item.get("submission_ref_ready_count", 0)),
|
||||
submission_ref_total=html_text(item.get("submission_ref_total_count", 0)),
|
||||
supporting_ready=html_text(item.get("supporting_artifact_ready_count", 0)),
|
||||
supporting_total=html_text(item.get("supporting_artifact_total_count", 0)),
|
||||
source_pass=html_text(item.get("source_pass_count", 0)),
|
||||
source_total=html_text(item.get("source_check_count", 0)),
|
||||
owner=html_text(item.get("owner", "")),
|
||||
@@ -291,12 +300,15 @@ def render_html(report: dict[str, Any]) -> str:
|
||||
summary = report["summary"]
|
||||
artifact_ready = summary.get("artifact_ready_count", 0)
|
||||
artifact_total = summary.get("artifact_checklist_count", 0)
|
||||
submission_ref_ready = summary.get("submission_ref_ready_count", 0)
|
||||
submission_ref_total = summary.get("submission_ref_count", 0)
|
||||
stats = [
|
||||
("Requested", summary["requested_count"]),
|
||||
("Written", summary["written_count"]),
|
||||
("Existing", summary["existing_count"]),
|
||||
("Skipped", summary["skipped_count"]),
|
||||
("Artifacts", f"{artifact_ready}/{artifact_total}"),
|
||||
("Submit refs", f"{submission_ref_ready}/{submission_ref_total}"),
|
||||
("Support", f"{artifact_ready - submission_ref_ready}/{artifact_total - submission_ref_total}"),
|
||||
("Prefilled", summary.get("artifact_ref_prefill_count", 0)),
|
||||
]
|
||||
stat_html = "".join(
|
||||
@@ -378,9 +390,9 @@ def render_html(report: dict[str, Any]) -> str:
|
||||
<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="matrix"><h2>Evidence Matrix</h2><p class="muted">The matrix combines draft status, artifact readiness, source checks, and the next operator action. It is guidance only and never counts as accepted evidence.</p><div class="matrix-grid">{matrix_html}</div></section>
|
||||
<section class="section" id="matrix"><h2>Evidence Matrix</h2><p class="muted">The matrix separates submission artifact_refs from supporting assets, then combines draft status, source checks, and the next operator action. It is guidance only and never counts as accepted evidence.</p><div class="matrix-grid">{matrix_html}</div></section>
|
||||
<section class="section" id="drafts"><h2>Drafts</h2><div class="draft-grid">{render_html_files(report['files'])}</div></section>
|
||||
<section class="section" id="artifacts"><h2>Artifact Checklist</h2><p class="muted">Copy concrete paths and SHA-256 digests from here into artifact_refs after real evidence exists. Glob patterns are expanded for operator convenience only.</p><div class="artifact-grid">{artifact_html}</div></section>
|
||||
<section class="section" id="artifacts"><h2>Artifact Checklist</h2><p class="muted">Rows marked submission-ref are the paths expected in artifact_refs. Supporting-evidence rows help reviewers audit the packet but do not all need to be copied into the submission. Glob patterns are expanded for operator convenience only.</p><div class="artifact-grid">{artifact_html}</div></section>
|
||||
<section class="section" id="source"><h2>Source Evidence Snapshot</h2><p class="muted">This section shows current aggregate source checks. It explains remaining blockers without changing the ledger.</p><div class="source-grid">{source_html}</div></section>
|
||||
<section class="section" id="evidence"><h2>Evidence Requirements</h2><div class="evidence-grid">{evidence_html}</div></section>
|
||||
<section class="section" id="safety"><h2>Safety Boundary</h2><div class="notice"><ul><li>Drafts never count as accepted ledger evidence.</li><li>Valid intake means ready for ledger review, not world-class completion.</li><li>Do not include credentials, raw prompts, raw outputs, transcripts, notes, or private user content.</li></ul></div></section>
|
||||
|
||||
@@ -93,6 +93,10 @@ def build_evidence_matrix(
|
||||
draft_status, draft_path = _draft_status(files_by_key, key)
|
||||
artifact_ready_count = sum(1 for row in artifact_rows if row.get("artifact_ref_ready"))
|
||||
artifact_missing_count = len(artifact_rows) - artifact_ready_count
|
||||
submission_ref_rows = [row for row in artifact_rows if row.get("submission_ref_required")]
|
||||
supporting_rows = [row for row in artifact_rows if not row.get("submission_ref_required")]
|
||||
submission_ref_ready_count = sum(1 for row in submission_ref_rows if row.get("artifact_ref_ready"))
|
||||
supporting_artifact_ready_count = sum(1 for row in supporting_rows if row.get("artifact_ref_ready"))
|
||||
source_pass_count = sum(1 for row in source_rows if row.get("status") == "pass")
|
||||
source_blocked_count = len(source_rows) - source_pass_count
|
||||
invalid_draft = draft_status not in {"written", "exists"}
|
||||
@@ -115,6 +119,12 @@ def build_evidence_matrix(
|
||||
"artifact_ready_count": artifact_ready_count,
|
||||
"artifact_total_count": len(artifact_rows),
|
||||
"artifact_missing_count": artifact_missing_count,
|
||||
"submission_ref_ready_count": submission_ref_ready_count,
|
||||
"submission_ref_total_count": len(submission_ref_rows),
|
||||
"submission_ref_missing_count": len(submission_ref_rows) - submission_ref_ready_count,
|
||||
"supporting_artifact_ready_count": supporting_artifact_ready_count,
|
||||
"supporting_artifact_total_count": len(supporting_rows),
|
||||
"supporting_artifact_missing_count": len(supporting_rows) - supporting_artifact_ready_count,
|
||||
"source_pass_count": source_pass_count,
|
||||
"source_check_count": len(source_rows),
|
||||
"source_blocked_count": source_blocked_count,
|
||||
@@ -137,5 +147,15 @@ def summarize_evidence_matrix(matrix: list[dict[str, Any]]) -> dict[str, int]:
|
||||
"evidence_matrix_prepare_draft_count": sum(1 for item in matrix if item.get("stage") == "prepare-draft"),
|
||||
"evidence_matrix_fix_artifacts_count": sum(1 for item in matrix if item.get("stage") == "fix-artifacts"),
|
||||
"evidence_matrix_validate_packet_count": sum(1 for item in matrix if item.get("stage") == "validate-packet"),
|
||||
"evidence_matrix_submission_ref_count": sum(int(item.get("submission_ref_total_count", 0)) for item in matrix),
|
||||
"evidence_matrix_submission_ref_ready_count": sum(
|
||||
int(item.get("submission_ref_ready_count", 0)) for item in matrix
|
||||
),
|
||||
"evidence_matrix_supporting_artifact_count": sum(
|
||||
int(item.get("supporting_artifact_total_count", 0)) for item in matrix
|
||||
),
|
||||
"evidence_matrix_supporting_artifact_ready_count": sum(
|
||||
int(item.get("supporting_artifact_ready_count", 0)) for item in matrix
|
||||
),
|
||||
"evidence_matrix_counts_as_completion": 0,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user