ci-score
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseci-score
ci-score
CI Score gauges best-practice adherence on CI speed and gate hygiene: a
straightforward pass/fail rubric of best-practice checks computed from a
repo's workflow configuration. Eleven configuration facts, each
self-verifiable in the repo's own YAML in under a minute; score = checks
passed / applicable; one refusal (no workflows to check). Registry:
references/ci-score-spec.json (frozen
v0.1.6); rubric write-up:
references/ci-score-methodology.md.
The score measures adherence, not speed — measured, faster repos can
hold lower scores. The report says this beside the card; never let a user
read the score as a speed verdict. Nor is it a security audit: the
rubric grades best practices, and exactly two of its eleven checks
(action pinning, job-scoped OIDC tokens) happen to be security-related —
it claims nothing further about workflow security.
CI Score 衡量 CI 速度和门禁卫生方面的最佳实践遵循度:使用一套直接了当的通过/失败最佳实践检查规则,根据仓库的 workflow 配置计算结果。 共 11 项配置事实,每项都可在仓库自身的 YAML 中于一分钟内自行验证;分数 = 通过的检查数 / 适用检查数;存在一种拒绝情况(没有可检查的 workflow)。相关规范:references/ci-score-spec.json(冻结版本 v0.1.6);评分标准说明:references/ci-score-methodology.md。
该分数衡量的是遵循度,而非速度——经实际测量,速度更快的仓库得分反而可能更低。报告会在评分卡旁说明这一点;绝不能让用户将分数理解为速度结论。它也不是安全审计:该规则集用于评估最佳实践,11 项检查中恰好只有两项(action 固定版本、限定到 job 作用域的 OIDC token)与安全相关;除此之外,它不对 workflow 的安全性作任何进一步声明。
Requirements
Requirements
- A full local checkout of the target repo. The score reads three input classes — workflow YAML, local composite actions, and repo-root build-tool configs — so a partial view (an API fetch of workflows alone) silently inflates scores. The collector refuses outside a git checkout. A full clone you make yourself is a valid path (it carries the full input surface — the ban is on partial fetches, never on cloning): ask before cloning (network + disk), and a score of a repo the user does not own stays for their eyes — never published or shared.
- PyYAML (). The scripts exit loudly with that hint when it is missing.
pip install pyyaml - No network access is needed or used; everything reads the local tree.
- 目标仓库的完整本地 checkout。 评分需要读取三类输入——workflow YAML、本地 composite action,以及仓库根目录中的构建工具配置——因此不完整的视图(例如仅通过 API 获取 workflow)会悄然抬高分数。收集器拒绝处理 git checkout 之外的目标。自行执行完整 clone 是有效路径(它包含完整的输入范围——禁止的是部分 fetch,绝非 clone):克隆前须征得同意(会使用网络和磁盘);对于用户并不拥有的仓库,评分结果仅供其本人查看——绝不发布或分享。
- PyYAML()。缺少该依赖时,脚本会明确退出并提示安装方式。
pip install pyyaml - 不需要也不会使用网络访问;所有内容均从本地工作树读取。
Flow
Flow
All paths below are relative to this skill's directory.
Interaction contract (steps 0 and 4). Both user-facing questions are a
single structured question — one question, fixed-order options (always
≥2 options unless a step below drops one by name), nothing open-ended, no machinery narration — via your platform's
structured-question tool where one exists: on Claude
Code; on Codex, its built-in user-input request tool (
/ , experimental — call it when exposed, never
predict it absent (a live ci-score Codex run skipped it on that prediction), and ALSO end your
plain message with the same question as a numbered option list, same options,
same order, sent before the call: an experimental tool can silently fail to render
or never return — live Codex run 2026-10-29). Only when the tool is genuinely absent or the call fails,
that plain message alone asks it — same options, same order, the close's save option still last
and verbatim (), the target confirm still
one keystroke ("Reply y to score <owner/repo>, or name a different repo/path").
Never say you are waiting on a choice unless its options are visible in your own last message. Only the delivery mechanism varies; the contract is
agent-independent. This whole contract mirrors ci-speedup's interaction
contract — same tools, same fallback shape, same verbatim save option —
keep the two parallel when either changes.
AskUserQuestionrequest_user_inputtool/requestUserInputNone, just save the report (.md)- Confirm the target before scoring. Default is the repo you are
standing in: is the checkout root, and the origin remote (when GitHub) names it
git rev-parse --show-toplevel. Ask ONE structured question per the interaction contract — ≥ 2 options: one confirms the detected<owner/repo>(or path when there is no GitHub remote), one is "a different repo or path" (its pick or Other supplies the target). When the working directory is not a git checkout (<owner/repo>fails) there is no local default to confirm — deliver the question as the plain-message ask or an Other-only prompt (the ≥ 2-option confirm/different shape needs a detected default) and have the user name the target (git rev-parseor a path). If the user already named a target, re-confirm only if ambiguous (the named target doesn't resolve to a single local checkout or a single clonable<owner/repo>). The confirm is load-bearing even though scoring is local, fast, and free: any applied fix — and the report, once saved — land in the scored repo's root, so a wrong working directory acts on the wrong project. A pick that is another local checkout is scored directly; a pick that is an<owner/repo>not already on disk follows the clone-then-score rule in Requirements (confirm access with<owner/repo>— in sandboxed agent shells (Codex) a keyring credential can be unreachable and gh false-fails: retry with host access before reading a failure as no-access, mirroring ci-speedup's gh gate — then ask before cloning — network + disk). Do not run the collector until the target is settled.gh repo view - Collect + score (one command, writes the findings document):
.
python3 scripts/collect_config.py --repo <checkout-root> --out <workdir>/findings.json= a scratch directory outside the target checkout — the session scratchpad if one exists, else<workdir>; use a per-repo subdirectory (mktemp -d) so a second repo or a re-run never overwrites a prior run's files, and never default to cwd (an untracked findings.json inside the repo makes the next run's provenance<workdir>/<repo-name>/). Exit 0 = scored or an honest refusal in the stamp; exit 2 = collection refusal (not a checkout / no parseable workflows); exit 3 = scoring failed (-dirtyrecords why). Every outcome is stamped in the document — read it, don't guess.data_sources.ci_score_error - Render the report to scratch: — score card, the adherence-not-speed disclosure, then one recommendation per failed check ranked by impact × risk, each with a fix recipe, its best-practices page, and an agent handoff prompt. The rendered file stays in
python3 scripts/render_report.py --findings <workdir>/findings.json --out <workdir>/report.mduntil the user asks for it (the ci-speedup convention, issue #18 there): writing it into the target repo is what the close's<workdir>pick does — copyNone, just save the report (.md)to<workdir>/report.md(the target repo root) and say where it landed in one clause (a generated artifact they can gitignore or delete — never auto-commit it or edit their .gitignore). No other pick writes the report into the working tree. This also keeps the run self-clean: an unsaved report leaves no untracked file behind to flag the NEXT run's provenance./ci-score-report.md. Raw-dirtystays infindings.jsonalways.<workdir> - Verify before presenting: must print
python3 scripts/verify_report.py --findings <workdir>/findings.json --report <workdir>/report.md. If it fails, re-run step 2 and re-verify once (a stale report.md or mismatched findings/report pair is the common cause). If it still fails, the rendered report is unsafe to present: tell the user verification failed (a skill bug, not their repo), give them their result by reading the score line directly from thereport: OKstamp, and withhold the recommendations; show the violation lines only if asked.ci_score - Close per the close/kickoff protocol — read
references/close-contract.md first (the
"Close contract — invariants" section below is the point-of-use summary,
not the whole protocol) — two parts, both in the SAME turn:
(a) the close text (banner → disclosure → recommendations → report
note), sent as an ordinary message and never packed into the question
call, then (b) the kickoff question per the interaction contract above
— a SEPARATE structured-question tool call where one exists
(on Claude Code; for Codex's experimental tool, ALSO print the option list below); where none does (most Codex runs), the question IS the FINAL block of that message: the numbered option list itself, same fixed options in the same order, save option last and verbatim, and the turn ends on it. Writing close text never completes the close — on EITHER platform, only the question does (the tool call, or that final printed option list). Ending the turn after (a) leaves the user with no choices at all (live miss #2, quasar 2026-07-29: the close referenced "the last option below" and then ended — no question ever appeared; the user had to type their pick freehand).
AskUserQuestion
Debug tracing: set (logs counts, file names,
and check states — never file contents).
STARSLING_LOG_LEVEL=DEBUG以下所有路径均相对于此 skill 所在目录。
交互契约(步骤 0 和 4)。 两个面向用户的问题均须作为一个结构化问题提出——一个问题、固定顺序的选项(除非下文某一步明确删去某个选项,否则始终至少 2 个选项)、不得开放式作答、不得叙述工具调用机制——若平台提供结构化提问工具,则通过该工具提出:Claude Code 使用 ;Codex 使用其内置的用户输入请求工具( / ,实验性——工具暴露时必须调用,绝不能臆测其不存在(一次实时 ci-score Codex 运行因作出这种臆测而跳过了调用)),并且还要在普通消息末尾以编号选项列表附上同一个问题,选项及顺序完全相同,并在调用前发送:实验性工具可能静默无法渲染或永不返回——实时 Codex 运行 2026-10-29)。只有当工具确实不存在或调用失败时,才由该普通消息单独提出问题——选项及顺序相同,结束环节的保存选项仍须置于最后且逐字保持(),目标确认仍只需按一次键()。
除非选项已显示在上一条消息中,否则绝不能声称正在等待用户选择。 只有交付方式有所不同;该契约与具体 Agent 无关。整个契约与 ci-speedup 的交互契约保持一致——工具、备用形式和逐字保存选项均相同——任一方发生变化时,两者须同步更新。
AskUserQuestionrequest_user_inputtool/requestUserInputNone, just save the report (.md)Reply **y** to score <owner/repo>, or name a different repo/path- 评分前确认目标。 默认目标是当前所在的仓库:返回 checkout 根目录,origin remote(若为 GitHub)会将其标识为
git rev-parse --show-toplevel。按照交互契约提出一个结构化问题——至少 2 个选项:一个用于确认检测到的<owner/repo>(没有 GitHub remote 时则为路径),另一个是<owner/repo>(选择该项或使用 Other 提供目标)。当前工作目录不是 git checkout(a different repo or path失败)时,没有可供确认的本地默认值——应以普通消息提问或提供仅含 Other 的提示(至少 2 个选项的确认/不同目标形式需要预先检测到默认值),并让用户指定目标(git rev-parse或路径)。如果用户已经指定目标,仅在目标存在歧义时重新确认(指定的目标无法解析为唯一的本地 checkout 或唯一可 clone 的<owner/repo>)。即使评分在本地执行且快速、免费,确认步骤仍至关重要:任何应用的修复以及保存后的报告都会落入被评分仓库的根目录,因此在错误的工作目录中操作会作用于错误的项目。选择另一个本地 checkout 时直接评分;选择尚未存在于本地的<owner/repo>时,遵循 Requirements 中的先 clone 后评分规则——使用<owner/repo>确认访问权限——在沙箱 Agent shell(Codex)中,keyring 凭据可能无法访问,导致 gh 错误地报告失败:应先使用宿主访问重试,再将失败解读为无访问权限,并与 ci-speedup 的 gh gate 保持一致——然后在 clone 前征得同意——会使用网络和磁盘)。目标确定前不得运行收集器。gh repo view - 收集并评分(一条命令,写入 findings 文档):
。
python3 scripts/collect_config.py --repo <checkout-root> --out <workdir>/findings.json是目标 checkout 之外的临时目录——如果会话提供 scratchpad 则使用它,否则使用<workdir>;应使用按仓库划分的子目录(mktemp -d),避免第二个仓库或重新运行时覆盖先前运行的文件,并且绝不能默认使用 cwd(在仓库内生成未跟踪的 findings.json 会导致下次运行的来源状态变为<workdir>/<repo-name>/)。 退出码 0 = 已评分,或文档标记中如实记录了拒绝结果;退出码 2 = 收集阶段拒绝(不是 checkout 或没有可解析的 workflow);退出码 3 = 评分失败(-dirty会记录原因)。每种结果都会标记在文档中——应读取文档,不要猜测。data_sources.ci_score_error - 将报告渲染到临时目录: ——包含评分卡、“衡量遵循度而非速度”的披露说明,随后为每项未通过的检查提供一条建议,并按影响 × 风险排序;每条建议均包含修复方案、对应的最佳实践页面和 Agent 交接提示词。渲染后的文件会一直保留在
python3 scripts/render_report.py --findings <workdir>/findings.json --out <workdir>/report.md中,直至用户要求保存(遵循 ci-speedup 的约定及其 issue #18):只有结束环节的<workdir>选项会将报告写入目标仓库——把None, just save the report (.md)复制到目标仓库根目录下的<workdir>/report.md,并用一句话说明其落盘位置(这是用户可加入 gitignore 或删除的生成物——绝不能自动提交,也绝不能修改其 .gitignore)。其他任何选项都不会将报告写入工作树。 这也能保持运行过程干净:未保存的报告不会留下未跟踪文件,从而影响下次运行的来源状态./ci-score-report.md。原始-dirty始终保留在findings.json中。<workdir> - 展示前验证: 必须打印
python3 scripts/verify_report.py --findings <workdir>/findings.json --report <workdir>/report.md。如果验证失败,重新执行步骤 2 并再次验证一次(最常见原因是 report.md 已过期,或 findings 与 report 不匹配)。如果再次失败,则渲染后的报告不适合展示:告知用户验证失败(这是 skill 的缺陷,而非其仓库的问题),直接从report: OK标记中读取分数行并告知结果,同时暂不提供建议;仅在用户要求时展示违规行。ci_score - 按结束/启动协议收尾——先阅读 references/close-contract.md(下文“Close contract — invariants”部分只是使用时的摘要,并非完整协议)——两部分须在同一轮完成:
(a) 结束文本(横幅 → 披露说明 → 建议 → 报告说明),以普通消息发送,绝不能打包进问题调用;随后 (b) 按照上述交互契约提出启动问题——若平台存在结构化提问工具,则另起一次结构化提问工具调用(Claude Code 使用 ;对于 Codex 的实验性工具,还要打印下述选项列表);若不存在(大多数 Codex 运行均如此),该问题必须作为该消息的最后一个区块:直接使用编号选项列表,保持固定选项及相同顺序,保存选项置于最后且逐字保持,并以该列表结束本轮。写出结束文本绝不表示结束流程已经完成——在任一平台上,只有问题才算完成(无论是工具调用,还是最后打印的选项列表)。如果完成 (a) 后就结束本轮,用户将完全看不到任何选项(实际遗漏 #2,quasar 2026-07-29:结束文本提到“下面的最后一个选项”,随后却直接结束——始终没有出现问题,导致用户不得不手动输入选择)。
AskUserQuestion
调试跟踪:设置 (记录数量、文件名和检查状态——绝不记录文件内容)。
STARSLING_LOG_LEVEL=DEBUGClose contract — invariants
Close contract — invariants
The full close/kickoff protocol lives in
references/close-contract.md. These
non-negotiables stay here, at point of use, because burying them regressed
live runs before — in this skill, text position is behavior. Each carries its
one-line incident anchor; hold them while you follow the protocol:
- Banner: copy it VERBATIM from the collector's stdout — never redraw,
re-pad, or adjust it freehand (a hand-drawn bar mis-counted 29 of 30 blocks
in a live run). A refusal or recorded scoring error prints NO banner and
keeps its plain-sentence close — never a banner with an empty or invented
bar. Read the score straight from the stamp; never fabricate a score to fill a gap.
ci_score - The close = the close text, THEN the question, in the SAME turn. Writing
the close text never completes the close — on either platform, only the
question does. The options exist ONLY in the question: the
structured-question tool where the platform has one (on Claude Code); the final printed option list where it doesn't (most Codex runs) — and for Codex's experimental tool, ALSO print that list, since the tool can fail to render. Never narrate the menu in prose ahead of the question (three live misses — quasar 2026-07-29 twice, plus a Codex run — had perfect content and asked no question at all).
AskUserQuestion - Menu shapes. First close: up to TWO fix slots (a one-edit-closes-both
bundle counts as ONE slot; each slot carries that fix's consent scope + risk
note), then a different recommendation, then last and verbatim
. The post-apply re-offer leads with "Commit this branch and open a PR" — that pick IS the commit + push + PR ask (owner 2026-07-30: a developer lands work before starting more). A ship pick ENDS the loop; NEVER merge. A reported merge starts a fresh banner-led round — re-score the merged base and present it as a full first close, never a bare "re-scored, N/100" aside (live miss 2026-07-30).
None, just save the report (.md) - Only the save pick writes the report into the working tree (copies
→
<workdir>/report.md); no other pick does../ci-score-report.md - One recommendation per approval; stop at the diff. The apply "yes" authorizes the EDIT only — never commit, push, or open a PR on its strength (a live session treated apply as a commit license). The re-scored check is the completion oracle; never fabricate a score and never mutate YAML just to force a pass.
READ references/close-contract.md BEFORE
composing the close, every session — it is the executable protocol (consent
scope rules, deliberate-absence judgment, ship and post-merge rounds, refusal
closes, PR-request rules); the invariants above are what you hold in mind while
following it.
完整的结束/启动协议位于 references/close-contract.md。以下不可协商项保留在此处,供实际使用时查阅,因为过去将其埋藏曾导致实际运行回退——在此 skill 中,文本位置就是行为。每项均附有一行事故锚点;执行协议时须始终遵守:
- 横幅:从收集器的 stdout 中逐字复制——绝不能手工重绘、重新填充或随意调整(一次实际运行中,手绘进度条错误计算了 30 个区块中的 29 个)。拒绝结果或已记录的评分错误不显示横幅,并使用普通语句完成收尾——绝不能显示带有空白或虚构进度条的横幅。直接从 标记中读取分数;绝不能编造分数来填补空缺。
ci_score - 结束流程 = 先发送结束文本,再提出问题,而且位于同一轮。 写出结束文本绝不表示结束流程已经完成——在任一平台上,只有问题才算完成。选项只应存在于问题中:平台提供结构化提问工具时使用该工具;不提供时使用最后打印的选项列表(大多数 Codex 运行均如此);对于 Codex 的实验性工具,还要打印该列表,因为工具可能无法渲染。绝不能在问题之前用叙述性文字介绍菜单(三次实际遗漏——quasar 2026-07-29 两次,以及一次 Codex 运行——内容均完全正确,却根本没有提出问题)。
- 菜单结构。 首次收尾最多提供两个修复槽位(一个可同时解决两项问题的编辑捆绑包只算一个槽位;每个槽位均须包含该项修复的同意范围和风险说明),随后提供一个不同的建议,最后逐字提供 。应用修复后再次提供选项时,须以
None, just save the report (.md)开头——选择该项即表示请求执行 commit、push 和 PR。ship 选项会结束本轮;绝不能执行 merge。 用户报告已合并后,应开启一轮由横幅引导的新流程——对合并后的 base 重新评分,并将其作为完整的首次收尾展示,绝不能只附带一句“已重新评分,N/100”(实际遗漏 2026-07-30)。Commit this branch and open a PR - 只有保存选项会将报告写入工作树(将 复制到
<workdir>/report.md);其他选项均不会。./ci-score-report.md - 每次批准只处理一条建议;执行到 diff 即停止。 回答 只会授权进行编辑——绝不能据此执行 commit、push 或创建 PR。重新评分后的结果才是完成状态的判定依据;绝不能编造分数,也绝不能为了让检查通过而修改 YAML。
yes
每次会话编写结束文本前,都要阅读 references/close-contract.md——它是可执行的协议(同意范围规则、刻意缺省判断、ship 和合并后轮次、拒绝收尾、PR 请求规则);上述不可协商项是执行协议时需要牢记的要点。
Gotchas
Gotchas
- Never write findings.json OR the unsaved report into the target
repo — an untracked file makes the NEXT run's provenance (and published profiles forbid dirty). Both render to
-dirty; only the user's explicit save pick copies the report to<workdir>. A saved report left untracked will honestly read./ci-score-report.mdon a later run; tell the user to gitignore or delete it first when a clean-provenance run matters.-dirty - The score is not a speed verdict — measured, the correlation runs the other way (the lowest-scoring repo held the fastest gate in calibration). The disclosure line beside the card exists for this; keep it visible.
- Path filters are the one risky recommendation — a wrong filter can skip CI that should run, and a skipped required check blocks merges. The report's risk note says so; repeat it when applying that fix.
- A subdirectory path still scores the whole repo — the collector
anchors to the git top level (a partial view would inflate the score),
so anywhere inside the checkout is equivalent.
--repo - provenance is conservative — untracked files count, and an unverifiable tree (git status failing) is marked dirty, never clean.
-dirty
- 绝不能将 findings.json 或未保存的报告写入目标仓库——未跟踪文件会导致下次运行的来源状态变为 (已发布的配置不允许 dirty 状态)。两者均应渲染到
-dirty;只有用户明确选择保存时,才会将报告复制到<workdir>。如果后续运行需要干净的来源状态,应告知用户先加入 gitignore 或删除已保存的报告。./ci-score-report.md - 分数不是速度结论——经实际测量,相关性甚至呈相反方向(校准过程中得分最低的仓库反而拥有最快的门禁)。评分卡旁的披露说明正是为此而存在;必须保持可见。
- 路径过滤器是唯一具有风险的推荐项——错误的过滤器可能跳过本应运行的 CI,而被跳过的必需检查会阻止合并。报告中的风险说明已指出这一点;应用该修复时必须再次说明。
- 即使从子目录路径运行,也会为整个仓库评分——收集器以 git 顶层为锚点(不完整的视图会抬高分数),因此在 checkout 内任意位置使用 都具有相同效果。
--repo - 来源状态采用保守判定——未跟踪文件会计入其中,无法验证的工作树(
-dirty失败)会被标记为 dirty,绝不会标记为 clean。git status
Boundaries
Boundaries
ci-score grades configuration hygiene. It never measures speed, never
estimates savings, and never renders money. This ban is operational,
not just descriptive: if the user asks about speed, cost, or wall-clock
at any point — including right after their score ("so why is our CI
slow?") — do not improvise an answer from the config facts (a missing
cache is not a measurement); say so in one line and route to ci-speedup. For measured wall-clock and
runner-minute findings, that is — a separate skill; a
ci-speedup run never surfaces a score, and this skill never claims a
measurement. Prompt-routing contract (which skill answers what):
evals/prompt-routing.json.
ci-speedupci-score 用于评估配置规范性。它绝不测量速度,绝不估算节省量,也绝不呈现金额。这一禁令是操作层面的,而不只是描述层面的: 如果用户在任何时候询问速度、成本或实际耗时——包括在评分后立即询问(“那我们的 CI 为什么这么慢?”)——绝不能根据配置事实临场推断答案(缺少 cache 并不是一次测量);应用一句话说明这一点,并转至 ci-speedup。对于实际耗时和 runner-minute 的测量结果,应使用 ci-speedup——这是另一个独立 skill;ci-speedup 运行不会给出本分数,而本 skill 也绝不声称进行过测量。提示词路由契约(哪个 skill 应回答哪些问题):evals/prompt-routing.json。