diff --git a/.github/workflows/bridge-gate.yml b/.github/workflows/bridge-gate.yml index cbd400f3..c52295eb 100644 --- a/.github/workflows/bridge-gate.yml +++ b/.github/workflows/bridge-gate.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Copyright (c) 2026 Jonathan D.A. Jewell diff --git a/.github/workflows/build-gossamer-gui.yml b/.github/workflows/build-gossamer-gui.yml index 10f75d2b..e0296ea8 100644 --- a/.github/workflows/build-gossamer-gui.yml +++ b/.github/workflows/build-gossamer-gui.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Build the Hypatia GUI wasm artifact from the Ephapax/Gossamer sources. diff --git a/.github/workflows/ci-health-sweep.yml b/.github/workflows/ci-health-sweep.yml index 41307ba1..c26d8ecc 100644 --- a/.github/workflows/ci-health-sweep.yml +++ b/.github/workflows/ci-health-sweep.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Owner: Jonathan D.A. Jewell diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 635b0aca..d1cb2927 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Main CI workflow for hypatia diff --git a/.github/workflows/clusterfuzzlite.yml b/.github/workflows/clusterfuzzlite.yml index 54325ca0..997373e9 100644 --- a/.github/workflows/clusterfuzzlite.yml +++ b/.github/workflows/clusterfuzzlite.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Consolidated workflow (behaviour-preserving merge). diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 5c4e581b..99ca3959 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. name: CodeQL Security Analysis diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index e9bc6c96..4742b314 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index ef788203..0be6e453 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) diff --git a/.github/workflows/estate-rescan.yml b/.github/workflows/estate-rescan.yml index 3ec768fc..55b97534 100644 --- a/.github/workflows/estate-rescan.yml +++ b/.github/workflows/estate-rescan.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Hypatia Estate Rescan — refresh verisimdb-data/scans with current truth diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index f43e5995..c656a376 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: PMPL-1.0-or-later # This workflow is managed by gh actions-lock. name: Governance diff --git a/.github/workflows/hypatia-remediation-sweep.yml b/.github/workflows/hypatia-remediation-sweep.yml index 8653900f..c7c96598 100644 --- a/.github/workflows/hypatia-remediation-sweep.yml +++ b/.github/workflows/hypatia-remediation-sweep.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Hypatia Remediation Sweep — fleet-wide proactive scan diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 53bf1884..ff5ee89b 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: PMPL-1.0-or-later # This workflow is managed by gh actions-lock. name: Hypatia Security Scan diff --git a/.github/workflows/inbox-steward-intake.yml b/.github/workflows/inbox-steward-intake.yml index 728b82db..a8d0b089 100644 --- a/.github/workflows/inbox-steward-intake.yml +++ b/.github/workflows/inbox-steward-intake.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Inbox Steward Intake — Process reports from gitbot-fleet inbox-steward diff --git a/.github/workflows/label-triage.yml b/.github/workflows/label-triage.yml index e61baef9..3e747485 100644 --- a/.github/workflows/label-triage.yml +++ b/.github/workflows/label-triage.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Label Triage diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index c536afb9..a4208d7f 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Labels diff --git a/.github/workflows/language-blockers.yml b/.github/workflows/language-blockers.yml index 3262df36..a90f2996 100644 --- a/.github/workflows/language-blockers.yml +++ b/.github/workflows/language-blockers.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Consolidated workflow (behaviour-preserving merge). diff --git a/.github/workflows/merge-orchestrate.yml b/.github/workflows/merge-orchestrate.yml index 746195bc..1532b6ce 100644 --- a/.github/workflows/merge-orchestrate.yml +++ b/.github/workflows/merge-orchestrate.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Copyright (c) 2026 Jonathan D.A. Jewell diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 92623846..5440e468 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. name: Mirror to Git Forges diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 3be681d6..6fdc0d3a 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. name: GitHub Pages (Ddraig SSG) diff --git a/.github/workflows/push-email-notify.yml b/.github/workflows/push-email-notify.yml index 9e133d7c..52757af7 100644 --- a/.github/workflows/push-email-notify.yml +++ b/.github/workflows/push-email-notify.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Dormant push-email notification. ARMED by setting the repo variable diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index c5349a6e..12096113 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Consolidated workflow (behaviour-preserving merge). diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 56d6946d..fd5f0ba4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Consolidated workflow (behaviour-preserving merge). diff --git a/.github/workflows/roadmap-sync.yml b/.github/workflows/roadmap-sync.yml index afdcfc09..5afaab76 100644 --- a/.github/workflows/roadmap-sync.yml +++ b/.github/workflows/roadmap-sync.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Centralized roadmap sweeper: adds recently-touched issues & PRs from every diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 197e2f17..8fe34863 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Consolidated workflow (behaviour-preserving merge). diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index cf10069e..f9e7cb0f 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: PMPL-1.0-or-later # This workflow is managed by gh actions-lock. name: OSSF Scorecard diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index 414eed91..52c6f1e8 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. name: Secret Scanner diff --git a/.github/workflows/security-policy.yml b/.github/workflows/security-policy.yml index 239c5ad8..4ca09e3b 100644 --- a/.github/workflows/security-policy.yml +++ b/.github/workflows/security-policy.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Consolidated workflow (behaviour-preserving merge). diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ca53e18e..a0fda1ca 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Consolidated workflow (behaviour-preserving merge). diff --git a/.github/workflows/verify-proofs.yml b/.github/workflows/verify-proofs.yml index 2323ca72..4c4dad72 100644 --- a/.github/workflows/verify-proofs.yml +++ b/.github/workflows/verify-proofs.yml @@ -1,3 +1,4 @@ +# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. # Consolidated workflow (behaviour-preserving merge). diff --git a/data/verisim/recipes/recipe-retired-descriptile-policy.json b/data/verisim/recipes/recipe-retired-descriptile-policy.json new file mode 100644 index 00000000..f545410b --- /dev/null +++ b/data/verisim/recipes/recipe-retired-descriptile-policy.json @@ -0,0 +1,16 @@ +{ + "id": "recipe-retired-descriptile-policy", + "name": "Align CI descriptile checks with the canonical tree", + "description": "Repair the policy reference only after the canonical descriptile exists; never recreate retired copies.", + "triangle_tier": "eliminate", + "pattern_ids": ["SD024", "structural_drift/SD024"], + "target_categories": ["RetiredDescriptilePolicy"], + "languages": ["yaml", "shell", "bash", "just"], + "confidence": 0.95, + "auto_fixable": true, + "fix_script": "fix-retired-descriptile-policy.sh", + "action": "replace", + "formally_proven": false, + "successful_fixes": 0, + "failed_fixes": 0 +} diff --git a/lib/hypatia/cli.ex b/lib/hypatia/cli.ex index d1c156b2..768e0d22 100644 --- a/lib/hypatia/cli.ex +++ b/lib/hypatia/cli.ex @@ -791,6 +791,7 @@ defmodule Hypatia.CLI do reason: f.reason, action: to_string(f.action) } + |> Map.merge(Map.take(f, [:line, :category, :recipe_id, :fix_script, :target])) end) results ++ normalized diff --git a/lib/rules/structural_drift.ex b/lib/rules/structural_drift.ex index d75c5b3d..992a3180 100644 --- a/lib/rules/structural_drift.ex +++ b/lib/rules/structural_drift.ex @@ -745,7 +745,8 @@ defmodule Hypatia.Rules.StructuralDrift do sd013_path_specific_gitignore(repo_path) ++ sd014_safedom_example_dialect(repo_path) ++ sd022_stale_path_after_rename(repo_path) ++ - sd023_state_a2ml_divergence(repo_path) + sd023_state_a2ml_divergence(repo_path) ++ + sd024_retired_descriptile_policy(repo_path) needs_intensive = Enum.any?(findings, & &1[:trigger_intensive]) needs_alert = Enum.any?(findings, & &1[:alert_user]) @@ -762,6 +763,73 @@ defmodule Hypatia.Rules.StructuralDrift do # ─── Helpers ─────────────────────────────────────────────────────────── + @doc """ + SD024: Detect executable policy that requires descriptiles in retired paths. + + A canonical tree can never satisfy such a check and SD004 simultaneously. + Report the policy file for reference repair; do not suggest recreating the + retired file. Documentation and commented examples are outside this rule. + """ + def sd024_retired_descriptile_policy(repo_path) do + files = + Path.wildcard(Path.join(repo_path, ".github/workflows/*.{yml,yaml}")) ++ + Path.wildcard(Path.join(repo_path, "scripts/*.sh")) ++ + Path.wildcard(Path.join(repo_path, ".githooks/*.sh")) ++ + Enum.map(["Justfile", "justfile"], &Path.join(repo_path, &1)) + + Enum.flat_map(files, fn file -> + case File.read(file) do + {:ok, content} -> + content + |> String.split("\n") + |> Enum.with_index(1) + |> Enum.flat_map(fn {line, number} -> + legacy = + ~r/\.machine_readable\/(?:6a2\/)?(?:STATE|META|ECOSYSTEM|AGENTIC|NEUROSYM|PLAYBOOK|ANCHOR)\.a2ml/ + + # Quoted examples are prose, but quoted literal paths and shell + # command substitutions still participate in executable checks. + code = + Regex.replace(~r/"(?:\\.|[^"\\])*"|'[^']*'/, line, fn quoted -> + value = String.slice(quoted, 1, String.length(quoted) - 2) + + if Regex.match?(~r/^#{legacy.source}$/, value) or + (String.starts_with?(quoted, "\"") and + (String.contains?(value, "$(") or String.contains?(value, "`"))) do + value + else + " " + end + end) + + if not String.starts_with?(String.trim_leading(line), "#") and + Regex.match?(~r/(?:\s-f\s|\s-e\s|check_file\s)/, code) and + Regex.match?(legacy, code) do + [ + %{ + rule: "SD024", + file: Path.relative_to(file, repo_path), + line: number, + severity: :high, + reason: + "CI policy requires a retired descriptile path; align the check with .machine_readable/descriptiles/", + category: "RetiredDescriptilePolicy", + action: :update_reference, + recipe_id: "recipe-retired-descriptile-policy", + fix_script: "fix-retired-descriptile-policy.sh" + } + ] + else + [] + end + end) + + {:error, _} -> + [] + end + end) + end + defp group_by_severity(findings) do findings |> Enum.group_by(& &1.severity) diff --git a/test/retired_descriptile_policy_test.exs b/test/retired_descriptile_policy_test.exs new file mode 100644 index 00000000..8673520a --- /dev/null +++ b/test/retired_descriptile_policy_test.exs @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: MPL-2.0 +defmodule Hypatia.RetiredDescriptilePolicyTest do + use ExUnit.Case, async: true + alias Hypatia.Rules.StructuralDrift + + test "detects the contradictory check and routes reference repair" do + repo = + Path.join(System.tmp_dir!(), "descriptile-policy-#{System.unique_integer([:positive])}") + + File.mkdir_p!(Path.join(repo, ".github/workflows")) + on_exit(fn -> File.rm_rf!(repo) end) + file = Path.join(repo, ".github/workflows/compliance.yml") + File.write!(file, "run: |\n if [ ! -f .machine_readable/STATE.a2ml ]; then exit 1; fi\n") + [finding] = StructuralDrift.sd024_retired_descriptile_policy(repo) + assert finding.rule == "SD024" + assert finding.line == 2 + assert finding.action == :update_reference + assert finding.fix_script == "fix-retired-descriptile-policy.sh" + + normalized = + Hypatia.CLI.collect_findings(repo, [:structural_drift]) + |> Enum.find(&(&1.type == "SD024")) + + assert normalized.category == "RetiredDescriptilePolicy" + assert normalized.recipe_id == "recipe-retired-descriptile-policy" + assert normalized.fix_script == "fix-retired-descriptile-policy.sh" + assert normalized.line == 2 + + File.write!( + file, + "run: |\n if [ ! -f .machine_readable/descriptiles/STATE.a2ml ]; then exit 1; fi\n" + ) + + assert StructuralDrift.sd024_retired_descriptile_policy(repo) == [] + File.write!(file, "# if [ ! -f .machine_readable/6a2/STATE.a2ml ]; then exit 1; fi\n") + assert StructuralDrift.sd024_retired_descriptile_policy(repo) == [] + + for prose <- [ + ~s(echo "test -f .machine_readable/STATE.a2ml"), + ~s(printf '%s' 'check_file .machine_readable/META.a2ml') + ] do + File.write!(file, "run: |\n #{prose}\n") + assert StructuralDrift.sd024_retired_descriptile_policy(repo) == [] + end + + for command <- [ + ~s(test -f ".machine_readable/STATE.a2ml"), + ~s(test -e '.machine_readable/6a2/META.a2ml'), + ~s(check_file '.machine_readable/AGENTIC.a2ml') + ] do + File.write!(file, "run: |\n #{command}\n") + assert [%{rule: "SD024"}] = StructuralDrift.sd024_retired_descriptile_policy(repo) + end + end +end