From 5178d63c159e4f9c93be9f2511be7167a17df5c2 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:27:38 +0100 Subject: [PATCH] feat(action): composite GitHub Action + Climate Warrior positioning doc Generation-1 completion surface: - action.yml: container-based composite action (report/check/compare modes, .oikos.yml passthrough, Pareto trade-off check inputs) so consumers can 'uses: hyperpolymath/oikosbot@' with no Rust toolchain. Runs ghcr.io/hyperpolymath/oikos with the workspace mounted, non-root UID. - docs/COMPARISON-climate-warrior.adoc: point-in-time positioning vs the nearest Marketplace neighbour (a storage-with-SCI GitHub App; overlap is the SCI vocabulary only). Includes an honesty section on OikosBot's own gaps (heuristic estimates, App mode pending). - README: GitHub Action usage + comparison link. - ROADMAP/STATE.a2ml: record gen-1 landings and the publish-image root cause (GHCR package Actions-access, not a repo defect). Co-Authored-By: Claude Fable 5 --- .machine_readable/6a2/STATE.a2ml | 6 +- README.adoc | 22 ++++++ ROADMAP.adoc | 6 ++ action.yml | 106 ++++++++++++++++++++++++++ docs/COMPARISON-climate-warrior.adoc | 110 +++++++++++++++++++++++++++ 5 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 action.yml create mode 100644 docs/COMPARISON-climate-warrior.adoc diff --git a/.machine_readable/6a2/STATE.a2ml b/.machine_readable/6a2/STATE.a2ml index 31eea6f..178d2a6 100644 --- a/.machine_readable/6a2/STATE.a2ml +++ b/.machine_readable/6a2/STATE.a2ml @@ -5,14 +5,14 @@ [metadata] project = "oikosbot" version = "0.1.0-dev" -last-updated = "2026-06-21" +last-updated = "2026-07-28" status = "active" -session = "2026-06-21 close-out of the post-extraction work: fleet bridge → BotId::Oikosbot + ReScript-era containers removed (#5); finding taxonomy in NEUROSYM.a2ml [finding-taxonomy] + policies/finding_taxonomy.ecl (#9); robot-repo-automaton build fix + Rust build/test/clippy CI gate (gitbot-fleet); stale-identity sweep across SECURITY/CLAUDE/META (#11); standards reusable-workflow pin refresh (#13); LICENSE dual-SPDX MPL-2.0 + CC-BY-SA-4.0, SECURITY.md finalized (reporting → j.d.a.jewell@open.ac.uk), redundant trufflehog job dropped (#14); added docs/README.adoc documentation map. Open follow-ups: #12 (taxonomy vocab reconciliation), #16 (developer+maintainer docs + README split), #17 (end-user docs), #18 (taxonomy tags through Finding types)." +session = "2026-07-28 generation-1 go-live: Pareto engine made executable (#42 — crates/oikosbot-pareto: ε-tolerant dominance, normalized weighted frontier, base-vs-head verdicts with confidence gating, oikosbot compare, SARIF pareto_* properties, EconScore composition per ARCHITECTURE); .oikos.yml --config + auto-discovery (estate configs + governance flag now real); push-email-notify removed (dual-use ruling); publish-image root-caused to GHCR package access (permission_denied: write_package — owner grant, Containerfile verified sound via podman); composite action.yml + docs/COMPARISON-climate-warrior.adoc. Rulings: Action-mode first then App; NO interim listener (upstream AffineScript Http::Server instead); advisor + machine-checked trade-offs; Scallop replaces DeepProbLog. Prior session: 2026-06-21 close-out of the post-extraction work: fleet bridge → BotId::Oikosbot + ReScript-era containers removed (#5); finding taxonomy in NEUROSYM.a2ml [finding-taxonomy] + policies/finding_taxonomy.ecl (#9); robot-repo-automaton build fix + Rust build/test/clippy CI gate (gitbot-fleet); stale-identity sweep across SECURITY/CLAUDE/META (#11); standards reusable-workflow pin refresh (#13); LICENSE dual-SPDX MPL-2.0 + CC-BY-SA-4.0, SECURITY.md finalized (reporting → j.d.a.jewell@open.ac.uk), redundant trufflehog job dropped (#14); added docs/README.adoc documentation map. Open follow-ups: #12 (taxonomy vocab reconciliation), #16 (developer+maintainer docs + README split), #17 (end-user docs), #18 (taxonomy tags through Finding types)." [project-context] name = "OikosBot" purpose = "OikosBot — an ecological & economic code-analysis GitHub/GitLab App. NB: the similarly-named 'oikos' DSL (separate repo hyperpolymath/oikos-economics-accounting-dsl) and the 'sustainabot' gitbot-fleet slot are SEPARATE projects — see DISAMBIGUATION.adoc." -completion-percentage = 40 +completion-percentage = 50 [position] phase = "implementation" # design | implementation | testing | maintenance | archived diff --git a/README.adoc b/README.adoc index 247bb12..2a18bc5 100644 --- a/README.adoc +++ b/README.adoc @@ -124,6 +124,28 @@ link:DISAMBIGUATION.adoc[`DISAMBIGUATION.adoc`]. `just affine-check` expects an AffineScript compiler. Override the compiler path with `AS_BIN=/path/to/main.exe` when needed. +== GitHub Action + +OikosBot runs in CI as a composite action wrapping the published container +(`ghcr.io/hyperpolymath/oikos`): + +[source,yaml] +---- +- uses: hyperpolymath/oikosbot@main # pin a tag/SHA in production + with: + mode: report # report | check | compare + path: . + output: results.sarif # upload with github/codeql-action/upload-sarif +---- + +`compare` mode issues a Pareto verdict against a base checkout (see +link:QUICKSTART.adoc[QUICKSTART]). A repo-local `.oikos.yml` is honored +automatically. Full workflow example: +link:examples/oikosbot-ci.yml[`examples/oikosbot-ci.yml`]. + +How OikosBot relates to the nearest Marketplace neighbour: +link:docs/COMPARISON-climate-warrior.adoc[OikosBot vs Climate Warrior]. + == GitHub App The app manifest lives at link:.github/app.yml[`.github/app.yml`]. The setup diff --git a/ROADMAP.adoc b/ROADMAP.adoc index e6fca1b..22ffd95 100644 --- a/ROADMAP.adoc +++ b/ROADMAP.adoc @@ -36,6 +36,12 @@ Status legend: `done` | `in-progress` | `blocked` | `planned` * [x] `.oikos.yml` config support in the CLI (`--config` + auto-discovery): mode, thresholds, exclude globs, languages — makes the estate consumer configs and the governance workflow's `--config` flag real +* [x] Composite GitHub Action (`action.yml`, container-based) — OikosBot + consumable as `uses: hyperpolymath/oikosbot@`; Marketplace-shaped +* [x] Marketplace positioning doc: `docs/COMPARISON-climate-warrior.adoc` +* [x] publish-image root-caused: GHCR `oikos` package lacked Actions write + access for this repo (`permission_denied: write_package`); fixed by owner + grant, not by code — the Containerfile was verified sound via local podman === Remaining diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..e31fa3e --- /dev/null +++ b/action.yml @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell +# +# OikosBot composite action — ecological & economic code analysis with +# Pareto verdicts. Runs the published CLI container (ENTRYPOINT oikosbot), +# so consumers need no Rust toolchain. See examples/oikosbot-ci.yml for a +# full workflow and docs/ for interpretation guidance. +name: 'OikosBot Sustainability Analysis' +description: >- + Ecological & economic code analysis: carbon/energy estimates, Pareto + verdicts on pull requests (improvement / regression / trade-off), and + SARIF output for GitHub code scanning. Honors the repo's .oikos.yml. +author: 'Jonathan D.A. Jewell' +branding: + icon: 'trending-down' + color: 'green' + +inputs: + mode: + description: 'Subcommand: report (SARIF, default), check (threshold gate), compare (Pareto verdict vs base)' + required: false + default: 'report' + path: + description: 'Directory to analyze (or head directory for compare)' + required: false + default: '.' + base: + description: 'Base directory for compare mode (e.g. a checkout of the target branch)' + required: false + default: '' + format: + description: 'Output format: sarif | json | text' + required: false + default: 'sarif' + output: + description: 'Output file path (empty = stdout)' + required: false + default: 'results.sarif' + config: + description: 'Path to .oikos.yml (empty = auto-discover in path)' + required: false + default: '' + eco-threshold: + description: 'Eco-score floor 0-100 (empty = config value, else 50)' + required: false + default: '' + pr-body: + description: 'File with the PR description, for the Pareto trade-off documentation check (compare mode)' + required: false + default: '' + check: + description: 'compare mode: fail on an undocumented, measured Pareto regression/trade-off (true/false)' + required: false + default: 'false' + image: + description: 'Container image to run' + required: false + default: 'ghcr.io/hyperpolymath/oikos:latest' + +outputs: + output-file: + description: 'Path of the written output file (when output is set)' + value: ${{ inputs.output }} + +runs: + using: 'composite' + steps: + - name: Run oikosbot + shell: bash + env: + MODE: ${{ inputs.mode }} + TARGET: ${{ inputs.path }} + BASE: ${{ inputs.base }} + FORMAT: ${{ inputs.format }} + OUTFILE: ${{ inputs.output }} + CONFIG: ${{ inputs.config }} + THRESHOLD: ${{ inputs.eco-threshold }} + PR_BODY: ${{ inputs.pr-body }} + DO_CHECK: ${{ inputs.check }} + IMAGE: ${{ inputs.image }} + run: | + set -euo pipefail + args=("$MODE") + case "$MODE" in + compare) + [ -n "$BASE" ] || { echo "::error::compare mode requires the 'base' input"; exit 1; } + args+=("$BASE" "$TARGET") + [ "$DO_CHECK" = "true" ] && args+=(--check) + [ -n "$PR_BODY" ] && args+=(--pr-body "$PR_BODY") + [ "$FORMAT" = "json" ] && args+=(--format json) + ;; + report|check) + args+=("$TARGET" --format "$FORMAT") + ;; + *) + echo "::error::unknown mode '$MODE' (report | check | compare)"; exit 1 + ;; + esac + [ -n "$OUTFILE" ] && args+=(--output "$OUTFILE") + [ -n "$CONFIG" ] && args+=(--config "$CONFIG") + [ -n "$THRESHOLD" ] && args+=(--eco-threshold "$THRESHOLD") + docker run --rm \ + --user "$(id -u):$(id -g)" \ + -v "$GITHUB_WORKSPACE:$GITHUB_WORKSPACE" \ + -w "$GITHUB_WORKSPACE" \ + "$IMAGE" "${args[@]}" diff --git a/docs/COMPARISON-climate-warrior.adoc b/docs/COMPARISON-climate-warrior.adoc new file mode 100644 index 0000000..416b430 --- /dev/null +++ b/docs/COMPARISON-climate-warrior.adoc @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: CC-BY-SA-4.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell += OikosBot vs Climate Warrior: positioning +:toc: macro +:icons: font + +_A point-in-time comparison (2026-07-28) against +https://github.com/marketplace/climate-warrior-official[Climate Warrior Official], +the closest sustainability-branded product on the GitHub Marketplace. Facts +about Climate Warrior are taken from its Marketplace listing and +https://github.com/rollthecloudinc/climatewarrior/wiki/Github-App-Docs[App docs] +as of that date; corrections welcome._ + +toc::[] + +== Short version + +The two products share the Software Carbon Intensity (SCI) vocabulary and +almost nothing else. **Climate Warrior is a storage product with carbon +telemetry** — a GitHub App (category: API Management) that turns repositories +into versioned JSON document stores ("energy-efficient databases") and records +SCI for its own operations, mainly lambda executions. **OikosBot is a code +analyzer with an economics engine** — it reads your source, estimates +per-function carbon/energy/time/memory, and renders a *Pareto verdict* on +every change: improvement, regression, or a trade-off that must be documented. + +They are not substitutes. A team could run both without overlap: Climate +Warrior to store documents with an SCI ledger, OikosBot to review code. + +== Matrix + +[cols="1,2,2",options="header"] +|=== +| Dimension | OikosBot | Climate Warrior + +| What it is +| Code-analysis CLI + GitHub Action (GitHub App planned; AffineScript + receiver in flight) +| GitHub App + +| Marketplace category fit +| Code quality / code scanning (SARIF into the Security tab) +| API Management (its actual listing category) + +| Unit of analysis +| Functions and files in your source (tree-sitter AST; Rust, JavaScript, + Python today) +| Its own storage/API operations (JSON documents, lambda executions) + +| Metrics +| Per-function energy (J), carbon (gCO2e), time (ms), memory (bytes); + Eco/Econ/Quality scores; composite health index +| Software Carbon Intensity of service operations; historical SCI + +| The economics layer +| **Pareto engine**: ε-tolerant dominance over weighted, normalized + objectives; frontier membership + ParetoScore per unit; base-vs-head + verdicts (improvement / regression / trade-off / neutral); + EconScore = 0.5·Pareto + 0.3·allocation (shadow prices) + 0.2·debt +| None (no notion of trade-offs or optimality) + +| PR workflow +| Verdict per change; trade-offs and regressions ask for a documented + justification (`Pareto-Trade-off:` trailer, machine-checked); advisor by + default, regulator opt-in per repo +| None (not a review tool) + +| Output formats +| SARIF 2.1.0 (GitHub code scanning, with `pareto_*` properties), JSON, text +| App-internal; repository documents + +| Configuration +| `.oikos.yml` per repo: mode, thresholds, excludes, languages +| App installation settings + +| Methodology transparency +| Open source (MPL-2.0); estimation model in-tree and SCI-spec-referenced + (ISO/IEC 21031:2024); every figure carries a confidence label + (Measured / Calibrated / Estimated / Unknown), and heuristic figures are + barred from blocking decisions by construction +| Not disclosed on the listing + +| Pricing / maturity +| Free, open source, v0.1.x (pre-1.0; App mode not yet live) +| Free tier ("Squire"); ~16 installs at time of writing +|=== + +== What OikosBot does not do (honesty section) + +* Estimates are **static heuristics** today (`Confidence::Estimated`), not + measurements. Runtime calibration and the VeriSimDB-backed praxis loop are + roadmap items. This is exactly why the confidence gate exists: heuristics + advise; only measured or calibrated figures may ever block a merge. +* The GitHub **App** (webhook, PR comments without CI) is not yet live — CI + Action mode is the generation-1 surface. The App is gated on upstream + AffineScript stdlib work by deliberate ruling (no interim listener). +* Language coverage is Rust / JavaScript / Python; listed-but-unsupported + languages in `.oikos.yml` are ignored loudly, never silently. +* Climate Warrior's document-storage use case is entirely out of scope: + OikosBot stores nothing about your repository (until the optional + VeriSimDB layer lands, and then only where configured). + +== Positioning takeaway + +If a Marketplace listing is pursued, OikosBot belongs in **code quality / +security (code scanning)** next to linters and SAST tools — not in the +sustainability-storage niche where Climate Warrior sits. Its differentiator +is not "we mention carbon" but the **economics**: making every PR answer +"did this change move us toward or away from the efficient frontier, and if +it traded something away, did the author say so?"