diff --git a/.github/scripts/test-verify-trusted-policy-check.py b/.github/scripts/test-verify-trusted-policy-check.py index 3bbc081a..22844ee6 100755 --- a/.github/scripts/test-verify-trusted-policy-check.py +++ b/.github/scripts/test-verify-trusted-policy-check.py @@ -70,7 +70,27 @@ def run_verifier(root: Path, base: str, head: str, fixture: dict) -> subprocess. "conclusion": "success", "app": {"id": 15368, "slug": "github-actions"}, "external_id": f"specsync-trusted-policy:{base}:{head}", - "details_url": "https://github.com/CorvidLabs/spec-sync/actions/runs/9001", + "details_url": "https://github.com/CorvidLabs/spec-sync/runs/20", + } + runs_endpoint = ( + "repos/CorvidLabs/spec-sync/actions/runs?event=pull_request_target" + f"&head_sha={head}&per_page=100" + ) + run = { + "id": 9001, + "event": "pull_request_target", + "status": "completed", + "conclusion": "success", + "path": WORKFLOW, + "head_sha": head, + "repository": {"full_name": "CorvidLabs/spec-sync"}, + "pull_requests": [ + { + "number": 480, + "base": {"sha": base}, + "head": {"sha": head}, + } + ], } fixture = { "repos/CorvidLabs/spec-sync/commits/" @@ -81,27 +101,119 @@ def run_verifier(root: Path, base: str, head: str, fixture: dict) -> subprocess. "name": "GitHub Actions", "owner": {"login": "github"}, }, - "repos/CorvidLabs/spec-sync/actions/runs/9001": { - "id": 9001, - "event": "pull_request_target", - "status": "completed", - "conclusion": "success", - "path": WORKFLOW, - "head_sha": base, - "repository": {"full_name": "CorvidLabs/spec-sync"}, - "pull_requests": [{"number": 480, "head": {"sha": head}}], - }, + runs_endpoint: {"total_count": 1, "workflow_runs": [run]}, } passed = run_verifier(repository, base, head, fixture) assert passed.returncode == 0, passed.stderr + workflow_details = copy.deepcopy(fixture) + workflow_details[ + f"repos/CorvidLabs/spec-sync/commits/{head}/check-runs?per_page=100" + ]["check_runs"][0]["details_url"] = ( + "https://github.com/CorvidLabs/spec-sync/actions/runs/9001" + ) + passed = run_verifier(repository, base, head, workflow_details) + assert passed.returncode == 0, passed.stderr + + wrong_workflow_details = copy.deepcopy(workflow_details) + wrong_workflow_details[ + f"repos/CorvidLabs/spec-sync/commits/{head}/check-runs?per_page=100" + ]["check_runs"][0]["details_url"] = ( + "https://github.com/CorvidLabs/spec-sync/actions/runs/9999" + ) + rejected = run_verifier(repository, base, head, wrong_workflow_details) + assert rejected.returncode != 0 + assert "names a different workflow run" in rejected.stderr + + (repository / "README.md").write_text("archive child\n", encoding="utf-8") + git(repository, "add", ".") + git(repository, "commit", "-m", "archive child") + descendant = git(repository, "rev-parse", "HEAD") + moved_tip = copy.deepcopy(fixture) + moved_tip[runs_endpoint]["workflow_runs"][0]["pull_requests"][0]["head"][ + "sha" + ] = descendant + passed = run_verifier(repository, base, head, moved_tip) + assert passed.returncode == 0, passed.stderr + wrong_event = copy.deepcopy(fixture) - wrong_event["repos/CorvidLabs/spec-sync/actions/runs/9001"]["event"] = "pull_request" + wrong_event[runs_endpoint]["workflow_runs"][0]["event"] = "pull_request" rejected = run_verifier(repository, base, head, wrong_event) assert rejected.returncode != 0 assert "not base-controlled" in rejected.stderr + wrong_details = copy.deepcopy(fixture) + wrong_details[ + f"repos/CorvidLabs/spec-sync/commits/{head}/check-runs?per_page=100" + ]["check_runs"][0]["details_url"] = "https://attacker.invalid/runs/20" + rejected = run_verifier(repository, base, head, wrong_details) + assert rejected.returncode != 0 + assert "not a recognized GitHub check or workflow run" in rejected.stderr + + wrong_app = copy.deepcopy(fixture) + wrong_app[f"repos/CorvidLabs/spec-sync/commits/{head}/check-runs?per_page=100"][ + "check_runs" + ][0]["app"]["id"] = 999 + rejected = run_verifier(repository, base, head, wrong_app) + assert rejected.returncode != 0 + assert "not from GitHub Actions" in rejected.stderr + + wrong_path = copy.deepcopy(fixture) + wrong_path[runs_endpoint]["workflow_runs"][0]["path"] = ".github/workflows/ci.yml" + rejected = run_verifier(repository, base, head, wrong_path) + assert rejected.returncode != 0 + assert "missing or ambiguous" in rejected.stderr + + wrong_repository = copy.deepcopy(fixture) + wrong_repository[runs_endpoint]["workflow_runs"][0]["repository"][ + "full_name" + ] = "CorvidLabs/other" + rejected = run_verifier(repository, base, head, wrong_repository) + assert rejected.returncode != 0 + assert "belongs to another repository" in rejected.stderr + + wrong_candidate = copy.deepcopy(fixture) + wrong_candidate[runs_endpoint]["workflow_runs"][0]["head_sha"] = base + rejected = run_verifier(repository, base, head, wrong_candidate) + assert rejected.returncode != 0 + assert "exact candidate revision" in rejected.stderr + + wrong_pr = copy.deepcopy(fixture) + wrong_pr[runs_endpoint]["workflow_runs"][0]["pull_requests"][0]["number"] = 481 + rejected = run_verifier(repository, base, head, wrong_pr) + assert rejected.returncode != 0 + assert "exact PR and base revision" in rejected.stderr + + wrong_base = copy.deepcopy(fixture) + wrong_base[runs_endpoint]["workflow_runs"][0]["pull_requests"][0]["base"][ + "sha" + ] = head + rejected = run_verifier(repository, base, head, wrong_base) + assert rejected.returncode != 0 + assert "exact PR and base revision" in rejected.stderr + + unsuccessful_run = copy.deepcopy(fixture) + unsuccessful_run[runs_endpoint]["workflow_runs"][0]["conclusion"] = "failure" + rejected = run_verifier(repository, base, head, unsuccessful_run) + assert rejected.returncode != 0 + assert "workflow run is not successful" in rejected.stderr + + ambiguous_runs = copy.deepcopy(fixture) + ambiguous_runs[runs_endpoint] = { + "total_count": 2, + "workflow_runs": [run, {**run, "id": 9002}], + } + rejected = run_verifier(repository, base, head, ambiguous_runs) + assert rejected.returncode != 0 + assert "missing or ambiguous" in rejected.stderr + + incomplete_lookup = copy.deepcopy(fixture) + incomplete_lookup[runs_endpoint]["total_count"] = 2 + rejected = run_verifier(repository, base, head, incomplete_lookup) + assert rejected.returncode != 0 + assert "incomplete or exceeds its bound" in rejected.stderr + stale_success = copy.deepcopy(fixture) stale_success[ f"repos/CorvidLabs/spec-sync/commits/{head}/check-runs?per_page=100" diff --git a/.github/scripts/verify-trusted-policy-check.py b/.github/scripts/verify-trusted-policy-check.py index 5ee6a144..e5fb939e 100755 --- a/.github/scripts/verify-trusted-policy-check.py +++ b/.github/scripts/verify-trusted-policy-check.py @@ -202,36 +202,85 @@ def exact_sha(value: str, label: str) -> str: if external_match is None: raise ValueError("check lacks the exact trusted revision binding") trusted = external_match.group(1) + if trusted != base: + raise ValueError("check is not bound to the exact PR base revision") + + check_id = int(check.get("id", 0)) + if check_id <= 0: + raise ValueError("check has no valid GitHub identity") details = str(check.get("details_url") or "") - details_match = re.fullmatch( + workflow_details = re.fullmatch( rf"{re.escape(server_url)}/{re.escape(repository)}/actions/runs/([0-9]+)", details, ) - if details_match is None: - raise ValueError("check details URL is not an exact workflow run") - run_id = int(details_match.group(1)) - run = api(f"repos/{repository}/actions/runs/{run_id}") - if run.get("id") != run_id: - raise ValueError("wrong workflow run ID") + canonical_check_details = f"{server_url}/{repository}/runs/{check_id}" + if workflow_details is None and details != canonical_check_details: + raise ValueError("check details URL is not a recognized GitHub check or workflow run") + + runs_endpoint = ( + f"repos/{repository}/actions/runs?event=pull_request_target" + f"&head_sha={candidate}&per_page=100" + ) + runs_payload = api(runs_endpoint) + runs = runs_payload.get("workflow_runs") + total_count = runs_payload.get("total_count") + if ( + not isinstance(runs, list) + or not isinstance(total_count, int) + or isinstance(total_count, bool) + or total_count != len(runs) + or total_count > 100 + ): + raise ValueError("workflow run lookup is incomplete or exceeds its bound") + + policy_runs = [ + run + for run in runs + if isinstance(run, dict) + and str(run.get("path") or "").split("@", 1)[0] == WORKFLOW_PATH + ] + if len(policy_runs) != 1: + raise ValueError("workflow run lookup is missing or ambiguous") + run = policy_runs[0] + run_id = int(run.get("id", 0)) + if run_id <= 0: + raise ValueError("workflow run has no valid GitHub identity") + if workflow_details is not None and int(workflow_details.group(1)) != run_id: + raise ValueError("check details URL names a different workflow run") if run.get("event") != "pull_request_target": raise ValueError("workflow run is not base-controlled") if run.get("status") != "completed" or run.get("conclusion") != "success": raise ValueError("workflow run is not successful") - if str(run.get("path") or "").split("@", 1)[0] != WORKFLOW_PATH: - raise ValueError("workflow run has the wrong path") if (run.get("repository") or {}).get("full_name") != repository: raise ValueError("workflow run belongs to another repository") - if run.get("head_sha") != trusted: - raise ValueError("workflow run does not use the bound trusted revision") + if run.get("head_sha") != trusted and run.get("head_sha") != candidate: + raise ValueError("workflow run is unrelated to the trusted or candidate revision") + if run.get("head_sha") != candidate: + raise ValueError("workflow run does not use the exact candidate revision") pull_requests = run.get("pull_requests") or [] matching_prs = [ item for item in pull_requests if item.get("number") == pull_request - and (item.get("head") or {}).get("sha") == candidate + and (item.get("base") or {}).get("sha") == trusted + and ( + (item.get("head") or {}).get("sha") == candidate + or git( + root, + "merge-base", + "--is-ancestor", + candidate, + str((item.get("head") or {}).get("sha") or ""), + check=False, + ).returncode + == 0 + ) ] if len(matching_prs) != 1: - raise ValueError("workflow run is not bound to the exact PR head") + raise ValueError("workflow run is not bound to the exact PR and base revision") + pull_head = str((matching_prs[0].get("head") or {}).get("sha") or "") + if re.fullmatch(r"[0-9a-f]{40}", pull_head) is None: + raise ValueError("workflow run has no exact PR head revision") if git(root, "rev-parse", "--verify", f"{trusted}^{{commit}}").stdout.strip() != trusted: raise ValueError("trusted workflow revision is unavailable") if git(root, "merge-base", "--is-ancestor", trusted, candidate, check=False).returncode != 0: diff --git a/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/accepted-state.json b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/accepted-state.json new file mode 100644 index 00000000..7451a3a1 --- /dev/null +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/accepted-state.json @@ -0,0 +1,49 @@ +{ + "schema_version": 1, + "workflow_version": 2, + "workflow_origin_version": 2, + "id": "CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa", + "slug": "approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa", + "title": "Approve rejects living ADDED REQs and draft next_action waits on complete artifacts", + "description": "Approve rejects living ADDED REQs and draft next_action waits on complete artifacts", + "kind": "bug_fix", + "state": "accepted", + "canonical_applied": true, + "base_commit": "109164ad9faccb598ae7e8caf7a2d488722cc237", + "created_at": 1785595484, + "updated_at": 1785627961, + "affected_specs": [ + "change", + "cmd_change" + ], + "affected_paths": [ + ".specsync/change-sequence.json", + "AGENTS.md", + "CHANGELOG.md", + "CONTRIBUTING.md", + "fledge.toml", + "scripts/", + "specs/change/", + "specs/cmd_change/", + "src/change.rs", + "src/commands/change.rs" + ], + "no_spec_change": false, + "no_spec_change_rationale": null, + "acceptance_criteria": [ + "Approve fails closed when ## ADDED targets a living requirement ID and steers agents to ## MODIFIED; draft next_action prefers completing incomplete selected artifacts over approve when artifacts_complete is false." + ], + "selected_artifacts": [ + "context", + "testing", + "tasks", + "design", + "requirements", + "docs" + ], + "dependencies": [], + "answers": { + "architecture_risk": "no", + "public_contract": "yes" + } +} diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/approvals.json b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/approvals.json similarity index 96% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/approvals.json rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/approvals.json index fca5c9a3..7cdc8de7 100644 --- a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/approvals.json +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/approvals.json @@ -151,6 +151,13 @@ "public_contract": "yes" } } + }, + { + "gate": "finalization", + "actor": "specsync:finalization", + "timestamp": 1785627961, + "digest": "6fc0821b4f677810b36199fec875f59e4b9a3129bad7b3c5b322a835d76a4630", + "note": "Same-PR finalization closing digest" } ], "reopenings": [] diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/change.md b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/change.md similarity index 97% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/change.md rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/change.md index c61fb892..ab6f4c6c 100644 --- a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/change.md +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/change.md @@ -1,6 +1,6 @@ --- id: CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa -state: implementing +state: archived type: bug_fix base_commit: 109164ad9faccb598ae7e8caf7a2d488722cc237 --- diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/context.md b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/context.md similarity index 50% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/context.md rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/context.md index dbb0e4d7..5b4dca33 100644 --- a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/context.md +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/context.md @@ -5,6 +5,16 @@ artifact: context # Context +## CLI next-action regression coverage + +- Trigger: independent review found that domain-summary coverage did not execute the text command + adapter used by `change status`, `change show`, and `change list`. +- Root cause: the testing map treated shared domain logic as proof of command-renderer behavior. +- Invariant: every text draft surface prefers incomplete-artifact guidance and never recommends + `change approve` until selected artifacts are complete. +- Regression: `draft_text_surfaces_require_complete_artifacts_before_approval` exercises the exact + shared renderer used by all three text surfaces against one interview-complete draft. + Sandbox dogfood (issues #14 and #16) showed two agent-facing lifecycle gaps on SpecSync 6.0: 1. Draft next_action recommended change approve while selected artifacts still contained incomplete HTML TODO comment stubs (artifacts_complete=false). diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/deltas/change.md b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/deltas/change.md similarity index 100% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/deltas/change.md rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/deltas/change.md diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/deltas/cmd_change.md b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/deltas/cmd_change.md similarity index 100% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/deltas/cmd_change.md rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/deltas/cmd_change.md diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/design.md b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/design.md similarity index 100% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/design.md rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/design.md diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/docs.md b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/docs.md similarity index 100% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/docs.md rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/docs.md diff --git a/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/finalization.json b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/finalization.json new file mode 100644 index 00000000..698c117a --- /dev/null +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/finalization.json @@ -0,0 +1,12 @@ +{ + "schema_version": 2, + "change_id": "CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa", + "implementation_commit": "5a2547ec2f8fc516c2dc28bc04f84346dec62971", + "implementation_tree": "907d6ab8c738628bdebad1e82abb0d8e1b0d7590", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "closing_digest": "6fc0821b4f677810b36199fec875f59e4b9a3129bad7b3c5b322a835d76a4630", + "review_digest": "9897c41148a0f19a1dcb4591c3edc1cb933ea3d77507f4ce823e17080c160842", + "finalization_digest": "63a91e81cfdaa958c042ddd26ffb6b299800e175f6f4c40ec762bf63cf36ae47", + "timestamp": 1785627961 +} diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/requirements.md b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/requirements.md similarity index 100% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/requirements.md rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/requirements.md diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review-attempts.json b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review-attempts.json similarity index 51% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review-attempts.json rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review-attempts.json index 890d965c..8c7ee304 100644 --- a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review-attempts.json +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review-attempts.json @@ -32,6 +32,38 @@ "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", "workspace_digest": "c7c77d0ee4f3cee9a55824be8bcc28d525d064ba7a5da0726607ba92bf622283", "timestamp": 1785603972 + }, + { + "schema_version": 2, + "change_id": "CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa", + "reviewer": "Ohm", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "6a0956fbe53669aa9a7bc564fd472e0952a70f2a", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "43ac93c221045e4fd2b7571d5bac1b05715a66c54a6d4d53997cc911a9e1f057", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "timestamp": 1785621014 + }, + { + "schema_version": 2, + "change_id": "CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa", + "reviewer": "Ohm", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "5a2547ec2f8fc516c2dc28bc04f84346dec62971", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "43ac93c221045e4fd2b7571d5bac1b05715a66c54a6d4d53997cc911a9e1f057", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "timestamp": 1785627934 } ] } diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review.json b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review.json similarity index 54% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review.json rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review.json index ce3ea295..b67ab0c1 100644 --- a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review.json +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/review.json @@ -1,16 +1,16 @@ { "schema_version": 2, "change_id": "CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa", - "reviewer": "product-scoped-review", + "reviewer": "Ohm", "provenance": { "schema_version": 1, "provider": "github_actions_check", "required_check": "SpecSync scoped review" }, "verdict": "pass", - "implementation_commit": "ec4eb1bcde91e8158bba08286d242da85a705244", + "implementation_commit": "5a2547ec2f8fc516c2dc28bc04f84346dec62971", "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", - "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", - "workspace_digest": "c7c77d0ee4f3cee9a55824be8bcc28d525d064ba7a5da0726607ba92bf622283", - "timestamp": 1785603972 + "execution_digest": "43ac93c221045e4fd2b7571d5bac1b05715a66c54a6d4d53997cc911a9e1f057", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "timestamp": 1785627934 } diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/state.json b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/state.json similarity index 96% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/state.json rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/state.json index ab75f780..8f68c2cb 100644 --- a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/state.json +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/state.json @@ -7,11 +7,11 @@ "title": "Approve rejects living ADDED REQs and draft next_action waits on complete artifacts", "description": "Approve rejects living ADDED REQs and draft next_action waits on complete artifacts", "kind": "bug_fix", - "state": "verifying", + "state": "archived", "canonical_applied": true, "base_commit": "109164ad9faccb598ae7e8caf7a2d488722cc237", "created_at": 1785595484, - "updated_at": 1785605361, + "updated_at": 1785628109, "affected_specs": [ "change", "cmd_change" diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/tasks.md b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/tasks.md similarity index 100% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/tasks.md rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/tasks.md diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/testing.md b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/testing.md similarity index 58% rename from .specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/testing.md rename to .specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/testing.md index c86247b5..18a5a1ff 100644 --- a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/testing.md +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/testing.md @@ -7,4 +7,7 @@ artifact: testing - `REQ-change-047`: covered by `draft_next_action_prefers_complete_artifacts_over_approve`. - `REQ-change-048`: covered by `added_requirement_already_in_living_tree_fails_delta_validation` (validate + approve fail; MODIFIED ok). -- `REQ-cmd-change-006`: covered by text-mode next-action path using `artifacts_complete_for_guidance` (unit coverage via draft completeness summarize path). +- `REQ-cmd-change-006`: covered directly by + `draft_text_surfaces_require_complete_artifacts_before_approval`, which exercises the shared + text renderer used by `change status`, `change show`, and `change list` and rejects any approval + recommendation. diff --git a/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification-attempts.json b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification-attempts.json new file mode 100644 index 00000000..6f73d643 --- /dev/null +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification-attempts.json @@ -0,0 +1,571 @@ +{ + "schema_version": 1, + "attempts": [ + { + "timestamp": 1785601545, + "commit": "bcbd8e4689bb217cff655bbad82e76726afdd5a6", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", + "workspace_digest": "c9d9b0c6773c37b95ae2c175926a068ab02ae09dbc701d6d2342253f4ef3b833", + "passed": false, + "commands": [ + { + "command": "cargo test change::", + "success": false, + "exit_code": 101 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + }, + { + "timestamp": 1785602614, + "commit": "bcbd8e4689bb217cff655bbad82e76726afdd5a6", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", + "workspace_digest": "b94b577b2dfd9bae68c7206895e795fd0d5ebdc3218a6901782c0c689cd49b1b", + "passed": true, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + }, + { + "timestamp": 1785603957, + "commit": "09ecfe962b6e10d9b47249cdc5e4395815eae01f", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", + "workspace_digest": "c7c77d0ee4f3cee9a55824be8bcc28d525d064ba7a5da0726607ba92bf622283", + "passed": true, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + }, + { + "timestamp": 1785605359, + "commit": "481fa4f9f508abd1a8b1eab7807bb1aaa66d1c4f", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", + "workspace_digest": "332be3e5923156cf939064165c5b5ef692fbb25a33bff2f4525d75b024acb8d2", + "passed": true, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + }, + { + "timestamp": 1785618519, + "commit": "6a0956fbe53669aa9a7bc564fd472e0952a70f2a", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", + "workspace_digest": "1c69ee13b34891668c0df3e6b0556c12ebe6b78ab1fdc36fc119bcdc51f16c51", + "passed": true, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + }, + { + "timestamp": 1785619829, + "commit": "6a0956fbe53669aa9a7bc564fd472e0952a70f2a", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "84013438f3238eca30a4912e6c37e12371d475c490319fefcc1633579ad36970", + "workspace_digest": "a1b4afdea5d18d2f74db30a1e85d6f766627ae7f3caec0496da347e4554dc139", + "passed": true, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + }, + { + "timestamp": 1785621002, + "commit": "6a0956fbe53669aa9a7bc564fd472e0952a70f2a", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "43ac93c221045e4fd2b7571d5bac1b05715a66c54a6d4d53997cc911a9e1f057", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "passed": true, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + }, + { + "timestamp": 1785624393, + "commit": "e0d87f7f377b55eb0299791dddaff52ed3cc9fe5", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "43ac93c221045e4fd2b7571d5bac1b05715a66c54a6d4d53997cc911a9e1f057", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "passed": false, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": false, + "exit_code": 101 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + }, + { + "timestamp": 1785625474, + "commit": "e0d87f7f377b55eb0299791dddaff52ed3cc9fe5", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "43ac93c221045e4fd2b7571d5bac1b05715a66c54a6d4d53997cc911a9e1f057", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "passed": true, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + }, + { + "timestamp": 1785625474, + "commit": "5a2547ec2f8fc516c2dc28bc04f84346dec62971", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "43ac93c221045e4fd2b7571d5bac1b05715a66c54a6d4d53997cc911a9e1f057", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "acceptance_input_digest": "fcd3fba557049f8df2b838402abaf02e20b0c8e3b541098e3c301c3356dbf649", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "40fd2217155be270a3338ff40170cc8f3da7cc310611e25edf3b6a6bd742aa5e", + "entry_digest": "f6b5998bd04ac78c871325897131f37cf365bcbcc0f9741c880439eb4822733d", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "AGENTS.md", + "kind": "file", + "mode": 33188, + "payload_digest": "458c78b8d534d7bda6f6d1d80347ef0814be529aab6b79ffa16e048e49ae166c", + "entry_digest": "5eb1a8db5207c4d10fb5b5da694b0ad794cc27293c28632d86623476e0a58fef", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "CHANGELOG.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7486b32b42299ba26c13641a9e600be1669b851b7ade74de6b774df6f587506f", + "entry_digest": "b3075f6d42c2becb150df99bbbc0b1ff0b2b11c2ef6cdf9f9542ef5e69c4efba", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "CONTRIBUTING.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1065a9e102726c5cc6345e7f40244b155b3672cc4baa3435f35dd4d3c3d30e40", + "entry_digest": "abbe1f4d93268fb865b3c61e4fe0a0a80593f83d73271a06dc4a0bd8da6fe269", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "fledge.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "ff5fdc238533e7dcac55fda6905017c1f3de55bb89eac2e90809157ee500450f", + "entry_digest": "e76d75aff490699fcd003612377ac7a45f1c61ed053e89a69d477bbf41f8f3b9", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "scripts/pre-push-gate.sh", + "kind": "file", + "mode": 33188, + "payload_digest": "03e38faf3f4ede3fd519721f37317fbeee660ff248c447f0795f5bc48ab28b2c", + "entry_digest": "885cbe09bb6e4374f7df133d9cf1f076d41095129bd7c0d66236b660c9224c4d", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "scripts/spec-check-fast.sh", + "kind": "file", + "mode": 33188, + "payload_digest": "e28a8b2c092865e77f74f8c10627d2fed9b7d9550cec7db13c5d875af0b7389c", + "entry_digest": "a8f0605ad2c6fe19582b295d6f700ad8cbb912711df141b762441618dbc4d92f", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "specs/change/change.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "d4039c8fd5b68eb29ad5ad6bc66c3c8d46c1b5609a6479de2f101a8c234c4414", + "entry_digest": "f2f59e2c5bfdbe4e8f0a42c7a9d9a8d469703ce35504fcf9cee8680fc50889e7", + "owners": [ + "change" + ] + }, + { + "path": "specs/change/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "53264f9e044a325eeaaf9bbab9ce4ec7fb81234c5de1da9d1c0c456deabb4bc2", + "entry_digest": "56a342417525f9c38a2fd5a5a3c7cf8152d368293c20779a903bcaa522752d9f", + "owners": [ + "change" + ] + }, + { + "path": "specs/change/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7c12b273e2d8f15b1d1dd5f69e069d1face3bb5d454b07d4ccfd4580b8036679", + "entry_digest": "fba655191ae47bca0adfeb7d358e5671afcdca7b845e4f0e15412bcad8f28513", + "owners": [ + "change" + ] + }, + { + "path": "specs/change/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "6577ef2ded2ab188315517e2efb10c3d0d8fb1cfd8886851006dcb4537b19d6c", + "entry_digest": "8ee88b74fea6b2bf11ee576dce8e0c3625ac30118dbebf5a9ae3b5ef879a33fc", + "owners": [ + "change" + ] + }, + { + "path": "specs/change/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "b75180bfc29c3bd53742535c06bc4372aa5a1d11ce08ea774aebb9c6312a3d37", + "entry_digest": "38ceb53dac77e42ee875c8144d961cf291ca67a9f06e7ee16a342b4ea86bed55", + "owners": [ + "change" + ] + }, + { + "path": "specs/cmd_change/cmd_change.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "b48aefa9bcbed8f948cda2e8caa0062463a79690ae39fb76f92ba50e520f8b0a", + "entry_digest": "1d8655b2e5e7310ed4d40257d99b4a3ebc19ea286955636589caf54717e5f185", + "owners": [ + "cmd_change" + ] + }, + { + "path": "specs/cmd_change/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "21c040d2e4a0bf31a9d3cb680476682633512976a77f995a267ceba52f11a9f4", + "entry_digest": "5b5176b937b3005e790c8f4d2c69256b81c60e533ef9443edd883e360c4b48bf", + "owners": [ + "cmd_change" + ] + }, + { + "path": "specs/cmd_change/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "3ff7bd7066b2db7951914b88893d28a4b77a8ce78aa3a6920e30601afbc5806e", + "entry_digest": "b131344380defc258888d02ded1fcb7bb75e8582442507b8c1eeb0c4083b71cc", + "owners": [ + "cmd_change" + ] + }, + { + "path": "specs/cmd_change/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "fece9e991d1efd6963cb06d466f144817caad768aa9fb910f741ce7a67c3d5f9", + "entry_digest": "c490f682b96303b1e0312e81a06cf3510ce4a5f99915daf5c57cd2625e93866a", + "owners": [ + "cmd_change" + ] + }, + { + "path": "specs/cmd_change/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "a74290a3fd5164cff9f5091810c18f84ddfcde5515a36863c638cad3e7127b09", + "entry_digest": "95b5b719353d3eb5f7a099caa8ebc62c559327c6714da9d54ef8a40e74b2073a", + "owners": [ + "cmd_change" + ] + }, + { + "path": "src/change.rs", + "kind": "file", + "mode": 33188, + "payload_digest": "7c6c1cf0ba6641dcf42efac11c84236d492d44665f0021043a9a9ce81c545abc", + "entry_digest": "5baf8793cefad814988ef41d301c88c6d0db6364f672e9fac4575f0c88314669", + "owners": [ + "change" + ] + }, + { + "path": "src/commands/change.rs", + "kind": "file", + "mode": 33188, + "payload_digest": "b250f0c76cd2b72e9d785db58caa5250fd8f5f44bd4025cac8600b2eb93f81e7", + "entry_digest": "0c9378c56c0580bfdb338dd5ce534f0ebe940dee2ea0318611112c808bd294d0", + "owners": [ + "cmd_change" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] + } + ] +} diff --git a/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification.json b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification.json new file mode 100644 index 00000000..58988ae8 --- /dev/null +++ b/.specsync/archive/changes/2026-08-01-CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification.json @@ -0,0 +1,236 @@ +{ + "timestamp": 1785625474, + "commit": "5a2547ec2f8fc516c2dc28bc04f84346dec62971", + "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", + "execution_digest": "43ac93c221045e4fd2b7571d5bac1b05715a66c54a6d4d53997cc911a9e1f057", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "acceptance_input_digest": "fcd3fba557049f8df2b838402abaf02e20b0c8e3b541098e3c301c3356dbf649", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "40fd2217155be270a3338ff40170cc8f3da7cc310611e25edf3b6a6bd742aa5e", + "entry_digest": "f6b5998bd04ac78c871325897131f37cf365bcbcc0f9741c880439eb4822733d", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "AGENTS.md", + "kind": "file", + "mode": 33188, + "payload_digest": "458c78b8d534d7bda6f6d1d80347ef0814be529aab6b79ffa16e048e49ae166c", + "entry_digest": "5eb1a8db5207c4d10fb5b5da694b0ad794cc27293c28632d86623476e0a58fef", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "CHANGELOG.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7486b32b42299ba26c13641a9e600be1669b851b7ade74de6b774df6f587506f", + "entry_digest": "b3075f6d42c2becb150df99bbbc0b1ff0b2b11c2ef6cdf9f9542ef5e69c4efba", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "CONTRIBUTING.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1065a9e102726c5cc6345e7f40244b155b3672cc4baa3435f35dd4d3c3d30e40", + "entry_digest": "abbe1f4d93268fb865b3c61e4fe0a0a80593f83d73271a06dc4a0bd8da6fe269", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "fledge.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "ff5fdc238533e7dcac55fda6905017c1f3de55bb89eac2e90809157ee500450f", + "entry_digest": "e76d75aff490699fcd003612377ac7a45f1c61ed053e89a69d477bbf41f8f3b9", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "scripts/pre-push-gate.sh", + "kind": "file", + "mode": 33188, + "payload_digest": "03e38faf3f4ede3fd519721f37317fbeee660ff248c447f0795f5bc48ab28b2c", + "entry_digest": "885cbe09bb6e4374f7df133d9cf1f076d41095129bd7c0d66236b660c9224c4d", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "scripts/spec-check-fast.sh", + "kind": "file", + "mode": 33188, + "payload_digest": "e28a8b2c092865e77f74f8c10627d2fed9b7d9550cec7db13c5d875af0b7389c", + "entry_digest": "a8f0605ad2c6fe19582b295d6f700ad8cbb912711df141b762441618dbc4d92f", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "specs/change/change.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "d4039c8fd5b68eb29ad5ad6bc66c3c8d46c1b5609a6479de2f101a8c234c4414", + "entry_digest": "f2f59e2c5bfdbe4e8f0a42c7a9d9a8d469703ce35504fcf9cee8680fc50889e7", + "owners": [ + "change" + ] + }, + { + "path": "specs/change/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "53264f9e044a325eeaaf9bbab9ce4ec7fb81234c5de1da9d1c0c456deabb4bc2", + "entry_digest": "56a342417525f9c38a2fd5a5a3c7cf8152d368293c20779a903bcaa522752d9f", + "owners": [ + "change" + ] + }, + { + "path": "specs/change/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7c12b273e2d8f15b1d1dd5f69e069d1face3bb5d454b07d4ccfd4580b8036679", + "entry_digest": "fba655191ae47bca0adfeb7d358e5671afcdca7b845e4f0e15412bcad8f28513", + "owners": [ + "change" + ] + }, + { + "path": "specs/change/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "6577ef2ded2ab188315517e2efb10c3d0d8fb1cfd8886851006dcb4537b19d6c", + "entry_digest": "8ee88b74fea6b2bf11ee576dce8e0c3625ac30118dbebf5a9ae3b5ef879a33fc", + "owners": [ + "change" + ] + }, + { + "path": "specs/change/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "b75180bfc29c3bd53742535c06bc4372aa5a1d11ce08ea774aebb9c6312a3d37", + "entry_digest": "38ceb53dac77e42ee875c8144d961cf291ca67a9f06e7ee16a342b4ea86bed55", + "owners": [ + "change" + ] + }, + { + "path": "specs/cmd_change/cmd_change.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "b48aefa9bcbed8f948cda2e8caa0062463a79690ae39fb76f92ba50e520f8b0a", + "entry_digest": "1d8655b2e5e7310ed4d40257d99b4a3ebc19ea286955636589caf54717e5f185", + "owners": [ + "cmd_change" + ] + }, + { + "path": "specs/cmd_change/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "21c040d2e4a0bf31a9d3cb680476682633512976a77f995a267ceba52f11a9f4", + "entry_digest": "5b5176b937b3005e790c8f4d2c69256b81c60e533ef9443edd883e360c4b48bf", + "owners": [ + "cmd_change" + ] + }, + { + "path": "specs/cmd_change/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "3ff7bd7066b2db7951914b88893d28a4b77a8ce78aa3a6920e30601afbc5806e", + "entry_digest": "b131344380defc258888d02ded1fcb7bb75e8582442507b8c1eeb0c4083b71cc", + "owners": [ + "cmd_change" + ] + }, + { + "path": "specs/cmd_change/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "fece9e991d1efd6963cb06d466f144817caad768aa9fb910f741ce7a67c3d5f9", + "entry_digest": "c490f682b96303b1e0312e81a06cf3510ce4a5f99915daf5c57cd2625e93866a", + "owners": [ + "cmd_change" + ] + }, + { + "path": "specs/cmd_change/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "a74290a3fd5164cff9f5091810c18f84ddfcde5515a36863c638cad3e7127b09", + "entry_digest": "95b5b719353d3eb5f7a099caa8ebc62c559327c6714da9d54ef8a40e74b2073a", + "owners": [ + "cmd_change" + ] + }, + { + "path": "src/change.rs", + "kind": "file", + "mode": 33188, + "payload_digest": "7c6c1cf0ba6641dcf42efac11c84236d492d44665f0021043a9a9ce81c545abc", + "entry_digest": "5baf8793cefad814988ef41d301c88c6d0db6364f672e9fac4575f0c88314669", + "owners": [ + "change" + ] + }, + { + "path": "src/commands/change.rs", + "kind": "file", + "mode": 33188, + "payload_digest": "b250f0c76cd2b72e9d785db58caa5250fd8f5f44bd4025cac8600b2eb93f81e7", + "entry_digest": "0c9378c56c0580bfdb338dd5ce534f0ebe940dee2ea0318611112c808bd294d0", + "owners": [ + "cmd_change" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "cargo test change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test commands::change::", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-change-047", + "REQ-change-048", + "REQ-cmd-change-006" + ] +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/accepted-state.json b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/accepted-state.json new file mode 100644 index 00000000..e9bb67c6 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/accepted-state.json @@ -0,0 +1,47 @@ +{ + "schema_version": 1, + "workflow_version": 2, + "workflow_origin_version": 2, + "id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "slug": "simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "title": "Simplify SpecSync CI to one expensive-suite authority with residual Trust identity gates, preserving full local verification and documenting the 95% confidence model", + "description": "Simplify SpecSync CI to one expensive-suite authority with residual Trust identity gates, preserving full local verification and documenting the 95% confidence model", + "kind": "operations", + "state": "accepted", + "canonical_applied": true, + "base_commit": "0ea95ce5a409ee7685bb31a182ef2c633bf9baee", + "created_at": 1785608765, + "updated_at": 1785628845, + "affected_specs": [ + "github" + ], + "affected_paths": [ + "fledge.toml", + ".trust.toml", + "docs/ci-confidence.md", + "AGENTS.md", + "CONTRIBUTING.md", + "specs/github/", + ".specsync/change-sequence.json" + ], + "no_spec_change": false, + "no_spec_change_rationale": null, + "acceptance_criteria": [ + "Trust lifecycle no longer invokes the full verify lane or cargo test; fledge keeps a full local verify lane and adds a fast trust-lifecycle lane; docs assign each confidence signal to one authority, describe the current protected-workflow boundary and Tier B multi-OS plan, and quantify expected Trust/product-tip wall-clock improvement; fledge lanes run trust-lifecycle and strict spec validation pass; this PR contains no protected workflow files or unrelated ship-status/#486 history." + ], + "selected_artifacts": [ + "context", + "plan", + "testing", + "design", + "tasks", + "requirements", + "docs", + "research" + ], + "dependencies": [], + "answers": { + "architecture_risk": "yes", + "public_contract": "yes" + } +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/approvals.json b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/approvals.json new file mode 100644 index 00000000..5bc1a34e --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/approvals.json @@ -0,0 +1,49 @@ +{ + "approvals": [ + { + "gate": "definition", + "actor": "OXLEIF", + "timestamp": 1785608878, + "digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "note": "Approved digest 3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "approved_scope": { + "schema_version": 1, + "change_id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "title": "Simplify SpecSync CI to one expensive-suite authority with residual Trust identity gates, preserving full local verification and documenting the 95% confidence model", + "description": "Simplify SpecSync CI to one expensive-suite authority with residual Trust identity gates, preserving full local verification and documenting the 95% confidence model", + "kind": "operations", + "affected_specs": [ + "github" + ], + "affected_paths": [ + ".specsync/change-sequence.json", + ".trust.toml", + "AGENTS.md", + "CONTRIBUTING.md", + "docs/ci-confidence.md", + "fledge.toml", + "specs/github/" + ], + "no_spec_change": false, + "no_spec_change_rationale": null, + "acceptance_criteria": [ + "Trust lifecycle no longer invokes the full verify lane or cargo test; fledge keeps a full local verify lane and adds a fast trust-lifecycle lane; docs assign each confidence signal to one authority, describe the current protected-workflow boundary and Tier B multi-OS plan, and quantify expected Trust/product-tip wall-clock improvement; fledge lanes run trust-lifecycle and strict spec validation pass; this PR contains no protected workflow files or unrelated ship-status/#486 history." + ], + "dependencies": [], + "supersedes": [], + "answers": { + "architecture_risk": "yes", + "public_contract": "yes" + } + } + }, + { + "gate": "finalization", + "actor": "specsync:finalization", + "timestamp": 1785628845, + "digest": "02bf8a658d2198179ba2c2ebf50cd386e912ffbdd856136cef96e0f6014d334a", + "note": "Same-PR finalization closing digest" + } + ], + "reopenings": [] +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/change.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/change.md new file mode 100644 index 00000000..e3e0562b --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/change.md @@ -0,0 +1,24 @@ +--- +id: CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi +state: archived +type: operations +base_commit: 0ea95ce5a409ee7685bb31a182ef2c633bf9baee +--- + +# Simplify SpecSync CI to one expensive-suite authority with residual Trust identity gates, preserving full local verification and documenting the 95% confidence model + +## Intent + +Simplify SpecSync CI to one expensive-suite authority with residual Trust identity gates, preserving full local verification and documenting the 95% confidence model + +## Affected Canonical Specs + +- `github` + +## Acceptance Criteria + +- Trust lifecycle no longer invokes the full verify lane or cargo test; fledge keeps a full local verify lane and adds a fast trust-lifecycle lane; docs assign each confidence signal to one authority, describe the current protected-workflow boundary and Tier B multi-OS plan, and quantify expected Trust/product-tip wall-clock improvement; fledge lanes run trust-lifecycle and strict spec validation pass; this PR contains no protected workflow files or unrelated ship-status/#486 history. + +## No-spec Rationale + +Not applicable diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/context.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/context.md new file mode 100644 index 00000000..02ff63f5 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/context.md @@ -0,0 +1,41 @@ +--- +change: CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi +artifact: context +--- + +# Context + +## Trigger + +Normal product PRs currently pay for the full Rust test suite in GitHub CI and again through +Trust's `lanes.verify` lifecycle. The duplicate Trust run accounted for roughly 17 minutes of a +roughly 20-minute Trust job while adding little independent signal. + +## Root cause + +`.trust.toml` points at the all-purpose local `verify` lane. That lane correctly contains the full +suite for human and agent completion, but it is the wrong lifecycle command for hosted Trust after +CI already owns compilation, linting, tests, audit, and coverage. + +## Durable invariant + +Each expensive confidence signal has one hosted authority. CI owns the product suite; Trust owns +release-binary identity, contract binding, risk, and provenance. Full local verification remains +available and unchanged. Platform matrix reduction is a separate protected-workflow change. + +## Scope boundary + +This change does not modify `.github/workflows/**` or protected lifecycle scripts. It removes the +duplicate Trust lifecycle suite with non-protected configuration and documents the current state, +the intended Tier B multi-OS plan, and the required pinned-policy process for a later workflow PR. + +## Focused result + +`fledge lanes run trust-lifecycle --non-interactive` selected only `check-types` and completed from +a cold local target in 18.1 seconds. Fledge validated all seven lane definitions, and the thin diff +contained no protected workflow, protected verifier, product source, or `cmd_change` spec path. + +`change check` materialized the new requirement before re-validating the living-`ADDED` rule, so +the same invocation required the now-living requirement delta to be classified `MODIFIED`. The +retry's duplicate version/changelog materialization was normalized back to one version increment +and one generated changelog row before final verification. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/deltas/github.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/deltas/github.md new file mode 100644 index 00000000..1c1f64a1 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/deltas/github.md @@ -0,0 +1,18 @@ +## MODIFIED + +### REQUIREMENT REQ-github-006 + +Hosted verification SHALL assign each expensive confidence signal to one authoritative workflow, +while Trust SHALL retain release-binary identity, strict contract, risk, and provenance checks +without re-running the full product test suite. + +Acceptance Criteria + +- GitHub CI remains the authority for formatting, linting, full Rust tests, strict spec coverage, + audit, coverage measurement, site, editor extension, and packaged-action consumer checks. +- `.trust.toml` invokes a dedicated `trust-lifecycle` lane that does not contain `cargo test`, + clippy, or the full `verify` lane. +- `lanes.verify` remains the full local completion suite for agents and humans. +- Documentation identifies the current multi-OS matrix as Tier B work that requires a separately + pinned protected-workflow update; this change does not silently weaken platform coverage. +- The thin PR contains no protected workflow files and no ship-status/product-code feature. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/design.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/design.md new file mode 100644 index 00000000..153af1e3 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/design.md @@ -0,0 +1,19 @@ +--- +change: CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi +artifact: design +--- + +# Design + +```text +GitHub CI (suite authority) Trust (residual authority) +fmt / clippy / cargo test release binary identity +strict spec coverage contract on that binary +audit / coverage / consumers Augur risk + Attest provenance + +Local `lanes.verify` = complete suite retained for humans and agents +Hosted `lanes.trust-lifecycle` = lightweight lifecycle prerequisite only +``` + +The two hosted workflows provide complementary evidence. Trust does not treat the lightweight lane +as a substitute for CI, and CI does not replace binary identity, risk, or provenance. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/docs.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/docs.md new file mode 100644 index 00000000..ef4dced0 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/docs.md @@ -0,0 +1,13 @@ +--- +change: CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi +artifact: docs +--- + +# Docs + +- Add `docs/ci-confidence.md` as the operator-facing ownership matrix. +- Link it from `AGENTS.md` and `CONTRIBUTING.md`. +- State the everyday target as approximately 95% merge confidence, not an absolute guarantee. +- Distinguish the immediately delivered no-duplicate Trust change from the protected Tier B matrix + follow-up so agents do not claim Windows/macOS have moved before the workflow PR lands. +- Explicitly forbid reintroducing full `lanes.verify` into hosted Trust. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/finalization.json b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/finalization.json new file mode 100644 index 00000000..9862c282 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/finalization.json @@ -0,0 +1,12 @@ +{ + "schema_version": 2, + "change_id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "implementation_commit": "b117382f08bf5f56965a2a1cf394b2d690662b8d", + "implementation_tree": "30010a52e1fb750022e5fe47b894751fe3fe23b7", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "closing_digest": "02bf8a658d2198179ba2c2ebf50cd386e912ffbdd856136cef96e0f6014d334a", + "review_digest": "e403e9fd68ae6141bbf6bc187c1bccb2344a95bd60861ea949aa73dd2fe6db2a", + "finalization_digest": "3530996c8a369607f2ebf780a2937a4e9110cc7fe27e6b7bd1976b369cbb2f2a", + "timestamp": 1785628845 +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/plan.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/plan.md new file mode 100644 index 00000000..ba838c84 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/plan.md @@ -0,0 +1,14 @@ +--- +change: CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi +artifact: plan +--- + +# Plan + +1. Split the hosted Trust lifecycle from the full local verification lane. +2. Point `.trust.toml` at the residual, no-test lifecycle lane. +3. Canonicalize the CI-versus-Trust ownership invariant in the GitHub spec. +4. Document confidence tiers, measured before/after expectations, and the protected-workflow + follow-up required to move macOS, Windows, and expensive coverage off ordinary PRs. +5. Run the fast lane, strict spec validation, configuration assertions, and full local verification + once before delivery; do not run staged-tip waiter loops. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/requirements.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/requirements.md new file mode 100644 index 00000000..be8efcb8 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/requirements.md @@ -0,0 +1,23 @@ +--- +change: CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi +artifact: requirements +--- + +# Requirements + +### REQ-github-006 + +Hosted verification SHALL assign each expensive confidence signal to one authoritative workflow, +while Trust SHALL retain release-binary identity, strict contract, risk, and provenance checks +without re-running the full product test suite. + +Acceptance Criteria + +- GitHub CI remains the authority for formatting, linting, full Rust tests, strict spec coverage, + audit, coverage measurement, site, editor extension, and packaged-action consumer checks. +- `.trust.toml` invokes a dedicated `trust-lifecycle` lane that does not contain `cargo test`, + clippy, or the full `verify` lane. +- `lanes.verify` remains the full local completion suite for agents and humans. +- Documentation identifies the current multi-OS matrix as Tier B work that requires a separately + pinned protected-workflow update; this change does not silently weaken platform coverage. +- The thin PR contains no protected workflow files and no ship-status/product-code feature. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/research.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/research.md new file mode 100644 index 00000000..e71f3201 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/research.md @@ -0,0 +1,24 @@ +--- +change: CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi +artifact: research +--- + +# Research + +## Finding + +The previous Trust job spent about 17 of about 20 minutes inside a lifecycle command that selected +the same full `cargo test` suite already owned by CI. Parallel execution does not remove that runner +cost and can extend the PR critical path. + +## Protected-policy constraint + +The base-controlled lifecycle policy protects `.github/workflows/**` and selected verifier scripts. +Those paths require a separately pinned required-workflow update. Mixing them into this ordinary PR +caused the existing remote #490 branch to fail the trusted-policy guard. + +## Consequence + +Land the non-protected Trust split first. Treat macOS/Windows/coverage scheduling and ancestor-check +reuse as a separate protected-policy change with its own pinning plan. This keeps the first PR thin, +reviewable, and immediately valuable. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/review-attempts.json b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/review-attempts.json new file mode 100644 index 00000000..4b97fe81 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/review-attempts.json @@ -0,0 +1,85 @@ +{ + "schema_version": 1, + "reviews": [ + { + "schema_version": 2, + "change_id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "reviewer": "codex-agent-singer", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "0ea95ce5a409ee7685bb31a182ef2c633bf9baee", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "399de0238ed83a9d4e81f38cf1bbf49031fb5b95c86963cac1509381d61fa806", + "timestamp": 1785610446 + }, + { + "schema_version": 2, + "change_id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "reviewer": "codex-agent-mendel", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "5cce84e2a850bb74a76249e3ea5bd01aff282577", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "1c69ee13b34891668c0df3e6b0556c12ebe6b78ab1fdc36fc119bcdc51f16c51", + "timestamp": 1785611655 + }, + { + "schema_version": 2, + "change_id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "reviewer": "Ohm", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "6a0956fbe53669aa9a7bc564fd472e0952a70f2a", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "timestamp": 1785621686 + }, + { + "schema_version": 2, + "change_id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "reviewer": "Ohm", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "5a2547ec2f8fc516c2dc28bc04f84346dec62971", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "timestamp": 1785627942 + }, + { + "schema_version": 2, + "change_id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "reviewer": "Ohm", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "f7fff1f32f6372aa6852f75220294970b1f2ff03", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "timestamp": 1785628820 + } + ] +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/review.json b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/review.json new file mode 100644 index 00000000..35441f00 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/review.json @@ -0,0 +1,16 @@ +{ + "schema_version": 2, + "change_id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "reviewer": "Ohm", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "f7fff1f32f6372aa6852f75220294970b1f2ff03", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "timestamp": 1785628820 +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/state.json b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/state.json new file mode 100644 index 00000000..57239efd --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/state.json @@ -0,0 +1,47 @@ +{ + "schema_version": 1, + "workflow_version": 2, + "workflow_origin_version": 2, + "id": "CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "slug": "simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi", + "title": "Simplify SpecSync CI to one expensive-suite authority with residual Trust identity gates, preserving full local verification and documenting the 95% confidence model", + "description": "Simplify SpecSync CI to one expensive-suite authority with residual Trust identity gates, preserving full local verification and documenting the 95% confidence model", + "kind": "operations", + "state": "archived", + "canonical_applied": true, + "base_commit": "0ea95ce5a409ee7685bb31a182ef2c633bf9baee", + "created_at": 1785608765, + "updated_at": 1785628993, + "affected_specs": [ + "github" + ], + "affected_paths": [ + "fledge.toml", + ".trust.toml", + "docs/ci-confidence.md", + "AGENTS.md", + "CONTRIBUTING.md", + "specs/github/", + ".specsync/change-sequence.json" + ], + "no_spec_change": false, + "no_spec_change_rationale": null, + "acceptance_criteria": [ + "Trust lifecycle no longer invokes the full verify lane or cargo test; fledge keeps a full local verify lane and adds a fast trust-lifecycle lane; docs assign each confidence signal to one authority, describe the current protected-workflow boundary and Tier B multi-OS plan, and quantify expected Trust/product-tip wall-clock improvement; fledge lanes run trust-lifecycle and strict spec validation pass; this PR contains no protected workflow files or unrelated ship-status/#486 history." + ], + "selected_artifacts": [ + "context", + "plan", + "testing", + "design", + "tasks", + "requirements", + "docs", + "research" + ], + "dependencies": [], + "answers": { + "architecture_risk": "yes", + "public_contract": "yes" + } +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/tasks.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/tasks.md new file mode 100644 index 00000000..66ffa455 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/tasks.md @@ -0,0 +1,12 @@ +--- +change: CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi +artifact: tasks +--- + +# Tasks + +- [x] Remove the full `verify` lane from hosted Trust lifecycle execution. +- [x] Preserve the full local `verify` lane. +- [x] Add the CI confidence ownership documentation and canonical GitHub requirement. +- [x] Prove the thin diff contains no protected workflow or ship-status files. +- [x] Run focused configuration checks and initiate the one authoritative full verification pass. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/testing.md b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/testing.md new file mode 100644 index 00000000..61bf8510 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/testing.md @@ -0,0 +1,42 @@ +--- +change: CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi +artifact: testing +--- + +# Testing + +## Requirement Evidence + +| Requirement | Evidence | +|-------------|----------| +| `REQ-github-006` | `fledge lanes validate`, the one-step `trust-lifecycle` run, thin-diff inspection, and recorded `change check` evidence for both validators, 2,178 unit tests, 333 integration tests, and release validation prove single hosted suite ownership while retaining full product confidence. | + +## Focused checks + +- `fledge lanes run trust-lifecycle --non-interactive` completes quickly and runs `check-types` + without invoking the `test`, `lint`, or `verify` lanes. +- Parse `fledge.toml` and `.trust.toml` to assert that Trust targets `trust-lifecycle`, the residual + lane excludes expensive tasks, and `verify` still contains the full suite. +- `git diff --name-only origin/main...HEAD` contains no `.github/workflows/**`, protected script, + `src/**`, or `specs/cmd_change/**` path. + +## Completion checks + +- [x] `fledge lanes validate --non-interactive` +- [x] `fledge lanes run trust-lifecycle --non-interactive` +- [x] `specsync change check ` (includes the one full `cargo test` run) +- [ ] Run residual Trust against the current candidate binary after the implementation commit +- [ ] Confirm the hosted PR's strict 100% spec/path coverage gate + +`fledge lanes run verify` remains the full local suite by configuration. It is intentionally not +run after `change check`, because both select the same full `cargo test` suite and this change exists +to remove that kind of duplicate execution. + +## Hosted observation + +Compare the prior Trust job (roughly 20 minutes, roughly 17 minutes in duplicated tests) with the +new Trust lifecycle timing. Product PR wall clock may still be bounded by the current protected +macOS/Windows/coverage jobs until the separately pinned Tier B workflow change lands. + +Focused local observation: `trust-lifecycle` selected exactly one `check-types` step and completed +in 18.1 seconds from a cold target; `fledge lanes validate` reported seven valid lanes. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/verification-attempts.json b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/verification-attempts.json new file mode 100644 index 00000000..c8b12f92 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/verification-attempts.json @@ -0,0 +1,352 @@ +{ + "schema_version": 1, + "attempts": [ + { + "timestamp": 1785609643, + "commit": "0ea95ce5a409ee7685bb31a182ef2c633bf9baee", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "843e3a997ff1ac4637321e12baa00bf7b64ff23afea0c25ddb637f9620e5c304", + "workspace_digest": "05600cb9fee8b588b725ab043a2232fe24f6d7e9aaa80e3532cc5128f72d3b45", + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-github-006" + ] + }, + { + "timestamp": 1785610427, + "commit": "0ea95ce5a409ee7685bb31a182ef2c633bf9baee", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "399de0238ed83a9d4e81f38cf1bbf49031fb5b95c86963cac1509381d61fa806", + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-github-006" + ] + }, + { + "timestamp": 1785611314, + "commit": "5cce84e2a850bb74a76249e3ea5bd01aff282577", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "1c69ee13b34891668c0df3e6b0556c12ebe6b78ab1fdc36fc119bcdc51f16c51", + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-github-006" + ] + }, + { + "timestamp": 1785621578, + "commit": "6a0956fbe53669aa9a7bc564fd472e0952a70f2a", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-github-006" + ] + }, + { + "timestamp": 1785626045, + "commit": "e0d87f7f377b55eb0299791dddaff52ed3cc9fe5", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-github-006" + ] + }, + { + "timestamp": 1785628702, + "commit": "f7fff1f32f6372aa6852f75220294970b1f2ff03", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-github-006" + ] + }, + { + "timestamp": 1785628702, + "commit": "b117382f08bf5f56965a2a1cf394b2d690662b8d", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "acceptance_input_digest": "2fbdedb2fd629fdd0b9fe69959225ce3165e6d8e5c4c0978fa55469b34be7732", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "fed02472a71a862bd21459e3708ee9311ffff1979a9be6ba740aa6272fb45bdd", + "entry_digest": "728bf6e5e69af9e782b590d94d716459bbed12d8955db4431bdf910b2d6f149d", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".trust.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "bb948a1aa15195656027d9e0cec61fae9ecd1bbcd3a805fdeb917540d65565f8", + "entry_digest": "a9770865de6f153cb96725f433d1165c4113b45b9fa98eda5edb6fcdf2a1528f", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "AGENTS.md", + "kind": "file", + "mode": 33188, + "payload_digest": "458c78b8d534d7bda6f6d1d80347ef0814be529aab6b79ffa16e048e49ae166c", + "entry_digest": "5eb1a8db5207c4d10fb5b5da694b0ad794cc27293c28632d86623476e0a58fef", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "CONTRIBUTING.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1065a9e102726c5cc6345e7f40244b155b3672cc4baa3435f35dd4d3c3d30e40", + "entry_digest": "abbe1f4d93268fb865b3c61e4fe0a0a80593f83d73271a06dc4a0bd8da6fe269", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "docs/ci-confidence.md", + "kind": "file", + "mode": 33188, + "payload_digest": "4d9dcd266525bda75cc1945da942366ca73bd1fff43f99a0d74dd3539b7b0ed6", + "entry_digest": "527f33130f1e772fb8ee159ad85f307f69f26d104a98975e7cae5012bfac7c15", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "fledge.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "ff5fdc238533e7dcac55fda6905017c1f3de55bb89eac2e90809157ee500450f", + "entry_digest": "e76d75aff490699fcd003612377ac7a45f1c61ed053e89a69d477bbf41f8f3b9", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "specs/github/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "6ba9554c4ce312a2b88c35a3bc23301194f8eb686ca0bcdf9152bf1305288db8", + "entry_digest": "4c0783a7143da27d553af371a5d1065595b938c48459531c1e200310b9d8d990", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/github.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "156646b4097a2969f20c73b9033060638a9216f28730524d3884cae0f7e5da0d", + "entry_digest": "d99a44b4b34d82a13fa084fa4f47faab0231891c8e7aba83f954cd4f34a26655", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "a26c7a57105f06078d4d3deccfb9987aa891cbec037a5d2fa65d78d5af292715", + "entry_digest": "a9968738d1ac7275f47bf42df8c4cbd925ec5340edc381a9c642926ba26721b4", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ae07e58f99686f2e63b8eb6c806b15aa807a140bce70f2bc12506530662e4987", + "entry_digest": "5280b1ac3a230da44e711ce32ec17f1b5d1f4a57399cb158bfa734dd61a1c2ea", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "dfe4c9957cf27e66e6bf361c62b8ea066a14b7aff97ea066e7350a2c4758c3ea", + "entry_digest": "0d827370a16f27aef25e7988369d6bcbff5798ea9054b14824c74c30221ed2a9", + "owners": [ + "github" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-github-006" + ] + } + ] +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/verification.json b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/verification.json new file mode 100644 index 00000000..d49faa13 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi/verification.json @@ -0,0 +1,149 @@ +{ + "timestamp": 1785628702, + "commit": "b117382f08bf5f56965a2a1cf394b2d690662b8d", + "contract_digest": "3f6c3bc6aaf1bffef82fee470b3a88c57c9127422f5a407fe1bbef0e40d4db81", + "execution_digest": "67801ba3c1882eeb6761810703f950e3651af807fb105fd196157aca63653ad4", + "workspace_digest": "5b2ee5cca0f72cbd13dcc5ae664cd04099a5ea6f508ef9021269e252af971dac", + "acceptance_input_digest": "2fbdedb2fd629fdd0b9fe69959225ce3165e6d8e5c4c0978fa55469b34be7732", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "fed02472a71a862bd21459e3708ee9311ffff1979a9be6ba740aa6272fb45bdd", + "entry_digest": "728bf6e5e69af9e782b590d94d716459bbed12d8955db4431bdf910b2d6f149d", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".trust.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "bb948a1aa15195656027d9e0cec61fae9ecd1bbcd3a805fdeb917540d65565f8", + "entry_digest": "a9770865de6f153cb96725f433d1165c4113b45b9fa98eda5edb6fcdf2a1528f", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "AGENTS.md", + "kind": "file", + "mode": 33188, + "payload_digest": "458c78b8d534d7bda6f6d1d80347ef0814be529aab6b79ffa16e048e49ae166c", + "entry_digest": "5eb1a8db5207c4d10fb5b5da694b0ad794cc27293c28632d86623476e0a58fef", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "CONTRIBUTING.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1065a9e102726c5cc6345e7f40244b155b3672cc4baa3435f35dd4d3c3d30e40", + "entry_digest": "abbe1f4d93268fb865b3c61e4fe0a0a80593f83d73271a06dc4a0bd8da6fe269", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "docs/ci-confidence.md", + "kind": "file", + "mode": 33188, + "payload_digest": "4d9dcd266525bda75cc1945da942366ca73bd1fff43f99a0d74dd3539b7b0ed6", + "entry_digest": "527f33130f1e772fb8ee159ad85f307f69f26d104a98975e7cae5012bfac7c15", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "fledge.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "ff5fdc238533e7dcac55fda6905017c1f3de55bb89eac2e90809157ee500450f", + "entry_digest": "e76d75aff490699fcd003612377ac7a45f1c61ed053e89a69d477bbf41f8f3b9", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "specs/github/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "6ba9554c4ce312a2b88c35a3bc23301194f8eb686ca0bcdf9152bf1305288db8", + "entry_digest": "4c0783a7143da27d553af371a5d1065595b938c48459531c1e200310b9d8d990", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/github.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "156646b4097a2969f20c73b9033060638a9216f28730524d3884cae0f7e5da0d", + "entry_digest": "d99a44b4b34d82a13fa084fa4f47faab0231891c8e7aba83f954cd4f34a26655", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "a26c7a57105f06078d4d3deccfb9987aa891cbec037a5d2fa65d78d5af292715", + "entry_digest": "a9968738d1ac7275f47bf42df8c4cbd925ec5340edc381a9c642926ba26721b4", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ae07e58f99686f2e63b8eb6c806b15aa807a140bce70f2bc12506530662e4987", + "entry_digest": "5280b1ac3a230da44e711ce32ec17f1b5d1f4a57399cb158bfa734dd61a1c2ea", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "dfe4c9957cf27e66e6bf361c62b8ea066a14b7aff97ea066e7350a2c4758c3ea", + "entry_digest": "0d827370a16f27aef25e7988369d6bcbff5798ea9054b14824c74c30221ed2a9", + "owners": [ + "github" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-github-006" + ] +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/accepted-state.json b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/accepted-state.json new file mode 100644 index 00000000..4b415f0e --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/accepted-state.json @@ -0,0 +1,41 @@ +{ + "schema_version": 1, + "workflow_version": 2, + "workflow_origin_version": 2, + "id": "CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c", + "slug": "accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c", + "title": "Accept GitHub's rewritten trusted-policy check URL while preserving exact base-controlled workflow provenance", + "description": "Accept GitHub's rewritten trusted-policy check URL while preserving exact base-controlled workflow provenance", + "kind": "bug_fix", + "state": "accepted", + "canonical_applied": true, + "base_commit": "505b01d9b919b0aa18516961e67c1a52ab88e815", + "created_at": 1785629351, + "updated_at": 1785631576, + "affected_specs": [ + "github" + ], + "affected_paths": [ + ".github/scripts/verify-trusted-policy-check.py", + ".github/scripts/test-verify-trusted-policy-check.py", + ".specsync/change-sequence.json" + ], + "no_spec_change": true, + "no_spec_change_rationale": "This corrects the protected verifier to satisfy the existing trusted-policy contract; it does not change the public CI policy or lifecycle workflow.", + "acceptance_criteria": [ + "The verifier accepts the successful official GitHub Actions trusted-policy check even when GitHub rewrites its details URL, independently authenticates one successful base-controlled pull_request_target workflow run by exact candidate SHA, repository, workflow path, trusted revision, and PR number, and rejects mismatched app, event, path, repository, candidate, revision, PR, unsuccessful, missing, or ambiguous provenance. Focused fixtures reproduce the rewritten URL and moved-PR-tip behavior." + ], + "selected_artifacts": [ + "context", + "testing", + "tasks", + "research", + "design", + "plan" + ], + "dependencies": [], + "answers": { + "architecture_risk": "yes", + "public_contract": "no" + } +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/approvals.json b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/approvals.json new file mode 100644 index 00000000..cac36a17 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/approvals.json @@ -0,0 +1,45 @@ +{ + "approvals": [ + { + "gate": "definition", + "actor": "OXLEIF", + "timestamp": 1785629458, + "digest": "d04eae7428183bd15a88f020db6988499dcff74e388c8b90499a64911c2039b4", + "note": "User approved digest d04eae7428183bd15a88f020db6988499dcff74e388c8b90499a64911c2039b4.", + "approved_scope": { + "schema_version": 1, + "change_id": "CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c", + "title": "Accept GitHub's rewritten trusted-policy check URL while preserving exact base-controlled workflow provenance", + "description": "Accept GitHub's rewritten trusted-policy check URL while preserving exact base-controlled workflow provenance", + "kind": "bug_fix", + "affected_specs": [ + "github" + ], + "affected_paths": [ + ".github/scripts/test-verify-trusted-policy-check.py", + ".github/scripts/verify-trusted-policy-check.py", + ".specsync/change-sequence.json" + ], + "no_spec_change": true, + "no_spec_change_rationale": "This corrects the protected verifier to satisfy the existing trusted-policy contract; it does not change the public CI policy or lifecycle workflow.", + "acceptance_criteria": [ + "The verifier accepts the successful official GitHub Actions trusted-policy check even when GitHub rewrites its details URL, independently authenticates one successful base-controlled pull_request_target workflow run by exact candidate SHA, repository, workflow path, trusted revision, and PR number, and rejects mismatched app, event, path, repository, candidate, revision, PR, unsuccessful, missing, or ambiguous provenance. Focused fixtures reproduce the rewritten URL and moved-PR-tip behavior." + ], + "dependencies": [], + "supersedes": [], + "answers": { + "architecture_risk": "yes", + "public_contract": "no" + } + } + }, + { + "gate": "finalization", + "actor": "specsync:finalization", + "timestamp": 1785631576, + "digest": "f552df6e86b7937519a4b6fcf62dd3195ab98a2e46c03b84cada42dff004b10b", + "note": "Same-PR finalization closing digest" + } + ], + "reopenings": [] +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/change.md b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/change.md new file mode 100644 index 00000000..eb44ad07 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/change.md @@ -0,0 +1,24 @@ +--- +id: CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c +state: archived +type: bug_fix +base_commit: 505b01d9b919b0aa18516961e67c1a52ab88e815 +--- + +# Accept GitHub's rewritten trusted-policy check URL while preserving exact base-controlled workflow provenance + +## Intent + +Accept GitHub's rewritten trusted-policy check URL while preserving exact base-controlled workflow provenance + +## Affected Canonical Specs + +- `github` + +## Acceptance Criteria + +- The verifier accepts the successful official GitHub Actions trusted-policy check even when GitHub rewrites its details URL, independently authenticates one successful base-controlled pull_request_target workflow run by exact candidate SHA, repository, workflow path, trusted revision, and PR number, and rejects mismatched app, event, path, repository, candidate, revision, PR, unsuccessful, missing, or ambiguous provenance. Focused fixtures reproduce the rewritten URL and moved-PR-tip behavior. + +## No-spec Rationale + +This corrects the protected verifier to satisfy the existing trusted-policy contract; it does not change the public CI policy or lifecycle workflow. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/context.md b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/context.md new file mode 100644 index 00000000..8b2ff304 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/context.md @@ -0,0 +1,29 @@ +--- +change: CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c +artifact: context +--- + +# Context + +## Trigger + +PR #491's first non-bootstrap archive-only gate rejected a successful trusted-policy result with +`check details URL is not an exact workflow run`. + +## Root cause and invariant + +The publisher requested an `/actions/runs/` details URL, but GitHub persisted the +official Actions check with its canonical `/runs/` URL. A display URL is not a stable +provenance field. Acceptance must instead bind the official check and a successful base-controlled +workflow run to the same repository, candidate SHA, trusted revision, workflow path, and PR. + +## Scope + +This is a protected verifier correction only. It does not weaken the trusted-policy rule, alter the +workflow, or expand CHG-0074's product behavior. + +## Implementation status + +The verifier now accepts only GitHub's canonical check URL or the requested workflow URL, then +independently queries a bounded exact-candidate run set and requires one matching successful +base-controlled policy run. PR #491's previously rejected parent now verifies successfully. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/design.md b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/design.md new file mode 100644 index 00000000..95398b9e --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/design.md @@ -0,0 +1,15 @@ +--- +change: CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c +artifact: design +--- + +# Design + +1. Keep the existing official GitHub Actions app, exact candidate SHA, successful conclusion, and + external trusted-revision binding checks. +2. Enumerate bounded runs for the trusted lifecycle-policy workflow and select the unique successful + `pull_request_target` run whose immutable fields match the repository, candidate, workflow path, + trusted revision, and PR number. +3. Accept GitHub's canonical rewritten check URL without deriving authority from it. +4. Fail closed for zero matches, multiple matches, wrong app/event/path/repository/SHA/revision/PR, + or unsuccessful runs. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/finalization.json b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/finalization.json new file mode 100644 index 00000000..0b709bf6 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/finalization.json @@ -0,0 +1,12 @@ +{ + "schema_version": 2, + "change_id": "CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c", + "implementation_commit": "d1bd5f38d4f59d4266fc6f9a845f4269c2ee9ad1", + "implementation_tree": "bb7fe0a42e2708b1d0a0b41877d4b96249a78d65", + "contract_digest": "d04eae7428183bd15a88f020db6988499dcff74e388c8b90499a64911c2039b4", + "workspace_digest": "956869a596bd1d0ed4f946d817e5b859462c667ded252ca635789c89f9a63616", + "closing_digest": "f552df6e86b7937519a4b6fcf62dd3195ab98a2e46c03b84cada42dff004b10b", + "review_digest": "e521da95ffe61b021e5ae712bc04a02a7d153409fced9ed52cfff40216245f8a", + "finalization_digest": "753cc117867fce0a299074e7a7c3802b304cfe7e0ffee092b07a5792304d7255", + "timestamp": 1785631576 +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/plan.md b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/plan.md new file mode 100644 index 00000000..88f90c53 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/plan.md @@ -0,0 +1,14 @@ +--- +change: CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c +artifact: plan +--- + +# Plan + +1. Add fixtures reproducing GitHub's rewritten check URL and a PR tip that advanced after the parent + policy run. +2. Refactor trusted-run discovery to authenticate bounded Actions API results instead of parsing the + check details URL. +3. Add negative fixtures for every provenance mismatch and ambiguity. +4. Run the focused verifier tests, lifecycle workflow checks, strict spec check, and the PR's + lightweight archive gate. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/research.md b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/research.md new file mode 100644 index 00000000..2aa9f0cc --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/research.md @@ -0,0 +1,20 @@ +--- +change: CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c +artifact: research +--- + +# Research + +## Observed evidence + +- Candidate `b117382f08bf5f56965a2a1cf394b2d690662b8d` had a successful official GitHub Actions + `SpecSync trusted policy` check with the expected external revision binding. +- GitHub returned `https://github.com/CorvidLabs/spec-sync/runs/` despite the publisher + supplying an Actions workflow-run URL. +- The successful `pull_request_target` workflow run remained queryable with the exact candidate SHA, + repository, workflow path, event, conclusion, and PR association. + +## Conclusion + +The verifier must treat the check details URL as presentation metadata and independently authenticate +the workflow run through GitHub's Actions API. Ambiguous or mismatched runs remain failures. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/review-attempts.json b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/review-attempts.json new file mode 100644 index 00000000..6b43fe76 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/review-attempts.json @@ -0,0 +1,21 @@ +{ + "schema_version": 1, + "reviews": [ + { + "schema_version": 2, + "change_id": "CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c", + "reviewer": "Ohm", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "a8d97e11f9dd482486b49644ea2a0eb872ce7d55", + "contract_digest": "d04eae7428183bd15a88f020db6988499dcff74e388c8b90499a64911c2039b4", + "execution_digest": "3734bcf0e99063638f67ecc277f781fd0cfab9b0d72bcbc7437e29349d187985", + "workspace_digest": "956869a596bd1d0ed4f946d817e5b859462c667ded252ca635789c89f9a63616", + "timestamp": 1785631533 + } + ] +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/review.json b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/review.json new file mode 100644 index 00000000..e2ab2868 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/review.json @@ -0,0 +1,16 @@ +{ + "schema_version": 2, + "change_id": "CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c", + "reviewer": "Ohm", + "provenance": { + "schema_version": 1, + "provider": "github_actions_check", + "required_check": "SpecSync scoped review" + }, + "verdict": "pass", + "implementation_commit": "a8d97e11f9dd482486b49644ea2a0eb872ce7d55", + "contract_digest": "d04eae7428183bd15a88f020db6988499dcff74e388c8b90499a64911c2039b4", + "execution_digest": "3734bcf0e99063638f67ecc277f781fd0cfab9b0d72bcbc7437e29349d187985", + "workspace_digest": "956869a596bd1d0ed4f946d817e5b859462c667ded252ca635789c89f9a63616", + "timestamp": 1785631533 +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/state.json b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/state.json new file mode 100644 index 00000000..886d657e --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/state.json @@ -0,0 +1,41 @@ +{ + "schema_version": 1, + "workflow_version": 2, + "workflow_origin_version": 2, + "id": "CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c", + "slug": "accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c", + "title": "Accept GitHub's rewritten trusted-policy check URL while preserving exact base-controlled workflow provenance", + "description": "Accept GitHub's rewritten trusted-policy check URL while preserving exact base-controlled workflow provenance", + "kind": "bug_fix", + "state": "archived", + "canonical_applied": true, + "base_commit": "505b01d9b919b0aa18516961e67c1a52ab88e815", + "created_at": 1785629351, + "updated_at": 1785631713, + "affected_specs": [ + "github" + ], + "affected_paths": [ + ".github/scripts/verify-trusted-policy-check.py", + ".github/scripts/test-verify-trusted-policy-check.py", + ".specsync/change-sequence.json" + ], + "no_spec_change": true, + "no_spec_change_rationale": "This corrects the protected verifier to satisfy the existing trusted-policy contract; it does not change the public CI policy or lifecycle workflow.", + "acceptance_criteria": [ + "The verifier accepts the successful official GitHub Actions trusted-policy check even when GitHub rewrites its details URL, independently authenticates one successful base-controlled pull_request_target workflow run by exact candidate SHA, repository, workflow path, trusted revision, and PR number, and rejects mismatched app, event, path, repository, candidate, revision, PR, unsuccessful, missing, or ambiguous provenance. Focused fixtures reproduce the rewritten URL and moved-PR-tip behavior." + ], + "selected_artifacts": [ + "context", + "testing", + "tasks", + "research", + "design", + "plan" + ], + "dependencies": [], + "answers": { + "architecture_risk": "yes", + "public_contract": "no" + } +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/tasks.md b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/tasks.md new file mode 100644 index 00000000..a604677a --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/tasks.md @@ -0,0 +1,14 @@ +--- +change: CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c +artifact: tasks +--- + +# Tasks + +- [x] Add rewritten-URL and moved-tip characterization fixtures. +- [x] Implement bounded exact-provenance workflow-run discovery. +- [x] Add fail-closed mismatch and ambiguity coverage. +- [x] Verify the protected policy tests and live archive-parent PR #491 path. + +Independent scoped review remains the lifecycle gate after verification; it is not an unfinished +implementation task. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/testing.md b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/testing.md new file mode 100644 index 00000000..72d6fca6 --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/testing.md @@ -0,0 +1,27 @@ +--- +change: CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c +artifact: testing +--- + +# Testing + +## Focused regressions + +- Successful official check with GitHub-rewritten `/runs/` URL passes only when one exact + successful base-controlled workflow run exists. +- A completed parent run remains attributable after the PR head advances to its archive child. +- Wrong app, event, workflow path, repository, candidate SHA, trusted revision, PR number, status, + conclusion, missing run, and ambiguous runs fail closed. + +## Verification + +- `python3 -S .github/scripts/test-verify-trusted-policy-check.py` +- `bash .github/scripts/test-lifecycle-workflows.sh` +- `specsync check --strict` +- PR #491 archive-integrity and required CI gates on the exact final child + +## Results + +- The focused fixture suite passes. +- A live read-only verification of PR #491 parent `b117382` resolves and authenticates trusted + policy run `30724416401` at base revision `0ea95ce5`, reproducing and fixing the hosted failure. diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/verification-attempts.json b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/verification-attempts.json new file mode 100644 index 00000000..ce2cef3c --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/verification-attempts.json @@ -0,0 +1,179 @@ +{ + "schema_version": 1, + "attempts": [ + { + "timestamp": 1785630740, + "commit": "a8d97e11f9dd482486b49644ea2a0eb872ce7d55", + "contract_digest": "d04eae7428183bd15a88f020db6988499dcff74e388c8b90499a64911c2039b4", + "execution_digest": "3734bcf0e99063638f67ecc277f781fd0cfab9b0d72bcbc7437e29349d187985", + "workspace_digest": "956869a596bd1d0ed4f946d817e5b859462c667ded252ca635789c89f9a63616", + "passed": false, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": false, + "exit_code": 101 + } + ], + "requirement_ids": [] + }, + { + "timestamp": 1785631277, + "commit": "a8d97e11f9dd482486b49644ea2a0eb872ce7d55", + "contract_digest": "d04eae7428183bd15a88f020db6988499dcff74e388c8b90499a64911c2039b4", + "execution_digest": "3734bcf0e99063638f67ecc277f781fd0cfab9b0d72bcbc7437e29349d187985", + "workspace_digest": "956869a596bd1d0ed4f946d817e5b859462c667ded252ca635789c89f9a63616", + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] + }, + { + "timestamp": 1785631277, + "commit": "d1bd5f38d4f59d4266fc6f9a845f4269c2ee9ad1", + "contract_digest": "d04eae7428183bd15a88f020db6988499dcff74e388c8b90499a64911c2039b4", + "execution_digest": "3734bcf0e99063638f67ecc277f781fd0cfab9b0d72bcbc7437e29349d187985", + "workspace_digest": "956869a596bd1d0ed4f946d817e5b859462c667ded252ca635789c89f9a63616", + "acceptance_input_digest": "d2cae3f508d375acc87658c1cf50c844c78f83c942295010502e19be47288d3c", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".github/scripts/test-verify-trusted-policy-check.py", + "kind": "file", + "mode": 33261, + "payload_digest": "7cb28d1222ea847aea69efbc83a6751d4b27ee1168174980c8460966627233c8", + "entry_digest": "d7d5895702e9def5a3f7ae64839401e0ba0fe4f4fba91684d4ff68bc878634df", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".github/scripts/verify-trusted-policy-check.py", + "kind": "file", + "mode": 33261, + "payload_digest": "927392607a75adf6f3ae38a4dc92ce967504d7c00d093d41883fb6f3750b7521", + "entry_digest": "db157d1da8f175999e089b7a8ec5668198a4137c1e047ebad4208bb2bbb68ee4", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "5c21287e721415b0808da1dbc6f303f9473c9c6568dca8d2fc43c673aeac7b7d", + "entry_digest": "12072000ec09137383d371c8daa839429cfc2cbc76142210b51cfac25233728f", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "specs/github/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "6ba9554c4ce312a2b88c35a3bc23301194f8eb686ca0bcdf9152bf1305288db8", + "entry_digest": "4c0783a7143da27d553af371a5d1065595b938c48459531c1e200310b9d8d990", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/github.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "156646b4097a2969f20c73b9033060638a9216f28730524d3884cae0f7e5da0d", + "entry_digest": "d99a44b4b34d82a13fa084fa4f47faab0231891c8e7aba83f954cd4f34a26655", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "a26c7a57105f06078d4d3deccfb9987aa891cbec037a5d2fa65d78d5af292715", + "entry_digest": "a9968738d1ac7275f47bf42df8c4cbd925ec5340edc381a9c642926ba26721b4", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ae07e58f99686f2e63b8eb6c806b15aa807a140bce70f2bc12506530662e4987", + "entry_digest": "5280b1ac3a230da44e711ce32ec17f1b5d1f4a57399cb158bfa734dd61a1c2ea", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "dfe4c9957cf27e66e6bf361c62b8ea066a14b7aff97ea066e7350a2c4758c3ea", + "entry_digest": "0d827370a16f27aef25e7988369d6bcbff5798ea9054b14824c74c30221ed2a9", + "owners": [ + "github" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] + } + ] +} diff --git a/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/verification.json b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/verification.json new file mode 100644 index 00000000..17b5cfed --- /dev/null +++ b/.specsync/archive/changes/2026-08-02-CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c/verification.json @@ -0,0 +1,117 @@ +{ + "timestamp": 1785631277, + "commit": "d1bd5f38d4f59d4266fc6f9a845f4269c2ee9ad1", + "contract_digest": "d04eae7428183bd15a88f020db6988499dcff74e388c8b90499a64911c2039b4", + "execution_digest": "3734bcf0e99063638f67ecc277f781fd0cfab9b0d72bcbc7437e29349d187985", + "workspace_digest": "956869a596bd1d0ed4f946d817e5b859462c667ded252ca635789c89f9a63616", + "acceptance_input_digest": "d2cae3f508d375acc87658c1cf50c844c78f83c942295010502e19be47288d3c", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".github/scripts/test-verify-trusted-policy-check.py", + "kind": "file", + "mode": 33261, + "payload_digest": "7cb28d1222ea847aea69efbc83a6751d4b27ee1168174980c8460966627233c8", + "entry_digest": "d7d5895702e9def5a3f7ae64839401e0ba0fe4f4fba91684d4ff68bc878634df", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".github/scripts/verify-trusted-policy-check.py", + "kind": "file", + "mode": 33261, + "payload_digest": "927392607a75adf6f3ae38a4dc92ce967504d7c00d093d41883fb6f3750b7521", + "entry_digest": "db157d1da8f175999e089b7a8ec5668198a4137c1e047ebad4208bb2bbb68ee4", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "5c21287e721415b0808da1dbc6f303f9473c9c6568dca8d2fc43c673aeac7b7d", + "entry_digest": "12072000ec09137383d371c8daa839429cfc2cbc76142210b51cfac25233728f", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "specs/github/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "6ba9554c4ce312a2b88c35a3bc23301194f8eb686ca0bcdf9152bf1305288db8", + "entry_digest": "4c0783a7143da27d553af371a5d1065595b938c48459531c1e200310b9d8d990", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/github.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "156646b4097a2969f20c73b9033060638a9216f28730524d3884cae0f7e5da0d", + "entry_digest": "d99a44b4b34d82a13fa084fa4f47faab0231891c8e7aba83f954cd4f34a26655", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "a26c7a57105f06078d4d3deccfb9987aa891cbec037a5d2fa65d78d5af292715", + "entry_digest": "a9968738d1ac7275f47bf42df8c4cbd925ec5340edc381a9c642926ba26721b4", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ae07e58f99686f2e63b8eb6c806b15aa807a140bce70f2bc12506530662e4987", + "entry_digest": "5280b1ac3a230da44e711ce32ec17f1b5d1f4a57399cb158bfa734dd61a1c2ea", + "owners": [ + "github" + ] + }, + { + "path": "specs/github/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "dfe4c9957cf27e66e6bf361c62b8ea066a14b7aff97ea066e7350a2c4758c3ea", + "entry_digest": "0d827370a16f27aef25e7988369d6bcbff5798ea9054b14824c74c30221ed2a9", + "owners": [ + "github" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "bash .github/scripts/test-classify-ci-paths.sh", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", + "success": true, + "exit_code": 0 + }, + { + "command": "cargo test", + "success": true, + "exit_code": 0 + }, + { + "command": "python3 -S .github/scripts/validate-release-version.py", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] +} diff --git a/.specsync/change-sequence.json b/.specsync/change-sequence.json index 0259e1bb..df0d413e 100644 --- a/.specsync/change-sequence.json +++ b/.specsync/change-sequence.json @@ -1,7 +1,7 @@ { "schema_version": 1, - "sequence": 73, - "id": "CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa", + "sequence": 76, + "id": "CHG-0076-accept-github-s-rewritten-trusted-policy-check-url-while-preserving-exact-base-c", "acknowledged_collisions": [ { "sequence": 16, diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification-attempts.json b/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification-attempts.json deleted file mode 100644 index e082563b..00000000 --- a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification-attempts.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "schema_version": 1, - "attempts": [ - { - "timestamp": 1785601545, - "commit": "bcbd8e4689bb217cff655bbad82e76726afdd5a6", - "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", - "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", - "workspace_digest": "c9d9b0c6773c37b95ae2c175926a068ab02ae09dbc701d6d2342253f4ef3b833", - "passed": false, - "commands": [ - { - "command": "cargo test change::", - "success": false, - "exit_code": 101 - } - ], - "requirement_ids": [ - "REQ-change-047", - "REQ-change-048", - "REQ-cmd-change-006" - ] - }, - { - "timestamp": 1785602614, - "commit": "bcbd8e4689bb217cff655bbad82e76726afdd5a6", - "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", - "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", - "workspace_digest": "b94b577b2dfd9bae68c7206895e795fd0d5ebdc3218a6901782c0c689cd49b1b", - "passed": true, - "commands": [ - { - "command": "cargo test change::", - "success": true, - "exit_code": 0 - }, - { - "command": "cargo test commands::change::", - "success": true, - "exit_code": 0 - }, - { - "command": "cargo test", - "success": true, - "exit_code": 0 - }, - { - "command": "python3 -S .github/scripts/validate-release-version.py", - "success": true, - "exit_code": 0 - }, - { - "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", - "success": true, - "exit_code": 0 - } - ], - "requirement_ids": [ - "REQ-change-047", - "REQ-change-048", - "REQ-cmd-change-006" - ] - }, - { - "timestamp": 1785603957, - "commit": "09ecfe962b6e10d9b47249cdc5e4395815eae01f", - "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", - "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", - "workspace_digest": "c7c77d0ee4f3cee9a55824be8bcc28d525d064ba7a5da0726607ba92bf622283", - "passed": true, - "commands": [ - { - "command": "cargo test change::", - "success": true, - "exit_code": 0 - }, - { - "command": "cargo test commands::change::", - "success": true, - "exit_code": 0 - }, - { - "command": "cargo test", - "success": true, - "exit_code": 0 - }, - { - "command": "python3 -S .github/scripts/validate-release-version.py", - "success": true, - "exit_code": 0 - }, - { - "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", - "success": true, - "exit_code": 0 - } - ], - "requirement_ids": [ - "REQ-change-047", - "REQ-change-048", - "REQ-cmd-change-006" - ] - }, - { - "timestamp": 1785605359, - "commit": "481fa4f9f508abd1a8b1eab7807bb1aaa66d1c4f", - "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", - "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", - "workspace_digest": "332be3e5923156cf939064165c5b5ef692fbb25a33bff2f4525d75b024acb8d2", - "passed": true, - "commands": [ - { - "command": "cargo test change::", - "success": true, - "exit_code": 0 - }, - { - "command": "cargo test commands::change::", - "success": true, - "exit_code": 0 - }, - { - "command": "cargo test", - "success": true, - "exit_code": 0 - }, - { - "command": "python3 -S .github/scripts/validate-release-version.py", - "success": true, - "exit_code": 0 - }, - { - "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", - "success": true, - "exit_code": 0 - } - ], - "requirement_ids": [ - "REQ-change-047", - "REQ-change-048", - "REQ-cmd-change-006" - ] - } - ] -} diff --git a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification.json b/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification.json deleted file mode 100644 index 42a6067b..00000000 --- a/.specsync/changes/CHG-0073-approve-rejects-living-added-reqs-and-draft-next-action-waits-on-complete-artifa/verification.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "timestamp": 1785605359, - "commit": "481fa4f9f508abd1a8b1eab7807bb1aaa66d1c4f", - "contract_digest": "83601613808f75a7ca0550a4f9566c835b943bebd7a61b53587b77eaefa11f53", - "execution_digest": "ccf5fb9a066c478344c84bf1fa3f1e7e2087882a89f820ea41b55b881d6d4b10", - "workspace_digest": "332be3e5923156cf939064165c5b5ef692fbb25a33bff2f4525d75b024acb8d2", - "passed": true, - "commands": [ - { - "command": "cargo test change::", - "success": true, - "exit_code": 0 - }, - { - "command": "cargo test commands::change::", - "success": true, - "exit_code": 0 - }, - { - "command": "cargo test", - "success": true, - "exit_code": 0 - }, - { - "command": "python3 -S .github/scripts/validate-release-version.py", - "success": true, - "exit_code": 0 - }, - { - "command": "python3 -S .github/scripts/validate-workflow-runtime-pins.py", - "success": true, - "exit_code": 0 - } - ], - "requirement_ids": [ - "REQ-change-047", - "REQ-change-048", - "REQ-cmd-change-006" - ] -} diff --git a/.trust.toml b/.trust.toml index 82c7ed80..96549ba7 100644 --- a/.trust.toml +++ b/.trust.toml @@ -1,8 +1,10 @@ schema_version = 1 profile = "standard" +# Lifecycle on GitHub must NOT re-run cargo test (CI matrix owns that). +# Local full suite: `fledge lanes run verify`. See docs/ci-confidence.md. [lifecycle] -command = ["fledge", "lanes", "run", "verify"] +command = ["fledge", "lanes", "run", "trust-lifecycle"] [contract] enabled = true diff --git a/AGENTS.md b/AGENTS.md index 851e8fd6..8b7327e1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -130,6 +130,21 @@ Each `*.spec.md` needs YAML frontmatter (`module`, `version`, `status`, `files`) For richer integration, run `specsync mcp` to start the MCP server. This exposes `specsync_check`, `specsync_generate`, `specsync_coverage`, and `specsync_score` as callable tools. +## CI vs Trust (approximately 95% confidence, no duplicate suites) + +See **[docs/ci-confidence.md](docs/ci-confidence.md)**. + +| Gate | Authority | +|------|-----------| +| Product tests, clippy, coverage, audit, site, vscode | **GitHub CI** | +| PR release binary + contract on that binary + Augur + Attest | **Trust workflow** | +| Full local suite before "done" | `fledge lanes run verify` | +| Trust GitHub lifecycle | `trust-lifecycle` (types only — CI already tested) | + +Do **not** reintroduce `cargo test` into the Trust GitHub lifecycle lane. +The current protected workflow still runs macOS, Windows, and expensive coverage on each product PR; +moving those to Tier B requires a separately pinned required-workflow update. + ## CorvidLabs trust toolchain diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 78651e44..010d1a47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -144,3 +144,7 @@ vscode-extension/ # VS Code integration ## License By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE). + +## CI and Trust + +Merge confidence and the CI/Trust split are documented in [docs/ci-confidence.md](docs/ci-confidence.md). GitHub CI owns the test matrix; Trust must not re-run it. diff --git a/docs/ci-confidence.md b/docs/ci-confidence.md new file mode 100644 index 00000000..6d9041c2 --- /dev/null +++ b/docs/ci-confidence.md @@ -0,0 +1,128 @@ +# CI confidence architecture (no duplicate suites) + +**Goal:** approximately 95% merge confidence for ordinary product PRs **without** running the same +expensive suite twice. Release validation and explicitly sensitive changes may add stricter checks. + +## Ownership (single source of truth) + +| Confidence need | Owner | Where | +|-----------------|-------|--------| +| Format (`rustfmt`) | **CI** | `fmt` job | +| Lint (`clippy -D warnings`) | **CI** | required Ubuntu product lane | +| Unit + integration tests | **CI** | required Ubuntu product lane; Tier B adds macOS + Windows | +| Typecheck | **CI** | covered by `cargo test` / build; local `check-types` | +| Release binary build | **CI** (consumer) + **Trust** (identity) | CI: action-consumer; Trust: packages PR binary for contract | +| Spec contract + 100% path coverage | **CI** + **Trust contract gate** | CI `spec-check` proves tree; Trust re-checks with **PR release binary** (identity, not a second matrix) | +| Security advisories | **CI** | `audit` | +| Coverage measurement | **CI** | Tier B when expensive; strict 100% spec/path coverage stays Tier A | +| Site / VS Code extension | **CI** | `site`, `vscode-extension` | +| Action packaging consumer | **CI** | `action-consumer` | +| Deterministic risk (Augur) | **Trust only** | Trust action risk gate | +| Provenance (Attest) | **Trust only** | Trust action provenance | +| Lifecycle *re-suite* (full test again) | **None — removed** | Was duplicate of CI | + +## Confidence tiers + +### Tier A: every ordinary product PR + +- `cargo fmt --check` +- Ubuntu `cargo clippy -- -D warnings` and full `cargo test` +- `specsync check --strict --require-coverage 100` +- `cargo audit` +- cheap path classification, Action validation, and required readiness gates +- Trust's release-binary identity, contract, Augur, and Attest gates + +### Tier B: immutable release candidates + +- Ubuntu, macOS, and Windows integration and release validation against one exact candidate SHA +- expensive line coverage/tarpaulin +- any additional release or security matrix required by project policy + +The Trust split in this change is non-protected and lands first. Moving macOS, Windows, and expensive +coverage from every PR into Tier B changes `.github/workflows/**`; that requires the repository's +separately pinned required-workflow process. Until that follow-up lands, the existing CI workflow +continues running those jobs on every product PR. + +The future protected-workflow follow-up should make Ubuntu the authoritative integration platform +for ordinary development and product PRs. macOS and Windows should not consume runner time on those +PRs. Instead, a release-candidate cycle should: + +1. Freeze an exact candidate commit on an RC branch and create an immutable RC marker/tag for that + SHA. +2. Run the required Ubuntu, macOS, and Windows integration/release gates against that same SHA. +3. Refuse the final release tag and uploads unless every required platform is green for the unchanged + candidate SHA. + +If the candidate changes, create a new immutable RC marker and rerun the cross-platform gate. Do not +create the final release tag first and use its uploads to discover platform failures afterward. + +## Wall-clock model + +Before this change, Trust was roughly 20 minutes, including roughly 17 minutes spent re-running the +full Rust suite. After this change, Trust should take roughly 3–8 minutes for release build, light +lifecycle, contract, risk, and provenance. + +The immediate product-tip critical path remains: + +```text +Parallel critical path ≈ max( + test/windows, # historically 15–45m + test/macos, # ~16m + trust, # should be ~3–8m after this redesign (release build + light lifecycle + contract + augur) + spec-check, # ~10–12m + coverage # ~10m +) +``` + +After the separately pinned Tier B workflow update, the ordinary PR target is approximately 5–15 +minutes. This PR does not claim that matrix scheduling improvement before the protected change lands. + +Trust must **not** re-run `cargo test` / clippy / full verify after CI already did. + +## Trust lifecycle policy + +| Lane | Command | When | +|------|---------|------| +| `verify` | full fmt+lint+types+**test**+release build+spec-check | **Local** `fledge lanes run verify` / agent complete | +| `trust-lifecycle` | **types only** (no test suite) | **GitHub** Trust action via `.trust.toml` | + +`.trust.toml` `[lifecycle]` points at `trust-lifecycle` so the Trust GitHub job does not duplicate CI tests. + +Trust still: + +1. Builds **this PR’s** `cargo build --release` binary (identity artifact) +2. Runs **contract** against that binary (`require_coverage = 100`) +3. Runs **Augur** + **Attest** + +That preserves “this binary is the contract” without a second multi-OS suite. + +## Tip classes (unchanged) + +| Tip | CI | Trust | +|-----|----|-------| +| Product / full | Full matrix + gates | Full Trust action (light lifecycle) | +| `review_only` | Reuse / skip heavy | Reuse ancestor trust | +| `archive_only` | archive-integrity | Reuse ancestor trust | + +## Local agent checklist + +```bash +bash scripts/pre-push-gate.sh # fast: fmt + check + path coverage +fledge lanes run verify # one full local completion suite +fledge trust verify # contract + risk + light lifecycle + attest +``` + +Do **not** expect `fledge trust verify` alone to replace multi-OS CI; GitHub CI is the multi-OS authority. + +## Anti-patterns (do not reintroduce) + +1. Putting `test` / full `lanes.verify` back into Trust’s GitHub lifecycle +2. Making Trust the only place that runs tests (drops release-candidate multi-OS validation) +3. Dropping Windows/macOS without an immutable-SHA release-candidate gate +4. Running `cargo test` in both CI and Trust “just to be safe” (doubles cost, same bugs) + +## Related + +- Protected matrix scheduling and ancestor-reuse follow-ups require a separately pinned workflow update +- `fledge.toml` lanes: `verify` vs `trust-lifecycle` +- `.trust.toml` lifecycle command diff --git a/fledge.toml b/fledge.toml index 7091ec98..b75e8d3f 100644 --- a/fledge.toml +++ b/fledge.toml @@ -66,8 +66,10 @@ steps = [{ parallel = ["fmt", "lint"] }, "check-types"] description = "MANDATORY before every git push — fast: fmt + types + path/spec coverage" steps = ["pre-push"] +# Full local confidence. GitHub CI owns the multi-OS suite; do not point +# .trust.toml lifecycle at this lane on CI (that was a duplicate suite). [lanes.verify] -description = "Required Trust gate for the SpecSync repository (slower: + clippy + full test + release build)" +description = "Full local verification (fmt/lint/types + test + release build + strict specs)" steps = [ { parallel = ["fmt", "lint", "check-types"] }, "test", @@ -75,6 +77,12 @@ steps = [ "spec-check", ] +# Used by .trust.toml [lifecycle] for the GitHub Trust action only. +# CI already ran tests/clippy/spec-check; Trust must not re-run them. +[lanes.trust-lifecycle] +description = "Trust GitHub lifecycle — no duplicate cargo test (CI is authority)" +steps = ["check-types"] + [lanes.repo] description = "Full repository validation, including docs and editor extension" steps = [ diff --git a/specs/github/context.md b/specs/github/context.md index 44614a7b..d16ad5c3 100644 --- a/specs/github/context.md +++ b/specs/github/context.md @@ -100,6 +100,9 @@ spec: github.spec.md - `.github/scripts/validate-release-version.py` - Current package, Action, docs, CI consumer, and Trust candidate version consistency - `.github/scripts/validate-workflow-runtime-pins.py` - Exact hosted Bun runtime enforcement +- `fledge.toml` and `.trust.toml` - Keep full local verification separate from hosted Trust's + residual lifecycle prerequisite +- `docs/ci-confidence.md` - CI/Trust ownership, confidence tiers, and protected Tier B follow-up ## Current Status diff --git a/specs/github/github.spec.md b/specs/github/github.spec.md index 275a5421..b7cc863d 100644 --- a/specs/github/github.spec.md +++ b/specs/github/github.spec.md @@ -1,6 +1,6 @@ --- module: github -version: 14 +version: 15 status: stable files: - src/github.rs @@ -73,6 +73,9 @@ supported Bun runtime across site deployment, site CI, and VS Code extension CI. 12. Archive introduction verification checks every bounded path-touching commit and readable parent against the exact introduction tree, rejecting deletion or rewrite even when final bytes are restored. +13. Hosted verification assigns expensive signals to one authority: CI owns the product suite, + while Trust binds the release binary to the strict contract, risk decision, and provenance + without invoking the full local verification lane a second time. ## Behavioral Examples @@ -106,6 +109,13 @@ supported Bun runtime across site deployment, site CI, and VS Code extension CI. - **When** its only child moves the matching active package into the dated archive with valid finalization evidence - **Then** required CI runs the lightweight archive-integrity lane without repeating product tests or scoped review +### Scenario: Trust validates identity without repeating CI tests + +- **Given** GitHub CI owns formatting, linting, product tests, strict spec coverage, and audit +- **When** the Trust lifecycle runs for the same product tip +- **Then** it checks the release binary, contract, risk, and provenance without re-running the full + local `verify` lane or `cargo test` + ## Error Cases | Condition | Behavior | @@ -170,3 +180,4 @@ supported Bun runtime across site deployment, site CI, and VS Code extension CI. | 2026-07-30 | CHG-0068 adversarial hardening: Protect root Action manifests from optimized trust reuse | | 2026-07-30 | CHG-0068 adversarial hardening: Preserve NUL filename boundaries in trusted-policy matching | | 2026-07-30 | CHG-0068 review hardening: Reject archive rewrite-then-restore history | +| 2026-08-01 | CHG-0074-simplify-specsync-ci-to-one-expensive-suite-authority-with-residual-trust-identi: Simplify SpecSync CI to one expensive-suite authority with residual Trust identity gates, preserving full local verification and documenting the 95% confidence model | diff --git a/specs/github/requirements.md b/specs/github/requirements.md index b3805992..a32c5e9d 100644 --- a/specs/github/requirements.md +++ b/specs/github/requirements.md @@ -177,3 +177,20 @@ Acceptance Criteria - Fork PRs run the same read-only scoped-review analysis without secrets, comments, or review writes. - Classifier and finalizer history limits come from one committed limits document shared with the native validator. + +### REQ-github-006 + +Hosted verification SHALL assign each expensive confidence signal to one authoritative workflow, +while Trust SHALL retain release-binary identity, strict contract, risk, and provenance checks +without re-running the full product test suite. + +Acceptance Criteria + +- GitHub CI remains the authority for formatting, linting, full Rust tests, strict spec coverage, + audit, coverage measurement, site, editor extension, and packaged-action consumer checks. +- `.trust.toml` invokes a dedicated `trust-lifecycle` lane that does not contain `cargo test`, + clippy, or the full `verify` lane. +- `lanes.verify` remains the full local completion suite for agents and humans. +- Documentation identifies the current multi-OS matrix as Tier B work that requires a separately + pinned protected-workflow update; this change does not silently weaken platform coverage. +- The thin PR contains no protected workflow files and no ship-status/product-code feature. diff --git a/specs/github/testing.md b/specs/github/testing.md index 6071b295..866ae1cc 100644 --- a/specs/github/testing.md +++ b/specs/github/testing.md @@ -61,6 +61,7 @@ spec: github.spec.md | Root security guidance changes | CI runs the Action documentation validator | Change `SECURITY.md` alone and confirm the workflow path filters schedule `validate-action` | | Root security guidance uses a moving inline ref | The inline recommendation remains pinned to the current floating major | Replace the inline `@v5` ref with `@main` and require the release validator to fail | | Candidate mirror input is removed | Release CI continues to exercise the just-built candidate rather than a published binary | Remove either runner-local mirror input from the packaged Action consumer or Trust gate and require the release validator to fail | +| Trust lifecycle points at the full local suite | Hosted Trust duplicates `cargo test` already owned by CI | Parse `.trust.toml` and `fledge.toml`; require `trust-lifecycle`, reject `test`/`lint`/`verify` in that lane, and require the full `verify` lane to remain intact | | Release workflow changes | CI runs the full validation path and release validator scans its Action steps | Change `.github/workflows/release.yml` alone and confirm CI is scheduled; use a disallowed spec-sync ref to require the validator to fail | | Workflow `uses` key is quoted | The YAML-equivalent step remains in runtime-pin validation scope | Add a quoted `"uses"` key with a moving setup-bun ref and require the runtime validator to fail | | Workflow checkout `uses` key is quoted | The YAML-equivalent checkout remains in release validation scope | Add a quoted `"uses"` key for an extra checkout step and require the release validator to fail | diff --git a/src/commands/change.rs b/src/commands/change.rs index b45e8bb2..bbeb3226 100644 --- a/src/commands/change.rs +++ b/src/commands/change.rs @@ -739,3 +739,55 @@ fn parse_owner_manifest(path: &Path) -> Result, String> { } Ok(resolved) } + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + + #[test] + fn draft_text_surfaces_require_complete_artifacts_before_approval() { + let temp = TempDir::new().expect("temp project"); + let root = temp.path(); + let mut record = change::create_change( + root, + CreateChangeRequest { + description: "Clarify contributor guidance".into(), + kind: ChangeKind::Documentation, + affected_specs: Vec::new(), + affected_paths: vec!["src/commands/change.rs".into()], + requested_artifacts: Vec::new(), + no_spec_change: true, + rationale: Some( + "Documentation-only behavior does not alter a technical contract".into(), + ), + }, + ) + .expect("create draft"); + for (question, answer) in [ + ( + "acceptance_criteria", + "Every draft text surface requires complete artifacts before approval", + ), + ("public_contract", "no"), + ("architecture_risk", "no"), + ] { + record = change::answer_question(root, &record.id, question, answer) + .expect("answer interview question"); + } + let questions = change::next_questions(&record); + assert!(questions.is_empty(), "interview must be complete"); + + for surface in ["status", "show", "list"] { + let next = text_mode_next_action(root, &record, &questions); + assert!( + next.contains("complete selected artifacts"), + "{surface} text recommended the wrong next action: {next}" + ); + assert!( + !next.contains("change approve"), + "{surface} text recommended premature approval: {next}" + ); + } + } +}