docs: второй край предиката — обнаружение тоже продукт #92
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: gates | |
| # The first CI this repository has had. Until now every check on a pure-markdown plugin | |
| # was a thing someone remembered to run, which is why a version could sit wrong in two | |
| # READMEs through a release and a section could advertise 97 catalog entries against 92. | |
| # | |
| # Runs on ubuntu-latest deliberately, not macOS: the tools are written against BSD | |
| # behaviour on the author's machine, and a Linux runner is the second opinion that catches | |
| # a GNU/BSD divergence before a contributor does. The tools set LC_ALL=C for the same | |
| # reason they set it locally — the corpus is Russian, and locale-dependent string handling | |
| # has already silently mis-parsed it once. | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: gates-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| gates: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Tools are executable and POSIX-parseable | |
| # A syntax error in a checker reads as a passing checker to anything that only | |
| # looks at whether the file exists. | |
| # `find`, not the `tools/*.sh` glob: the glob stops at the top level, and a checker | |
| # one directory down was invisible here and to tools/gates.sh both. Kept identical | |
| # to the loop in tools/gates.sh — tools/selftest.sh fails when the two diverge. | |
| run: | | |
| set -eu | |
| for f in $(find tools -name '*.sh' -type f | sort); do | |
| [ -x "$f" ] || { echo "not executable: $f"; exit 1; } | |
| sh -n "$f" || { echo "syntax error: $f"; exit 1; } | |
| done | |
| - name: Python tools are executable and parseable | |
| # Mirrors the .py loop in tools/gates.sh — tools/ gained its first Python file | |
| # and the shell loop cannot see it. | |
| run: | | |
| for f in $(find tools -name '*.py' -type f | sort); do | |
| test -x "$f" || { echo "not executable: $f"; exit 1; } | |
| python3 -c "import ast,io,sys; ast.parse(io.open(sys.argv[1],encoding='utf-8').read(), sys.argv[1])" "$f" | |
| done | |
| - name: Selftest — every checker must be able to fail | |
| # Runs first. If the checkers cannot fail, their passing below means nothing. | |
| run: tools/selftest.sh | |
| - name: check-frozen — the paid-MCP corpus contract | |
| run: tools/check-frozen.sh | |
| - name: check-version — one version, one description budget, one set of triggers | |
| # The description is the entire trigger on hosts that have no instruction file. | |
| # Losing a Russian phrase there stops the skill firing for the people it is for, | |
| # silently, with every other gate still green. | |
| run: tools/check-version.sh | |
| - name: check-dogfood — the numbers the product states about itself | |
| # The catalogue size lives in seven files. Four of them went stale unnoticed | |
| # the last time it moved, which is why the completeness guard exists. | |
| run: tools/check-dogfood.sh | |
| - name: check-typography — the product obeys its own typography | |
| # The README claims every dash, quote and space in it follows the plugin's own | |
| # rules. Until this step that claim rested on someone remembering to ask a model, | |
| # and in one afternoon a literal ~ and a missing non-breaking space both shipped. | |
| run: tools/check-typography.sh | |
| - name: selftest-check-private — the boundary gate proves it can red | |
| # Nine cases, including the control that the mere SUBJECT of the paid service stays | |
| # allowed. Without this step the gate's own claim rested on someone having run it | |
| # once by hand — the promise-without-a-mechanism this whole change set is against. | |
| run: tools/selftest-check-private.sh | |
| - name: check-private — no identifier of the private contour in tracked files | |
| # Guards an irreversible event rather than a fixable one: a hostname, a repo name | |
| # or a token in a public commit cannot be recalled from the release tags or from | |
| # any clone. The deny-list holds identifiers only — this repository legitimately | |
| # discusses the paid service, and a gate that reds on the subject would be off | |
| # within a week. | |
| run: tools/check-private.sh | |
| - name: check-assets — manifest images exist, are square, and fit the limit | |
| run: tools/check-assets.sh | |
| - name: No-loss gate — every atom of the pinned baseline still accounted for | |
| # The baseline is the v1.10.1 corpus, frozen as a historical artefact: it is NOT | |
| # regenerated when the corpus changes. That is the whole point — it is the "before" | |
| # side of every comparison from here to the v2.0 release. A rule that leaves the | |
| # corpus must appear in tools/atom-map.tsv with what happened to it, or this fails. | |
| run: | | |
| set -eu | |
| # The baseline is pinned by content, not by policy. Without this, deleting a | |
| # rule from the corpus AND its line from the baseline passes every gate green: | |
| # the comparison is with a file the same commit is free to edit. Changing the | |
| # pin is then a visible line in a diff rather than a quiet edit inside a | |
| # 1825-line data file. It moves once, at the v2.0 release. | |
| sha256sum -c - <<'PIN' | |
| 98b2e03bc771d7c0b3268f4eb406fbb3641bc8fdb01243ff3053a1790e2b3aa4 tools/baseline/atoms-v1.10.1.tsv | |
| PIN | |
| tools/extract-atoms.sh skills/ru-text > /tmp/atoms-now.tsv | |
| tools/diff-atoms.sh tools/baseline/atoms-v1.10.1.tsv /tmp/atoms-now.tsv | |
| - name: The release assets can still be built | |
| # Writes nothing. A renamed reference file leaves the skill installing and | |
| # answering from whatever it still has — a failure with no symptom. | |
| run: tools/build-release.sh --check |