Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion .github/workflows/gate-topology.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@ on:
# passing, and there was no way to fire it at the default branch and find
# out. `tri gates unmeasured` reports the gap; this is what closes it.
workflow_dispatch:
# Only the claw-t27f job below runs on this schedule (#10268).
schedule: [{cron: "*/10 * * * *"}]

# A run for a commit that has since been superseded measures nothing anyone
# will read. Without a group, every merge to master queued one more run of
# this gate behind the last: 62 were waiting on 2026-09-17 while the 15
# workflows that do carry a group had exactly one each.
concurrency:
group: gate-topology-${{ github.ref }}
group: gate-topology-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true

jobs:
gate-topology:
if: github.event_name != 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -72,3 +75,37 @@ jobs:
printf '#include <stdio.h>\n#include <string.h>\n#include "gen/c/claw/gates.c"\nint main(void){static unsigned char dir[65536],out[65536];size_t n=fread(dir,1,sizeof dir,stdin);int p=claw_status(dir,n,out,sizeof out);fwrite(out,1,strlen((const char*)out),stdout);return p>0?1:0;}\n' \
| cc -w -I . -x c -o "$d/claw-audit" -
(cd .github/workflows && ls *.yml | sed 's/\.yml$//') | "$d/claw-audit"

# claw/t27f (#10268): one seal-gated T27F check-run per open .t27 pull request in every repository
# the TRI CLAW App is installed on. The law is specs/claw/t27f_gate.t27 (gen/c/claw/t27f_gate.c); this
# job only mints the App token, fetches, runs the signed t27o release t27c and posts what the law prints.
claw-t27f:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-24.04
permissions: {contents: read}
steps:
- id: have
env: {K: "${{ secrets.CLAW_APP_PRIVATE_KEY }}", I: "${{ vars.CLAW_APP_ID }}"}
run: if [ -n "$K" ] && [ -n "$I" ]; then echo ok=1 >> "$GITHUB_OUTPUT"; else echo "claw/t27f posted nothing - secret CLAW_APP_PRIVATE_KEY or variable CLAW_APP_ID is not set (docs/claw/README.md)." >> "$GITHUB_STEP_SUMMARY"; fi
- {id: tok, if: steps.have.outputs.ok, uses: actions/create-github-app-token@v2, with: {app-id: "${{ vars.CLAW_APP_ID }}", private-key: "${{ secrets.CLAW_APP_PRIVATE_KEY }}", owner: gHashTag, permission-checks: write, permission-pull-requests: read, permission-contents: read}}
- {if: steps.have.outputs.ok, uses: actions/checkout@v6}
- {if: steps.have.outputs.ok, uses: sigstore/cosign-installer@v3}
- if: steps.have.outputs.ok
env: {GH_TOKEN: "${{ steps.tok.outputs.token }}", V: t27o-v0.1.0}
shell: bash {0}
run: |
set -e; cd /tmp; B=https://github.com/gHashTag/t27/releases/download/$V
for f in t27c checksums-$V.txt; do curl -fsSLo $f $B/$f; curl -fsSLo $f.sigstore.json $B/$f.sigstore.json
cosign verify-blob $f --bundle $f.sigstore.json --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp "^https://github.com/gHashTag/t27/.github/workflows/t27o-release.yml@refs/tags/$V$"; done
sha256sum -c --ignore-missing checksums-$V.txt; chmod +x t27c; T=/tmp/t27c
printf '#include "%s/gen/c/claw/t27f_gate.c"\n#include <stdio.h>\n#include <stdlib.h>\nint main(int c,char**v){size_t k=1<<24,m;uint8_t*b=malloc(k),*o=malloc(k);size_t n=fread(b,1,k,stdin);if(n==k)return 2;\nif(c>1){m=t27f_scope(b,n,o,k);if(m>k)return 3;}else m=t27f_report(b,n,o,65000);fwrite(o,1,m,stdout);return 0;}\n' "$GITHUB_WORKSPACE" > h.c; cc -O1 -w -o law h.c; L=/tmp/law; set +e
gh api --paginate /installation/repositories -q '.repositories[].full_name' | while read -r r; do
gh pr list -R $r -L 100 --json number,headRefOid,baseRefName,files -q '.[]|select(any(.files[];.path|endswith(".t27")))|"\(.number) \(.headRefOid) \(.baseRefName)"' </dev/null | while read -r n s b; do
[ "$(gh api "repos/$r/commits/$s/check-runs?check_name=claw/t27f" -q .total_count </dev/null)" = 0 ] || continue
( set -eo pipefail; rm -rf /tmp/w; git clone -q --filter=blob:none --no-checkout "https://x-access-token:$GH_TOKEN@github.com/$r" /tmp/w; cd /tmp/w
git fetch -q origin "pull/$n/head"; git sparse-checkout set --no-cone '*.t27' '/.trinity/seals/'; git checkout -q FETCH_HEAD
{ git diff --name-only --diff-filter=d "origin/$b...HEAD"; echo --graph; if [ -d specs ]; then $T use-edges --specs-dir specs; fi; } | $L s > ../scope || echo --unreadable > ../scope
{ printf 'repo\t%s\n' $r; while read -r f; do if [ "$f" = --unreadable ]; then echo $f; else printf '@\t%s\t%s\thttps://raw.githubusercontent.com/%s/%s/%s\n' "$f" "$(sha256sum < "$f" | cut -c1-64)" $r $s "$f"; $T seal "$f" --verify 2>&1 </dev/null || true; fi; done < ../scope; } | $L > ../out
jq -Rs --arg s $s 'split("\n") as $l | {name: $l[0], head_sha: $s, status: "completed", conclusion: $l[1], output: {title: $l[2], summary: ($l[3:] | join("\n"))}}' ../out | gh api -X POST repos/$r/check-runs --input - > /dev/null ); [ $? = 0 ] || echo "::warning::claw/t27f did not check $r#$n"
done
done
5 changes: 3 additions & 2 deletions .github/workflows/spec-guards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,11 @@ jobs:
# committed C would keep answering for a spec it no longer lowers.
# Same shape as t27o-golden's check mode (#8996): regenerate, then
# diff --exit-code.
- name: committed gen/c/claw/gates.c is what the spec lowers to today
- name: committed gen/c/claw/gates.c and t27f_gate.c are what their specs lower to today
run: |
./target/release/t27c gen-c specs/claw/gates.t27 > gen/c/claw/gates.c
git diff --exit-code -- gen/c/claw/gates.c
./target/release/t27c gen-c specs/claw/t27f_gate.t27 > gen/c/claw/t27f_gate.c
git diff --exit-code -- gen/c/claw/gates.c gen/c/claw/t27f_gate.c

# #7565: test only the specs this change can affect. specs/ci/affected.t27 picks them (the
# changed specs and every spec that imports one through `use`, transitively); the graph is the
Expand Down
14 changes: 7 additions & 7 deletions .trinity/seals/claw_ClawGates.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"built_by": "t27c-bootstrap@0.5.2+d2c8e7d41",
"built_by": "t27c-bootstrap@0.5.2+043cdea36",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:fab414aec1539c4212435e1ae3bdd41a070b4018a9a93f33b03e0ec4155b8379",
"gen_hash_rust": "sha256:e110e3736d4857882d3879cb7fbfad65a58d147e3b5264837edd8932fa184981",
"gen_hash_verilog": "sha256:c6716a19fcbbc555dc888aae7d5c7cb4b5af3ac90d7221f16eee449f2ba19017",
"gen_hash_zig": "sha256:7ca573a135276493df60266248f8fd1e4a890685064830c0e679e7dc02f58367",
"gen_hash_c": "sha256:97188898c4d8cda3d8bbb551de2a41cc67db142ee15f724bbfee30bb4cedd0c4",
"gen_hash_rust": "sha256:6192bfeb11dcb93a415eb8ef5f30a07d2588e58b0ea832f8ac99f4428a6829ee",
"gen_hash_verilog": "sha256:4ec80fa7604787bbb30bfee71cc6e838f4ac24e33348cbfbe46682182ff43b5e",
"gen_hash_zig": "sha256:3f6afb1b574d5a4346abf72b7a313ccae1316f56c04ef486c999fa2e160e4ee9",
"module": "ClawGates",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T05:03:44Z",
"sealed_at": "2026-10-18T07:27:06Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:9b6229226a0de2129fc9ca6fb890bd0aa923fce0fd880ce6ca85b299ab9e57d1",
"spec_hash": "sha256:2b49b0ada4cf5323472026378a33a07831bcc750ccc3a5a7b649e115dbcb6ab6",
"spec_path": "specs/claw/gates.t27",
"tests": {
"failed": 0,
Expand Down
6 changes: 3 additions & 3 deletions .trinity/seals/claw_ClawGatesPins.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"built_by": "t27c-bootstrap@0.5.2+d2c8e7d41",
"built_by": "t27c-bootstrap@0.5.2+043cdea36",
"closure": [
{
"spec": "specs/claw/gates.t27",
"spec_hash": "sha256:9b6229226a0de2129fc9ca6fb890bd0aa923fce0fd880ce6ca85b299ab9e57d1"
"spec_hash": "sha256:2b49b0ada4cf5323472026378a33a07831bcc750ccc3a5a7b649e115dbcb6ab6"
}
],
"config": "gen=c,rust,verilog,zig;tests=zig",
Expand All @@ -14,7 +14,7 @@
"module": "ClawGatesPins",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T05:03:44Z",
"sealed_at": "2026-10-18T07:27:08Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:e18a7800770e75607f68b089aaa38880e490276fbe6706372b0e302bb990238c",
"spec_path": "specs/claw/gates_pins.t27",
Expand Down
14 changes: 7 additions & 7 deletions .trinity/seals/claw_ClawService.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"built_by": "t27c-bootstrap@0.5.2+d2c8e7d41",
"built_by": "t27c-bootstrap@0.5.2+043cdea36",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:a46b772287b86e54f09b8b161a7a413325bede4b00b811709d7fc99a90e40f98",
"gen_hash_rust": "sha256:39154b909f8cc996ddf4a955407e5c9598c74e8ecbf245d114687b347a7e6624",
"gen_hash_verilog": "sha256:d5ff17eed0fd5470487a76f45ad79f6c943182b234d54abade1cfaf505ce506c",
"gen_hash_zig": "sha256:ab22e01d9a92e7bce4b694c4f0c52c3d28a8b763f1b133da262a331705fd5abd",
"gen_hash_c": "sha256:de9f70b57e80b4aa791a51d8ea356605a0c1c99611c7d25017654742fb5a6a39",
"gen_hash_rust": "sha256:5c38af81d488436c4cb4db98ade0c82a4a63a4fff1b9a4cfa6a24e68f89e391e",
"gen_hash_verilog": "sha256:4c83cf81d17f778a8a07ee15277e7a4685b479851f036f8048f9e8f35a46dbdf",
"gen_hash_zig": "sha256:23cdedf78a2b4e9a324ad4653ea746379a3beacfea7ded12fd1d4cd4cbf91f5a",
"module": "ClawService",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T05:03:45Z",
"sealed_at": "2026-10-18T07:27:09Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:ed78b892c6b5936d45ffad0fc4fa184b0616c757c057a611f7ca44f10b8257d0",
"spec_hash": "sha256:e704897a637b01492a2608aad832cb63461cbdf6f45222cd0c46e8728764381e",
"spec_path": "specs/claw/service.t27",
"tests": {
"failed": 0,
Expand Down
45 changes: 45 additions & 0 deletions .trinity/seals/claw_ClawT27fGate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"built_by": "t27c-bootstrap@0.5.2+043cdea36",
"closure": [
{
"spec": "specs/ci/affected.t27",
"spec_hash": "sha256:38a7bdc245f1974f0d5f1a55a45966decd70098afdb89e882f568da3650ee9f9"
},
{
"spec": "specs/policy/own_language.t27",
"spec_hash": "sha256:bba616b5794a7c390b13de07a81f0b2a066f22d6620ae349e5d164dddc17429a"
},
{
"spec": "specs/policy/text_lines.t27",
"spec_hash": "sha256:13c90895a4a44c760cce6b3b65f72f937298633062f040f8489486108ab0c2d1"
},
{
"spec": "specs/t27f/hot.t27",
"spec_hash": "sha256:a2ea398b37bd4d85e279b69a7e3d12d7944a2f849a2d231db24973f5731d557b"
}
],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:75cfffcfd6a4ba47ac96aa1c0ebbc232f9cbb9c85003eb458014abffc2ee8350",
"gen_hash_rust": "sha256:79e0722227eb57bccb498fb165c69f59e0f38b0d93e0bfb5b803263289c56e33",
"gen_hash_verilog": "sha256:1d75c13c148bb32ac17685f94d1dba57ec103911c3c0074bdfd169bc10882a91",
"gen_hash_zig": "sha256:a5fcd394b3d9538a3fc58fc2dad169db8ca0a43d722ee12f65a8a79dc8e2d55d",
"module": "ClawT27fGate",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T07:27:09Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:d971531b23c57834d5ffbfd200748db34f51a291f44b87716dcb02910f8e1993",
"spec_path": "specs/claw/t27f_gate.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 23,
"total": 23,
"vacuous": 0
},
"toolchain": {
"test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d",
"zig": "0.16.0"
}
}
37 changes: 37 additions & 0 deletions .trinity/seals/claw_ClawT27fGatePins.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"built_by": "t27c-bootstrap@0.5.2+043cdea36",
"closure": [
{
"spec": "specs/claw/t27f_gate.t27",
"spec_hash": "sha256:d971531b23c57834d5ffbfd200748db34f51a291f44b87716dcb02910f8e1993"
},
{
"spec": "specs/t27f/hot.t27",
"spec_hash": "sha256:a2ea398b37bd4d85e279b69a7e3d12d7944a2f849a2d231db24973f5731d557b"
}
],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:e4d2bd19107fff5f42908395ebd42a855bc4821f85014db435196d0a360aff79",
"gen_hash_rust": "sha256:4dc02033cea4ae56d81cca417e99faf450bfd97ffcbd7fed702b56b30119ce25",
"gen_hash_verilog": "sha256:a5a99dff99da4b4ebd3c15c210a92a3cb487039215158b2bb3c89a83eab168df",
"gen_hash_zig": "sha256:50ca11596eb89c8ffac6476a97349bd9efd9ec5e64ef6ea5ed1bf42793aeb8c9",
"module": "ClawT27fGatePins",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T07:27:10Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:8cc4c19346755daeec7f6ad0e004bbd5b496fdf105eef7c36b4db2bb976c6f34",
"spec_path": "specs/claw/t27f_gate_pins.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 4,
"total": 4,
"vacuous": 0
},
"toolchain": {
"test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d",
"zig": "0.16.0"
}
}
18 changes: 11 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,17 @@ reusable workflow. Epic #9271.
| Service contract: modes, exit codes, release tag `claw-v*`, check prefix `claw/` | [`specs/claw/service.t27`](specs/claw/service.t27) |
| Auto-fix recipes and the fix engine: which red check gets which deterministic fix | [`specs/claw/recipes.t27`](specs/claw/recipes.t27), [`specs/claw/engine.t27`](specs/claw/engine.t27) |
| Docs, release checklist, competitors | [`docs/claw/`](docs/claw/README.md) |
| The GitHub App | [github.com/apps/tri-claw](https://github.com/apps/tri-claw), id 5166765. Today it is the reviewer-bee identity ([`tools/bees/README.md`](tools/bees/README.md)): bees approve under its name, so a review is never the author approving itself |

In flight (2026-10-18): a `claw/t27f` check that the App posts on pull requests
in every gHashTag repository that holds `.t27` specs. It is seal-gated, so only
changed specs and their importers are checked again, and the summary links to
each spec's T27F graph. No file is added to consumer repositories. Until it
lands, read `docs/claw/README.md` for the status, not this paragraph.
| The GitHub App | [github.com/apps/tri-claw](https://github.com/apps/tri-claw), id 5166765, installed on all repositories. It is the reviewer-bee identity ([`tools/bees/README.md`](tools/bees/README.md)): bees approve under its name, so a review is never the author approving itself. It also posts the `claw/t27f` check below |
| T27F check `claw/t27f` (#10268): changed specs and their direct importers, judged by their seals | law [`specs/claw/t27f_gate.t27`](specs/claw/t27f_gate.t27), lowered to `gen/c/claw/t27f_gate.c`; run every 10 minutes by the `claw-t27f` job of `.github/workflows/gate-topology.yml` |

The `claw/t27f` check: one scheduled job in this repository posts it on every
open pull request that touches `.t27`, in each repository the App is installed
on. No consumer repository carries a workflow file. An unsealed spec is neutral
and a stale seal is failure; each summary row links to the spec's T27F graph.
It posts nothing until the owner finishes the four steps in
[`docs/claw/README.md`](docs/claw/README.md#the-t27f-check-in-every-t27-repository-10268):
App permission checks: write, accepting that permission, secret
`CLAW_APP_PRIVATE_KEY`, variable `CLAW_APP_ID`. That section has the status.

Adding a workflow file means adding its row to `gates.t27` in the same commit.
The drift gate refuses anything else.
Expand Down
16 changes: 16 additions & 0 deletions docs/claw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,25 @@ behind one front door, with the gate logic in `.t27` specs. Epic
| Drift audit law | `claw_status` in `specs/claw/gates.t27` | T2, this branch |
| Drift audit in CI | `gate-topology` workflow, "claw drift" step | T3, this branch |
| GitHub App manifest | release step, see `docs/claw/RELEASE.md` | T4, deferred to App setup |
| T27F check in every `.t27` repository | `specs/claw/t27f_gate.t27`, the `claw-t27f` job of `gate-topology.yml` | #10268, live once the owner steps below are done |
| Service skeleton | Railway, thin I/O glue only | T5, merged (spec contract) |
| Positioning / pricing | `docs/claw/competitors.md` | T6, started |

## The T27F check in every `.t27` repository (#10268)

Every 10 minutes the `claw-t27f` job of `gate-topology.yml` in gHashTag/t27 lists the repositories the TRI CLAW App
is installed on, and for each open pull request that touches `.t27` posts one `claw/t27f`
check-run on its head commit: the changed specs and their direct importers, each judged by
`t27c seal --verify` from the signed t27o-v0.1.0 release. The law is `specs/claw/t27f_gate.t27`:
unsealed is neutral; a stale, vacuous or unreadable seal is failure; gHashTag/ghashtag.github.io
only reports. No consumer repository carries a workflow file. Until the owner does these four
steps, it posts nothing:

1. Give the App the permission checks: write at https://github.com/settings/apps/tri-claw.
2. Accept the new permission on the App's installation for gHashTag.
3. Set the secret `CLAW_APP_PRIVATE_KEY` in gHashTag/t27 to the App's private key.
4. Set the variable `CLAW_APP_ID` in gHashTag/t27 to `5166765`.

## The inventory spec and how to regenerate it

`specs/claw/gates.t27` holds three parallel `[67]str`-shaped arrays — `NAMES`
Expand Down
Loading
Loading