verifiable-gates
A gate registry that is enforced two ways against the tests and CI jobs behind it, gates that must carry evidence of having gone red on a real defect, and a portable rule set that lets an AI coding agent work under the same rules in another project.
Archived under a DOI. The state the claims point at is tagged
evidence-freeze-1
in both this repository and the reference implementation, and archived at
doi:10.5281/zenodo.22103110. That DOI resolves to the
latest version; each release also gets one of its own. evidence-freeze-1 and
v0.1.0 are different commits on purpose: the freeze is the state the
measurements were taken on, the release is the state the package first shipped in.
Status: the extraction is complete (2026-08-28). Every stage has landed. What
remains in the reference implementation,
sayam/flask-todolist, is the
registers — which test, which job, which threshold — read by thin adapters on
the paths its hooks and jobs already called. The decision, what moved and what
stayed, is
ADR 0075 §6
and the file-by-file census is
extraction.yaml
there, which now records nothing outstanding: 0 to move, 58 that stayed, 13
split between the two. First release: v0.1.0 (2026-08-28) — the point at
which this repository and the reference implementation separate. Consume it as
a pinned submodule or a versioned dependency, never from main.
| Stage | What lands | Status |
|---|---|---|
| 1 | Package skeleton · CI · hash-pinned tools · registry schema | here |
| 2 | The nine checks · the doctor · preflight · the skill generator | here |
| 3 | The governance checkers (ratchets, censuses, platform posture) | here |
| 4 | The supply-chain checkers | here |
| 5 | The measurement instruments and the comparison data | here |
| 6 | Registry handover · citation · DOI | here |
| — | Extraction closed — census at move 0 / stay 58 / split 13 · v0.1.0 |
2026-08-28 |
Stage 6 was taken out of order on purpose. The rules are what this project is; stages 3 to 5 move the machinery that happens to enforce some of them, and a bundle that shipped the machinery before the rules would have had nothing to say about the ones it cannot enforce.
What is here today
rules.yaml — 92 rules, each carrying the incident that produced
it. They are rendered into an agent skill in the layout of the
Agent Skills specification, so any of the
products that read that layout can be handed it unchanged:
skills/verifiable-gates/SKILL.md is the front
page — how to read the rules, the five practices underneath them, and one line per
rule — and the full entries sit beside it in
references/baseline.md, the
baseline layer, where deviating is a defect, and
references/business.md,
agreements an application of a given kind may legitimately decide differently. (The
two sheets lived at the repository root as SKILL.md and SKILL-BUSINESS.md until
v0.1.12; DECISIONS.md the-sheets-live-under-skills says why they moved and why
no copy stayed.)
Two ways to take the skill without cloning, through pipes this repository does
not own: npx skills add sayam/verifiable-gates puts it into whichever agent you use
(the Skills CLI reads the skills/ directory), and in Claude Code
claude plugin marketplace add sayam/verifiable-gates then
claude plugin install verifiable-gates@verifiable-gates (the one-entry marketplace
in .claude-plugin/). The npx pipe lands the four files under skills/verifiable-gates/
— the sheet and its references — copied, not linked, and nothing else; the
marketplace pipe lands the whole repository — its plugin is the root ("source": "./")
because the hook runs src/verifiable_gates/edit_hook.py — as a git clone of the
marketplace plus a copy per version in Claude Code's plugin cache (measured on
2.1.261, 2026-09-05), so its manifest declares both licences, Apache-2.0 AND CC-BY-4.0. A skill is instructions. The scanners are still
pip install verifiable-gates and
python -m verifiable_gates.install, because a checker is not something to be
handed an agent as prose. DECISIONS.md distribution-is-two-pipes-nobody-here-owns
says why there is no marketplace or registry of this project's own. Neither pipe is
this repository's, so what each does on the way is the pipe's to say: the Skills CLI's
own README (at 435076e) says an npx skills add sends the repository and skill
identifiers as install telemetry, off with DISABLE_TELEMETRY=1 or DO_NOT_TRACK=1;
what the marketplace pipe sends is not measured here. The npx pipe fetches the
default branch at the moment of the command and takes no ref (@<sha> is read as a
skill name); its skills-lock.json records a content hash it does not enforce, and
skills update re-fetches and moves the copy to .agents/skills/ with a symlink in
its place — so through this pipe you get main of that moment, and the pinned routes
are the submodule and the versioned dependency above (measured on Skills CLI 1.5.23,
2026-09-05). The bundle itself opens no
network — no shipped file imports one, and tests/test_checks_are_standalone.py
holds it.
Three front doors for a project that has installed the bundle. In CI,
uses: sayam/verifiable-gates@<commit-sha> runs the doctor the project installed —
action.yml is run: steps only, with nothing inside it to pin, and an
optional sarif: input; it is listed on the
GitHub Marketplace as
verifiable-gates (since 2026-09-05) — pin the SHA, not the tag the listing offers. With pre-commit, repo: https://github.com/sayam/verifiable-gates
offers gates-doctor and one hook per scanner, by the id of
the rule it decides. All three run tools/ as the project has it and none carries a
copy, so a SHA, a rev or a plugin update moving changes nothing about what the project
is held to (DECISIONS.md ci-runs-the-bundle-the-project-installed). The third opens at
edit time: with the plugin enabled in Claude Code and VERIFIABLE_GATES_AT_EDIT=1 in
the project's .claude/settings.json under env, a hook runs the
installed doctor after every Edit or Write and hands a finding back to the agent
while it still holds the file. Off by default; it reports and refuses nothing
(DECISIONS.md the-edit-hook-reports-and-does-not-refuse).
A rule and its enforcement live in separate files, because they have separate
lifetimes. rules.yaml is what this project publishes; gates.yaml is what this
project is itself held to.
import verifiable_gates
catalogue = verifiable_gates.rules.load("rules.yaml")
# `package_dir` is where a rule's `script:` is looked for; without it, only the
# shape of the path is checked — a checker that is not there would go unnoticed.
for problem in verifiable_gates.rules.problems(catalogue, package_dir="src/verifiable_gates"):
print(problem)
rules.yaml ships with the checkout, not with the wheel — it is the published
artefact (CC BY 4.0), and the package is the machinery that reads it.
Also here: the nine stdlib-only checkers, the installer and the doctor that runs them in a project that has installed nothing, preflight, the sheet renderer, and the fail-fix harness.
What the bundle decides, and what it does not. Of the 92 rules, nine have a
checker in the bundle (script: in rules.yaml); the doctor and the installer
decide those and nothing else. The other 83 are the rule sheets — an agent is held
to them by reading, and the Enforced in the reference line on each says how one
project turned it into a test. rules.yaml and the sheets are not installed by
install() either; they come with the checkout. Two consequences worth knowing
before trusting a green: the doctor reports a rule it cannot decide as NA, and a
project where every rule is NA exits 0 — that is "nothing was measured", not "the
project passed" (a path that scaffold.json names and the project does not have is
never NA, though: that is a broken configuration, and it is a finding — and so are a
value of the wrong shape, a list where one path goes or a string where a list of names
goes, and a path that leads outside the project, each naming the key it came from; and a
key no scanner reads — templates_pth for templates_path — is a finding that names the
nearest key the bundle does read, because every scanner would answer from its default
while the project pointed elsewhere; and a
Dockerfile* the project has but never named is a finding too, not "no
Dockerfile"; a directory that is there and holds no file of the kind a checker reads
— an app/ of Go, a templates directory of .ejs — is NA naming what it looked for,
not a pass, because a rule the tool cannot check must not look like a rule it checked;
and a scan that crashes, hangs past its timeout, answers half a verdict
before crashing, meets a file it cannot decode, may not open, or that is larger than the
8 MiB a scanner reads whole, cannot read scaffold.json as a configuration at all, or
cannot walk the whole tree it was pointed at — a directory closed to it is not the
answer a directory that is not there gets, which stays NA — is [error] with its
stderr passed through, never [found] — red,
but no verdict); and rules.problems() only checks that a script: exists when it is
given the package directory (package_dir=), as the doctor does. On a fresh
install the only pass is the shipped index (gates-registry-total), which has
to be true about itself; the starting workflow the installer wrote is NA to the
two pinning checkers until a line of it changes — a green on the bundle's own
file says nothing about the project. That index check reads its own direction too: a
gate whose job cannot turn the build red — a workflow with no trigger, if: false, or
continue-on-error: true — is a finding, because a row nothing can fail is a row and
nothing else; and each of its findings names the file to open and the row to add or
change, so the first line a stranger reads points at the second. And the bundle keeps a
record of what it installed: gates_doctor --installed holds every file it wrote to the contents it wrote, not merely to being
present — and writes nothing into the tree it checks, not even bytecode — and an upgrade names what this version no longer ships and leaves it in place,
because a file in your repository is yours to remove. An install that stopped partway is
read as one: the doctor leads with the last install into this tree did not finish
rather than reporting the files that did land as files somebody edited — and one still
under way is read as one too, because the record is written before the first file and
names what each file is about to become. And
gates_doctor --rules prints the rules the bundle decides — each with where it came
from and which scanner reads it — for the instruction file a project keeps for its
agents (AGENTS.md, CLAUDE.md) to point at: read at run time from the installed
manifest, so an upgrade cannot leave an agent on yesterday's rule, and only the rules a
scanner here can decide, so no instruction stands without a gate behind it. It prints
them only off a bundle the installed record still vouches for: an edited manifest, an
edited scanner, or no record at all prints no rules and exits 2, because the file it
reads lives inside the project it holds to account (DECISIONS.md
the-rules-are-read-off-a-bundle-that-is-still-intact). A finding in the report
carries the same two lines — [found] <gate> — <rule> and born from: <incident> above
the scanner's own — and off a bundle the record no longer vouches for it carries the gate
alone and one line saying why, the findings printed either way. And
gates_doctor --sarif FILE writes the same run as SARIF 2.1.0 for code scanning,
reviewdog or an IDE — a finding is a result; NA is a notification on the invocation,
never a result; and a scan that did not answer is both — an error notification, and a
result of the doctor's own rule scan-did-not-answer — because GitHub keeps a SARIF's
results and drops its invocation (measured, round 23), so a reader counting results cannot
mistake "could not look" for "looked and found nothing"; and every result carries a
location the tree has — the file the finding names, else scaffold.json — because GitHub
refuses a whole file over one result without one (measured, round 23); and the
invocation names the doctor's exit code and why, the one line about the run that
GitHub keeps — the notifications it drops (measured, round 23). A file already at that path is
replaced only if it is this doctor's run over the same root; another tree's run, or
anything else, is left where it is and named — two trees given one path lose no answer.
What the two pinning checkers read. ci-tools-hash-pinned and
actions-sha-pinned read every workflow, every composite action a workflow names
with uses: ./<path> wherever it lives, folded or not, and — for installs — every
shell script a run: line hands off to, known by its .sh name or by its shebang,
quoted or not, from wherever the shell stands (a cd dir && before it, or the
step's working-directory:), with comments stripped first — a # inside a word
($#, ${#PKGS}, \#) is not one. In a workflow only what run: executes is judged —
a name: or an env: that quotes the command is prose — and --require-hashes
counts only as an argument of the install itself, quoted or not; so do
PIP_REQUIRE_HASHES=1 on the command or in the step's own env:, and a
requirements file whose every line carries a --hash=, because pip requires
hashes in each of those cases on its own; --no-index, and a wheel installed
with --no-deps, fetch nothing and are left alone. A command inside $( ),
backticks, a ( ) subshell, an sh -c string (-c folded into other flags
too — bash -lc), a string python -c hands to os.system, or after a lone &
executes and is judged; a bare echo of the words is prose — unless a shell is
handed the words, by a pipe (echo … | bash), a here-string or eval, and a
${PIP:-pip} default is read as the word. The YAML shapes uses : and - {uses: …} are
read the way the platform reads them — and so are a quoted key ("run":), an
alias of an anchor set anywhere in the file (run: *cmd, uses: *co, with the
anchor's version comment), a tagged value (!!str), and a plain, quoted or
folded (>) scalar that continues onto the next line, which YAML joins with a
space before the shell sees it; only a literal block (|) keeps its lines
apart, and a uses under with: is an input, not a step. An install is judged
whether it says pip, pipx, uv tool install, uv tool run, uv add, uvx,
uv run --with, poetry add, pdm add or pipenv install, and on the Node side
npm install, npm exec, npx, yarn add, pnpm add or pnpm dlx; pip wheel
builds in an isolated environment like python -m build and is held to
--no-build-isolation; uv run --locked, uv sync --locked, uv build, npm ci,
yarn install --immutable and pnpm install --frozen-lockfile install from a lock
and are left alone, as are npx --no, npm exec --no and pnpm exec, which run the
installed copy and refuse to fetch. A Node finding says what replaces that line —
npm ci for npm install, npx --no <tool> for npx <tool> — and names the lock it
needs, or says to commit one when it is not there. A uses: folded onto the next line is
read from that line. actions-sha-pinned judges both halves of its title: a
floating tag is a finding, and so is a commit SHA with no version comment beside
it — a pin nobody can read or move (a docker:// digest needs none). Of the
other checkers, adr-index-complete reports two records sharing a number as
well as a gap, and csp-no-inline reads ONCLICK=, STYLE= and a <style>
element the way a browser does — in any case, split over lines or not (the =
on the line after the name too), with comments blanked first (one that never
closes runs to the end of the file), entities inside an attribute value decoded
before the scheme is read (javascript:), and .htm, .jinja, .jinja2
and .j2 templates read like .html.
Since the extraction finished, so are the deciders that used to live in the
reference implementation: ratchets and the measurements that feed them, the
removal census, the synchroniser for numbers a project advertises about itself,
the platform-posture reader, the advisory deciders for pip, npm, and container
images, the scanner-coverage check, the two censuses that watch what CI cannot see
(when a schedule last fired, how long redness stood), the gh wrapper, and the
ASVS worksheet builder with the gate-to-ASVS crosswalk — and the research
instruments, the ASVS
probe and the battery that runs it over a directory of generated applications,
together with the experiment they measured. Each arrives with
its messages as an input, so a project can keep printing in its own language.
The two schemas — rules.py for this catalogue, registry.py for a project's
gates.yaml — encode five rules that came from real traps, not from theory:
- a gate whose
layerisinternalcannot beportable— a rule tied to one project's architecture, exported as universal, is an overclaim; that hold is in the gate schema, and the rule schema refusesinternaloutright, since a rule in this catalogue is published whole (portableon a rule is refused as a gate's field); - a key neither schema knows is refused, not skipped — a misspelt
born_frmis a rule with no origin that looks like one with; - anything exported must name the trap that created it (
born_from), because a rule with no origin is a rule nobody knows when to remove; proved_byentries must say what they caught and when — a gate nobody has seen go red is indistinguishable from a gate that checks nothing;- the vocabularies for
kind,severity,layer, andpillarare closed.
Licence
- Code: Apache-2.0. Contributors sign
CLA.md— one line in the pull request; you keep your copyright. - Rules and documentation: CC BY 4.0.
The application this was extracted from stays AGPL-3.0-or-later. The two differ on purpose: a CI tool is not a network service, and a rule meant to be adopted inside an organisation's internal handbook must not require share-alike.
ภาษาไทย
ทะเบียน gate ที่ถูกบังคับให้ตรงกับความจริงสองทิศ · gate ที่ต้องพกหลักฐานว่าเคยแดง ตอนของเสียจริง · และชุดกฎที่ส่งออกไปให้ AI agent ทำงานใต้กติกาเดียวกันในโปรเจกต์อื่นได้
เก็บถาวรใต้ DOI แล้ว — สถานะที่ข้ออ้างชี้ถึงถูกตรึงไว้ที่ tag evidence-freeze-1
ทั้ง repo นี้และ reference implementation · archive อยู่ที่
doi:10.5281/zenodo.22103110 ซึ่งชี้รุ่นล่าสุดเสมอ
(แต่ละ release มี DOI ของตัวเองด้วย) · evidence-freeze-1 กับ v0.1.0 เป็นคนละคอมมิตโดยตั้งใจ —
tag แรกคือสถานะตอนวัด tag หลังคือสถานะตอนแพ็กเกจออกครั้งแรก
สถานะ: ถอดครบทุกขั้นแล้ว (2026-08-28) — ที่
flask-todolist เหลือ ทะเบียน
(เทสต์ไหน · job ไหน · พื้นเท่าไหร่) กับ adapter บาง ๆ บนพาธเดิม ตาม ADR 0075
ข้อ 6 และ extraction.yaml ที่นั่น ซึ่งไม่เหลืออะไรค้างแล้ว (move 0 · stay 58 · split 13) ·
รุ่นแรก v0.1.0 (2026-08-28) คือจุดที่สอง repo แยกทางกัน — ใช้ผ่าน submodule
ที่ pin ไว้หรือ dependency ที่ระบุรุ่น ไม่ใช่จาก main
วันนี้มี คลังกฎ 92 ข้อ (rules.yaml) ที่แต่ละข้อพกกับดักจริงที่ให้กำเนิดมันมาด้วย
· ตัวตรวจ stdlib ล้วนเก้าตัว · ตัวติดตั้งกับ doctor · preflight · ตัวเรนเดอร์แผ่นกฎ
· harness ของ fail-fix loop · ตัวตัดสินฝั่ง governance กับ supply chain
(ratchet · สำมะโนของที่ถอด · ตัวซิงก์เลขที่โฆษณา · ท่าทีแพลตฟอร์ม · advisory ของ
pip/npm/image · ขอบเขตของตัวสแกน · สำมะโนตารางเวลากับสายแดงที่ CI มองไม่เห็น ·
แผ่นงาน ASVS กับ crosswalk) · และ เครื่องมือวิจัย (ASVS probe + battery)
พร้อมการทดลองที่มันวัด — ทุกตัวรับถ้อยคำเป็น input
โปรเจกต์ปลายทางจึงพิมพ์ภาษาของตัวเองได้
กฎกับตัวบังคับอยู่คนละไฟล์โดยตั้งใจ เพราะอายุไม่เท่ากัน — rules.yaml คือสิ่งที่
repo นี้เผยแพร่ ส่วน gates.yaml คือสิ่งที่ repo นี้ถูกบังคับด้วยตัวเอง
แผ่นกฎเป็น Agent Skill ตาม spec แล้ว อยู่ที่ skills/verifiable-gates/ (หน้าแรก SKILL.md + entry เต็มใน references/)
· ติดตั้งโดยไม่ต้อง clone ได้สองทางผ่านท่อที่ repo นี้ไม่ได้เป็นเจ้าของ: npx skills add sayam/verifiable-gates
(Skills CLI ลงให้ agent ที่คุณใช้) หรือใน Claude Code claude plugin marketplace add sayam/verifiable-gates แล้ว
claude plugin install verifiable-gates@verifiable-gates · ท่อ npx ลง สี่ ไฟล์ใต้ skills/verifiable-gates/ (แผ่นกฎกับ references) แบบ copy และไม่มีอะไรอื่น
ส่วนท่อ marketplace ลงทั้ง repo (plugin คือ root "source": "./" เพราะ hook รัน src/verifiable_gates/edit_hook.py)
เป็น git clone ของ marketplace บวก copy ต่อเวอร์ชันใน plugin cache ของ Claude Code (วัดกับ 2.1.261, 2026-09-05) manifest จึงประกาศทั้งสอง licence Apache-2.0 AND CC-BY-4.0 — skill คือคำสั่ง ส่วนตัวสแกนยังเป็น pip install verifiable-gates + python -m verifiable_gates.install
เพราะตัวตรวจไม่ใช่ของที่จะยื่นให้ agent เป็นร้อยแก้ว · ท่อไม่ใช่ของ repo นี้ สิ่งที่ท่อทำระหว่างทางจึงเป็นเรื่องที่ท่อบอกเอง:
README ของ Skills CLI (ที่ 435076e) บอกว่า npx skills add ส่ง identifier ของ repo และ skill เป็น install telemetry
ปิดได้ด้วย DISABLE_TELEMETRY=1 หรือ DO_NOT_TRACK=1 ส่วนท่อ marketplace ยังไม่ได้วัด · ท่อ npx ดึง default branch ณ เวลาที่สั่ง ไม่รับ ref (@<sha> ถูกอ่านเป็นชื่อ skill)
skills-lock.json ของมันบันทึก hash ที่ไม่ได้บังคับ และ skills update ดึงใหม่แล้วย้ายสำเนาไป .agents/skills/ ทิ้ง symlink ไว้แทน —
ผ่านท่อนี้จึงได้ main ณ ขณะนั้น ทางที่ pin ได้คือ submodule กับ dependency ที่ระบุเวอร์ชันข้างบน (วัดกับ Skills CLI 1.5.23, 2026-09-05)
· ตัว bundle เองไม่เปิดเครือข่าย —
ไม่มีไฟล์ที่ ship ไป import network module และ tests/test_checks_are_standalone.py ถือไว้
สามประตูหน้าสำหรับโปรเจกต์ที่ติดตั้ง bundle แล้ว — ใน CI uses: sayam/verifiable-gates@<commit-sha> รัน doctor
ที่โปรเจกต์ติดตั้งไว้ (action.yml เป็น run: ล้วน ไม่มีอะไรข้างในให้ pin · มี input sarif: ให้เลือก · อยู่บน GitHub Marketplace ในชื่อ verifiable-gates ตั้งแต่ 2026-09-05 — pin ด้วย SHA ไม่ใช่ tag ที่หน้า listing เสนอ) · ใน pre-commit
repo: https://github.com/sayam/verifiable-gates มี hook gates-doctor กับ hook ต่อ scanner ตาม id ของกฎ · ทั้งสองรัน tools/
ตามที่โปรเจกต์มี ทั้งสามประตูไม่พกสำเนาเลย — SHA · rev · หรือ plugin ขยับ จึงไม่เปลี่ยนสิ่งที่โปรเจกต์ถูกบังคับ · ประตูที่สามเปิดตอน edit: เปิด plugin
ใน Claude Code แล้วตั้ง VERIFIABLE_GATES_AT_EDIT=1 ใน .claude/settings.json ของโปรเจกต์ใต้ env — hook (hooks/hooks.json)
จะรัน doctor ที่ติดตั้งไว้หลังทุก Edit/Write แล้วส่ง finding กลับให้ agent ตอนที่ยังถือไฟล์อยู่ · ปิดเป็นค่าเริ่มต้น · รายงาน ไม่ปฏิเสธ
(DECISIONS.md the-edit-hook-reports-and-does-not-refuse)
บันเดิลตัดสินได้ 9 จาก 92 — เฉพาะกฎที่มี script: เท่านั้นที่ doctor กับ installer
ตัดสินให้ อีก 83 ข้อคือแผ่นกฎที่ agent ถูกบังคับด้วยการอ่าน · doctor รายงานกฎที่ตัดสินไม่ได้เป็น NA
และโปรเจกต์ที่ทุกข้อเป็น NA ออก 0 แปลว่า "ไม่ได้วัดอะไร" ไม่ใช่ "ผ่าน" · ไดเรกทอรีที่ มีอยู่ แต่ไม่มีไฟล์ชนิดที่ตัวตรวจอ่านเลย
— app/ ที่มีแต่ Go หรือไดเรกทอรี template ที่มีแต่ .ejs — เป็น NA พร้อมบอกว่าไปหาอะไร ไม่ใช่ pass
เพราะกฎที่เครื่องมือตรวจไม่ได้ต้องไม่หน้าตาเหมือนกฎที่ตรวจแล้ว · สแกนที่ล่ม ค้างเกินเวลา พิมพ์คำตัดสินได้ครึ่งเดียวแล้วพัง
เจอไฟล์ที่ถอดรหัสไม่ได้ ไม่มีสิทธิ์เปิด หรือใหญ่เกิน 8 MiB ที่ตัวสแกนอ่านทั้งไฟล์ อ่าน scaffold.json เป็นคอนฟิกไม่ได้เลย
หรือเดินต้นไม้ที่ถูกชี้ให้ดูไม่ทั่ว — ไดเรกทอรีที่ปิดไม่ให้เข้าไม่ใช่คำตอบเดียวกับไดเรกทอรีที่ ไม่มีอยู่ ซึ่งยังเป็น NA —
รายงานเป็น [error] พร้อมส่ง stderr ต่อ ไม่ใช่ [found] · หลังติดตั้งใหม่
ด่านเดียวที่ pass คือทะเบียนที่ส่งมากับบันเดิล (gates-registry-total) ส่วน workflow
ตั้งต้นที่ตัวติดตั้งเขียนให้เป็น NA สำหรับตัวตรวจ pin ทั้งสองจนกว่าจะมีบรรทัดถูกแก้ —
เขียวบนไฟล์ของบันเดิลเองไม่ได้บอกอะไรเกี่ยวกับโปรเจกต์ · ตัวตรวจทะเบียนอ่านทิศของตัวเองด้วย: gate ที่งานของมัน
ทำให้ build แดงไม่ได้ — workflow ที่ไม่มี trigger, if: false, หรือ continue-on-error: true — เป็น finding
เพราะแถวที่ไม่มีอะไรทำให้ล้มได้คือแถวเปล่า ๆ · และ finding แต่ละข้อของมันบอกชื่อไฟล์ที่ต้องเปิดกับแถวที่ต้องเพิ่มหรือแก้
บรรทัดแรกที่คนแปลกหน้าอ่านจึงชี้ไปที่บรรทัดถัดไป · และบันเดิลจดสิ่งที่มันติดตั้งไว้: gates_doctor --installed
ถือทุกไฟล์ที่มันเขียนไว้กับ เนื้อ ที่มันเขียน ไม่ใช่แค่ว่ามีไฟล์อยู่ — และไม่เขียนอะไรลงต้นไม้ที่มันตรวจ แม้แต่ bytecode · และตอนอัปเกรด มันบอกว่ารุ่นนี้เลิกส่งอะไร
แล้วปล่อยไฟล์นั้นไว้ เพราะไฟล์ในรีโปของคุณเป็นสิทธิ์ของคุณที่จะลบ · การติดตั้งที่หยุดกลางทางถูกอ่านว่าอย่างนั้น —
doctor ขึ้นต้นด้วย การติดตั้งครั้งล่าสุดลงในต้นไม้นี้ยังไม่จบ แทนที่จะรายงานไฟล์ที่ลงไปแล้วว่าเป็นไฟล์ที่ถูกใครแก้ · และ gates_doctor --rules พิมพ์กฎที่บันเดิล
ตัดสินได้ — แต่ละข้อพร้อมที่มาและตัวสแกนที่อ่านมัน — ให้ไฟล์คำสั่งที่โปรเจกต์เก็บไว้ให้ agent (AGENTS.md, CLAUDE.md)
ชี้มาหา: อ่านตอนรันจาก manifest ที่ติดตั้งอยู่ การอัปเกรดจึงทิ้ง agent ไว้กับกฎเมื่อวานไม่ได้ และมีเฉพาะกฎที่ตัวสแกน
ตรงนี้ตัดสินได้ จึงไม่มีคำสั่งข้อไหนยืนอยู่โดยไม่มีด่านหนุนหลัง · และมันพิมพ์กฎเฉพาะจากบันเดิลที่ record ยังรับรอง —
manifest ถูกแก้ · scanner ถูกแก้ · หรือไม่มี record เลย = ไม่พิมพ์กฎสักข้อ exit 2 เพราะไฟล์ที่มันอ่านอยู่ในโปรเจกต์ที่มันกำลังตรวจเอง
(DECISIONS.md the-rules-are-read-off-a-bundle-that-is-still-intact) · finding ในรายงานพกสองบรรทัดเดียวกัน —
[found] <gate> — <กฎ> และ born from: <เหตุการณ์> เหนือบรรทัดของตัวสแกน — และจากบันเดิลที่ record ไม่รับรองแล้ว
พิมพ์แค่ชื่อ gate กับหนึ่งบรรทัดบอกว่าทำไม ส่วน finding พิมพ์ทั้งสองกรณี · และ gates_doctor --sarif FILE เขียนผลรอบเดียวกันเป็น SARIF 2.1.0
ให้ code scanning, reviewdog หรือ IDE — finding เป็น result · NA เป็น notification บน invocation ไม่ใช่ result
· สแกนที่ตอบไม่ได้เป็นทั้งสองอย่าง — error notification และ result ของกฎของ doctor เอง scan-did-not-answer — เพราะ GitHub เก็บ result
แต่ทิ้ง invocation ทั้งก้อน (วัดแล้ว รอบ 23) คนที่นับ result จึงเข้าใจ "ดูไม่ได้" เป็น "ดูแล้วไม่เจอ" ไม่ได้ · ทุก result มี location ที่ต้นไม้มีจริง — ไฟล์ที่ finding เอ่ยถึง
ไม่งั้น scaffold.json — เพราะ GitHub ปฏิเสธทั้งไฟล์ถ้า result เดียวไม่มี location (วัดแล้ว รอบ 23) · invocation บอก exit code ของ doctor และเหตุผล
ซึ่งเป็นบรรทัดเดียวเกี่ยวกับ run ที่ GitHub เก็บ — notification มันทิ้ง (วัดแล้ว รอบ 23) · ไฟล์ที่มีอยู่แล้วที่ path นั้นจะถูกแทนที่ก็ต่อเมื่อเป็นผลรอบของ doctor
ตัวนี้บน root เดียวกัน ถ้าเป็นผลของต้นไม้อื่นหรืออย่างอื่นจะถูกทิ้งไว้และบอกชื่อ — สองต้นไม้ที่ใช้ path เดียวกันไม่เสียคำตอบ · ตัวตรวจ pin อ่าน workflow ทุกไฟล์
composite action ที่ uses: ./<path> ชี้ไม่ว่าอยู่ที่ไหนหรือพับบรรทัดอย่างไร และเชลล์สคริปต์ที่ run:
เรียกต่อ ไม่ว่าจะรู้จากชื่อ .sh หรือจาก shebang ใส่เครื่องหมายคำพูดหรือไม่ก็ตาม จากที่ที่เชลล์ยืนอยู่ (cd dir && ก่อนหน้า หรือ working-directory: ของ step) โดยตัดคอมเมนต์ก่อน — # ที่อยู่ในคำ ($#, ${#PKGS}, \#) ไม่ใช่คอมเมนต์ · ใน workflow ตัดสินเฉพาะสิ่งที่
run: รันจริง (name: หรือ env: ที่ยกคำสั่งมาพูดถึงเป็นแค่ข้อความ) และ --require-hashes นับเมื่อเป็น
อาร์กิวเมนต์ของคำสั่งติดตั้งเองเท่านั้น (จะอยู่ในเครื่องหมายคำพูดหรือไม่ก็ตาม) · PIP_REQUIRE_HASHES=1 บนคำสั่งหรือใน env: ของ step และไฟล์ requirements ที่ทุกบรรทัดมี --hash= ก็นับ เพราะ pip บังคับ hash เองในกรณีเหล่านั้น · --no-index และ wheel ที่ติดตั้งด้วย --no-deps ไม่ดึงอะไรจาก index จึงไม่ถูกตัดสิน · คำสั่งใน $( ) backtick subshell สตริงของ sh -c (รวม -c ที่พับกับ flag อื่นเช่น bash -lc) สตริงที่ python -c ส่งให้ os.system หรือหลัง & เดี่ยว รันจริงจึงถูกตัดสิน
ส่วน echo ที่แค่พูดคำนั้นเป็นข้อความ — เว้นแต่คำนั้นถูกส่งให้เชลล์ผ่าน pipe (echo … | bash) here-string หรือ eval และค่าเริ่มต้นของ ${PIP:-pip} ถูกอ่านเป็นคำนั้น · รูป YAML uses : และ - {uses: …} ถูกอ่านแบบเดียวกับที่แพลตฟอร์มอ่าน — เช่นเดียวกับคีย์ในเครื่องหมายคำพูด ("run":) alias ของ anchor ที่ตั้งไว้ที่ใดก็ได้ในไฟล์ (run: *cmd, uses: *co พร้อม comment เวอร์ชันของ anchor) ค่าที่มี tag (!!str) และ scalar แบบ plain quoted หรือ folded (>) ที่ต่อลงบรรทัดถัดไป ซึ่ง YAML เชื่อมด้วยช่องว่างก่อนถึงเชลล์ · มีแต่ literal block (|) ที่คงบรรทัดแยกกัน และ uses ใต้ with: เป็น input ไม่ใช่ step · actions-sha-pinned ตัดสินทั้งสองครึ่งของชื่อกฎ: tag ลอยเป็น finding และ
commit SHA ที่ไม่มี comment บอกเวอร์ชันข้าง ๆ ก็เป็น finding (digest ของ docker:// ไม่ต้องมี) ·
adr-index-complete รายงานบันทึกสองฉบับที่ใช้เลขเดียวกันเช่นเดียวกับเลขที่ขาด · csp-no-inline อ่าน
ONCLICK= STYLE= และ <style> แบบไม่สนตัวพิมพ์และไม่สนการตัดบรรทัด (รวม = ที่อยู่บรรทัดถัดจากชื่อ) เหมือนที่เบราว์เซอร์อ่าน
โดยลบคอมเมนต์ก่อน (คอมเมนต์ที่ไม่ปิดกินถึงท้ายไฟล์) ถอด entity ในค่าของ attribute ก่อนอ่าน scheme (javascript:) และอ่าน .htm .jinja .jinja2 .j2 เหมือน .html · Dockerfile* ที่มีอยู่แต่ไม่ได้ตั้งชื่อไว้ใน scaffold.json ถือเป็น
finding ไม่ใช่ "ไม่มี Dockerfile" · ค่าใน scaffold.json ที่ผิดรูป — ลิสต์ในที่ที่ต้องเป็นพาธเดียว หรือสตริงในที่ที่ต้อง
เป็นลิสต์ของชื่อ — และพาธที่พาออกไปนอกโปรเจกต์ เป็น finding ที่บอกชื่อคีย์ เช่นเดียวกับพาธที่ตั้งชื่อไว้แต่ไม่มีอยู่จริง
· คีย์ที่ไม่มี scanner ตัวไหนอ่าน (templates_pth แทน templates_path) เป็น finding ที่บอกคีย์ใกล้เคียงที่ bundle อ่านจริง เพราะทุก scanner
จะตอบจากค่า default ขณะที่โปรเจกต์ชี้ไปที่อื่น
คลังเก็บสองภาษา: อังกฤษเป็นข้อความที่เผยแพร่ ส่วนถ้อยคำไทยต้นฉบับอยู่ในฟิลด์
*_th คู่กัน เพราะคำแปลของบันทึกเหตุการณ์คือการเล่าใหม่ และการเล่าใหม่ไม่ใช่ตัวบันทึก
โค้ด: Apache-2.0 (ผู้ร่วมพัฒนาลงนาม CLA.md) · กฎและเอกสาร: CC BY 4.0
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file verifiable_gates-0.3.0.tar.gz.
File metadata
- Download URL: verifiable_gates-0.3.0.tar.gz
- Upload date:
- Size: 496.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d0c5e80c8642ead8c476366eeeb0d7cefc3d74805aaa156768fe49d27b52464
|
|
| MD5 |
ef3c5a8c7d80ad5a2106676e2df51720
|
|
| BLAKE2b-256 |
7cbee593f434416e65af44ba069b2e5fe148112f1c6110759d1e4cd1f156527f
|
Provenance
The following attestation bundles were made for verifiable_gates-0.3.0.tar.gz:
Publisher:
release.yml on sayam/verifiable-gates
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
verifiable_gates-0.3.0.tar.gz -
Subject digest:
3d0c5e80c8642ead8c476366eeeb0d7cefc3d74805aaa156768fe49d27b52464 - Sigstore transparency entry: 2720852188
- Sigstore integration time:
-
Permalink:
sayam/verifiable-gates@aad57450437340b64de62a5c5bbf52cecbf1fc93 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/sayam
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@aad57450437340b64de62a5c5bbf52cecbf1fc93 -
Trigger Event:
release
-
Statement type:
File details
Details for the file verifiable_gates-0.3.0-py3-none-any.whl.
File metadata
- Download URL: verifiable_gates-0.3.0-py3-none-any.whl
- Upload date:
- Size: 279.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
076a42eb8e36f75a51a2b7a7a2e2edb2a77e4d47e19f8c973477f817a3eb22ff
|
|
| MD5 |
77a119941d9358f09f088abb320c6d0f
|
|
| BLAKE2b-256 |
16561f252084b86dbd8c576c0809b7e00114e6a9a4069cee825f6dc7951af965
|
Provenance
The following attestation bundles were made for verifiable_gates-0.3.0-py3-none-any.whl:
Publisher:
release.yml on sayam/verifiable-gates
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
verifiable_gates-0.3.0-py3-none-any.whl -
Subject digest:
076a42eb8e36f75a51a2b7a7a2e2edb2a77e4d47e19f8c973477f817a3eb22ff - Sigstore transparency entry: 2720853497
- Sigstore integration time:
-
Permalink:
sayam/verifiable-gates@aad57450437340b64de62a5c5bbf52cecbf1fc93 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/sayam
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@aad57450437340b64de62a5c5bbf52cecbf1fc93 -
Trigger Event:
release
-
Statement type: