[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6HYNJM43F36HQA6TYPS2FBJ":3,"skill-same:01M6HYNJM43F36HQA6TYPS2FBJ":98},{"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":31,"linkOnly":32,"markdownContent":33,"markdownBody":34,"frontMatter":35,"refName":41,"commitSha":42,"skillDirectory":43,"forksCount":44,"repositoryHtmlUrl":39,"repositoryHomepageUrl":45,"ownerHtmlUrl":46,"ownerBlogUrl":47,"ownerDescription":6,"updatedAt":48,"install":49,"resources":61},"01M6HYNJM43F36HQA6TYPS2FBJ","harness-ci",null,"Load to wire, tune, or debug a repo's changed-file CI skip.","vanillagreencom\u002Fkendex","vanillagreencom","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F960140?v=4","01M6HYKK40E293HJHZHAXBHRP3","vanillagreen","01M6HYKK8C03G1JWBP1S14HHFN",93,"MIT","https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30],"harness","harness ci","ci","repo","issues","true","load","debug","file","github","https","tune","2026-11-10 10:12:30",false,"---\nname: harness-ci\ndescription: \"Load to wire, tune, or debug a repo's changed-file CI skip.\"\nlicense: MIT\nuser-invocable: true\nmetadata:\n  author: \"vanillagreen\"\n  source: \"kendex\"\n  repository: \"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\"\n  bugs: \"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fissues\"\n---\n\n\u003C!-- kendex:project-instructions:start -->\n## Project Instructions\n\n\u003C!-- kendex:shared-instructions:start -->\nProblems with a kendex-owned skill go through `kendex report`; check ownership in the file first.\n\u003C!-- kendex:shared-instructions:end -->\n\u003C!-- kendex:project-instructions:end -->\n\n# Harness CI\n\nRun the classifier to decide whether CI can skip product checks. Commit `.kendex-generated.json` with the renders after `kendex refresh`. The engine writes that inventory from rendered artifacts. In-place content and carrier package source remain outside it.\n\n```bash\n.agents\u002Fskills\u002Fharness-ci\u002Fscripts\u002Fharness-only \\\n  --event pull_request --base \"$BASE\" --head \"$HEAD\"\n```\n\nThe endpoints: [references\u002Fwiring.md § The endpoint expressions](references\u002Fwiring.md#the-endpoint-expressions). Flags and exit codes: `harness-only --help`. Consumer setup and workflow shapes to copy: [references\u002Fwiring.md](references\u002Fwiring.md).\n\nUse `--mode render-candidate` only to gate the engine installation and mirror refresh. It prints `render_candidate=true|false` and grants no CI skip. It permits new head-owned paths and the adopted caller under the [caller ownership contract](#shared-refresh-caller-ownership). `change-class` must prove render before CI skips product checks.\n\nUse `--mode docs` for the docs-only path set that `harness-only --help` defines. It prints `docs_only=true|false`.\n\nDeclare exact product-text Markdown files through the trusted base's `HARNESS_CI_DOCS_PATHS`. Use the existing lane outputs so every lane claiming declared text still runs. Do not use a blanket docs or trivial waiver for declared text. Eligibility, refusal and ownership rules: [references\u002Fwiring.md § Declared product text](references\u002Fwiring.md#declared-product-text).\n\nUse `change-class --output FILE` for local lane selection. It appends `lanes` and `lanes_cause` beside `change_class` and `queue_only`. Pass `--lanes-from DIR` with a separate trusted checkout to read its lane declaration. The default declares no lanes. Pass `--queue-only-default true` to answer as the CI template's classify step does. The action and command use `scripts\u002Flib\u002Fchange-class.sh` for the lane rules in [references\u002Fwiring.md § Per-lane verdicts](references\u002Fwiring.md#per-lane-verdicts), the queue-only stand-down and the nightly full run included.\n\nA verification refusal carrying the source refresh remedy reads `cause=source-mirror-missing verb=source operation=refresh`. The caller refreshes its sources before retrying the proof.\n\nThe render proof passes the trusted sibling `bot-instructions` package to `kendex verify --bot-instructions-from` only when `verify --help` advertises the option. Otherwise it uses the legacy verify call and ownership rules. Its checker compares bot output files through `--spec`, which reads the judged package's doctrine as data. It reports whole files only. Before applying the standard-class path exclusion, the classifier can use this proof for outputs outside the engine inventory only with a supporting verifier. A failed optional proof keeps that exclusion; a failed required render proof keeps its existing failure result.\n\n`scripts\u002Fchange-class` answers the wider question every gate, workflow and lane asks: what kind of change is this diff. It prints one of `render`, `trivial`, `micro`, `small` and `standard`, takes the same event and endpoint flags, and hands every read of a diff range to `harness-only` or to orch's branch measurement rather than deriving one, the revision the base end of the range resolves to included. `render` is proved by re-rendering: `kendex verify --json` has to report files checked and none failed. The proof passes `--at-record` beside `--base`, so each package that follows its source renders at the commit the install record names, held to its source's history and to no older than the base's record, and a render the catalog has moved past since its push is still weighed and prints one `render-stale:` line per source commit it trails; and each surviving changed path needs ownership from a passing verifier position or the fallback in the [caller ownership contract](#shared-refresh-caller-ownership). A verifier position owns a file, a tree, or keys in a shared file whose rest kendex reports unchanged since the range's base. Neither the inventory nor the install record is taken as proof of its own provenance, because the branch can rewrite both; each is owned from the record `kendex verify` prints for it, which passes only where the file is as kendex writes it. Every other surviving path answers `standard` with `cause=render-path-unowned`, and a shared file kendex cannot vouch for answers `cause=render-path-partial`. Both ownership refusals carry `measured=true`: missing ownership evidence, including `foreign=unknown`, selects standard with full checks. A required classifier read failure or a failed verifier result stays unmeasured. Consumer refresh publishes a successful `standard measured=false` reading through standard review and product CI. A failed classifier call or an absent class line stops publication. An unmeasured render permits no check skip. Deletion requirements and refusals: `change-class --help`. A changed kendex configuration file refuses every narrow class, not only `render`, except the [manifest schema step](#manifest-schema-steps), a settings change to keys the base admits, those their declaring packages mark `# ci: unread` and those its `HARNESS_CI_TRIVIAL_SETTINGS` adds, which takes `trivial` alone, and a consumer's `kendex.toml` changed alone, where neither end holds an `is_source_catalog` key and the base commit's installed `bot-instructions` passes `check` at both ends, which takes `small` at most and a `light-ci: kind=manifest` record that stands down the lanes no default-branch claim reaches; `change-class --help` states both admissions. The paths `trivial`, `micro` and `small` all refuse are orch's [narrow-change.conf](..\u002Forch\u002Freferences\u002Fnarrow-change.conf), whose ceilings are the last two classes' alone and which [micro.md](..\u002Forch\u002Fworkflows\u002Fmicro.md) § Escape condition 3 states in prose. That list names the files a package's risk sits in, never the package whole. In the size path, a names-only inventory change that unlists only paths the diff deletes leaves the path set the list and the subsystem rule read, under the conditions in `change-class --help`. An agent instruction file, an `instruction` line of that list (`AGENTS.md` and `SKILL.md`), that earns `trivial` or `micro` answers `small`, `cause=instruction-file`, the lowest class the default review policy gives a bot round; `item-tier` holds a Location naming one to `small` from the same lines. Every verdict also prints a `queue-only: queue_only=true|false` line on stderr, off the `queue` lines of the same list, the repository's own `HARNESS_CI_QUEUE_PATHS`, the lanes its default branch's `.github\u002Fci-lanes.conf` marks `:queue`, which the change-class action defers off a pull request to the merge group, and the jobs its `HARNESS_CI_QUEUE_SELECTOR` command names for the merge group; `change-class --help` states when it reads `true`. `github.sh pr-merge` reads that line at the base branch's tip before it takes the admin route, and takes the queue without it where the pull request's recorded base is not that tip: [references\u002Fwiring.md § The endpoint expressions](references\u002Fwiring.md#the-endpoint-expressions). Flags and settings: `change-class --help`.\n\nRequired-context aggregators call `scripts\u002Faggregate-needs`. Pass the full `toJSON(needs)` object, the classifier job name, and each job a verdict may skip: `--skippable` beside the one `--waiver`, `--lane JOB=LANE`, whose skip the lane's own verdict in the classifier job's outputs authorizes, or both. The helper rejects a failed classifier, a failed or cancelled job, and a skipped job no verdict stood down. A workflow with several lanes declares the paths each reads in its default branch's `.github\u002Fci-lanes.conf`, and the change-class action answers one verdict per lane: [references\u002Fwiring.md § Per-lane verdicts](references\u002Fwiring.md#per-lane-verdicts).\n\n## Manifest schema steps\n\nIn harness and render-candidate modes, `harness-only` removes `kendex.toml` only when its bytes change by an exact canonical top-level `schema = N` to `schema = N+1` line replacement. It preserves every other byte. Docs mode keeps the path. A hand edit counts the same as the engine's edit. A schema-only step answers `true` in both non-docs modes and reaches head verification before it can earn `render measured=true`. Its queue result remains `queue_only=true cause=no-changed-paths`. An original empty diff still answers `false`. With product paths beside the step, the remaining paths use the existing classifier rules, including size classes without render verification. An engine that cannot read the head schema refuses the render proof.\n\n## Shared refresh caller ownership\n\n`scripts\u002Flib\u002Fchange-class.sh` declares `HARNESS_CI_REFRESH_CALLER`, the adopted caller path shared by candidacy and ownership checks. Render candidacy permits that path without an inventory entry so CI can install the verifier. It grants no class. `scripts\u002Fchange-class::render_paths_covered` first accepts a recorded adopted caller owned by a passing verifier position. Without verifier ownership, it uses `released_workflow_owned` as a fallback. The fallback fetches `refs\u002Ftags\u002F\u003Cpin>` from the public catalog into its private store. It runs that release's `refresh\u002Flib\u002Fcaller.py` with `python3 -I` to validate the caller's configuration and compare the normalized caller with the release template. Only configuration blocks the release's adopter accepts may differ. A commit pin, any other byte difference, invalid configuration, unavailable release, missing caller module or missing Python grants no fallback ownership. The fallback covers only the caller. Other excluded paths still require a supporting verifier.\n\n## This package never edits a workflow\n\nNothing here writes `.github\u002F`. Wire the one step yourself, once, from [references\u002Fwiring.md](references\u002Fwiring.md). A repository under the organization standard reports the aggregate `CI` context: it copies [templates\u002Fci.yml](templates\u002Fci.yml) to `.github\u002Fworkflows\u002Fci.yml`, or names its own workflow's aggregate job `CI`, per [references\u002Fwiring.md § The CI context](references\u002Fwiring.md#the-ci-context).\n\n## The rules to hold when wiring it\n\n**Classify inside a job, never in `on.\u003Cevent>.paths`.** A path filter stops the workflow from starting, the required context is never created, and a merge queue waits forever on a check nothing will report.\n\n**Keep the required-context job unconditional.** Gate the expensive lanes with a job-level `if:` off a `changes` job's output, or a step-level `if:` inside an aggregate, and let the aggregate that carries the required name run on every event.\n\n**A job-level `if:` needs a status function.** Without one it keeps the implicit `success()` and skips the lane whenever the classifying job failed, which stands the expensive lanes down on exactly the diffs nothing classified. An aggregate accepts a `skipped` lane only after checking that the classifier ran and cleared the diff.\n\n**A lane reading a path family beside the verdict needs more than the status function.** A dead classifying job publishes no outputs, so the family term reads empty and skips the lane on its own. Lift it behind `needs.changes.result != 'success'`, the two-gate shape in [references\u002Fwiring.md](references\u002Fwiring.md).\n\n**A step that installs a tool for an unconditional lane stays unconditional.** A harness-only `if:` on the install, while the lane that runs the tool runs on every event, fails that lane on a harness-only diff. The tools commit-guards needs are in [commit-guards CHECKS.md § py-names](..\u002Fcommit-guards\u002FCHECKS.md#py-names) and [§ secrets](..\u002Fcommit-guards\u002FCHECKS.md#secrets).\n\n## Reading a verdict\n\n`stdout` is the selected verdict line alone; changed paths and reasons go to `stderr`; exit `2` is a wiring error that prints no verdict.\n\n## Fail-closed\n\nEvery unprovable case answers `false`, which runs every lane ([DEVELOPMENT.md § Invariants](https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002Fskills\u002Fharness-ci\u002FDEVELOPMENT.md#invariants)). `--no-renames` is fixed. `change-class` answers `standard` on the same terms.\n\n**A class is never read from an author-writable field.** Not a label, not a branch name, not a pull request title, and no flag carries one: the author of the diff being judged writes all of them, so trusting one fails open on exactly the diffs that most want to pass. A caller that acts on a verdict without review runs the DEFAULT BRANCH's copy of the script against the pull request's tree, because the branch can change the script too.\n\n**Nothing out of the judged tree runs, and nothing in it is read as configuration.** `change-class` takes its measurement settings from its own process environment, else from the `[env]` tables of `kendex.settings.toml` and `.kendex\u002Fsettings.toml`, the second winning, as the commit `--base` names holds them (the pull request's base end [§ The endpoint expressions](references\u002Fwiring.md#the-endpoint-expressions) names or a merge group's base, never the merge base), parsed and never sourced; the judged tree's settings and the private env file never reach it. It runs `kendex verify` in a private checkout with a git directory of its own, which carries no kendex arming record, so no package's declared checker runs out of the tree under judgement.\n\n**The judged checkout is read, never written, and never weighed.** The `render` proof weighs the commit `--head` resolves to, checked out privately, whatever the judged checkout's own HEAD is and whatever uncommitted content it holds.\n","\u003C!-- kendex:project-instructions:start -->\n## Project Instructions\n\n\u003C!-- kendex:shared-instructions:start -->\nProblems with a kendex-owned skill go through `kendex report`; check ownership in the file first.\n\u003C!-- kendex:shared-instructions:end -->\n\u003C!-- kendex:project-instructions:end -->\n\n# Harness CI\n\nRun the classifier to decide whether CI can skip product checks. Commit `.kendex-generated.json` with the renders after `kendex refresh`. The engine writes that inventory from rendered artifacts. In-place content and carrier package source remain outside it.\n\n```bash\n.agents\u002Fskills\u002Fharness-ci\u002Fscripts\u002Fharness-only \\\n  --event pull_request --base \"$BASE\" --head \"$HEAD\"\n```\n\nThe endpoints: [references\u002Fwiring.md § The endpoint expressions](references\u002Fwiring.md#the-endpoint-expressions). Flags and exit codes: `harness-only --help`. Consumer setup and workflow shapes to copy: [references\u002Fwiring.md](references\u002Fwiring.md).\n\nUse `--mode render-candidate` only to gate the engine installation and mirror refresh. It prints `render_candidate=true|false` and grants no CI skip. It permits new head-owned paths and the adopted caller under the [caller ownership contract](#shared-refresh-caller-ownership). `change-class` must prove render before CI skips product checks.\n\nUse `--mode docs` for the docs-only path set that `harness-only --help` defines. It prints `docs_only=true|false`.\n\nDeclare exact product-text Markdown files through the trusted base's `HARNESS_CI_DOCS_PATHS`. Use the existing lane outputs so every lane claiming declared text still runs. Do not use a blanket docs or trivial waiver for declared text. Eligibility, refusal and ownership rules: [references\u002Fwiring.md § Declared product text](references\u002Fwiring.md#declared-product-text).\n\nUse `change-class --output FILE` for local lane selection. It appends `lanes` and `lanes_cause` beside `change_class` and `queue_only`. Pass `--lanes-from DIR` with a separate trusted checkout to read its lane declaration. The default declares no lanes. Pass `--queue-only-default true` to answer as the CI template's classify step does. The action and command use `scripts\u002Flib\u002Fchange-class.sh` for the lane rules in [references\u002Fwiring.md § Per-lane verdicts](references\u002Fwiring.md#per-lane-verdicts), the queue-only stand-down and the nightly full run included.\n\nA verification refusal carrying the source refresh remedy reads `cause=source-mirror-missing verb=source operation=refresh`. The caller refreshes its sources before retrying the proof.\n\nThe render proof passes the trusted sibling `bot-instructions` package to `kendex verify --bot-instructions-from` only when `verify --help` advertises the option. Otherwise it uses the legacy verify call and ownership rules. Its checker compares bot output files through `--spec`, which reads the judged package's doctrine as data. It reports whole files only. Before applying the standard-class path exclusion, the classifier can use this proof for outputs outside the engine inventory only with a supporting verifier. A failed optional proof keeps that exclusion; a failed required render proof keeps its existing failure result.\n\n`scripts\u002Fchange-class` answers the wider question every gate, workflow and lane asks: what kind of change is this diff. It prints one of `render`, `trivial`, `micro`, `small` and `standard`, takes the same event and endpoint flags, and hands every read of a diff range to `harness-only` or to orch's branch measurement rather than deriving one, the revision the base end of the range resolves to included. `render` is proved by re-rendering: `kendex verify --json` has to report files checked and none failed. The proof passes `--at-record` beside `--base`, so each package that follows its source renders at the commit the install record names, held to its source's history and to no older than the base's record, and a render the catalog has moved past since its push is still weighed and prints one `render-stale:` line per source commit it trails; and each surviving changed path needs ownership from a passing verifier position or the fallback in the [caller ownership contract](#shared-refresh-caller-ownership). A verifier position owns a file, a tree, or keys in a shared file whose rest kendex reports unchanged since the range's base. Neither the inventory nor the install record is taken as proof of its own provenance, because the branch can rewrite both; each is owned from the record `kendex verify` prints for it, which passes only where the file is as kendex writes it. Every other surviving path answers `standard` with `cause=render-path-unowned`, and a shared file kendex cannot vouch for answers `cause=render-path-partial`. Both ownership refusals carry `measured=true`: missing ownership evidence, including `foreign=unknown`, selects standard with full checks. A required classifier read failure or a failed verifier result stays unmeasured. Consumer refresh publishes a successful `standard measured=false` reading through standard review and product CI. A failed classifier call or an absent class line stops publication. An unmeasured render permits no check skip. Deletion requirements and refusals: `change-class --help`. A changed kendex configuration file refuses every narrow class, not only `render`, except the [manifest schema step](#manifest-schema-steps), a settings change to keys the base admits, those their declaring packages mark `# ci: unread` and those its `HARNESS_CI_TRIVIAL_SETTINGS` adds, which takes `trivial` alone, and a consumer's `kendex.toml` changed alone, where neither end holds an `is_source_catalog` key and the base commit's installed `bot-instructions` passes `check` at both ends, which takes `small` at most and a `light-ci: kind=manifest` record that stands down the lanes no default-branch claim reaches; `change-class --help` states both admissions. The paths `trivial`, `micro` and `small` all refuse are orch's [narrow-change.conf](..\u002Forch\u002Freferences\u002Fnarrow-change.conf), whose ceilings are the last two classes' alone and which [micro.md](..\u002Forch\u002Fworkflows\u002Fmicro.md) § Escape condition 3 states in prose. That list names the files a package's risk sits in, never the package whole. In the size path, a names-only inventory change that unlists only paths the diff deletes leaves the path set the list and the subsystem rule read, under the conditions in `change-class --help`. An agent instruction file, an `instruction` line of that list (`AGENTS.md` and `SKILL.md`), that earns `trivial` or `micro` answers `small`, `cause=instruction-file`, the lowest class the default review policy gives a bot round; `item-tier` holds a Location naming one to `small` from the same lines. Every verdict also prints a `queue-only: queue_only=true|false` line on stderr, off the `queue` lines of the same list, the repository's own `HARNESS_CI_QUEUE_PATHS`, the lanes its default branch's `.github\u002Fci-lanes.conf` marks `:queue`, which the change-class action defers off a pull request to the merge group, and the jobs its `HARNESS_CI_QUEUE_SELECTOR` command names for the merge group; `change-class --help` states when it reads `true`. `github.sh pr-merge` reads that line at the base branch's tip before it takes the admin route, and takes the queue without it where the pull request's recorded base is not that tip: [references\u002Fwiring.md § The endpoint expressions](references\u002Fwiring.md#the-endpoint-expressions). Flags and settings: `change-class --help`.\n\nRequired-context aggregators call `scripts\u002Faggregate-needs`. Pass the full `toJSON(needs)` object, the classifier job name, and each job a verdict may skip: `--skippable` beside the one `--waiver`, `--lane JOB=LANE`, whose skip the lane's own verdict in the classifier job's outputs authorizes, or both. The helper rejects a failed classifier, a failed or cancelled job, and a skipped job no verdict stood down. A workflow with several lanes declares the paths each reads in its default branch's `.github\u002Fci-lanes.conf`, and the change-class action answers one verdict per lane: [references\u002Fwiring.md § Per-lane verdicts](references\u002Fwiring.md#per-lane-verdicts).\n\n## Manifest schema steps\n\nIn harness and render-candidate modes, `harness-only` removes `kendex.toml` only when its bytes change by an exact canonical top-level `schema = N` to `schema = N+1` line replacement. It preserves every other byte. Docs mode keeps the path. A hand edit counts the same as the engine's edit. A schema-only step answers `true` in both non-docs modes and reaches head verification before it can earn `render measured=true`. Its queue result remains `queue_only=true cause=no-changed-paths`. An original empty diff still answers `false`. With product paths beside the step, the remaining paths use the existing classifier rules, including size classes without render verification. An engine that cannot read the head schema refuses the render proof.\n\n## Shared refresh caller ownership\n\n`scripts\u002Flib\u002Fchange-class.sh` declares `HARNESS_CI_REFRESH_CALLER`, the adopted caller path shared by candidacy and ownership checks. Render candidacy permits that path without an inventory entry so CI can install the verifier. It grants no class. `scripts\u002Fchange-class::render_paths_covered` first accepts a recorded adopted caller owned by a passing verifier position. Without verifier ownership, it uses `released_workflow_owned` as a fallback. The fallback fetches `refs\u002Ftags\u002F\u003Cpin>` from the public catalog into its private store. It runs that release's `refresh\u002Flib\u002Fcaller.py` with `python3 -I` to validate the caller's configuration and compare the normalized caller with the release template. Only configuration blocks the release's adopter accepts may differ. A commit pin, any other byte difference, invalid configuration, unavailable release, missing caller module or missing Python grants no fallback ownership. The fallback covers only the caller. Other excluded paths still require a supporting verifier.\n\n## This package never edits a workflow\n\nNothing here writes `.github\u002F`. Wire the one step yourself, once, from [references\u002Fwiring.md](references\u002Fwiring.md). A repository under the organization standard reports the aggregate `CI` context: it copies [templates\u002Fci.yml](templates\u002Fci.yml) to `.github\u002Fworkflows\u002Fci.yml`, or names its own workflow's aggregate job `CI`, per [references\u002Fwiring.md § The CI context](references\u002Fwiring.md#the-ci-context).\n\n## The rules to hold when wiring it\n\n**Classify inside a job, never in `on.\u003Cevent>.paths`.** A path filter stops the workflow from starting, the required context is never created, and a merge queue waits forever on a check nothing will report.\n\n**Keep the required-context job unconditional.** Gate the expensive lanes with a job-level `if:` off a `changes` job's output, or a step-level `if:` inside an aggregate, and let the aggregate that carries the required name run on every event.\n\n**A job-level `if:` needs a status function.** Without one it keeps the implicit `success()` and skips the lane whenever the classifying job failed, which stands the expensive lanes down on exactly the diffs nothing classified. An aggregate accepts a `skipped` lane only after checking that the classifier ran and cleared the diff.\n\n**A lane reading a path family beside the verdict needs more than the status function.** A dead classifying job publishes no outputs, so the family term reads empty and skips the lane on its own. Lift it behind `needs.changes.result != 'success'`, the two-gate shape in [references\u002Fwiring.md](references\u002Fwiring.md).\n\n**A step that installs a tool for an unconditional lane stays unconditional.** A harness-only `if:` on the install, while the lane that runs the tool runs on every event, fails that lane on a harness-only diff. The tools commit-guards needs are in [commit-guards CHECKS.md § py-names](..\u002Fcommit-guards\u002FCHECKS.md#py-names) and [§ secrets](..\u002Fcommit-guards\u002FCHECKS.md#secrets).\n\n## Reading a verdict\n\n`stdout` is the selected verdict line alone; changed paths and reasons go to `stderr`; exit `2` is a wiring error that prints no verdict.\n\n## Fail-closed\n\nEvery unprovable case answers `false`, which runs every lane ([DEVELOPMENT.md § Invariants](https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002Fskills\u002Fharness-ci\u002FDEVELOPMENT.md#invariants)). `--no-renames` is fixed. `change-class` answers `standard` on the same terms.\n\n**A class is never read from an author-writable field.** Not a label, not a branch name, not a pull request title, and no flag carries one: the author of the diff being judged writes all of them, so trusting one fails open on exactly the diffs that most want to pass. A caller that acts on a verdict without review runs the DEFAULT BRANCH's copy of the script against the pull request's tree, because the branch can change the script too.\n\n**Nothing out of the judged tree runs, and nothing in it is read as configuration.** `change-class` takes its measurement settings from its own process environment, else from the `[env]` tables of `kendex.settings.toml` and `.kendex\u002Fsettings.toml`, the second winning, as the commit `--base` names holds them (the pull request's base end [§ The endpoint expressions](references\u002Fwiring.md#the-endpoint-expressions) names or a merge group's base, never the merge base), parsed and never sourced; the judged tree's settings and the private env file never reach it. It runs `kendex verify` in a private checkout with a git directory of its own, which carries no kendex arming record, so no package's declared checker runs out of the tree under judgement.\n\n**The judged checkout is read, never written, and never weighed.** The `render` proof weighs the commit `--head` resolves to, checked out privately, whatever the judged checkout's own HEAD is and whatever uncommitted content it holds.\n",{"name":5,"license":15,"metadata":36,"description":7,"user-invocable":40},{"bugs":37,"author":12,"source":38,"repository":39},"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fissues","kendex","https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex",true,"main","4eba3596d2a59b43c50a18763e49bb15c56b7b3f",".agents\u002Fskills\u002Fharness-ci",37,"https:\u002F\u002Fkendex.ai","https:\u002F\u002Fgithub.com\u002Fvanillagreencom","http:\u002F\u002Fvanillagreen.com","2026-11-13 03:56:19",{"source":8,"skillName":5,"commands":50,"prompt":60,"zipAvailable":40},[51,54,57],{"tool":52,"command":53},"npx","npx skills add vanillagreencom\u002Fkendex --skill harness-ci",{"tool":55,"command":56},"bunx","bunx skills add vanillagreencom\u002Fkendex --skill harness-ci",{"tool":58,"command":59},"pnpm","pnpm dlx skills add vanillagreencom\u002Fkendex --skill harness-ci","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002FSKILL.md",[62,68,72,78,82,86,90,94],{"path":63,"resourceType":64,"resourceTypeDesc":65,"sizeBytes":66,"sourceHtmlUrl":67},"README.md","REFERENCE","参考文档",4855,"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002FREADME.md",{"path":69,"resourceType":64,"resourceTypeDesc":65,"sizeBytes":70,"sourceHtmlUrl":71},"references\u002Fwiring.md",65086,"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002Freferences\u002Fwiring.md",{"path":73,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":76,"sourceHtmlUrl":77},"scripts\u002Faggregate-needs","SCRIPT","脚本",5923,"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002Fscripts\u002Faggregate-needs",{"path":79,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":80,"sourceHtmlUrl":81},"scripts\u002Fchange-class",99176,"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002Fscripts\u002Fchange-class",{"path":83,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":84,"sourceHtmlUrl":85},"scripts\u002Fharness-only",29969,"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002Fscripts\u002Fharness-only",{"path":87,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":88,"sourceHtmlUrl":89},"scripts\u002Flib\u002Fchange-class.sh",28501,"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002Fscripts\u002Flib\u002Fchange-class.sh",{"path":91,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":92,"sourceHtmlUrl":93},"scripts\u002Flib\u002Fci-lanes.sh",3695,"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002Fscripts\u002Flib\u002Fci-lanes.sh",{"path":95,"resourceType":64,"resourceTypeDesc":65,"sizeBytes":96,"sourceHtmlUrl":97},"templates\u002Fci.yml",12210,"https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fharness-ci\u002Ftemplates\u002Fci.yml",[99,122,153,176,216,243],{"skillNo":100,"name":101,"nameZh":6,"description":102,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":105,"publishedAt":31},"01M6HZ0GYHMRX8JCH9RPPAY59N","youtube-publish","Load to draft a YouTube upload, comment or reply, or send a draft after the user's recorded approval.","https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-publish\u002FSKILL.md",[],[106,107,108,109,23,24,110,25,111,112,113,114,115,116,28,117,29,118,119,120,121],"youtube publish","publish","youtube","youtube upload","publishing","upload","api","synthetic","check","draft","with","integration","data","youtube data","content","send",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":133,"publishedAt":31},"01M6HZ0CT05TA9746ZG5Q4K1E0","xcode-run","Load to install or dispatch a macOS GitHub Actions build, test or simulator screenshot run for an Xcode project.","https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002Fskills\u002Fxcode-run\u002FSKILL.md",[128],{"categoryNo":129,"categoryName":130,"taxonomyType":131,"taxonomyTypeDesc":132},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[134,135,136,137,138,139,140,23,141,142,143,144,145,146,147,148,149,150,151,152],"xcode","run","xcode run","SET","project","actions","testing","github actions","workflows","workflow","logs","delete","artifact","scripts","repository","install","screenshot","macos","screenshots",{"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":159,"publishedAt":31},"01M6HZ060RP0FKH75JKN214909","worktree","Load to create, list, remove, push, or repair a git worktree.","https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002Fskills\u002Fworktree\u002FSKILL.md",[],[155,160,161,23,162,163,164,165,166,167,168,169,170,171,172,24,173,174,25,175],"branch","remove","create","git worktree","config","git","list","path","push","repair","conflict","pr","continue","reuse","replay","keep",{"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":180,"sourceHtmlUrl":181,"categories":182,"keywords":197,"publishedAt":31},"01M6HZ01AB8P28NTWWMHDMTM0A","social-media-content-calendar","Load to plan a social media campaign, build a content calendar, or adapt existing material into post drafts.","https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002Fskills\u002Fsocial-media-content-calendar\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002Fskills\u002Fsocial-media-content-calendar\u002FSKILL.md",[183,186,189,194],{"categoryNo":184,"categoryName":185,"taxonomyType":131,"taxonomyTypeDesc":132},"01JBSEEDCAT000000000000019","营销推广",{"categoryNo":187,"categoryName":188,"taxonomyType":131,"taxonomyTypeDesc":132},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":190,"categoryName":191,"taxonomyType":192,"taxonomyTypeDesc":193},"01JBSEEDCAT000000000000035","市场营销","PROFESSION","适用职业",{"categoryNo":195,"categoryName":196,"taxonomyType":192,"taxonomyTypeDesc":193},"01JBSEEDCAT000000000000036","内容创作者",[198,199,200,201,202,203,120,204,23,205,206,207,208,209,210,211,212,213,24,214,215],"Media","social","calendar","content calendar","social media content calendar","social media","adaptation","campaign","plan","drafts","asset","briefs","platform","planning","for","adapt","material","approval gate",{"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":225,"publishedAt":31},"01M6HYZS25D7RJK8CZ93MCZ3V8","slack","Load to bind an overseer's mailbox to a private Slack channel, run or check the relay, or post an alert or a file to Slack.","https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002Fskills\u002Fslack\u002FSKILL.md",[222],{"categoryNo":223,"categoryName":224,"taxonomyType":131,"taxonomyTypeDesc":132},"01JBSEEDCAT000000000000005","邮件与沟通",[218,226,227,228,229,230,231,232,233,23,24,25,234,235,236,237,238,135,149,239,240,27,114,241,242],"bind","back","checkout","lane","relay","messages","socket","mode","compact","alert","orchestration","mail","communication","linear","receiver","store","post",{"skillNo":244,"name":245,"nameZh":6,"description":246,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":247,"categories":248,"keywords":249,"publishedAt":31},"01M6HYZHPANA68C9WQQ11A1B5Z","second-opinion","Load for a cross-model review, challenge, audit, or quick consult.","https:\u002F\u002Fgithub.com\u002Fvanillagreencom\u002Fkendex\u002Fblob\u002Fmain\u002Fskills\u002Fsecond-opinion\u002FSKILL.md",[],[250,251,252,253,254,255,212,256,23,257,258,24,259,25,260,261,262,263,28,264,29],"second opinion","second","quick","ai cli","scope","model","pi","copilot","challenge","question","consult","review","codex","cli","audit"]