[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XDKCG1G4JMKNKTH68PC0C7":3,"skill-same:01M6XDKCG1G4JMKNKTH68PC0C7":94},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":60,"commitSha":61,"skillDirectory":62,"forksCount":14,"repositoryHtmlUrl":63,"repositoryHomepageUrl":6,"ownerHtmlUrl":64,"ownerBlogUrl":6,"ownerDescription":65,"updatedAt":66,"install":67,"resources":79},"01M6XDKCG1G4JMKNKTH68PC0C7","local-attest",null,"Run the configured CI matrix locally and, on a clean pass, post a SHA-pinned OWNER-authored PR comment that gates downstream GitHub Actions jobs off for that exact commit. Skips the redundant remote run after a maintainer has already verified locally, saving runner minutes without weakening the gate. A new push changes the head SHA, the attestation stops matching, CI runs again. Side-effectful (posts a PR comment, applies a label, optionally pushes) and slow — minutes to tens of minutes, whatever the configured matrix costs. Diagnostic modes (--only, --from) run subsets for the fix-retry loop and can never attest; --fail-fast works in both modes. Invoke only on explicit request.","kaiohenricunha\u002Fdotbabel","kaiohenricunha","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F69804490?v=4","01M6XCZD37W0KPCDGEA40Y610V","Kaio Cunha","01M6XCZD7SX714D114H3ZPNW09",0,"MIT","https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Flocal-attest\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"local attest","attest","local","only","invoke","posts","retry","remote","runner","clean","testing","never","fix","loop","Bash","attestation","github actions","ci","actions","commit","2026-11-10 10:12:30",false,"---\nid: local-attest\nname: local-attest\ntype: skill\nversion: 1.2.0\ndomain: [devex, observability]\nplatform: [github-actions]\ntask: [testing, runtime-ops]\nmaturity: draft\nowner: \"@kaiohenricunha\"\ncreated: 2026-05-23\nupdated: 2026-08-14\ndescription: >\n  Run the configured CI matrix locally and, on a clean pass, post a SHA-pinned\n  OWNER-authored PR comment that gates downstream GitHub Actions jobs off for\n  that exact commit. Skips the redundant remote run after a maintainer has\n  already verified locally, saving runner minutes without weakening the gate.\n  A new push changes the head SHA, the attestation stops matching, CI runs\n  again. Side-effectful (posts a PR comment, applies a label, optionally pushes)\n  and slow — minutes to tens of minutes, whatever the configured matrix costs.\n  Diagnostic modes (--only, --from) run subsets for the fix-retry loop and can\n  never attest; --fail-fast works in both modes. Invoke only on explicit request.\nargument-hint: \"[--pr \u003CN>] [--no-push] [--dry-run] [--fail-fast] [--only \u003Cleg>] [--from \u003Cleg>] [--config \u003Cpath>]\"\ntools: Bash\ndisable-model-invocation: true\nuser-invocable: true\n---\n\n# \u002Flocal-attest — Local CI attestation\n\nRun the project's configured CI matrix on your machine. On a clean pass, post\na hidden marker comment to the open PR so the remote `Test` \u002F `Preview` jobs\nread the marker and skip themselves for that exact commit.\n\n> **This skill is side-effectful and slow.** It runs every leg of your CI\n> matrix — serially within a lane, lanes in parallel; minutes to tens of\n> minutes, whatever the configured matrix costs — posts a PR comment, applies\n> a label, and pushes the current branch. Invoke only when you've decided to attest a specific PR. For the\n> fix-retry loop, use the diagnostic modes below instead: they run subsets,\n> tolerate a dirty tree, and are structurally unable to attest.\n\n## Quick start\n\n```bash\n# In a project root with a .local-attest.config.mjs\ndotbabel local-attest --pr 123\n\n# Try a config without posting anything:\ndotbabel local-attest --pr 123 --dry-run\n\n# Run + post + label, but do not git push (useful for offline review):\ndotbabel local-attest --pr 123 --no-push\n\n# Iterate on a fix: run one leg, dirty tree fine, no PR needed, never attests:\ndotbabel local-attest --only lint\n\n# Re-run the matrix from the leg that failed, stopping at the first hard failure:\ndotbabel local-attest --from bats --fail-fast\n\n# The on-demand full run: ignore every leg's diff rules and scope:\ndotbabel local-attest --pr 123 --full\n```\n\n## PR-scoped runs\n\nAn attest run verifies the scope of the pull request's changes and nothing\nmore. The full suite runs only on demand (`--full`) or on a schedule. Three\nconfig fields decide each leg's scope (schema in\n[references\u002Fconfig.md](references\u002Fconfig.md)):\n\n- **`scope: true`** — the leg scopes itself. When the runner knows the PR's\n  changed files, it writes them to a JSON file and passes its path as\n  `DOTBABEL_ATTEST_CHANGED_FILES`, plus a per-leg `DOTBABEL_ATTEST_SKIP_FILE`.\n  The leg runs only what the changes reach. When nothing is in scope, it writes\n  a one-line reason to the skip file and exits 0, and the leg is recorded\n  `skipped`. A non-zero exit is a failure, skip file or not. Without the file\n  list the leg gets neither variable and must run in full (fail-open).\n- **`when.changedPaths`** — the leg runs only when a changed file matches one\n  of its globs; use it for whole-repo invariant checks, listing their inputs.\n- **`skipWhenDiffOnly`** — the leg skips when every changed file matches.\n\n`--full` ignores all three: no file list is passed, every leg runs in full,\nand the attestation payload records `\"full\": true`.\n\n## Diagnostic modes — the fix-retry loop\n\n`--only \u003Cleg>` (repeatable or comma-separated) and `--from \u003Cleg>` run a subset\nof the matrix under relaxed preconditions: any git repo state is fine (dirty\ntree, detached HEAD, no PR — iterating is the point), Docker and toolchain\nproblems warn instead of failing. Leg names match exactly and case-sensitively;\nan unknown name lists every valid leg. The two flags are mutually exclusive.\n\nA diagnostic run **never** posts, labels, or pushes. Two mechanisms stack:\nthe diagnostic branch in `execute` returns before the publication path even\nexists (and the attest branch always runs the full config matrix), and the\n`shouldAttest` predicate that gates publication receives the diagnostic flag\nplus the expected leg count, so a subset record is rejected even if the\nbranch wiring were ever wrong. Exit is 1 when **any** selected leg fails,\nadvisory legs included, because there is no attestation gate to grade against\nand `--only \u003Cadvisory-leg>` exiting 0 on a failure would be useless in a loop.\n\nSubsets get exactly what they name — no dependency injection. `--from` a leg\nthat reads a build artifact measures whatever artifact is lying around.\n\n`--fail-fast` works in both modes: the first **hard** failure stops launching\nfurther legs, which are recorded `not run (fail-fast)` — never as passes. A\nfull run with `--fail-fast` where nothing failed completed the whole matrix\nand attests normally; one that stopped early cannot.\n\n## Prerequisites\n\n- **A `.local-attest` config** in the project root — see\n  [references\u002Fconfig.md](references\u002Fconfig.md) for the schema and three example\n  configs (Node-only, Node + Go monorepo, Python). Discovery order:\n  `.local-attest.config.mjs` → `.local-attest.config.json` → `package.json#local-attest`.\n- **A workflow gate** wired into your `.github\u002Fworkflows\u002Ftest.yml` (and any\n  other pipeline you want to skip). Paste the snippet from\n  [references\u002Fworkflow-gate.yml.tmpl](references\u002Fworkflow-gate.yml.tmpl). This\n  is a one-time manual setup — auto-injecting YAML across diverse CI layouts\n  is too risky to do for you.\n- **A clean worktree** — the skill aborts on any uncommitted change, because\n  the attestation must certify the exact tree that gets pushed. (Configurable\n  via `requireClean: false`, but generally not recommended.)\n- **Local HEAD must match the PR head** — the skill aborts if your local\n  branch tip differs from the remote PR head. Push any local commits first.\n- **`gh` authenticated** as a user whose `author_association` is in your\n  config's `trustedAssociations` list (default: `[\"OWNER\"]`).\n- **Docker running** if your config sets `requireDocker: true`.\n- **`auditLogPath` gitignored** (default `.local-attest-log.jsonl`). Every\n  run appends to it — including `--dry-run` — so with `requireClean` on, an\n  untracked log makes the next attest abort on the tool's own output.\n\n## How the gate works\n\nThe gate input is a PR comment authored by a trusted user (default: the repo\nOWNER) whose **first line** is exactly:\n\n```text\n\u003C!-- local-attest verified-sha=\u003Cfull-head-sha> -->\n\u003C!-- local-attest-payload \u003Cbase64url JSON> -->\n```\n\nThe CI workflow reads PR comments, applies a `jq select(.author_association == \"OWNER\")`\nfilter, takes the first line of each, and `grep -qFx`s (exact-line match)\nfor the marker that matches `github.event.pull_request.head.sha`. When it matches, every downstream\njob's `if:` evaluates false and skips at zero runner cost.\n\nLine 2 carries the evidence payload the **merge gate** reads\n(`schemas\u002Fdotbabel.attestation-evidence.schema.json`): the head SHA, the merge\nbase, a hash of the files that govern the run, and each leg's terminal status.\nThe CI gate ignores it; `dotbabel pr-stack gate --gate merge` does not. A marker\nalone says a matrix passed, which is enough to skip a redundant remote run but\nnot enough to authorize a merge — it does not say WHICH matrix. The\n`config_hash` closes that: the gate recomputes it from the base ref, so a pull\nrequest that rewrites a leg's command cannot attest its own change.\n\nFreshness is automatic: a new push changes the head SHA, the old comment no\nlonger matches, CI runs again. Editing or deleting the comment does **not**\nre-trigger CI (the gate only re-evaluates on `push`\u002F`synchronize`). If you\nneed to revoke an attestation, push any new (even empty) commit.\n\nFull operator contract: [references\u002Foperator-guide.md](references\u002Foperator-guide.md).\n\n## What the skill does, in order\n\n1. **Preconditions.** Branch exists, worktree clean (if `requireClean`), local\n   HEAD == PR head, `gh` authed, Docker available (if `requireDocker`), and the\n   running toolchain matches `config.toolchain` pins (if set) — a matrix run on\n   a different Node or Go than CI uses certifies a run CI would never perform,\n   so a pin mismatch fails closed here. Any failure aborts before a single test\n   runs. The skill also warns (does not fail) if your GitHub\n   `author_association` on the repo (OWNER \u002F MEMBER \u002F COLLABORATOR, etc.) is\n   not in the config's `trustedAssociations` list — the attestation will post\n   but CI will ignore it.\n2. **Run matrix.** Legs sharing a `lane` run serially in matrix order;\n   distinct lanes run concurrently; a config without lanes runs fully\n   sequentially. Diff rules (`when.changedPaths`, `skipWhenDiffOnly`) mark\n   legs skipped against the PR's changed files, and a `scope: true` leg that\n   reports nothing in scope is recorded skipped too — skipped legs still appear\n   in every table, and the comment headline says so rather than claiming a full\n   run. Hard legs must pass to attest; advisory legs are reported but never\n   block. Stdout + stderr are tailed at 10 lines per leg. With `--fail-fast`,\n   the first hard failure stops launching further legs in every lane; the\n   rest are recorded `not run`, never as passes.\n3. **Attestation bar.** Posting is gated on a run-record predicate: every leg\n   accounted for — executed or diff-skipped — at least one leg actually\n   executed (an all-skipped run has verified nothing and refuses to attest;\n   CI's own path filters already skip the same jobs), zero hard fails, not a\n   diagnostic subset. A run that fails the bar aborts — no comment, no label,\n   no push — and still writes its audit line (`result: \"hard-fail\"`).\n4. **Re-check HEAD.** First, `restoreFiles` snapshots taken before the matrix\n   are restored byte-exact (a leg that seeds fixture stubs over tracked files\n   would otherwise fail this recheck on its own writes, every run). Then: the\n   matrix takes minutes — long enough for another agent\n   session, another worktree, or you in a second terminal to commit onto the\n   same branch. Step 1's check is stale by now, so HEAD and the worktree are\n   re-asserted against what was actually tested. If either moved, everything\n   aborts (`result: \"head-moved\"`): no comment, no label, no push. Without\n   this the skill would attest the pre-matrix SHA and then push whatever HEAD\n   had become — publishing commits it never tested and labelling the PR\n   verified on an unrun head.\n5. **Post comment, minimize older ones.** A new comment is POSTed and this\n   tool's own earlier attestation comments are minimized as `OUTDATED` — before\n   the push, so the marker is already visible when the push event fires GitHub\n   Actions. Body always goes via `gh api --input -` so multiline markdown can't\n   be mangled by shell quoting.\n\n   **This never edits a comment**, which is a deliberate change from the\n   previous PATCH-in-place behaviour and matches what `dotbabel criteria`\n   already does (OPS-4). The merge gate refuses an edited comment — text that is\n   not what the tool wrote is not evidence — so under the old model the second\n   attestation on any pull request would have been rejected as untrusted.\n   Comments written by other users are never minimized.\n\n6. **Push** (if `pushAfterAttest` and not `--no-push`). A failed push records\n   `result: \"push-fail\"`; the comment is already in place, so a bare\n   `git push` retry completes the attestation.\n7. **Apply label** (default `ci\u002Flocal-verified`). Best-effort; failure warns\n   but does not abort.\n8. **Append audit log line** to the configured `auditLogPath` (default\n   `.local-attest-log.jsonl`). **Every run whose matrix executes writes\n   exactly one line**, tagged `result: attested | hard-fail | head-moved |\npush-fail | post-fail | dry-run | diagnostic`, with per-leg\n   `{name, mode, status, durationS}` — failures leave a record too, which is\n   what makes failure and duration distributions measurable. Lines written by\n   older versions have no `result` field and imply `attested`. Best-effort:\n   an unwritable log warns but never changes the run's outcome.\n\n## Flags\n\n| Flag              | Effect                                                                                                                                       |\n| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |\n| `--pr \u003CN>`        | Target PR number. Defaults to the open PR for the current branch.                                                                            |\n| `--no-push`       | Run matrix + post + label, but skip `git push`.                                                                                              |\n| `--dry-run`       | Run matrix, render the comment, print it. Post nothing, label nothing, push nothing. Use this to validate a new project's config end-to-end. |\n| `--fail-fast`     | Stop launching legs after the first hard failure. Unstarted legs record as `not run`; a run that stopped early can never attest.             |\n| `--only \u003Cleg>`    | Diagnostic mode — run only the named leg(s). Repeatable or comma-separated. Relaxed preconditions; never attests. Exit 1 on any failure.     |\n| `--from \u003Cleg>`    | Diagnostic mode — run the matrix from the named leg to the end. Mutually exclusive with `--only`.                                            |\n| `--config \u003Cpath>` | Override config discovery.                                                                                                                   |\n\n## What this skill never does\n\n- **Auto-install the workflow gate.** Paste it manually using the template.\n- **Skip the Secret-scan job** (or any other gate you didn't put behind the\n  attestation `if:`). Configure each workflow's `if:` explicitly.\n- **Merge or deploy.** It only attests and pushes the current branch.\n- **Edit a comment.** Each run posts a new one and minimizes its own older\n  ones, so at most one attestation is visible per PR and `lastEditedAt` stays\n  null — which is what makes it trustworthy to the merge gate.\n- **Auto-unlabel on stale attestation.** A new push silently invalidates the\n  prior attestation by SHA mismatch; the label stays as audit decoration.\n\n## Trust model\n\nDefault `trustedAssociations: [\"OWNER\"]`. Only comments from the repo OWNER\nwill gate CI. A non-trusted user's comment will post (and the label may apply)\nbut CI will still run. Widen the trust list in your config for multi-maintainer\nrepos:\n\n```js\ntrustedAssociations: [\"OWNER\", \"MEMBER\", \"COLLABORATOR\"];\n```\n\nThe generated workflow gate snippet ([references\u002Fworkflow-gate.yml.tmpl](references\u002Fworkflow-gate.yml.tmpl))\nautomatically matches the configured list.\n","# \u002Flocal-attest — Local CI attestation\n\nRun the project's configured CI matrix on your machine. On a clean pass, post\na hidden marker comment to the open PR so the remote `Test` \u002F `Preview` jobs\nread the marker and skip themselves for that exact commit.\n\n> **This skill is side-effectful and slow.** It runs every leg of your CI\n> matrix — serially within a lane, lanes in parallel; minutes to tens of\n> minutes, whatever the configured matrix costs — posts a PR comment, applies\n> a label, and pushes the current branch. Invoke only when you've decided to attest a specific PR. For the\n> fix-retry loop, use the diagnostic modes below instead: they run subsets,\n> tolerate a dirty tree, and are structurally unable to attest.\n\n## Quick start\n\n```bash\n# In a project root with a .local-attest.config.mjs\ndotbabel local-attest --pr 123\n\n# Try a config without posting anything:\ndotbabel local-attest --pr 123 --dry-run\n\n# Run + post + label, but do not git push (useful for offline review):\ndotbabel local-attest --pr 123 --no-push\n\n# Iterate on a fix: run one leg, dirty tree fine, no PR needed, never attests:\ndotbabel local-attest --only lint\n\n# Re-run the matrix from the leg that failed, stopping at the first hard failure:\ndotbabel local-attest --from bats --fail-fast\n\n# The on-demand full run: ignore every leg's diff rules and scope:\ndotbabel local-attest --pr 123 --full\n```\n\n## PR-scoped runs\n\nAn attest run verifies the scope of the pull request's changes and nothing\nmore. The full suite runs only on demand (`--full`) or on a schedule. Three\nconfig fields decide each leg's scope (schema in\n[references\u002Fconfig.md](references\u002Fconfig.md)):\n\n- **`scope: true`** — the leg scopes itself. When the runner knows the PR's\n  changed files, it writes them to a JSON file and passes its path as\n  `DOTBABEL_ATTEST_CHANGED_FILES`, plus a per-leg `DOTBABEL_ATTEST_SKIP_FILE`.\n  The leg runs only what the changes reach. When nothing is in scope, it writes\n  a one-line reason to the skip file and exits 0, and the leg is recorded\n  `skipped`. A non-zero exit is a failure, skip file or not. Without the file\n  list the leg gets neither variable and must run in full (fail-open).\n- **`when.changedPaths`** — the leg runs only when a changed file matches one\n  of its globs; use it for whole-repo invariant checks, listing their inputs.\n- **`skipWhenDiffOnly`** — the leg skips when every changed file matches.\n\n`--full` ignores all three: no file list is passed, every leg runs in full,\nand the attestation payload records `\"full\": true`.\n\n## Diagnostic modes — the fix-retry loop\n\n`--only \u003Cleg>` (repeatable or comma-separated) and `--from \u003Cleg>` run a subset\nof the matrix under relaxed preconditions: any git repo state is fine (dirty\ntree, detached HEAD, no PR — iterating is the point), Docker and toolchain\nproblems warn instead of failing. Leg names match exactly and case-sensitively;\nan unknown name lists every valid leg. The two flags are mutually exclusive.\n\nA diagnostic run **never** posts, labels, or pushes. Two mechanisms stack:\nthe diagnostic branch in `execute` returns before the publication path even\nexists (and the attest branch always runs the full config matrix), and the\n`shouldAttest` predicate that gates publication receives the diagnostic flag\nplus the expected leg count, so a subset record is rejected even if the\nbranch wiring were ever wrong. Exit is 1 when **any** selected leg fails,\nadvisory legs included, because there is no attestation gate to grade against\nand `--only \u003Cadvisory-leg>` exiting 0 on a failure would be useless in a loop.\n\nSubsets get exactly what they name — no dependency injection. `--from` a leg\nthat reads a build artifact measures whatever artifact is lying around.\n\n`--fail-fast` works in both modes: the first **hard** failure stops launching\nfurther legs, which are recorded `not run (fail-fast)` — never as passes. A\nfull run with `--fail-fast` where nothing failed completed the whole matrix\nand attests normally; one that stopped early cannot.\n\n## Prerequisites\n\n- **A `.local-attest` config** in the project root — see\n  [references\u002Fconfig.md](references\u002Fconfig.md) for the schema and three example\n  configs (Node-only, Node + Go monorepo, Python). Discovery order:\n  `.local-attest.config.mjs` → `.local-attest.config.json` → `package.json#local-attest`.\n- **A workflow gate** wired into your `.github\u002Fworkflows\u002Ftest.yml` (and any\n  other pipeline you want to skip). Paste the snippet from\n  [references\u002Fworkflow-gate.yml.tmpl](references\u002Fworkflow-gate.yml.tmpl). This\n  is a one-time manual setup — auto-injecting YAML across diverse CI layouts\n  is too risky to do for you.\n- **A clean worktree** — the skill aborts on any uncommitted change, because\n  the attestation must certify the exact tree that gets pushed. (Configurable\n  via `requireClean: false`, but generally not recommended.)\n- **Local HEAD must match the PR head** — the skill aborts if your local\n  branch tip differs from the remote PR head. Push any local commits first.\n- **`gh` authenticated** as a user whose `author_association` is in your\n  config's `trustedAssociations` list (default: `[\"OWNER\"]`).\n- **Docker running** if your config sets `requireDocker: true`.\n- **`auditLogPath` gitignored** (default `.local-attest-log.jsonl`). Every\n  run appends to it — including `--dry-run` — so with `requireClean` on, an\n  untracked log makes the next attest abort on the tool's own output.\n\n## How the gate works\n\nThe gate input is a PR comment authored by a trusted user (default: the repo\nOWNER) whose **first line** is exactly:\n\n```text\n\u003C!-- local-attest verified-sha=\u003Cfull-head-sha> -->\n\u003C!-- local-attest-payload \u003Cbase64url JSON> -->\n```\n\nThe CI workflow reads PR comments, applies a `jq select(.author_association == \"OWNER\")`\nfilter, takes the first line of each, and `grep -qFx`s (exact-line match)\nfor the marker that matches `github.event.pull_request.head.sha`. When it matches, every downstream\njob's `if:` evaluates false and skips at zero runner cost.\n\nLine 2 carries the evidence payload the **merge gate** reads\n(`schemas\u002Fdotbabel.attestation-evidence.schema.json`): the head SHA, the merge\nbase, a hash of the files that govern the run, and each leg's terminal status.\nThe CI gate ignores it; `dotbabel pr-stack gate --gate merge` does not. A marker\nalone says a matrix passed, which is enough to skip a redundant remote run but\nnot enough to authorize a merge — it does not say WHICH matrix. The\n`config_hash` closes that: the gate recomputes it from the base ref, so a pull\nrequest that rewrites a leg's command cannot attest its own change.\n\nFreshness is automatic: a new push changes the head SHA, the old comment no\nlonger matches, CI runs again. Editing or deleting the comment does **not**\nre-trigger CI (the gate only re-evaluates on `push`\u002F`synchronize`). If you\nneed to revoke an attestation, push any new (even empty) commit.\n\nFull operator contract: [references\u002Foperator-guide.md](references\u002Foperator-guide.md).\n\n## What the skill does, in order\n\n1. **Preconditions.** Branch exists, worktree clean (if `requireClean`), local\n   HEAD == PR head, `gh` authed, Docker available (if `requireDocker`), and the\n   running toolchain matches `config.toolchain` pins (if set) — a matrix run on\n   a different Node or Go than CI uses certifies a run CI would never perform,\n   so a pin mismatch fails closed here. Any failure aborts before a single test\n   runs. The skill also warns (does not fail) if your GitHub\n   `author_association` on the repo (OWNER \u002F MEMBER \u002F COLLABORATOR, etc.) is\n   not in the config's `trustedAssociations` list — the attestation will post\n   but CI will ignore it.\n2. **Run matrix.** Legs sharing a `lane` run serially in matrix order;\n   distinct lanes run concurrently; a config without lanes runs fully\n   sequentially. Diff rules (`when.changedPaths`, `skipWhenDiffOnly`) mark\n   legs skipped against the PR's changed files, and a `scope: true` leg that\n   reports nothing in scope is recorded skipped too — skipped legs still appear\n   in every table, and the comment headline says so rather than claiming a full\n   run. Hard legs must pass to attest; advisory legs are reported but never\n   block. Stdout + stderr are tailed at 10 lines per leg. With `--fail-fast`,\n   the first hard failure stops launching further legs in every lane; the\n   rest are recorded `not run`, never as passes.\n3. **Attestation bar.** Posting is gated on a run-record predicate: every leg\n   accounted for — executed or diff-skipped — at least one leg actually\n   executed (an all-skipped run has verified nothing and refuses to attest;\n   CI's own path filters already skip the same jobs), zero hard fails, not a\n   diagnostic subset. A run that fails the bar aborts — no comment, no label,\n   no push — and still writes its audit line (`result: \"hard-fail\"`).\n4. **Re-check HEAD.** First, `restoreFiles` snapshots taken before the matrix\n   are restored byte-exact (a leg that seeds fixture stubs over tracked files\n   would otherwise fail this recheck on its own writes, every run). Then: the\n   matrix takes minutes — long enough for another agent\n   session, another worktree, or you in a second terminal to commit onto the\n   same branch. Step 1's check is stale by now, so HEAD and the worktree are\n   re-asserted against what was actually tested. If either moved, everything\n   aborts (`result: \"head-moved\"`): no comment, no label, no push. Without\n   this the skill would attest the pre-matrix SHA and then push whatever HEAD\n   had become — publishing commits it never tested and labelling the PR\n   verified on an unrun head.\n5. **Post comment, minimize older ones.** A new comment is POSTed and this\n   tool's own earlier attestation comments are minimized as `OUTDATED` — before\n   the push, so the marker is already visible when the push event fires GitHub\n   Actions. Body always goes via `gh api --input -` so multiline markdown can't\n   be mangled by shell quoting.\n\n   **This never edits a comment**, which is a deliberate change from the\n   previous PATCH-in-place behaviour and matches what `dotbabel criteria`\n   already does (OPS-4). The merge gate refuses an edited comment — text that is\n   not what the tool wrote is not evidence — so under the old model the second\n   attestation on any pull request would have been rejected as untrusted.\n   Comments written by other users are never minimized.\n\n6. **Push** (if `pushAfterAttest` and not `--no-push`). A failed push records\n   `result: \"push-fail\"`; the comment is already in place, so a bare\n   `git push` retry completes the attestation.\n7. **Apply label** (default `ci\u002Flocal-verified`). Best-effort; failure warns\n   but does not abort.\n8. **Append audit log line** to the configured `auditLogPath` (default\n   `.local-attest-log.jsonl`). **Every run whose matrix executes writes\n   exactly one line**, tagged `result: attested | hard-fail | head-moved |\npush-fail | post-fail | dry-run | diagnostic`, with per-leg\n   `{name, mode, status, durationS}` — failures leave a record too, which is\n   what makes failure and duration distributions measurable. Lines written by\n   older versions have no `result` field and imply `attested`. Best-effort:\n   an unwritable log warns but never changes the run's outcome.\n\n## Flags\n\n| Flag              | Effect                                                                                                                                       |\n| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |\n| `--pr \u003CN>`        | Target PR number. Defaults to the open PR for the current branch.                                                                            |\n| `--no-push`       | Run matrix + post + label, but skip `git push`.                                                                                              |\n| `--dry-run`       | Run matrix, render the comment, print it. Post nothing, label nothing, push nothing. Use this to validate a new project's config end-to-end. |\n| `--fail-fast`     | Stop launching legs after the first hard failure. Unstarted legs record as `not run`; a run that stopped early can never attest.             |\n| `--only \u003Cleg>`    | Diagnostic mode — run only the named leg(s). Repeatable or comma-separated. Relaxed preconditions; never attests. Exit 1 on any failure.     |\n| `--from \u003Cleg>`    | Diagnostic mode — run the matrix from the named leg to the end. Mutually exclusive with `--only`.                                            |\n| `--config \u003Cpath>` | Override config discovery.                                                                                                                   |\n\n## What this skill never does\n\n- **Auto-install the workflow gate.** Paste it manually using the template.\n- **Skip the Secret-scan job** (or any other gate you didn't put behind the\n  attestation `if:`). Configure each workflow's `if:` explicitly.\n- **Merge or deploy.** It only attests and pushes the current branch.\n- **Edit a comment.** Each run posts a new one and minimizes its own older\n  ones, so at most one attestation is visible per PR and `lastEditedAt` stays\n  null — which is what makes it trustworthy to the merge gate.\n- **Auto-unlabel on stale attestation.** A new push silently invalidates the\n  prior attestation by SHA mismatch; the label stays as audit decoration.\n\n## Trust model\n\nDefault `trustedAssociations: [\"OWNER\"]`. Only comments from the repo OWNER\nwill gate CI. A non-trusted user's comment will post (and the label may apply)\nbut CI will still run. Widen the trust list in your config for multi-maintainer\nrepos:\n\n```js\ntrustedAssociations: [\"OWNER\", \"MEMBER\", \"COLLABORATOR\"];\n```\n\nThe generated workflow gate snippet ([references\u002Fworkflow-gate.yml.tmpl](references\u002Fworkflow-gate.yml.tmpl))\nautomatically matches the configured list.\n",{"id":5,"name":5,"task":44,"type":46,"owner":47,"tools":33,"domain":48,"created":51,"updated":52,"version":53,"maturity":54,"platform":55,"description":57,"argument-hint":58,"user-invocable":59,"disable-model-invocation":59},[29,45],"runtime-ops","skill","@kaiohenricunha",[49,50],"devex","observability",1779494400000,1786665600000,"1.2.0","draft",[56],"github-actions","Run the configured CI matrix locally and, on a clean pass, post a SHA-pinned OWNER-authored PR comment that gates downstream GitHub Actions jobs off for that exact commit. Skips the redundant remote run after a maintainer has already verified locally, saving runner minutes without weakening the gate. A new push changes the head SHA, the attestation stops matching, CI runs again. Side-effectful (posts a PR comment, applies a label, optionally pushes) and slow — minutes to tens of minutes, whatever the configured matrix costs. Diagnostic modes (--only, --from) run subsets for the fix-retry loop and can never attest; --fail-fast works in both modes. Invoke only on explicit request.\n","[--pr \u003CN>] [--no-push] [--dry-run] [--fail-fast] [--only \u003Cleg>] [--from \u003Cleg>] [--config \u003Cpath>]",true,"main","91c337164997b99943fbcc64e8e362577b67bc81","skills\u002Flocal-attest","https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel","https:\u002F\u002Fgithub.com\u002Fkaiohenricunha","I'm a Platform\u002FInfra engineer who happens to work in cybersec and write Go code.","2026-10-26 14:12:51",{"source":8,"skillName":5,"commands":68,"prompt":78,"zipAvailable":59},[69,72,75],{"tool":70,"command":71},"npx","npx skills add kaiohenricunha\u002Fdotbabel --skill local-attest",{"tool":73,"command":74},"bunx","bunx skills add kaiohenricunha\u002Fdotbabel --skill local-attest",{"tool":76,"command":77},"pnpm","pnpm dlx skills add kaiohenricunha\u002Fdotbabel --skill local-attest","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Flocal-attest\u002FSKILL.md",[80,86,90],{"path":81,"resourceType":82,"resourceTypeDesc":83,"sizeBytes":84,"sourceHtmlUrl":85},"references\u002Fconfig.md","REFERENCE","参考文档",11101,"https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Flocal-attest\u002Freferences\u002Fconfig.md",{"path":87,"resourceType":82,"resourceTypeDesc":83,"sizeBytes":88,"sourceHtmlUrl":89},"references\u002Foperator-guide.md",9657,"https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Flocal-attest\u002Freferences\u002Foperator-guide.md",{"path":91,"resourceType":82,"resourceTypeDesc":83,"sizeBytes":92,"sourceHtmlUrl":93},"references\u002Fworkflow-gate.yml.tmpl",3009,"https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Flocal-attest\u002Freferences\u002Fworkflow-gate.yml.tmpl",[95,124,155,176,197,220],{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":106,"publishedAt":39},"01M6XDQ7QMASPFZ355MYKCD06T","yolo","Start temporary autonomy for the current task or an explicit scope with a required --for expiry. Codex permits scoped pushes, merges, deploys, and releases after strict testing and validation; Full access is a separate runtime setting. Claude Code uses a session permission hook with a protected-action denylist.","https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Fyolo\u002FSKILL.md",[101],{"categoryNo":102,"categoryName":103,"taxonomyType":104,"taxonomyTypeDesc":105},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[97,29,107,108,109,110,111,112,113,114,115,116,117,118,49,119,120,121,122,123],"merge","code","start","access","session","ops","autonomy","releases","permission","full","runtime","scope","hook","true","action","for","scoped",{"skillNo":125,"name":126,"nameZh":6,"description":127,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":128,"categories":129,"keywords":138,"publishedAt":39},"01M6XDQ1N21VE4KH98JWGZ7GN9","veracity-audit","Audit a data pipeline for Veracity and Value. Dispatches data-scientist, compliance-auditor, and data-engineer agents with project context injected at dispatch time. All source paths are supplied via flags — no defaults, no project assumptions. Subcommands: audit (full pipeline walk), score-check (scoring math only), gate-check (gate coverage only), source-trace \u003Clabel> (single source end-to-end). Invoke when: \"audit pipeline\", \"veracity check\", \"scoring math correct?\", \"check gates\", \"trace source\", \"verify quality gates\", \"formula correct?\".","https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Fveracity-audit\u002FSKILL.md",[130,135],{"categoryNo":131,"categoryName":132,"taxonomyType":133,"taxonomyTypeDesc":134},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",{"categoryNo":136,"categoryName":137,"taxonomyType":104,"taxonomyTypeDesc":105},"01JBSEEDCAT000000000000037","律师与法务",[139,140,29,141,142,143,33,144,145,22,146,147,148,149,150,151,152,153,23,154],"audit","veracity audit","review","data pipeline","pipeline","math","coverage","engineer","formula","scoring","time","project","worker","end","at","when",{"skillNo":156,"name":157,"nameZh":6,"description":158,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":159,"categories":160,"keywords":161,"publishedAt":39},"01M6XDPVDKBR376C06CEK9BC5S","validate-spec","Audit an already-implemented spec against the codebase. Walks each constraint (ARCH-N, PERF-N, KD-N, etc.) and acceptance criterion, grounds findings in file:line evidence, runs the spec.json acceptance_commands, and writes a single audit doc to docs\u002Faudits\u002F. Use whenever the user asks to \"validate a spec\", \"audit a spec\", \"check if spec is implemented\", \"verify the spec is done\", \"is this spec really done\", or otherwise wants closure on spec-driven work. Read-only against the spec — produces an audit, never modifies the spec itself.","https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Fvalidate-spec\u002FSKILL.md",[],[162,163,164,29,141,165,166,139,33,167,168,30,22,169,170,171,172,173,174,175],"spec","validate","validate spec","run","acceptance","work","read","max","done","arch","line","2025","spec-driven","closure",{"skillNo":177,"name":178,"nameZh":6,"description":179,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":180,"categories":181,"keywords":185,"publishedAt":39},"01M6XDPMMECH82RAW553Y94HDS","terragrunt-specialist","Deep-dive Terragrunt hierarchy review, DRY pattern audits, and run-all orchestration analysis. Use for structured investigations of multi-environment Terragrunt layouts, dependency graphs, remote state config, and hook correctness. Triggers on: \"Terragrunt audit\", \"run-all review\", \"dependency block\", \"DRY pattern review\", \"env hierarchy audit\", \"mock_outputs\", \"terragrunt hooks\".","https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Fterragrunt-specialist\u002FSKILL.md",[182],{"categoryNo":183,"categoryName":184,"taxonomyType":133,"taxonomyTypeDesc":134},"01JBSEEDCAT000000000000011","运维与部署",[186,187,188,141,189,190,165,139,191,33,168,192,193,169,194,195,173,26,153,196],"terragrunt","specialist","terragrunt specialist","orchestration","debugging","analysis","dependency","hooks","deep","dry","hierarchy",{"skillNo":198,"name":199,"nameZh":6,"description":200,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":201,"categories":202,"keywords":210,"publishedAt":39},"01M6XDPFJS2HGSE13RXF1HV8S7","terraform-specialist","Deep-dive Terraform architecture review, module design, state management, and migration. Use for structured investigations of Terraform workspaces, provider configuration, module coupling, import workflows, and test coverage. Triggers on: \"Terraform audit\", \"module review\", \"state management\", \"Terraform import\", \"workspace design\", \"provider config review\", \"Terraform testing\".","https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Fterraform-specialist\u002FSKILL.md",[203,206,207],{"categoryNo":204,"categoryName":205,"taxonomyType":133,"taxonomyTypeDesc":134},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":183,"categoryName":184,"taxonomyType":133,"taxonomyTypeDesc":134},{"categoryNo":208,"categoryName":209,"taxonomyType":133,"taxonomyTypeDesc":134},"01JBSEEDCAT000000000000015","视觉设计",[211,212,187,29,213,141,214,215,216,190,139,33,168,145,217,218,194,219,173,153],"terraform","terraform specialist","design","workflows","architecture","architecture review","test coverage","workspaces","problem",{"skillNo":221,"name":162,"nameZh":6,"description":222,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":223,"categories":224,"keywords":229,"publishedAt":39},"01M6XDP7V6H8WYPSXVEKYE6HJ6","Create structured engineering specs through interactive pairing. Use when the user wants to create a spec, design doc, technical specification, architecture document, RFC, or engineering plan. Triggers on \"let's spec this out\", \"create a spec\", \"design doc\", \"write a technical plan\", \"plan the architecture\". Works for greenfield and brownfield projects. Outputs to docs\u002Fspecs\u002F.","https:\u002F\u002Fgithub.com\u002Fkaiohenricunha\u002Fdotbabel\u002Fblob\u002Fmain\u002Fskills\u002Fspec\u002FSKILL.md",[225,226],{"categoryNo":208,"categoryName":209,"taxonomyType":133,"taxonomyTypeDesc":134},{"categoryNo":227,"categoryName":228,"taxonomyType":104,"taxonomyTypeDesc":105},"01JBSEEDCAT000000000000032","设计师",[162,213,230,215,231,232,233,234,235,236,237,238,239,240,241,122,242,243,244,245],"component","create","plan","projects","write","interactive","use","document","specification","triggers","opus","technical specification","trade-offs","technical plan","outputs","frontier"]