Skip to content

Commit 9016a21

Browse files
Merge branch 'main' into findings-submissions
2 parents 3e05f3a + c1083db commit 9016a21

25 files changed

Lines changed: 1653 additions & 1077 deletions

.github/workflows/hypatia-scan.yml

Lines changed: 11 additions & 396 deletions
Large diffs are not rendered by default.

.github/workflows/mirror.yml

Lines changed: 3 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# SPDX-License-Identifier: MPL-2.0
2-
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell
32
name: Mirror to Git Forges
43

54
on:
@@ -11,135 +10,6 @@ permissions:
1110
contents: read
1211

1312
jobs:
14-
mirror-gitlab:
15-
runs-on: ubuntu-latest
16-
if: vars.GITLAB_MIRROR_ENABLED == 'true'
17-
steps:
18-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19-
with:
20-
fetch-depth: 0
21-
22-
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
23-
with:
24-
ssh-private-key: ${{ secrets.GITLAB_SSH_KEY }}
25-
26-
- name: Mirror to GitLab
27-
run: |
28-
ssh-keyscan -t ed25519 gitlab.com >> ~/.ssh/known_hosts
29-
git remote add gitlab git@gitlab.com:${{ vars.GITLAB_ORG || vars.MIRROR_ORG || github.repository_owner }}/${{ github.event.repository.name }}.git || true
30-
git push --force gitlab main
31-
32-
mirror-bitbucket:
33-
runs-on: ubuntu-latest
34-
if: vars.BITBUCKET_MIRROR_ENABLED == 'true'
35-
steps:
36-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
37-
with:
38-
fetch-depth: 0
39-
40-
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
41-
with:
42-
ssh-private-key: ${{ secrets.BITBUCKET_SSH_KEY }}
43-
44-
- name: Mirror to Bitbucket
45-
run: |
46-
ssh-keyscan -t ed25519 bitbucket.org >> ~/.ssh/known_hosts
47-
git remote add bitbucket git@bitbucket.org:${{ vars.BITBUCKET_ORG || vars.MIRROR_ORG || github.repository_owner }}/${{ github.event.repository.name }}.git || true
48-
git push --force bitbucket main
49-
50-
mirror-codeberg:
51-
runs-on: ubuntu-latest
52-
if: vars.CODEBERG_MIRROR_ENABLED == 'true'
53-
steps:
54-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
55-
with:
56-
fetch-depth: 0
57-
58-
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
59-
with:
60-
ssh-private-key: ${{ secrets.CODEBERG_SSH_KEY }}
61-
62-
- name: Mirror to Codeberg
63-
run: |
64-
ssh-keyscan -t ed25519 codeberg.org >> ~/.ssh/known_hosts
65-
git remote add codeberg git@codeberg.org:${{ vars.CODEBERG_ORG || vars.MIRROR_ORG || github.repository_owner }}/${{ github.event.repository.name }}.git || true
66-
git push --force codeberg main
67-
68-
mirror-sourcehut:
69-
runs-on: ubuntu-latest
70-
if: vars.SOURCEHUT_MIRROR_ENABLED == 'true'
71-
steps:
72-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
73-
with:
74-
fetch-depth: 0
75-
76-
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
77-
with:
78-
ssh-private-key: ${{ secrets.SOURCEHUT_SSH_KEY }}
79-
80-
- name: Mirror to SourceHut
81-
run: |
82-
ssh-keyscan -t ed25519 git.sr.ht >> ~/.ssh/known_hosts
83-
git remote add sourcehut git@git.sr.ht:~${{ vars.SOURCEHUT_ORG || vars.MIRROR_ORG || github.repository_owner }}/${{ github.event.repository.name }} || true
84-
git push --force sourcehut main
85-
86-
mirror-disroot:
87-
runs-on: ubuntu-latest
88-
if: vars.DISROOT_MIRROR_ENABLED == 'true'
89-
steps:
90-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
91-
with:
92-
fetch-depth: 0
93-
94-
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
95-
with:
96-
ssh-private-key: ${{ secrets.DISROOT_SSH_KEY }}
97-
98-
- name: Mirror to Disroot
99-
run: |
100-
ssh-keyscan -t ed25519 git.disroot.org >> ~/.ssh/known_hosts
101-
git remote add disroot git@git.disroot.org:${{ vars.DISROOT_ORG || vars.MIRROR_ORG || github.repository_owner }}/${{ github.event.repository.name }}.git || true
102-
git push --force disroot main
103-
104-
mirror-gitea:
105-
runs-on: ubuntu-latest
106-
if: vars.GITEA_MIRROR_ENABLED == 'true'
107-
steps:
108-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
109-
with:
110-
fetch-depth: 0
111-
112-
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
113-
with:
114-
ssh-private-key: ${{ secrets.GITEA_SSH_KEY }}
115-
116-
- name: Mirror to Gitea
117-
run: |
118-
ssh-keyscan -t ed25519 ${{ vars.GITEA_HOST }} >> ~/.ssh/known_hosts
119-
git remote add gitea git@${{ vars.GITEA_HOST }}:${{ vars.GITEA_ORG || vars.MIRROR_ORG || github.repository_owner }}/${{ github.event.repository.name }}.git || true
120-
git push --force gitea main
121-
122-
mirror-radicle:
123-
runs-on: ubuntu-latest
124-
if: vars.RADICLE_MIRROR_ENABLED == 'true'
125-
steps:
126-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
127-
with:
128-
fetch-depth: 0
129-
130-
- name: Setup Rust
131-
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # stable
132-
with:
133-
toolchain: stable
134-
135-
- name: Install Radicle
136-
run: |
137-
# Install via cargo (safer than curl|sh)
138-
cargo install radicle-cli --locked
139-
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
140-
141-
- name: Mirror to Radicle
142-
run: |
143-
echo "${{ secrets.RADICLE_KEY }}" > ~/.radicle/keys/radicle
144-
chmod 600 ~/.radicle/keys/radicle
145-
rad sync --announce || echo "Radicle sync attempted"
13+
mirror:
14+
uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@e6b2884722350515934d443daf23442f2195796f
15+
secrets: inherit

.github/workflows/scorecard.yml

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,16 @@
11
# SPDX-License-Identifier: MPL-2.0
2-
name: OSSF Scorecard
2+
name: Scorecards supply-chain security
3+
34
on:
4-
push:
5-
branches: [main, master]
5+
branch_protection_rule:
66
schedule:
7-
- cron: '0 4 * * *'
8-
workflow_dispatch:
9-
10-
# Estate guardrail: cancel superseded runs so re-pushes / rebased PR
11-
# updates do not pile up queued runs against the shared account-wide
12-
# Actions concurrency pool. Applied only to read-only check workflows
13-
# (no publish/mutation), so cancelling a superseded run is always safe.
14-
concurrency:
15-
group: ${{ github.workflow }}-${{ github.ref }}
16-
cancel-in-progress: true
7+
- cron: '23 4 * * 1'
8+
push:
9+
branches: [main]
1710

18-
permissions:
19-
contents: read
11+
permissions: read-all
2012

2113
jobs:
2214
analysis:
23-
runs-on: ubuntu-latest
24-
permissions:
25-
security-events: write
26-
id-token: write
27-
steps:
28-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
29-
with:
30-
persist-credentials: false
31-
32-
- name: Run Scorecard
33-
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.3.1
34-
with:
35-
results_file: results.sarif
36-
results_format: sarif
37-
38-
- name: Upload results
39-
uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v3.31.8
40-
with:
41-
sarif_file: results.sarif
15+
uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@e0caf11508a3989574713c78f5f444f2ce5e33ef
16+
secrets: inherit
Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
# SPDX-License-Identifier: MPL-2.0
2-
# Prevention workflow - scans for hardcoded secrets before they reach main
32
name: Secret Scanner
43

54
on:
65
pull_request:
76
push:
87
branches: [main]
98

10-
# Estate guardrail: cancel superseded runs so re-pushes / rebased PR
11-
# updates do not pile up queued runs against the shared account-wide
12-
# Actions concurrency pool. Applied only to read-only check workflows
13-
# (no publish/mutation), so cancelling a superseded run is always safe.
149
concurrency:
1510
group: ${{ github.workflow }}-${{ github.ref }}
1611
cancel-in-progress: true
@@ -19,63 +14,6 @@ permissions:
1914
contents: read
2015

2116
jobs:
22-
trufflehog:
23-
runs-on: ubuntu-latest
24-
steps:
25-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
26-
with:
27-
fetch-depth: 0 # Full history for scanning
28-
29-
- name: TruffleHog Secret Scan
30-
uses: trufflesecurity/trufflehog@37b77001d0174ebec2fcca2bd83ff83a6d45a3ab # v3
31-
with:
32-
# The v3 action injects --fail automatically on pull_request events.
33-
# Passing --fail here triggers "flag 'fail' cannot be repeated".
34-
extra_args: --only-verified
35-
36-
gitleaks:
37-
runs-on: ubuntu-latest
38-
steps:
39-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
40-
with:
41-
fetch-depth: 0
42-
43-
- name: Gitleaks Secret Scan
44-
uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2
45-
env:
46-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47-
48-
# Rust-specific: Check for hardcoded crypto values
49-
rust-secrets:
50-
runs-on: ubuntu-latest
51-
steps:
52-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
53-
54-
- name: Check for hardcoded secrets in Rust
55-
run: |
56-
if ! find . -name Cargo.toml -not -path './target/*' -print -quit | grep -q .; then
57-
echo 'No Cargo.toml found — skipping Rust secrets check'
58-
exit 0
59-
fi
60-
# Patterns that suggest hardcoded secrets
61-
PATTERNS=(
62-
'const.*SECRET.*=.*"'
63-
'const.*KEY.*=.*"[a-zA-Z0-9]{16,}"'
64-
'const.*TOKEN.*=.*"'
65-
'let.*api_key.*=.*"'
66-
'HMAC.*"[a-fA-F0-9]{32,}"'
67-
'password.*=.*"[^"]+"'
68-
)
69-
70-
found=0
71-
for pattern in "${PATTERNS[@]}"; do
72-
if grep -rn --include="*.rs" -E "$pattern" src/; then
73-
echo "WARNING: Potential hardcoded secret found matching: $pattern"
74-
found=1
75-
fi
76-
done
77-
78-
if [ $found -eq 1 ]; then
79-
echo "::error::Potential hardcoded secrets detected. Use environment variables instead."
80-
exit 1
81-
fi
17+
scan:
18+
uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@28fdf197963c898e6bb80053c74b8d886f1c189d
19+
secrets: inherit
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
#
4+
# SESSION-2026-05-26-148.a2ml — sustainabot ReScript→AffineScript hand-port
5+
# validation session record. Machine-readable companion to
6+
# `docs/archive/SESSION-2026-05-26-sustainabot-148-validation.md`.
7+
8+
[metadata]
9+
session-id = "2026-05-26-sustainabot-148"
10+
date = "2026-05-26T08:30:00Z"
11+
agent = "claude-opus-4-7"
12+
agent-context = "1M"
13+
issue = "hyperpolymath/gitbot-fleet#148"
14+
duration-hours = 1.5
15+
status = "complete-pending-owner-merge"
16+
17+
[goal]
18+
description = """Reduce every parse error in the 13 sustainabot .affine
19+
files (4,939 LOC hand-ported from .res) to either type-check or
20+
Resolve.UndefinedModule. The latter is the acceptable residual (stdlib
21+
graph not loaded by single-file `check`; INT-02 territory)."""
22+
23+
[scope.in]
24+
paths = [
25+
"bots/sustainabot/bot-integration/src/Analysis.affine",
26+
"bots/sustainabot/bot-integration/src/Config.affine",
27+
"bots/sustainabot/bot-integration/src/GitHubAPI.affine",
28+
"bots/sustainabot/bot-integration/src/GitHubApp.affine",
29+
"bots/sustainabot/bot-integration/src/Main.affine",
30+
"bots/sustainabot/bot-integration/src/Oikos.affine",
31+
"bots/sustainabot/bot-integration/src/Report.affine",
32+
"bots/sustainabot/bot-integration/src/Router.affine",
33+
"bots/sustainabot/bot-integration/src/Types.affine",
34+
"bots/sustainabot/bot-integration/src/Webhook.affine",
35+
"bots/sustainabot/bot-integration/src/tea/Cmd.affine",
36+
"bots/sustainabot/bot-integration/src/tea/Runtime.affine",
37+
"bots/sustainabot/bot-integration/src/tea/Sub.affine"
38+
]
39+
file-count = 13
40+
41+
[scope.out]
42+
note = """SafeDOMExample.affine fixtures in 3 other bots
43+
(the-hotchocolabot, echidnabot, finishingbot) use a pre-stabilization
44+
AffineScript dialect (8+ grammar divergences). Tracked separately at
45+
gitbot-fleet#208; needs single dialect-migration PR, NOT parser
46+
changes."""
47+
filed-issue = "hyperpolymath/gitbot-fleet#208"
48+
49+
[validation-oracle]
50+
command = "affinescript check <path>"
51+
pass-criteria = "parse layer succeeds — `Type checking passed` OR `Resolution error: (Resolve.UndefinedModule …)`"
52+
fail-criteria = "any line starting with `<path>:<line>:<col>: parse error: Syntax error`"
53+
54+
[validation-oracle.before-session]
55+
files-passing = 0
56+
files-with-resolution-residual = 1 # tea/Sub.affine — only file simple enough to reach Resolution unaided
57+
files-with-parse-errors = 12
58+
59+
[validation-oracle.after-session]
60+
files-passing = 0 # full typecheck not in scope — stdlib not loaded
61+
files-with-resolution-residual = 13
62+
files-with-parse-errors = 0
63+
64+
[parser-prs]
65+
repo = "hyperpolymath/affinescript"
66+
count = 5
67+
conflict-cost-total = "+0 S/R, +0 R/R (parser builds at 21 S/R + 1 R/R, baseline unchanged)"
68+
prs = [
69+
{ number = 370, branch = "claude/parser-trailing-comma-148", scope = "trailing-comma in fn params + expr lists; effect-annotated lambda" },
70+
{ number = 371, branch = "claude/parser-fn-type-eff-arrow-148", scope = "fn-type with effect arrow `fn(A) -{E}-> B` in type position" },
71+
{ number = 372, branch = "claude/parser-builtin-qualified-paths-148", scope = "builtin/lowercase qualified paths + TOTAL field name" },
72+
{ number = 373, branch = "claude/lexer-underscore-idents-148", scope = "underscore-prefix idents `_key`/`_unused`" },
73+
{ number = 376, branch = "claude/parser-record-spread-148", scope = "record-update spread at start `#{ ..base, f: v }`" }
74+
]
75+
76+
[handport-prs]
77+
repo = "hyperpolymath/gitbot-fleet"
78+
count = 1
79+
prs = [
80+
{ number = 206, branch = "claude/sustainabot-parse-fixes-148", scope = "OCaml-style float ops → unified ops; `fn handle` → `fn dispatch` (HANDLE is a keyword)" }
81+
]
82+
83+
[ci-status]
84+
all-prs-real-signal = "green"
85+
known-baseline-noise = [
86+
"vscode-smoke (affinescript) — fails on every PR; npm 404 on @hyperpolymath/affine-vscode (affinescript#104)",
87+
"governance / Language / package anti-pattern policy (affinescript) — flags approved TypeScript exemptions"
88+
]
89+
90+
[gotchas-captured]
91+
description = """Rules surfaced during the session that apply to ALL
92+
future .affine hand-ports (not just sustainabot). Captured in agent
93+
persistent memory."""
94+
rules = [
95+
{ name = "handle-is-keyword", body = "`handle` is HANDLE token, parse-fails as fn name. Rename to `dispatch`/`handle_request` on hand-port.", scope = "hand-port-rewrite" },
96+
{ name = "no-ocaml-float-ops", body = "`+. -. *. /.` never accepted. Mechanical hand-port-rewrite to unified `+ - * /`, never parser-fix.", scope = "hand-port-rewrite" }
97+
]
98+
99+
[next-gate]
100+
type = "owner-merge"
101+
description = """All 6 PRs CI-green and merge-ready. Issue #148 is NOT
102+
auto-closed by any `Refs` keyword (ISSUE-CLOSURE rule). Owner-merge
103+
of the 6 PRs is the prerequisite to closing #148."""
104+
105+
[downstream-blocked]
106+
description = "What unblocks after #148 closes (next-gate state):"
107+
items = [
108+
"Resolution → typecheck cross-module loader work (INT-02 in affinescript docs/TECH-DEBT.adoc)",
109+
"Bigger .res → .affine sweep via tree-sitter walker (affinescript#57 Phase 2)"
110+
]
111+
112+
[artefacts]
113+
human-readable = "docs/archive/SESSION-2026-05-26-sustainabot-148-validation.md"
114+
issue-thread-comment = "https://github.com/hyperpolymath/gitbot-fleet/issues/148#issuecomment-4542225835"
115+
116+
[session.signature]
117+
agent = "claude-opus-4-7"
118+
verified-by = "validation-oracle re-run on integrated branch with all 6 PRs cherry-picked"
119+
verified-at = "2026-05-26T09:30:00Z"

0 commit comments

Comments
 (0)