[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JQ0MPSDXHM4HWMS2C60V5J":3,"skill-same:01M6JQ0MPSDXHM4HWMS2C60V5J":81},{"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":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":14,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":55,"updatedAt":56,"install":57,"resources":70},"01M6JQ0MPSDXHM4HWMS2C60V5J","verification-before-completion",null,"Evidence gate before any claim that work is done, fixed, passing, or deployed. Route here when a message declares the work finished or asks to commit, push, merge, ship, or close it out, including a brief sign-off, and before git-workflow or release-management act on it. Writing the commit message or PR text itself → git-workflow.","SWEStash\u002Fswe-workflow-skills","SWEStash","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F272895713?v=4","01M6JNRQ84JN5WFK9S1V5H4G46","SWE Stash","01M6JNRQCM45X8WPNHY30TSAB0",1,"MIT","https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fsecurity\u002Fskills\u002Fverification-before-completion\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"verification","verification before completion","text","close","act","message","done","claim","route","ready","fix","work","writing","ship","brief","git","release","merge","commit","workflow","2026-11-10 10:12:30",false,"---\nname: verification-before-completion\ndescription: \"Evidence gate before any claim that work is done, fixed, passing, or deployed. Route here when a message declares the work finished or asks to commit, push, merge, ship, or close it out, including a brief sign-off, and before git-workflow or release-management act on it. Writing the commit message or PR text itself → git-workflow.\"\nwhen_to_use: \"Triggers: is it done, did it work, tests pass, verify, ready to commit, before I push, confirm the fix.\"\n---\n\n# Verification Before Completion\n\nA discipline, not a phase: never assert that something works until you have run\nthe command that proves it and read the output — in this session, just now.\nClaiming work is complete without verification is dishonesty, not efficiency.\n\n## ⛔ The Iron Law\n\n**No completion claim without fresh verification evidence.**\n\nIf you haven't run the proving command in this exchange, you cannot claim it\npasses. \"Should pass\", \"looks right\", and \"I changed it the way they asked\"\nare predictions, not evidence.\n\n## The Gate Function\n\nBefore any completion claim, walk these five steps in order:\n\n1. **IDENTIFY** — What single command (or observation) would prove this claim?\n   (\"The tests pass\" → `pytest`. \"The build works\" → the build command. \"The\n   bug is fixed\" → the failing repro now succeeds.)\n2. **RUN** — Execute the full command, fresh and complete. Not a subset, not a\n   remembered result from earlier, not CI from yesterday.\n3. **READ** — Read the entire output. Check the exit code. Count the failures.\n   Don't skim for green and stop.\n4. **VERIFY** — Does the output actually confirm the claim? A passing exit code\n   on the wrong command proves nothing.\n5. **CLAIM** — Only now state the result, and state it precisely (\"142 passed,\n   0 failed\" beats \"tests pass\").\n\nSkipping any step turns a verification into a guess.\n\n## What counts as a claim\n\nAny of these is a completion claim and triggers the gate:\n- \"Done\", \"finished\", \"complete\", \"ready\"\n- \"It works\", \"this fixes it\", \"the bug is gone\"\n- \"Tests pass\", \"the build is green\", \"no errors\"\n- About to commit, push, open\u002Fmerge a PR, or deploy\n- Reporting that a sub-task or delegated agent succeeded\n\n## What counts as evidence\n\n- A command you ran **in this session**, with its output visible.\n- The exit code (a command can print reassuring text and still exit non-zero).\n- The relevant slice of output read in full (the failure count, not just the\n  last line).\n\nNOT evidence: a previous run, a CI result you didn't open, a teammate's report\nyou didn't confirm, or your own expectation of what the code does.\n\n## The tests aren't the only gate\n\nTests prove behavior; they don't prove the change is clean. When the project\nconfigures **static gates** — a type checker (`tsc --noEmit`, `mypy`), a linter\n(`eslint`, `ruff`), a dead-code \u002F unused-export check (`knip`, `vulture`) — those\nare part of \"done\" too. A change is not complete until they run green *this\nsession*, on the same five-step gate (IDENTIFY → RUN → READ → VERIFY → CLAIM).\n\nThe failure mode this prevents is a **rotted gate**: a repo declares\n`mypy strict = true` (or `eslint --max-warnings 0`) and it quietly sits on 40+\nerrors because no one runs it — so the gate that was supposed to stop `any`-casts,\nstale `type: ignore`s, and dead exports stops nothing. A declared strict gate with\nN errors is not \"passing\"; it is broken, and adding to it is adding slop the gate\nwas meant to catch. Run it, read the count, and either land it at zero or name the\npre-existing debt explicitly — never let \"the tests pass\" stand in for \"the gates pass.\"\n\n## The docs are part of the surface you changed\n\nTests prove behavior, the static gates prove the code is clean — neither notices\nthat the README still documents the flag you just renamed. A change carries a\n**documented surface** when it adds, renames, or removes something a person types,\nconfigures, or calls: a CLI flag, env var, config key, endpoint, command, script,\npackage or directory, install\u002Fsetup step, or public exported symbol. A purely\ninternal refactor usually carries none — say so and move on. This gate is for\nchanges that made an existing document wrong.\n\nSame five steps, with a grep as the proving command:\n\n1. **Derive** the identifiers your diff touched — the old names as well as the new\n   ones. The old name is what the docs still contain.\n2. **Grep** the doc set for them: README, `docs\u002F`, and inline docs.\n3. **Read** the hits, in place. A hit is not automatically drift; the surrounding\n   sentence decides.\n4. **Reconcile or report.** Fix what your change made wrong, or state plainly which\n   docs are stale and why you're leaving them.\n\nTwo things make this gate miss:\n\n- **Prose survives; inventories rot.** People reread the paragraph they're editing\n  and never reread the table three sections down. Endpoint tables, architecture\n  diagrams, project-layout blocks, ADR indexes, and command lists go stale first\n  and silently — check those before the prose.\n- **An empty grep is not evidence of no drift.** Docs use shorthand, globs, and\n  brace expansion (`\u002Fapi\u002F{users,orders}`), so a literal grep for `orders` can miss\n  a line that documents it. When the doc plausibly covers the area, open the\n  section and read it rather than trusting a clean grep.\n\nScope this to **reconciliation, not authoring**: it touches documents that already\nreference what you changed. It never means writing new docs nobody asked for —\nthat produces the doc-slop `project-documentation` exists to prevent.\n\n## What you're about to publish\n\nCommitting publishes. The tests prove the code runs and the doc gate proves the\ndocs still match — neither notices that the diff also carries a real customer's\nemail, or a comment citing \"Phase 3\" of a plan that exists only in this session's\ncontext. Both are invisible to every other gate and permanent once pushed.\n\n**This is not a gate on committing.** It reports; the user decides. Refusing to\ncommit doesn't prevent a leak — the commit happens without you — it just removes\nthe one reader who noticed. Your job is that the check ran and the finding is on\nthe record, in the same response, not that the commit was stopped.\n\nNot blocking is not the same as not urgent. Proportionality governs *how much room\na finding takes up*, never how plainly you state what it costs — the worst outcome\nhere is a hedged sentence about a live credential, which reads as diligence and\nfunctions as silence.\n\nRead `git diff --staged` and look for three things at once. It's a scan, not three\npasses, and it costs seconds:\n\n1. **Who is in this diff?** Real people or real credentials — in data files,\n   fixtures, seeds, configs\u002Fenv, logs, captured output, or a doc pasting a \"real\"\n   example.\n\n   **Look up what this project already decided** before reasoning from general\n   privacy principle: CLAUDE.md and project instructions, memory, `.gitignore`d\n   paths, and the convention the repo's other fixtures follow. That's the answer\n   when it exists, and it beats anything you'd derive. *Exit condition: \"This repo\n   declares `\u003CX>` protected\"* — or *\"nothing is declared; the other fixtures use\n   synthetic values, so I've matched them.\"*\n\n   **A staged credential is already compromised.** An API key, token, private key,\n   password, or connection string that reached a file you staged must be treated as\n   burned — it has been on disk, in your shell history, and in whatever tooling\n   watched the working tree. **Say that plainly and name rotation, not deletion, as\n   the fix**; dropping the line from the diff leaves a live secret that someone\n   already had. Don't hedge it into a conditional — \"if nothing was pushed you can\n   probably skip rotation\" is the sentence that gets a key left valid. Route the\n   durable fix to `configuration-strategy`.\n\n   For personal data, name what's exposed and offer redaction or synthetic values\n   (`test-data-strategy`); for internal-but-not-personal — hostnames, client names,\n   paths with usernames — one line is the right size. Full ladder in\n   [references\u002Fpre-commit-gate.md § Leg 1](references\u002Fpre-commit-gate.md).\n2. **Would a stranger understand this?** Internal identifiers in staged content:\n   `Phase 2`, `CP1.3`, `T02`, `finding #17`, `checkpoint 3\u002F5`. Check, don't assume\n   — `git ls-files '*.md' | xargs grep -l \"\u003Cterm>\"`. Anchored in a tracked document,\n   it's a real cross-reference; unanchored, it's a private label a reader can't\n   resolve, so **write the meaning instead**. Rewrites in\n   [§ Leg 2](references\u002Fpre-commit-gate.md).\n3. **Does this comment belong in the code?** New comment blocks of four or more\n   lines: system-level rationale belongs in an ADR with the comment carrying a\n   pointer; a local invariant the types can't express stays inline. Calibration in\n   [§ Leg 3](references\u002Fpre-commit-gate.md).\n\n**Exit condition** — one line in the response where you commit, then keep going.\nIt reports what you *found*, and it scales to that:\n\n- **Nothing found** → *\"Staged diff checked, nothing to flag.\"* That is the entire\n  line. **Do not list the categories you scanned.** \"No personal data, no unanchored\n  references\" on a diff that was never going to contain either teaches the reader\n  nothing, and asserting a category is clean is not the same as having had a reason\n  to raise it.\n- **Something found** → name only that, and what you did about it: *\"Staged diff:\n  `fixtures\u002Faccounts.json` carries 40 real-looking email addresses — replaced them\n  with synthetic values.\"*\n\n**A clean diff ends here.** Don't narrate the scan, don't open with \"not yet\",\ndon't ask permission to proceed, and don't open the reference file — a clean scan\nneeds none of it. A gate that makes every commit feel expensive is one that gets\nrouted around, and then it protects nothing.\n\n**If the diff has code hunks and something tripped, invoke `code-reviewing`** and\nlet it work the diff — it owns the repo-wide greps and the slop catalog, and it is\nalmost never reached at commit time on its own. A docs- or config-only diff never\nroutes there.\n\nOne handling rule that applies while checking, not after: **never echo a real\nsecret or a real person's data into context** to inspect it. Match on shape and\nlocation, report the file and line, and let the user decide. Pasting the value to\nprove it's there is the leak.\n\n## What you write into a doc is also a claim\n\nA document is a set of assertions about the code, and the Iron Law does not stop\nat prose. Every factual sentence you add must come from something you read **this\nsession**, not from what you remember the code doing. Sweeps introduce errors as\nreadily as they remove them, and a confidently-worded wrong sentence outlives the\nstale one it replaced.\n\n- **Enumerate from the source of truth.** Endpoints come from the router file,\n  scripts from listing the directory, packages from the manifest, flags from the\n  arg parser. Do not reconstruct a list from memory and then spot-check it.\n- **Re-read your own doc diff before committing**, against the code — the same\n  scrutiny you would give a code hunk. This is where a wrong claim gets caught.\n- **Verify links resolve.** A doc edit that moves or adds a relative link is a\n  change whose proving command is a link check.\n\n## Rationalizations to reject\n\n| Excuse | Reality |\n|--------|---------|\n| \"It's a trivial change, no need to run it\" | Trivial changes break builds too. The command takes seconds. |\n| \"Tests passed before my change\" | Your change is exactly what could have broken them. Re-run. |\n| \"CI will catch it\" | CI catches it *after* you claimed done — and after you pushed. |\n| \"The subagent said it succeeded\" | Reports aren't evidence. Verify the artifact yourself. |\n| \"I'm confident it works\" | Confidence is a feeling; the exit code is a fact. |\n| \"I'll verify after committing\" | Then the commit message is a claim you haven't backed. |\n| \"Lint and types are separate from tests\" | If the repo configures them, \"green\" means all of them — a strict gate on N errors is a broken gate, not an optional one. |\n| \"Docs can go in a follow-up PR\" | The follow-up is where doc debt goes to die, and meanwhile the README documents a flag that no longer exists. The reconciliation is a grep, not a project. |\n| \"The user didn't ask me to touch the docs\" | They asked for a working change. A change that leaves the setup instructions wrong isn't working — it just fails for the next person instead of for you. |\n| \"Nobody reads the README anyway\" | The people who read it are exactly the ones who can't ask you. And a *wrong* doc is worse than a missing one: it's followed, then it fails. |\n| \"I know what that endpoint does, I don't need to open the file\" | That's recall, and recall is what puts false sentences into docs. Read it, then write it. |\n| \"It's my own repo \u002F a private repo\" | Private today. Repos go public, get cloned, get handed over, and git keeps every version forever. The scan costs seconds; a rewrite of published history costs days. |\n| \"I'll scrub it before pushing\" | The commit is the artifact. Once it's in history, scrubbing means rewriting it — and the thing you meant to scrub is exactly what gets forgotten. |\n| \"The phase name is obvious from context\" | It's obvious from *your* context, which no reader has. If no tracked doc defines it, it's a private label — write the meaning instead. |\n| \"Reviewing the diff is what the PR is for\" | Review happens after the commit exists, and 98% of commits here never reach a review at all. The three checks are greps, not a review. |\n| \"The comment explains an important decision, it should stay in the code\" | Then the decision deserves an ADR, and the comment deserves a pointer to it. Rationale buried in a source file is found by nobody and rots unread. |\n| \"I found something, so I should refuse to commit until it's fixed\" | Reporting is the job; deciding is the user's. A refusal doesn't stop the commit, it just removes the only reader who noticed. Say what you found, say what it costs, commit if they say commit. |\n| \"This diff is clean, so I'll walk through the checks to show my work\" | The one-line exit condition *is* showing your work. Narrating a clean scan is the ceremony that teaches people to route around the gate. |\n| \"It's a trivial diff, the gate isn't worth running\" | The scan is seconds and the clean path is one line. Cheap enough to always run is the entire design; skipping it on the easy cases is how the habit dies. |\n\n## Red flags — stop and verify first\n\n- You're typing \"done\" \u002F \"fixed\" \u002F \"works\" without a command output above it.\n- You feel satisfaction (\"Great!\", \"Perfect!\") before running anything.\n- You're about to commit\u002Fpush\u002FPR and haven't run the test command this session.\n- You ran the tests but not the configured type checker \u002F linter the repo defines.\n- You're relying on a partial run or a stale result.\n- You renamed or removed something a user types or configures, and haven't grepped\n  the docs for the old name.\n- You're writing a sentence about how the code behaves into a doc, and you haven't\n  opened that code this session.\n- You're about to commit and haven't read `git diff --staged` — you're committing\n  what you *believe* you staged.\n- The diff touches a fixture, seed, log, capture, or `.env`-shaped file and nobody\n  has said what's protected in this project.\n- You're writing a plan's vocabulary — a phase, checkpoint, task code, or finding\n  number — into a commit message, comment, or doc, and the plan is not a tracked file.\n- You just wrote a comment longer than the code it sits above.\n- You're asking permission, or opening with \"not yet\", on a diff you have already\n  concluded is clean.\n- You're about to refuse a commit rather than report what you found and let the\n  user choose.\n\n## Cross-Skill References\n\nThis skill is the shared \"done\" gate for every workflow that ends in a claim:\n\n- `tdd-workflow` — the GREEN step is a verification: watch the test pass for real\n- `bug-investigating` — confirm the reproducing test now passes before claiming the fix\n- `deployment-checklist` — every checked box is an instance of this gate\n- `code-reviewing` — verify the change before approving, not from the diff alone; also\n  where the publish gate routes a tripped code diff\n- `code-slop-cleanup` — the slop catalog behind the comment-placement check\n- `git-workflow` — writes the commit this gate clears\n- `project-documentation` — establishes which docs are versioned, which the anchor\n  check reads as its baseline\n- `cicd-pipeline` — the pipeline automates this gate; locally, run it yourself first\n- `project-documentation` — the sync sweep and the accuracy method, when the grep\n  turns up docs that need reconciling\n","# Verification Before Completion\n\nA discipline, not a phase: never assert that something works until you have run\nthe command that proves it and read the output — in this session, just now.\nClaiming work is complete without verification is dishonesty, not efficiency.\n\n## ⛔ The Iron Law\n\n**No completion claim without fresh verification evidence.**\n\nIf you haven't run the proving command in this exchange, you cannot claim it\npasses. \"Should pass\", \"looks right\", and \"I changed it the way they asked\"\nare predictions, not evidence.\n\n## The Gate Function\n\nBefore any completion claim, walk these five steps in order:\n\n1. **IDENTIFY** — What single command (or observation) would prove this claim?\n   (\"The tests pass\" → `pytest`. \"The build works\" → the build command. \"The\n   bug is fixed\" → the failing repro now succeeds.)\n2. **RUN** — Execute the full command, fresh and complete. Not a subset, not a\n   remembered result from earlier, not CI from yesterday.\n3. **READ** — Read the entire output. Check the exit code. Count the failures.\n   Don't skim for green and stop.\n4. **VERIFY** — Does the output actually confirm the claim? A passing exit code\n   on the wrong command proves nothing.\n5. **CLAIM** — Only now state the result, and state it precisely (\"142 passed,\n   0 failed\" beats \"tests pass\").\n\nSkipping any step turns a verification into a guess.\n\n## What counts as a claim\n\nAny of these is a completion claim and triggers the gate:\n- \"Done\", \"finished\", \"complete\", \"ready\"\n- \"It works\", \"this fixes it\", \"the bug is gone\"\n- \"Tests pass\", \"the build is green\", \"no errors\"\n- About to commit, push, open\u002Fmerge a PR, or deploy\n- Reporting that a sub-task or delegated agent succeeded\n\n## What counts as evidence\n\n- A command you ran **in this session**, with its output visible.\n- The exit code (a command can print reassuring text and still exit non-zero).\n- The relevant slice of output read in full (the failure count, not just the\n  last line).\n\nNOT evidence: a previous run, a CI result you didn't open, a teammate's report\nyou didn't confirm, or your own expectation of what the code does.\n\n## The tests aren't the only gate\n\nTests prove behavior; they don't prove the change is clean. When the project\nconfigures **static gates** — a type checker (`tsc --noEmit`, `mypy`), a linter\n(`eslint`, `ruff`), a dead-code \u002F unused-export check (`knip`, `vulture`) — those\nare part of \"done\" too. A change is not complete until they run green *this\nsession*, on the same five-step gate (IDENTIFY → RUN → READ → VERIFY → CLAIM).\n\nThe failure mode this prevents is a **rotted gate**: a repo declares\n`mypy strict = true` (or `eslint --max-warnings 0`) and it quietly sits on 40+\nerrors because no one runs it — so the gate that was supposed to stop `any`-casts,\nstale `type: ignore`s, and dead exports stops nothing. A declared strict gate with\nN errors is not \"passing\"; it is broken, and adding to it is adding slop the gate\nwas meant to catch. Run it, read the count, and either land it at zero or name the\npre-existing debt explicitly — never let \"the tests pass\" stand in for \"the gates pass.\"\n\n## The docs are part of the surface you changed\n\nTests prove behavior, the static gates prove the code is clean — neither notices\nthat the README still documents the flag you just renamed. A change carries a\n**documented surface** when it adds, renames, or removes something a person types,\nconfigures, or calls: a CLI flag, env var, config key, endpoint, command, script,\npackage or directory, install\u002Fsetup step, or public exported symbol. A purely\ninternal refactor usually carries none — say so and move on. This gate is for\nchanges that made an existing document wrong.\n\nSame five steps, with a grep as the proving command:\n\n1. **Derive** the identifiers your diff touched — the old names as well as the new\n   ones. The old name is what the docs still contain.\n2. **Grep** the doc set for them: README, `docs\u002F`, and inline docs.\n3. **Read** the hits, in place. A hit is not automatically drift; the surrounding\n   sentence decides.\n4. **Reconcile or report.** Fix what your change made wrong, or state plainly which\n   docs are stale and why you're leaving them.\n\nTwo things make this gate miss:\n\n- **Prose survives; inventories rot.** People reread the paragraph they're editing\n  and never reread the table three sections down. Endpoint tables, architecture\n  diagrams, project-layout blocks, ADR indexes, and command lists go stale first\n  and silently — check those before the prose.\n- **An empty grep is not evidence of no drift.** Docs use shorthand, globs, and\n  brace expansion (`\u002Fapi\u002F{users,orders}`), so a literal grep for `orders` can miss\n  a line that documents it. When the doc plausibly covers the area, open the\n  section and read it rather than trusting a clean grep.\n\nScope this to **reconciliation, not authoring**: it touches documents that already\nreference what you changed. It never means writing new docs nobody asked for —\nthat produces the doc-slop `project-documentation` exists to prevent.\n\n## What you're about to publish\n\nCommitting publishes. The tests prove the code runs and the doc gate proves the\ndocs still match — neither notices that the diff also carries a real customer's\nemail, or a comment citing \"Phase 3\" of a plan that exists only in this session's\ncontext. Both are invisible to every other gate and permanent once pushed.\n\n**This is not a gate on committing.** It reports; the user decides. Refusing to\ncommit doesn't prevent a leak — the commit happens without you — it just removes\nthe one reader who noticed. Your job is that the check ran and the finding is on\nthe record, in the same response, not that the commit was stopped.\n\nNot blocking is not the same as not urgent. Proportionality governs *how much room\na finding takes up*, never how plainly you state what it costs — the worst outcome\nhere is a hedged sentence about a live credential, which reads as diligence and\nfunctions as silence.\n\nRead `git diff --staged` and look for three things at once. It's a scan, not three\npasses, and it costs seconds:\n\n1. **Who is in this diff?** Real people or real credentials — in data files,\n   fixtures, seeds, configs\u002Fenv, logs, captured output, or a doc pasting a \"real\"\n   example.\n\n   **Look up what this project already decided** before reasoning from general\n   privacy principle: CLAUDE.md and project instructions, memory, `.gitignore`d\n   paths, and the convention the repo's other fixtures follow. That's the answer\n   when it exists, and it beats anything you'd derive. *Exit condition: \"This repo\n   declares `\u003CX>` protected\"* — or *\"nothing is declared; the other fixtures use\n   synthetic values, so I've matched them.\"*\n\n   **A staged credential is already compromised.** An API key, token, private key,\n   password, or connection string that reached a file you staged must be treated as\n   burned — it has been on disk, in your shell history, and in whatever tooling\n   watched the working tree. **Say that plainly and name rotation, not deletion, as\n   the fix**; dropping the line from the diff leaves a live secret that someone\n   already had. Don't hedge it into a conditional — \"if nothing was pushed you can\n   probably skip rotation\" is the sentence that gets a key left valid. Route the\n   durable fix to `configuration-strategy`.\n\n   For personal data, name what's exposed and offer redaction or synthetic values\n   (`test-data-strategy`); for internal-but-not-personal — hostnames, client names,\n   paths with usernames — one line is the right size. Full ladder in\n   [references\u002Fpre-commit-gate.md § Leg 1](references\u002Fpre-commit-gate.md).\n2. **Would a stranger understand this?** Internal identifiers in staged content:\n   `Phase 2`, `CP1.3`, `T02`, `finding #17`, `checkpoint 3\u002F5`. Check, don't assume\n   — `git ls-files '*.md' | xargs grep -l \"\u003Cterm>\"`. Anchored in a tracked document,\n   it's a real cross-reference; unanchored, it's a private label a reader can't\n   resolve, so **write the meaning instead**. Rewrites in\n   [§ Leg 2](references\u002Fpre-commit-gate.md).\n3. **Does this comment belong in the code?** New comment blocks of four or more\n   lines: system-level rationale belongs in an ADR with the comment carrying a\n   pointer; a local invariant the types can't express stays inline. Calibration in\n   [§ Leg 3](references\u002Fpre-commit-gate.md).\n\n**Exit condition** — one line in the response where you commit, then keep going.\nIt reports what you *found*, and it scales to that:\n\n- **Nothing found** → *\"Staged diff checked, nothing to flag.\"* That is the entire\n  line. **Do not list the categories you scanned.** \"No personal data, no unanchored\n  references\" on a diff that was never going to contain either teaches the reader\n  nothing, and asserting a category is clean is not the same as having had a reason\n  to raise it.\n- **Something found** → name only that, and what you did about it: *\"Staged diff:\n  `fixtures\u002Faccounts.json` carries 40 real-looking email addresses — replaced them\n  with synthetic values.\"*\n\n**A clean diff ends here.** Don't narrate the scan, don't open with \"not yet\",\ndon't ask permission to proceed, and don't open the reference file — a clean scan\nneeds none of it. A gate that makes every commit feel expensive is one that gets\nrouted around, and then it protects nothing.\n\n**If the diff has code hunks and something tripped, invoke `code-reviewing`** and\nlet it work the diff — it owns the repo-wide greps and the slop catalog, and it is\nalmost never reached at commit time on its own. A docs- or config-only diff never\nroutes there.\n\nOne handling rule that applies while checking, not after: **never echo a real\nsecret or a real person's data into context** to inspect it. Match on shape and\nlocation, report the file and line, and let the user decide. Pasting the value to\nprove it's there is the leak.\n\n## What you write into a doc is also a claim\n\nA document is a set of assertions about the code, and the Iron Law does not stop\nat prose. Every factual sentence you add must come from something you read **this\nsession**, not from what you remember the code doing. Sweeps introduce errors as\nreadily as they remove them, and a confidently-worded wrong sentence outlives the\nstale one it replaced.\n\n- **Enumerate from the source of truth.** Endpoints come from the router file,\n  scripts from listing the directory, packages from the manifest, flags from the\n  arg parser. Do not reconstruct a list from memory and then spot-check it.\n- **Re-read your own doc diff before committing**, against the code — the same\n  scrutiny you would give a code hunk. This is where a wrong claim gets caught.\n- **Verify links resolve.** A doc edit that moves or adds a relative link is a\n  change whose proving command is a link check.\n\n## Rationalizations to reject\n\n| Excuse | Reality |\n|--------|---------|\n| \"It's a trivial change, no need to run it\" | Trivial changes break builds too. The command takes seconds. |\n| \"Tests passed before my change\" | Your change is exactly what could have broken them. Re-run. |\n| \"CI will catch it\" | CI catches it *after* you claimed done — and after you pushed. |\n| \"The subagent said it succeeded\" | Reports aren't evidence. Verify the artifact yourself. |\n| \"I'm confident it works\" | Confidence is a feeling; the exit code is a fact. |\n| \"I'll verify after committing\" | Then the commit message is a claim you haven't backed. |\n| \"Lint and types are separate from tests\" | If the repo configures them, \"green\" means all of them — a strict gate on N errors is a broken gate, not an optional one. |\n| \"Docs can go in a follow-up PR\" | The follow-up is where doc debt goes to die, and meanwhile the README documents a flag that no longer exists. The reconciliation is a grep, not a project. |\n| \"The user didn't ask me to touch the docs\" | They asked for a working change. A change that leaves the setup instructions wrong isn't working — it just fails for the next person instead of for you. |\n| \"Nobody reads the README anyway\" | The people who read it are exactly the ones who can't ask you. And a *wrong* doc is worse than a missing one: it's followed, then it fails. |\n| \"I know what that endpoint does, I don't need to open the file\" | That's recall, and recall is what puts false sentences into docs. Read it, then write it. |\n| \"It's my own repo \u002F a private repo\" | Private today. Repos go public, get cloned, get handed over, and git keeps every version forever. The scan costs seconds; a rewrite of published history costs days. |\n| \"I'll scrub it before pushing\" | The commit is the artifact. Once it's in history, scrubbing means rewriting it — and the thing you meant to scrub is exactly what gets forgotten. |\n| \"The phase name is obvious from context\" | It's obvious from *your* context, which no reader has. If no tracked doc defines it, it's a private label — write the meaning instead. |\n| \"Reviewing the diff is what the PR is for\" | Review happens after the commit exists, and 98% of commits here never reach a review at all. The three checks are greps, not a review. |\n| \"The comment explains an important decision, it should stay in the code\" | Then the decision deserves an ADR, and the comment deserves a pointer to it. Rationale buried in a source file is found by nobody and rots unread. |\n| \"I found something, so I should refuse to commit until it's fixed\" | Reporting is the job; deciding is the user's. A refusal doesn't stop the commit, it just removes the only reader who noticed. Say what you found, say what it costs, commit if they say commit. |\n| \"This diff is clean, so I'll walk through the checks to show my work\" | The one-line exit condition *is* showing your work. Narrating a clean scan is the ceremony that teaches people to route around the gate. |\n| \"It's a trivial diff, the gate isn't worth running\" | The scan is seconds and the clean path is one line. Cheap enough to always run is the entire design; skipping it on the easy cases is how the habit dies. |\n\n## Red flags — stop and verify first\n\n- You're typing \"done\" \u002F \"fixed\" \u002F \"works\" without a command output above it.\n- You feel satisfaction (\"Great!\", \"Perfect!\") before running anything.\n- You're about to commit\u002Fpush\u002FPR and haven't run the test command this session.\n- You ran the tests but not the configured type checker \u002F linter the repo defines.\n- You're relying on a partial run or a stale result.\n- You renamed or removed something a user types or configures, and haven't grepped\n  the docs for the old name.\n- You're writing a sentence about how the code behaves into a doc, and you haven't\n  opened that code this session.\n- You're about to commit and haven't read `git diff --staged` — you're committing\n  what you *believe* you staged.\n- The diff touches a fixture, seed, log, capture, or `.env`-shaped file and nobody\n  has said what's protected in this project.\n- You're writing a plan's vocabulary — a phase, checkpoint, task code, or finding\n  number — into a commit message, comment, or doc, and the plan is not a tracked file.\n- You just wrote a comment longer than the code it sits above.\n- You're asking permission, or opening with \"not yet\", on a diff you have already\n  concluded is clean.\n- You're about to refuse a commit rather than report what you found and let the\n  user choose.\n\n## Cross-Skill References\n\nThis skill is the shared \"done\" gate for every workflow that ends in a claim:\n\n- `tdd-workflow` — the GREEN step is a verification: watch the test pass for real\n- `bug-investigating` — confirm the reproducing test now passes before claiming the fix\n- `deployment-checklist` — every checked box is an instance of this gate\n- `code-reviewing` — verify the change before approving, not from the diff alone; also\n  where the publish gate routes a tripped code diff\n- `code-slop-cleanup` — the slop catalog behind the comment-placement check\n- `git-workflow` — writes the commit this gate clears\n- `project-documentation` — establishes which docs are versioned, which the anchor\n  check reads as its baseline\n- `cicd-pipeline` — the pipeline automates this gate; locally, run it yourself first\n- `project-documentation` — the sync sweep and the accuracy method, when the grep\n  turns up docs that need reconciling\n",{"name":5,"description":7,"when_to_use":49},"Triggers: is it done, did it work, tests pass, verify, ready to commit, before I push, confirm the fix.","main","800dc9ad0ca2914d6ad15b8c5159d9b57bb104c2","plugins\u002Fsecurity\u002Fskills\u002Fverification-before-completion","https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills","https:\u002F\u002Fgithub.com\u002FSWEStash","A stash of things we felt like building","2026-11-12 00:14:33",{"source":8,"skillName":5,"commands":58,"prompt":68,"zipAvailable":69},[59,62,65],{"tool":60,"command":61},"npx","npx skills add SWEStash\u002Fswe-workflow-skills --skill verification-before-completion",{"tool":63,"command":64},"bunx","bunx skills add SWEStash\u002Fswe-workflow-skills --skill verification-before-completion",{"tool":66,"command":67},"pnpm","pnpm dlx skills add SWEStash\u002Fswe-workflow-skills --skill verification-before-completion","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fsecurity\u002Fskills\u002Fverification-before-completion\u002FSKILL.md",true,[71,77],{"path":72,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":75,"sourceHtmlUrl":76},"evals\u002Fevals.json","REFERENCE","参考文档",15789,"https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fsecurity\u002Fskills\u002Fverification-before-completion\u002Fevals\u002Fevals.json",{"path":78,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":79,"sourceHtmlUrl":80},"references\u002Fpre-commit-gate.md",12057,"https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fsecurity\u002Fskills\u002Fverification-before-completion\u002Freferences\u002Fpre-commit-gate.md",[82,123,147,153,186,216],{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":103,"publishedAt":44},"01M6JQFADV3T1MS9V1BNZS1232","writing-skills","Use when authoring, editing, or reviewing a skill in this library — a new SKILL.md, one that under-triggers, or hardening an existing one. Covers description and listing-budget rules, frontmatter, progressive disclosure, the 3-eval rule, and pressure-testing against rationalizations.","https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fskills\u002Fwriting-skills\u002FSKILL.md",[88,91,94,95,100],{"categoryNo":89,"categoryName":90,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":92,"categoryName":93,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":96,"categoryName":97,"taxonomyType":98,"taxonomyTypeDesc":99},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",{"categoryNo":101,"categoryName":102,"taxonomyType":98,"taxonomyTypeDesc":99},"01JBSEEDCAT000000000000040","财务",[36,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122],"writing skills","testing","eval","write","edit","Budget","authoring","new skill","library","frontmatter","editing","triggers","use","Existing","Rules","write a skill","when","listing","progressive disclosure",{"skillNo":124,"name":125,"nameZh":6,"description":126,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":127,"categories":128,"keywords":130,"publishedAt":44},"01M6JQF5DGMXPMQQVZBP8DH9SY","working-agreement","Define and validate how a multi-session project is run — where plans, status and decisions live, session routines, new work, milestone close. README\u002FCONTRIBUTING → project-documentation.","https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fskills\u002Fworking-agreement\u002FSKILL.md",[129],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[131,132,133,30,134,135,35,136,137,138,27,139,140,141,142,143,144,145,115,146],"working agreement","project","definition of done","milestone","documentation","progress","how","contributing","run","sync","status","live","do","README","session","routines",{"skillNo":148,"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":149,"categories":150,"keywords":152,"publishedAt":44},"01M6JQF0SMRHN82RBSWMABC4J0","https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fskills\u002Fverification-before-completion\u002FSKILL.md",[151],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[24,25,38,33,34,31,36,43,30,42,26,32,35,29,27,41,40,39,28,37],{"skillNo":154,"name":155,"nameZh":6,"description":156,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":157,"categories":158,"keywords":166,"publishedAt":44},"01M6JQEVGE9NJZ11DV60AE6VWC","ui-ux-design","Design user experiences from an engineer's perspective — user flows, wireframes, interaction patterns, responsive strategy, navigation, loading\u002Ferror\u002Fempty states. Triggers: design the UI, user flow, wireframe, how should this screen look, loading state, error state, empty state, responsive design, navigation, page layout, form design, mockup.","https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fskills\u002Fui-ux-design\u002FSKILL.md",[159,162,163],{"categoryNo":160,"categoryName":161,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":164,"categoryName":165,"taxonomyType":98,"taxonomyTypeDesc":99},"01JBSEEDCAT000000000000032","设计师",[167,168,169,170,171,172,173,174,175,176,177,178,137,179,180,181,182,183,184,185],"design","ux","ui","ui ux design","error","states","loading","flows","engineer","interaction","layout","responsive design","screen","flow","patterns","mockup","responsive","form","page",{"skillNo":187,"name":188,"nameZh":6,"description":189,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":190,"categories":191,"keywords":196,"publishedAt":44},"01M6JQEN4HVFTZWQYWGTDS76K9","threat-modeling","Design-time security analysis of a system or feature before it's built — trust boundaries, attack surface, STRIDE per element, abuse cases, prioritized mitigations that become security requirements. Triggers: threat model, STRIDE, trust boundary, attack surface, what could go wrong security-wise, security design review, abuse case, secure this design, security requirements. Reviewing EXISTING code\u002Fconfig for vulnerabilities → security-audit; use this skill while the design is still on the whiteboard.","https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fskills\u002Fthreat-modeling\u002FSKILL.md",[192,193],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":194,"categoryName":195,"taxonomyType":98,"taxonomyTypeDesc":99},"01JBSEEDCAT000000000000030","开发工程师",[197,198,199,200,201,202,167,203,204,205,206,207,208,209,210,211,212,213,214,215],"modeling","threat modeling","threat","model","time","element","whiteboard","security analysis","analysis","code","review","threat model","audit","security-audit","secure","config","go","security","trust",{"skillNo":217,"name":218,"nameZh":6,"description":219,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":220,"categories":221,"keywords":227,"publishedAt":44},"01M6JQEFT7VT3FQM16170EG9FW","test-suite-design","Design comprehensive test suites for existing code — strategy across unit\u002Fintegration\u002Fe2e, fixtures, factories, helpers. Triggers: add tests, write tests for, increase coverage, test this module, testing strategy, test plan, what tests do I need, test infrastructure, test helpers, this has no tests. Not for TDD — use tdd-workflow.","https:\u002F\u002Fgithub.com\u002FSWEStash\u002Fswe-workflow-skills\u002Fblob\u002Fmain\u002Fskills\u002Ftest-suite-design\u002FSKILL.md",[222,223,224,225,226],{"categoryNo":89,"categoryName":90,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":160,"categoryName":161,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":194,"categoryName":195,"taxonomyType":98,"taxonomyTypeDesc":99},{"categoryNo":164,"categoryName":165,"taxonomyType":98,"taxonomyTypeDesc":99},[167,228,229,230,231,232,105,233,234,43,206,235,236,107,237,238,143,239,240,115],"test suite","test","test suite design","coverage","unit","write tests","testing strategy","across","plan","add","infrastructure","test plan","for"]