Skip to content

Commit e366109

Browse files
feat(gsbot)!: faithful Rust/SPARK port — eliminate Python from the fleet (#145)
* feat(gsbot)!: faithful Rust/SPARK port — eliminate Python from the fleet gsbot (Garment Sustainability Bot) was the fleet's only Python code (35 .py files) and the source of the 35 `cicd_rules/banned_language_file` critical findings. Python is banned estate-wide with no exceptions; this is a behaviour-preserving rewrite in Rust/SPARK, the sanctioned target. Stack (fleet conventions; gsbot is the fleet's first Discord bot): poise/serenity (prefix commands ↔ discord.py cogs), sqlx+SQLite (SQLAlchemy/Alembic ↔ models + embedded migrations), tracing (colorlog), tokio. Rust/SPARK seam: `src/domain.rs` is the correctness-critical scoring kernel — pure, total functions plus a `#[no_mangle] extern "C"` FFI surface so a verified SPARK/Ada module can be linked in later via the standard Idris2-ABI / Zig-FFI pattern (per the standard: "designed to admit SPARK modules even if it contains none yet"). All callers go through safe wrappers, so substitution is transparent. Faithful behaviour: - Identical env vars/defaults/validation (config.rs ↔ settings.py). - Same schema incl. garment_materials M2M; same scoring (material mean, grade ladder, garment lifespan multiplier + 100 cap, environmental impact averaging, user points/level ratchet, ranks, brand summary). - Every command preserved: names, aliases, argument shapes, point awards (+5/+5/+3/+2 / +5/+7/0 / +5 / 0/0 / admin), embed fields, the `on_command_error` message mapping, presence string. - `!loaddata` fixtures: same 10 materials / 7 garments / 6 brands. - scripts/*.py → 3 bins (gsbot-load-fixtures/-export-data/-backup-db). - Containerfile/docker-compose/Justfile rewritten for Rust (fleet convention: rust builder + debian-slim, non-root); Mustfile recipes (lint/test/fmt) preserved. Verification: `cargo build --all-targets` clean; `cargo clippy --all-targets -- -D warnings` clean; `cargo fmt --check` clean; `cargo test` 18/18 pass (domain 9, models 5, services 4). All Python removed (requirements.txt, setup.py, pytest.ini, alembic.ini, 35 .py) — `banned_language_file` clears for gsbot. Refs hyperpolymath/hypatia#252 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(gsbot): render real gateway latency in !stats Closes the one deliberate fidelity gap in the Rust/SPARK port: !stats showed a hardcoded `0ms` where the Python bot rendered `bot.latency * 1000`. poise's `ctx.ping()` is the gateway heartbeat latency (serenity `Shard::latency()`), the exact equivalent — zero only until the shard's first heartbeat ack, matching discord.py. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(gsbot): rewrite all docs for the Rust/SPARK implementation Every doc still described the deleted Python bot. Faithfully rewritten from the actual source: poise/serenity + sqlx/SQLite + tracing, the 4 cargo binaries, env vars, command surface, and the pure `domain.rs` kernel documented as the C-ABI SPARK verification seam. CLAUDE.md's stale Python-proposing template replaced with an accurate agent guide that states the estate Rust/SPARK standard governs and Hypatia self-scans this repo (no Python, ever). docs/ and content/docs/ both updated; CHANGELOG keeps the Python era as labelled history. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: resolve gitbot-fleet legacy hypatia findings at source; de-baseline Root-cause sweep of the legacy `.hypatia-baseline.json` (99 -> 59). Every real defect is fixed; every false positive is suppressed at the precise site with a documented inline directive; only genuinely-still-present entries remain baselined. REAL FIXES (idiomatic Rust/SPARK: propagate with `?`, `.expect(<invariant>)` for constant literals, explicit lock/rwlock poisoning handling — never a bare `.unwrap()` or silent dangerous default). Each crate verified green (build + clippy -D warnings + test): - cipherbot: 74 `Regex::new(..).unwrap()` -> `.expect("static regex is valid")` across 11 analyzers; completed the non-compiling `hashing.rs` stub; policy.rs clippy. - echidnabot: adapters {bitbucket,github,gitlab} clone-path + response parsing now propagate via `?`/`ok_or_else` (were `.unwrap()` / silent wrong defaults); stale test port assertion corrected at source. - finishingbot: analyzer regexes already `?`/`.ok()?`/`.expect()`; fixed pre-existing in-crate clippy blockers (default_constructed_unit_structs). - glambot: 3 constant accessibility regexes -> `.expect(..)`. - seambot: critical `count_seams(..).unwrap_or(0)` (masked a corrupt seam register as "0 seams, clean") -> `?` + context; rwlock anti-pattern fixed. - sustainabot-analysis: critical `load_snapshot` `.unwrap_or(0.0)` (fabricated false critical health regressions) -> anyhow context propagation; `compute_velocity` bounds `.expect(..)`. - the-hotchocolabot: mock Mutex `.lock().unwrap()` -> poisoning-safe `.unwrap_or_else(|e| e.into_inner())`; unwrap_without_check -> `.expect`; resolved pre-existing crate breakage so it builds/tests green (safety- relevant EuroBot hot-chocolate robot — robustness matters even in mocks). - shared-context: 15 bench `.unwrap()` -> `.expect("bench setup: ..")`. FALSE POSITIVES — suppressed with file-level `hypatia: allow` directives naming the rule and the reason (fix > inline > ignore > baseline): - 9 security_errors/secret_detected: cipherbot config.rs/infra.rs are secret-DETECTION analyzers (their pattern literals are not credentials); echidnabot {toml,example.toml} + docs and sustainabot DEPLOY.md / GITHUB_APP_SETUP.md are documentation placeholders; scripts/ fix-{hardcoded-secrets,secret-to-env}.sh are remediation tools whose illustration strings are the patterns they rewrite. - 2 code_safety/shell_download_then_run: scripts/fix-{eval-to-safe, heredoc-install}.sh are remediation tools that necessarily contain the curl|bash/eval patterns they search for and annotate. RESOLVED AT SOURCE: - root_hygiene/banned docker-compose.yml: renamed `docker-compose.yml` -> `compose.yml` (compose-spec canonical; podman-compose compatible) and pointed both build refs at the existing `Containerfile`s (the referenced `dashboard/Dockerfile` / `shared-context/fleet-cli/Dockerfile` did not exist — a real broken-build bug); updated tests/e2e.sh. Aligns with the estate Docker-policy change (Podman/Containerfile highly preferred; hypatia PR hyperpolymath/hypatia#255). REMAIN BASELINED (legitimately, with rationale): - 35 cicd_rules/banned_language_file: gsbot Python — still present on `main`; eliminated by the Rust/SPARK port PR #145. Baseline reduction tracked there (item C), not here. - 22 migration_rules/deprecated_api + 2 code_safety/obj_magic: all `bots/sustainabot/bot-integration/**/*.res` — ReScript is hands-off for bulk ops per estate policy; these are a deliberate, documented policy baseline, not unaddressed negligence. Refs hyperpolymath/hypatia#252 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): repair two pre-existing estate-wide workflow bugs - rsr-antipattern.yml: remove an orphaned dead second copy of the TypeScript-check program (lines after the first heredoc's PYEOF had no python3 opener, so bash executed 'BUILTIN_GLOBS = [' -> exit 127). The first step is complete and passes; the duplicate was a botched prior edit. Fails identically on main (pre-existing, not gsbot-port). - secret-scanner.yml: trufflehog action already passes --fail; the extra_args also passed --fail -> 'flag fail cannot be repeated'. Drop the duplicate, keep --only-verified. Pre-existing on main. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): post-port Hypatia baseline + repair pre-existing A2ML/K9 dogfood-gate Closes the remaining #145 CI blockers (all pre-existing estate-wide, not introduced by the gsbot port — dogfood-gate fails identically on main): - .hypatia-baseline.json regenerated for the post-port reality: Python is eliminated (35 banned_language_file entries dropped — files no longer exist), every real defect is fixed and every false positive is suppressed via inline directives (merged from the resolve-at-source sweep), so the baseline is now exactly the 24 deliberately-retained ReScript bot-integration entries (22 migration_rules/deprecated_api + 2 code_safety/obj_magic; ReScript is hands-off per estate policy). This is item C, folded into #145. - A2ML: 6 `.a2ml` manifests lacked the validator's required identity field (agent-id|name|project). Added a meaningful identity key to each (CICD-PATTERNS, CLADE, anchors/ANCHOR, agent_instructions/{coverage, debt,methodology}); verified 0 identity errors against the upstream hyperpolymath/a2ml-validate-action logic. - K9: deploy-bot-fleet.k9.ncl used the flat pedigree shape with no name; added `pedigree.name = "deploy-bot-fleet"` (validator accepts pedigree.name). Verified 0 errors with the upstream hyperpolymath/k9-validate-action (strict:false, as the workflow runs). The gsbot Rust/SPARK port itself introduces no new critical/high Hypatia findings (0 `.unwrap()`, no unsafe/panic!/secret literals; `unwrap_or` uses are benign config defaults faithful to the Python original). Refs hyperpolymath/hypatia#252 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): repair Hypatia baseline-identity bug + robust A2ML/K9 fixes Reproduced all three remaining #145 failures against the EXACT CI-pinned validators and fixed at source: - Hypatia gate (.github/workflows/hypatia-scan.yml): the matcher projected findings as a 5-tuple including `action` and did exact object equality, but its own contract comment says identity is the 4-tuple {severity,rule_module,type,file}. `action` is advisory, varies by run/strategy, and made every baseline entry fail to match (all 34 deliberately-baselined ReScript findings reported "new"). Dropped `action` from the projection and the regen hint so the documented 4-tuple is used; the 24-entry ReScript baseline now matches (Router.res ×3 etc. all collapse to one baseline entry). - A2ML (13 errors): the CI action is pinned to b2f28c39, an older, stricter validator than `main` (its is_manifest only exempts *AI-MANIFEST*; 6a2 typed manifests + contractiles are NOT exempt — why the earlier main-based simulation was wrong). Added a top-level `name` identity field to the 13 genuinely-failing files (.machine_readable/6a2/{AGENTIC,META,NEUROSYM,PLAYBOOK}, contractiles dust/trust/intend/must, robot-repo-automaton/6a2/*). - K9 (6 errors): the pinned validator (f985acb6) has a brace-tracking quirk — it doesn't count the `{` on the `pedigree =` line, so the first nested `}` ends pedigree scope prematurely and a `pedigree.metadata.name` is seen as outside pedigree. Added `name` as the FIRST pedigree field (before any nested block) to the 6 robot-repo-automaton k9 templates/examples. Verified locally with the byte-exact CI-pinned validator scripts (INPUT_STRICT=false, GITHUB_OUTPUT set): both now exit 0, Errors: 0. antipattern-check and trufflehog already pass from the prior commit. Refs hyperpolymath/hypatia#252 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6426272 commit e366109

172 files changed

Lines changed: 10632 additions & 6474 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/hypatia-scan.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,14 @@ jobs:
206206
# build paths normalised to repo-relative.
207207
if [ -f .hypatia-baseline.json ]; then
208208
# Normalise + project just the comparable keys from the current scan.
209+
# Identity is the 4-tuple (severity, rule_module, type, file) per
210+
# the contract above. `action` is advisory metadata that varies
211+
# by run/strategy and MUST NOT be part of finding identity —
212+
# including it made every baseline entry fail to match.
209213
jq '[ .[] | select(.severity == "critical" or .severity == "high")
210214
| {severity, rule_module, type,
211215
file: (.file | sub("^/home/runner/work/[^/]+/[^/]+/"; "")
212-
| sub("^/github/workspace/"; "")),
213-
action} ]' hypatia-findings.json > findings-current.json
216+
| sub("^/github/workspace/"; "")) } ]' hypatia-findings.json > findings-current.json
214217
215218
# Subtract baseline. A current finding is "new" iff there's no
216219
# element in baseline equal to it (by-value).
@@ -224,7 +227,7 @@ jobs:
224227
jq -r '.[] | " [\(.severity)] \(.rule_module)/\(.type) — \(.file)"' findings-new.json
225228
echo
226229
echo "If these are intentional, regenerate .hypatia-baseline.json:"
227-
echo " jq '[.[] | select(.severity == \"critical\" or .severity == \"high\") | {severity, rule_module, type, file, action}] | sort_by(.severity, .rule_module, .type, .file)' hypatia-findings.json > .hypatia-baseline.json"
230+
echo " jq '[.[] | select(.severity == \"critical\" or .severity == \"high\") | {severity, rule_module, type, file}] | sort_by(.severity, .rule_module, .type, .file)' hypatia-findings.json > .hypatia-baseline.json"
228231
exit 1
229232
fi
230233
echo "All critical/high findings present in baseline — gate passes."

.github/workflows/rsr-antipattern.yml

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -138,83 +138,6 @@ jobs:
138138
print(f"✅ No TypeScript files outside allowlist ({len(exemption_patterns)} per-repo exemption(s) parsed).")
139139
PYEOF
140140
141-
# Universal builtin allowlist — bridges that need no per-repo declaration.
142-
# Files matching any of these patterns are always allowed.
143-
BUILTIN_GLOBS = [
144-
'*.d.ts',
145-
'**/bindings/**',
146-
'**/tests/**', '**/test/**',
147-
'**/scripts/**',
148-
'**/mcp-adapter/**',
149-
'**/*vscode*/**',
150-
'**/cli/**',
151-
'**/mod.ts',
152-
'**/lsp-server.ts', '**/lsp_server.ts', '**/lsp.ts', '**/*-lsp.ts',
153-
'**/deno-*/**',
154-
'**/node_modules/**',
155-
'**/vendor/**',
156-
'**/examples/**',
157-
'**/ffi/**',
158-
]
159-
160-
# Per-repo exemptions parsed from .claude/CLAUDE.md "TypeScript Exemptions" table.
161-
# Single source of truth — adding a row here unblocks CI for that path.
162-
# Format expected:
163-
# ### TypeScript Exemptions ...
164-
# | Path | Files | Rationale | Unblock condition |
165-
# |---|---|---|---|
166-
# | `path/to/file.ts` | 1 | ... | ... |
167-
# | `dir/*.ts` | 6 | ... | ... |
168-
exemptions = []
169-
claude_md = pathlib.Path('.claude/CLAUDE.md')
170-
if claude_md.exists():
171-
in_table = False
172-
for line in claude_md.read_text(encoding='utf-8').splitlines():
173-
if re.search(r'TypeScript [Ee]xemptions', line):
174-
in_table = True
175-
continue
176-
if in_table and line.startswith(('### ', '## ', '# ')):
177-
break
178-
if in_table and line.startswith('|'):
179-
m = re.match(r'\|\s*`([^`]+)`', line)
180-
if m:
181-
exemptions.append(m.group(1))
182-
183-
# Find all .ts and .tsx files
184-
found = []
185-
for ext in ('ts', 'tsx'):
186-
found.extend(str(p) for p in pathlib.Path('.').rglob(f'*.{ext}'))
187-
188-
def allowed(path):
189-
p = path.lstrip('./')
190-
for g in BUILTIN_GLOBS + exemptions:
191-
if fnmatch.fnmatchcase(p, g):
192-
return True
193-
# also treat glob ending with / as a directory prefix
194-
base = g.rstrip('/').rstrip('*').rstrip('/')
195-
if base and (p == base or p.startswith(base + '/')):
196-
return True
197-
return False
198-
199-
bad = sorted(f for f in found if not allowed(f))
200-
if bad:
201-
print("❌ TypeScript files detected outside the allowlist.\n")
202-
for f in bad:
203-
print(f" {f}")
204-
print()
205-
print("To resolve, either:")
206-
print(" (a) migrate the file to AffineScript")
207-
print(" (see Human_Programming_Guide.adoc migration chapter), OR")
208-
print(" (b) move it to an allowlisted bridge path")
209-
print(" (bindings/, tests/, scripts/, mcp-adapter/, *vscode*/, cli/, deno-*/, etc.), OR")
210-
print(" (c) add an entry to the 'TypeScript Exemptions' table in .claude/CLAUDE.md")
211-
print(" with rationale + unblock condition.")
212-
if exemptions:
213-
print(f"\n(Currently {len(exemptions)} exemption(s) parsed from .claude/CLAUDE.md.)")
214-
sys.exit(1)
215-
print(f"✅ No TypeScript files outside allowlist ({len(exemptions)} per-repo exemption(s) parsed).")
216-
PYEOF
217-
218141
- name: Check for Go
219142
run: |
220143
if find . -name "*.go" | grep -q .; then

.github/workflows/secret-scanner.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ jobs:
2121
- name: TruffleHog Secret Scan
2222
uses: trufflesecurity/trufflehog@37b77001d0174ebec2fcca2bd83ff83a6d45a3ab # v3
2323
with:
24-
extra_args: --only-verified --fail
24+
# The action already passes --fail itself; repeating it errors
25+
# ("flag 'fail' cannot be repeated"). Only pass --only-verified.
26+
extra_args: --only-verified
2527

2628
gitleaks:
2729
runs-on: ubuntu-latest

0 commit comments

Comments
 (0)