From f4600ddbcf193cc5cc4f42490c9b7b7030a1092f Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:32:26 +0100 Subject: [PATCH] refactor: migrate repository documentation from Markdown to AsciiDoc --- CHANGELOG.adoc | 154 ++++ CHANGELOG.md | 83 -- CODE_OF_CONDUCT.adoc | 120 +++ CODE_OF_CONDUCT.md | 117 --- CONTRIBUTING.adoc | 20 + CONTRIBUTING.md | 20 - SECURITY.adoc | 75 ++ SECURITY.md | 69 -- crates/oikosbot-fleet/README.adoc | 45 + crates/oikosbot-fleet/README.md | 47 - .../notes/2026-08-03-consumer-repo-trace.adoc | 142 +++ .../notes/2026-08-03-consumer-repo-trace.md | 56 -- .../2026-08-03-round-one-verification.adoc | 259 ++++++ .../2026-08-03-round-one-verification.md | 233 ----- ...026-08-03-estate-economics-round-one.adoc} | 867 +++++++++++------- .../2026-08-03-estate-economics-design.adoc | 514 +++++++++++ .../2026-08-03-estate-economics-design.md | 429 --------- 17 files changed, 1857 insertions(+), 1393 deletions(-) create mode 100644 CHANGELOG.adoc delete mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.adoc delete mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.adoc delete mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.adoc delete mode 100644 SECURITY.md create mode 100644 crates/oikosbot-fleet/README.adoc delete mode 100644 crates/oikosbot-fleet/README.md create mode 100644 docs/superpowers/notes/2026-08-03-consumer-repo-trace.adoc delete mode 100644 docs/superpowers/notes/2026-08-03-consumer-repo-trace.md create mode 100644 docs/superpowers/notes/2026-08-03-round-one-verification.adoc delete mode 100644 docs/superpowers/notes/2026-08-03-round-one-verification.md rename docs/superpowers/plans/{2026-08-03-estate-economics-round-one.md => 2026-08-03-estate-economics-round-one.adoc} (66%) create mode 100644 docs/superpowers/specs/2026-08-03-estate-economics-design.adoc delete mode 100644 docs/superpowers/specs/2026-08-03-estate-economics-design.md diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc new file mode 100644 index 0000000..2ec0710 --- /dev/null +++ b/CHANGELOG.adoc @@ -0,0 +1,154 @@ +== Changelog + +All notable changes to `+oikosbot+` will be documented in this file. + +This file is generated from conventional commits by the +https://github.com/hyperpolymath/standards/blob/main/.github/workflows/changelog-reusable.yml[`+changelog-reusable.yml+`] +workflow (`+hyperpolymath/standards#206+`). Adopt the workflow in this +repo’s CI to keep this file in sync automatically — see +https://github.com/hyperpolymath/standards/blob/main/templates/cliff.toml[`+templates/cliff.toml+`] +for the canonical config. + +The format follows https://keepachangelog.com/en/1.1.0/[Keep a +Changelog]; this project aims to follow +https://semver.org/spec/v2.0.0.html[Semantic Versioning]. + +=== [Unreleased] + +==== Added + +* feat(policies): add the OikosBot *finding taxonomy* — three orthogonal +axes (`+intent+` / `+maintenance+` / `+locus+`) defined canonically in +`+NEUROSYM.a2ml [finding-taxonomy]+`. The confidence-derived `+intent+` +axis (≡ the gitbot-fleet Safety Triangle’s 0.95 / 0.85 thresholds) is +made executable in `+policies/finding_taxonomy.ecl+`; +`+locus=externalities+` is the eco/econ sense realised by the +sustainability policies. Adds `+policies/README.adoc+`. +* feat(crates): extract the Rust analysis workspace from +`+gitbot-fleet/bots/sustainabot/+` into this repo, renamed +`+sustainabot-*+` → `+oikosbot-*+` +(`+oikosbot-cli+`/`+-analysis+`/`+-metrics+`/`+-sarif+`/`+-eclexia+`, +plus the optional `+oikosbot-fleet+` bridge). Adds `+policies/+`, +`+fuzz/+`, `+examples/+`, and `+QUICKSTART.md+`. Builds and tests green +(35 tests). +* docs: `+DISAMBIGUATION.adoc+` — canonical breakdown of *oikos* (the +DSL) vs *OikosBot* (this App) vs *sustainabot* (a reserved gitbot-fleet +slot), with guardrails to prevent the misfiling recurring. +* ci(rust): add a `+rust+` job (fmt-check + build + test, informational +clippy) to `+ci.yml+` and a `+cargo+` dependabot ecosystem. New +`+just rust-build+` / `+rust-test+` targets. +* feat(bot): missing AffineScript modules `+GitHubAPI+`, `+GitHubApp+`, +and the TEA runtime (`+tea/Cmd+`, `+tea/Runtime+`, `+tea/Sub+`) added to +`+bot-integration-affine/src/+` from the former sustainabot tree. +* feat(bot): `+bot-integration-affine/+` Phase 5 AffineScript scaffold +(#35) — initial AS port of OikosBot; cross-module type-check, JSON +payload extraction, and HTTP-server accept loop are gated on upstream +`+affinescript+` stdlib work (Json v0.3 RSR rewire `+affinescript#421+` ++ Http server FFI `+affinescript#425+`). +* feat: Oikos Bot v0.1.0-beta - TEA architecture with typed HTTP routing +* feat: add GitHub App manifest for developer programme registration + +==== Removed + +* chore(containers): remove the stale ReScript-era `+containers/+` that +were ported with the extraction but still built the long-removed +`+bot-integration/+` ReScript bot (`+*.res.js+`, `+rescript-runtime/+`) +and predated the Rust/AffineScript stack. Deployment images will be +rebuilt natively when OikosBot is deployable. + +==== Fixed + +* fix(docs): purge stale post-extraction identity references — +`+SECURITY.md+` carried two dead project names (`+SustainaBot+` ×3 and a +`+scarcity-bot+` GitLab vuln-reporting URL, plus `+SCARCITY_API_KEY+`), +a Ruby example path, and leaked `+Memory #N+` artifacts; and the +`+crates/oikosbot-fleet+` bridge-identity note in `+.claude/CLAUDE.md+` ++ `+META.a2ml+` ADR-002 still said `+BotId::Sustainabot+` after the +bridge moved to its own `+BotId::Oikosbot+` identity. (Deeper +`+SECURITY.md+` security-substance — reporting channel, PGP placeholder +— flagged for a separate review.) +* fix(lexer): opt in to logos 0.16 `+allow_greedy+` for line-comment +skip (#37) — `+#[logos(skip("--[^\n]*", allow_greedy = true))]+` +replaces the unbounded greedy attribute that logos 0.16’s new lint +rejects. +* fix(codeql): switch language matrix to '`actions`' (no JS/TS in repo) +(#23) +* fix(codeql): switch language matrix to '`actions`' (no JS/TS in repo) +(#22) +* fix(ci): sync hypatia-scan.yml to canonical (413: +env.HOME+Phase-2+SARIF) (#20) +* fix(codeql): switch language matrix to '`actions`' (no JS/TS in repo) +(#19) +* fix(ci): rsr-antipattern.yml duplicate heredoc (#15) +* fix(ci): move secret-scanner Cargo.toml gate from job-level if: to +step-level (#17) +* fix(codeql): switch language matrix to '`actions`' (no JS/TS in repo) +(#16) +* fix(security): ERR-WF-008 and ERR-WF-009 fixes +* fix(ci): update quality.yml workflow +* fix(security): CodeQL language matrix correction + +==== Changed + +* chore(decouple): sever OikosBot’s dependency on `+gitbot-fleet+`. The +default `+cargo+` workspace *excludes* `+crates/oikosbot-fleet+` (the +only fleet-aware crate), and the optional `+panic-attacker+` / +`+eclexia+` path dependencies that escaped the repo were neutralised to +no-op feature seams — OikosBot now builds from a clean checkout with no +sibling repos present. +* chore(fleet-bridge): the optional `+oikosbot-fleet+` bridge now +publishes findings under its own `+BotId::Oikosbot+` identity instead of +borrowing the fleet’s `+BotId::Sustainabot+` slot (requires a +gitbot-fleet that provides the `+Oikosbot+` variant). Keeps OikosBot +distinct from the separate, reserved `+sustainabot+` fleet slot. +* chore(repo): split OikosBot into its own repository separate from the +Oikos economics accounting DSL. +* chore(bot-integration): clean shutoff of the legacy ReScript +`+bot-integration/+` (#41) — 208 files / -33,061 lines: removes +`+bot-integration/+`, `+containers/+`, `+.github/workflows/oikos.yml+`, +the `+rescript:+` job from `+ci.yml+`, the npm/bot-integration +dependabot entry, and `+.gitmodules+`. README / ARCHITECTURE / ROADMAP / +DEPLOY / disambiguation docs repointed at `+bot-integration-affine/+`. +No production blast radius (`+.github/app.yml+` URLs were +`+*.example.com+` placeholders). +* chore(license): align stale SPDX headers + `+Cargo.toml+` manifest +with `+MPL-2.0+` (#36) — completes the 2026-05-22 EUPL → MPL migration +that had left 45 file headers + the manifest at `+EUPL-1.2+`. +* chore(license): remove historical `+LICENSES/EUPL-1.2.txt+` (#40) — +final cleanup of the EUPL transition artefact, deferred from #38 as a +transition-period record. +* refactor: convert TypeScript to JavaScript (language policy +compliance) +* refactor: complete eco-bot → oikos rename across all files +* refactor: rename eco-bot to oikos, replace nerdctl with Svalinn/Vörðr + +==== Documentation + +* docs(license): DR-010 supersedes DR-002 — `+MPL-2.0+` adoption + +README badge / paragraph flip (#38) — closes the 2026-05-22 LICENSE +migration that lacked a Decision Record. Documents PMPL ↔ EUPL +distribution-compatibility incoherence as the migration rationale. +* docs(readme): add SPDX header and/or standard badges +* docs: add manifest flow instructions to DEPLOY.md +* docs(security): add SECURITY.md + +==== CI + +* ci(secret-scanner): drop duplicate –fail from trufflehog extra_args +(#14) +* ci(dependabot): restore cargo PR limit so security + version PRs flow +(#12) +* ci: fix workflow-linter YAML parse error + self-flag bug +* ci(antipattern): fix top-level dir matching + benchmarks/lsp/bench +filename allowlists (#10) +* ci(antipattern): TS check reads .claude/CLAUDE.md exemption table (#9) + +=== Pre-history + +Prior commits to this file’s introduction are recorded in git history +but not formally classified into Keep-a-Changelog sections. To backfill, +run `+git cliff -o CHANGELOG.md+` locally using the canonical +https://github.com/hyperpolymath/standards/blob/main/templates/cliff.toml[`+cliff.toml+`] +— this is one-shot mechanical work. + +''''' diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 3ecb2b8..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,83 +0,0 @@ - -# Changelog - -All notable changes to `oikosbot` will be documented in this file. - -This file is generated from conventional commits by the -[`changelog-reusable.yml`](https://github.com/hyperpolymath/standards/blob/main/.github/workflows/changelog-reusable.yml) -workflow (`hyperpolymath/standards#206`). Adopt the workflow in this repo's CI to keep this file in sync automatically — see -[`templates/cliff.toml`](https://github.com/hyperpolymath/standards/blob/main/templates/cliff.toml) -for the canonical config. - -The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); -this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -### Added - -- feat(policies): add the OikosBot **finding taxonomy** — three orthogonal axes (`intent` / `maintenance` / `locus`) defined canonically in `NEUROSYM.a2ml [finding-taxonomy]`. The confidence-derived `intent` axis (≡ the gitbot-fleet Safety Triangle's 0.95 / 0.85 thresholds) is made executable in `policies/finding_taxonomy.ecl`; `locus=externalities` is the eco/econ sense realised by the sustainability policies. Adds `policies/README.adoc`. -- feat(crates): extract the Rust analysis workspace from `gitbot-fleet/bots/sustainabot/` into this repo, renamed `sustainabot-*` → `oikosbot-*` (`oikosbot-cli`/`-analysis`/`-metrics`/`-sarif`/`-eclexia`, plus the optional `oikosbot-fleet` bridge). Adds `policies/`, `fuzz/`, `examples/`, and `QUICKSTART.md`. Builds and tests green (35 tests). -- docs: `DISAMBIGUATION.adoc` — canonical breakdown of **oikos** (the DSL) vs **OikosBot** (this App) vs **sustainabot** (a reserved gitbot-fleet slot), with guardrails to prevent the misfiling recurring. -- ci(rust): add a `rust` job (fmt-check + build + test, informational clippy) to `ci.yml` and a `cargo` dependabot ecosystem. New `just rust-build` / `rust-test` targets. -- feat(bot): missing AffineScript modules `GitHubAPI`, `GitHubApp`, and the TEA runtime (`tea/Cmd`, `tea/Runtime`, `tea/Sub`) added to `bot-integration-affine/src/` from the former sustainabot tree. -- feat(bot): `bot-integration-affine/` Phase 5 AffineScript scaffold (#35) — initial AS port of OikosBot; cross-module type-check, JSON payload extraction, and HTTP-server accept loop are gated on upstream `affinescript` stdlib work (Json v0.3 RSR rewire `affinescript#421` + Http server FFI `affinescript#425`). -- feat: Oikos Bot v0.1.0-beta - TEA architecture with typed HTTP routing -- feat: add GitHub App manifest for developer programme registration - -### Removed - -- chore(containers): remove the stale ReScript-era `containers/` that were ported with the extraction but still built the long-removed `bot-integration/` ReScript bot (`*.res.js`, `rescript-runtime/`) and predated the Rust/AffineScript stack. Deployment images will be rebuilt natively when OikosBot is deployable. - -### Fixed - -- fix(docs): purge stale post-extraction identity references — `SECURITY.md` carried two dead project names (`SustainaBot` ×3 and a `scarcity-bot` GitLab vuln-reporting URL, plus `SCARCITY_API_KEY`), a Ruby example path, and leaked `Memory #N` artifacts; and the `crates/oikosbot-fleet` bridge-identity note in `.claude/CLAUDE.md` + `META.a2ml` ADR-002 still said `BotId::Sustainabot` after the bridge moved to its own `BotId::Oikosbot` identity. (Deeper `SECURITY.md` security-substance — reporting channel, PGP placeholder — flagged for a separate review.) -- fix(lexer): opt in to logos 0.16 `allow_greedy` for line-comment skip (#37) — `#[logos(skip("--[^\n]*", allow_greedy = true))]` replaces the unbounded greedy attribute that logos 0.16's new lint rejects. -- fix(codeql): switch language matrix to 'actions' (no JS/TS in repo) (#23) -- fix(codeql): switch language matrix to 'actions' (no JS/TS in repo) (#22) -- fix(ci): sync hypatia-scan.yml to canonical (413: env.HOME+Phase-2+SARIF) (#20) -- fix(codeql): switch language matrix to 'actions' (no JS/TS in repo) (#19) -- fix(ci): rsr-antipattern.yml duplicate heredoc (#15) -- fix(ci): move secret-scanner Cargo.toml gate from job-level if: to step-level (#17) -- fix(codeql): switch language matrix to 'actions' (no JS/TS in repo) (#16) -- fix(security): ERR-WF-008 and ERR-WF-009 fixes -- fix(ci): update quality.yml workflow -- fix(security): CodeQL language matrix correction - -### Changed - -- chore(decouple): sever OikosBot's dependency on `gitbot-fleet`. The default `cargo` workspace **excludes** `crates/oikosbot-fleet` (the only fleet-aware crate), and the optional `panic-attacker` / `eclexia` path dependencies that escaped the repo were neutralised to no-op feature seams — OikosBot now builds from a clean checkout with no sibling repos present. -- chore(fleet-bridge): the optional `oikosbot-fleet` bridge now publishes findings under its own `BotId::Oikosbot` identity instead of borrowing the fleet's `BotId::Sustainabot` slot (requires a gitbot-fleet that provides the `Oikosbot` variant). Keeps OikosBot distinct from the separate, reserved `sustainabot` fleet slot. -- chore(repo): split OikosBot into its own repository separate from the Oikos economics accounting DSL. -- chore(bot-integration): clean shutoff of the legacy ReScript `bot-integration/` (#41) — 208 files / -33,061 lines: removes `bot-integration/`, `containers/`, `.github/workflows/oikos.yml`, the `rescript:` job from `ci.yml`, the npm/bot-integration dependabot entry, and `.gitmodules`. README / ARCHITECTURE / ROADMAP / DEPLOY / disambiguation docs repointed at `bot-integration-affine/`. No production blast radius (`.github/app.yml` URLs were `*.example.com` placeholders). -- chore(license): align stale SPDX headers + `Cargo.toml` manifest with `MPL-2.0` (#36) — completes the 2026-05-22 EUPL → MPL migration that had left 45 file headers + the manifest at `EUPL-1.2`. -- chore(license): remove historical `LICENSES/EUPL-1.2.txt` (#40) — final cleanup of the EUPL transition artefact, deferred from #38 as a transition-period record. -- refactor: convert TypeScript to JavaScript (language policy compliance) -- refactor: complete eco-bot → oikos rename across all files -- refactor: rename eco-bot to oikos, replace nerdctl with Svalinn/Vörðr - -### Documentation - -- docs(license): DR-010 supersedes DR-002 — `MPL-2.0` adoption + README badge / paragraph flip (#38) — closes the 2026-05-22 LICENSE migration that lacked a Decision Record. Documents PMPL ↔ EUPL distribution-compatibility incoherence as the migration rationale. -- docs(readme): add SPDX header and/or standard badges -- docs: add manifest flow instructions to DEPLOY.md -- docs(security): add SECURITY.md - -### CI - -- ci(secret-scanner): drop duplicate --fail from trufflehog extra_args (#14) -- ci(dependabot): restore cargo PR limit so security + version PRs flow (#12) -- ci: fix workflow-linter YAML parse error + self-flag bug -- ci(antipattern): fix top-level dir matching + benchmarks/lsp/bench filename allowlists (#10) -- ci(antipattern): TS check reads .claude/CLAUDE.md exemption table (#9) - -## Pre-history - -Prior commits to this file's introduction are recorded in git history but not formally classified into Keep-a-Changelog sections. To backfill, run `git cliff -o CHANGELOG.md` locally using the canonical [`cliff.toml`](https://github.com/hyperpolymath/standards/blob/main/templates/cliff.toml) — this is one-shot mechanical work. - ---- - - diff --git a/CODE_OF_CONDUCT.adoc b/CODE_OF_CONDUCT.adoc new file mode 100644 index 0000000..106d3d2 --- /dev/null +++ b/CODE_OF_CONDUCT.adoc @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: CC-BY-SA-4.0 // SPDX-FileCopyrightText: +2024-2025 hyperpolymath + +== Code of Conduct + +=== Our Pledge + +We as members, contributors, and leaders pledge to make participation in +our community a harassment-free experience for everyone, regardless of +age, body size, visible or invisible disability, ethnicity, sex +characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, +welcoming, diverse, inclusive, and healthy community. + +=== Our Standards + +Examples of behavior that contributes to a positive environment: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our +mistakes +* Focusing on what is best for the overall community +* Considering ecological and economic impacts of technical decisions + +Examples of unacceptable behavior: + +* The use of sexualized language or imagery, and sexual attention or +advances +* Trolling, insulting or derogatory comments, and personal or political +attacks +* Public or private harassment +* Publishing others’ private information without explicit permission +* Other conduct which could reasonably be considered inappropriate + +=== Ecological & Economic Responsibility + +As a project focused on ecological and economic code analysis, we also +expect: + +* Consideration of the environmental impact of contributions +* Respect for sustainable computing practices +* Thoughtful discussion of trade-offs between different approaches +* Recognition that economic efficiency includes long-term sustainability + +=== Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our +standards and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, +offensive, or harmful. + +=== Scope + +This Code of Conduct applies within all community spaces, and also +applies when an individual is officially representing the community in +public spaces. + +=== Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may +be reported to the community leaders responsible for enforcement at: + +* GitHub: Open an issue with the `+conduct+` label +* Email: conduct@hyperpolymath.com (if available) + +All complaints will be reviewed and investigated promptly and fairly. + +=== Enforcement Guidelines + +Community leaders will follow these guidelines in determining +consequences: + +==== 1. Correction + +*Community Impact*: Minor inappropriate behavior. + +*Consequence*: Private, written warning with clarity around the nature +of the violation. A public apology may be requested. + +==== 2. Warning + +*Community Impact*: A violation through a single incident or series of +actions. + +*Consequence*: A warning with consequences for continued behavior. No +interaction with the people involved for a specified period. This +includes avoiding interactions in community spaces as well as external +channels. + +==== 3. Temporary Ban + +*Community Impact*: A serious violation of community standards. + +*Consequence*: A temporary ban from any sort of interaction or public +communication with the community for a specified period. + +==== 4. Permanent Ban + +*Community Impact*: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment, or +aggression. + +*Consequence*: A permanent ban from any sort of public interaction +within the community. + +=== Attribution + +This Code of Conduct is adapted from the +https://www.contributor-covenant.org[Contributor Covenant], version 2.1, +available at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. + +Community Impact Guidelines were inspired by +https://github.com/mozilla/diversity[Mozilla’s code of conduct +enforcement ladder]. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index d34528d..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,117 +0,0 @@ - -// SPDX-License-Identifier: CC-BY-SA-4.0 -// SPDX-FileCopyrightText: 2024-2025 hyperpolymath - -# Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes -* Focusing on what is best for the overall community -* Considering ecological and economic impacts of technical decisions - -Examples of unacceptable behavior: - -* The use of sexualized language or imagery, and sexual attention or advances -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information without explicit permission -* Other conduct which could reasonably be considered inappropriate - -## Ecological & Economic Responsibility - -As a project focused on ecological and economic code analysis, we also expect: - -* Consideration of the environmental impact of contributions -* Respect for sustainable computing practices -* Thoughtful discussion of trade-offs between different approaches -* Recognition that economic efficiency includes long-term sustainability - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards -and will take appropriate and fair corrective action in response to any -behavior that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at: - -* GitHub: Open an issue with the `conduct` label -* Email: conduct@hyperpolymath.com (if available) - -All complaints will be reviewed and investigated promptly and fairly. - -## Enforcement Guidelines - -Community leaders will follow these guidelines in determining consequences: - -### 1. Correction - -**Community Impact**: Minor inappropriate behavior. - -**Consequence**: Private, written warning with clarity around the nature of the -violation. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved for a specified period. This includes -avoiding interactions in community spaces as well as external channels. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment, or -aggression. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc new file mode 100644 index 0000000..0e9f4c1 --- /dev/null +++ b/CONTRIBUTING.adoc @@ -0,0 +1,20 @@ +== Contributing + +Contributions are welcome! Please: + +[arabic] +. Fork the repository +. Create a feature branch from `+main+` +. Ensure all CI checks pass +. Submit a pull request + +=== Standards + +This project follows the +https://github.com/hyperpolymath/standards[Rhodium Standard Repository +(RSR)] conventions. + +=== License + +By contributing, you agree that your contributions will be licensed +under MPL-2.0 (with MPL-2.0 as automatic legal fallback). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 166fe96..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,20 +0,0 @@ - -# Contributing - -Contributions are welcome! Please: - -1. Fork the repository -2. Create a feature branch from `main` -3. Ensure all CI checks pass -4. Submit a pull request - -## Standards - -This project follows the [Rhodium Standard Repository (RSR)](https://github.com/hyperpolymath/standards) conventions. - -## License - -By contributing, you agree that your contributions will be licensed under MPL-2.0 (with MPL-2.0 as automatic legal fallback). diff --git a/SECURITY.adoc b/SECURITY.adoc new file mode 100644 index 0000000..dfa1fba --- /dev/null +++ b/SECURITY.adoc @@ -0,0 +1,75 @@ +== Security Policy + +_OikosBot_ adheres to *Rhodium Standard Repo (RSR)* principles, +emphasizing *reversibility, attestation, and minimal attack surfaces*. + +''''' + +=== Supported Versions + +[width="100%",cols="15%,28%,57%",options="header",] +|=== +|Version |Supported |Notes +|0.x.x |:white_check_mark: |Only the *latest minor version* receives +security updates. +|=== + +____ +*Note*: OikosBot is in *early development*. Security updates are +prioritized for the latest release. +____ + +''''' + +=== Reporting a Vulnerability + +*Do not report vulnerabilities publicly* (e.g., GitHub/GitLab Issues). +Instead, report privately by email to *j.d.a.jewell@open.ac.uk*. + +''''' + +==== What to Include + +Provide *detailed, actionable information*: - *Type of issue*: - +Example: Buffer overflow, XSS, supply chain tampering, or *waste metric +spoofing*. - *Affected components*: - Source file paths (e.g., +`+crates/oikosbot-analysis/src/security.rs+`). - *Commit +hash/tag/branch* or direct URL. - *Reproduction steps*: - Command-line +invocations, config snippets, or *Justfile recipes* used. - *Impact*: - +How could an attacker exploit this? (e.g., "`Fake carbon savings +reports,`" "`CI/CD pipeline hijacking.`") - *Proof-of-Concept*: - Code +snippets or *SHAKE256 hashes* of malicious inputs (if applicable). + +''''' + +=== Response Timeline + +[width="100%",cols="28%,21%,51%",options="header",] +|=== +|Phase |Target |Notes +|*Initial Response* |≤48 hours |Acknowledges receipt. +|*Status Update* |≤7 days |Progress or mitigation advice. +|*Resolution* |≤30 days |For *critical issues* (e.g., RCE, data leaks). +|*Attestation* |Post-resolution |Logs signed with *Ed448* in `+logs/+`. +|=== + +''''' + +=== Security Considerations + +==== Data Handling + +OikosBot processes: - *Source code* (for waste analysis). - *Dependency +graphs* (economic/ecological impact). - *CI/CD configurations* (e.g., +GitLab pipelines). - *VoID/Dublin Core metadata* (interoperability with +WordPress/Drupal). + +*Guiding Principles*: - *Minimal Retention*: Data deleted post-audit +unless *explicitly logged for reversibility*. - *Hashing*: All logs use +*SHAKE256/Ed448* (see `+logs/README.md+`). + +==== Integration Security + +* *Environment Variables*: ```bash # Example: .env +OIKOSBOT_API_KEY="`x`" # Never commit this! GITHUB_TOKEN="`y`" # Use +GitLab CI variables or encrypted secrets. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index b4289b8..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,69 +0,0 @@ - -# Security Policy -*OikosBot* adheres to **Rhodium Standard Repo (RSR)** principles, emphasizing **reversibility, attestation, and minimal attack surfaces**. - ---- - -## Supported Versions -| Version | Supported | Notes | -|---------|--------------------|----------------------------------------| -| 0.x.x | :white_check_mark: | Only the **latest minor version** receives security updates. | - -> **Note**: OikosBot is in **early development**. Security updates are prioritized for the latest release. - ---- - -## Reporting a Vulnerability -**Do not report vulnerabilities publicly** (e.g., GitHub/GitLab Issues). -Instead, report privately by email to **j.d.a.jewell@open.ac.uk**. - ---- - -### What to Include -Provide **detailed, actionable information**: -- **Type of issue**: - - Example: Buffer overflow, XSS, supply chain tampering, or **waste metric spoofing**. -- **Affected components**: - - Source file paths (e.g., `crates/oikosbot-analysis/src/security.rs`). - - **Commit hash/tag/branch** or direct URL. -- **Reproduction steps**: - - Command-line invocations, config snippets, or **Justfile recipes** used. -- **Impact**: - - How could an attacker exploit this? (e.g., "Fake carbon savings reports," "CI/CD pipeline hijacking.") -- **Proof-of-Concept**: - - Code snippets or **SHAKE256 hashes** of malicious inputs (if applicable). - ---- - -## Response Timeline -| Phase | Target | Notes | -|---------------------|-----------------|----------------------------------------| -| **Initial Response** | ≤48 hours | Acknowledges receipt. | -| **Status Update** | ≤7 days | Progress or mitigation advice. | -| **Resolution** | ≤30 days | For **critical issues** (e.g., RCE, data leaks). | -| **Attestation** | Post-resolution | Logs signed with **Ed448** in `logs/`. | - ---- - -## Security Considerations - -### Data Handling -OikosBot processes: -- **Source code** (for waste analysis). -- **Dependency graphs** (economic/ecological impact). -- **CI/CD configurations** (e.g., GitLab pipelines). -- **VoID/Dublin Core metadata** (interoperability with WordPress/Drupal). - -**Guiding Principles**: -- **Minimal Retention**: Data deleted post-audit unless **explicitly logged for reversibility**. -- **Hashing**: All logs use **SHAKE256/Ed448** (see `logs/README.md`). - -### Integration Security -- **Environment Variables**: - ```bash - # Example: .env - OIKOSBOT_API_KEY="x" # Never commit this! - GITHUB_TOKEN="y" # Use GitLab CI variables or encrypted secrets. diff --git a/crates/oikosbot-fleet/README.adoc b/crates/oikosbot-fleet/README.adoc new file mode 100644 index 0000000..aacd2ac --- /dev/null +++ b/crates/oikosbot-fleet/README.adoc @@ -0,0 +1,45 @@ +== `+oikosbot-fleet+` — optional gitbot-fleet bridge + +This crate is the *only* part of OikosBot that knows about +https://github.com/hyperpolymath/gitbot-fleet[`+hyperpolymath/gitbot-fleet+`]. +It converts OikosBot’s `+AnalysisResult+`s into the fleet’s +shared-context `+Finding+`s so OikosBot can run as a fleet member. + +=== Why it is excluded from the default workspace + +OikosBot and gitbot-fleet are *separate projects* (see +link:../../DISAMBIGUATION.adoc[`+../../DISAMBIGUATION.adoc+`]). To keep +that boundary honest, the root `+Cargo.toml+` lists this crate under +`+exclude+`, *not* `+members+`. As a result: + +* `+cargo build+` / `+cargo test+` at the repo root build OikosBot with +*zero* dependency on gitbot-fleet. +* This bridge is compiled only when you ask for it explicitly. + +=== Building / running the bridge + +The bridge has a `+path+` dependency on `+gitbot-fleet/shared-context+`, +so check out gitbot-fleet as a *sibling* of this repository: + +[source,text] +---- +parent/ +├── oikosbot/ # this repo +└── gitbot-fleet/ # hyperpolymath/gitbot-fleet +---- + +Then: + +[source,bash] +---- +cargo build --manifest-path crates/oikosbot-fleet/Cargo.toml +cargo run --manifest-path crates/oikosbot-fleet/Cargo.toml -- [--context ctx.json] +---- + +=== Fleet identity + +The bridge publishes under its own *`+BotId::Oikosbot+`* identity, so +the fleet distinguishes OikosBot from the separate, reserved +`+sustainabot+` slot. This requires a `+gitbot-shared-context+` that +provides the `+Oikosbot+` variant (added in gitbot-fleet alongside this +change); build with a sibling gitbot-fleet that has it. diff --git a/crates/oikosbot-fleet/README.md b/crates/oikosbot-fleet/README.md deleted file mode 100644 index f73c212..0000000 --- a/crates/oikosbot-fleet/README.md +++ /dev/null @@ -1,47 +0,0 @@ - -# `oikosbot-fleet` — optional gitbot-fleet bridge - -This crate is the **only** part of OikosBot that knows about -[`hyperpolymath/gitbot-fleet`](https://github.com/hyperpolymath/gitbot-fleet). -It converts OikosBot's `AnalysisResult`s into the fleet's shared-context -`Finding`s so OikosBot can run as a fleet member. - -## Why it is excluded from the default workspace - -OikosBot and gitbot-fleet are **separate projects** (see -[`../../DISAMBIGUATION.adoc`](../../DISAMBIGUATION.adoc)). To keep that boundary -honest, the root `Cargo.toml` lists this crate under `exclude`, **not** -`members`. As a result: - -- `cargo build` / `cargo test` at the repo root build OikosBot with **zero** - dependency on gitbot-fleet. -- This bridge is compiled only when you ask for it explicitly. - -## Building / running the bridge - -The bridge has a `path` dependency on `gitbot-fleet/shared-context`, so check -out gitbot-fleet as a **sibling** of this repository: - -```text -parent/ -├── oikosbot/ # this repo -└── gitbot-fleet/ # hyperpolymath/gitbot-fleet -``` - -Then: - -```bash -cargo build --manifest-path crates/oikosbot-fleet/Cargo.toml -cargo run --manifest-path crates/oikosbot-fleet/Cargo.toml -- [--context ctx.json] -``` - -## Fleet identity - -The bridge publishes under its own **`BotId::Oikosbot`** identity, so the fleet -distinguishes OikosBot from the separate, reserved `sustainabot` slot. This -requires a `gitbot-shared-context` that provides the `Oikosbot` variant (added in -gitbot-fleet alongside this change); build with a sibling gitbot-fleet that has -it. diff --git a/docs/superpowers/notes/2026-08-03-consumer-repo-trace.adoc b/docs/superpowers/notes/2026-08-03-consumer-repo-trace.adoc new file mode 100644 index 0000000..1118563 --- /dev/null +++ b/docs/superpowers/notes/2026-08-03-consumer-repo-trace.adoc @@ -0,0 +1,142 @@ +== Consumer repo trace: `+boj-server-mk2+` and `+idaptik-ums+` + +*Date:* 2026-08-03 *Why:* on 2026-07-28 an estate sweep opened +oikosbot-consumer PRs at `+hyperpolymath/boj-server-mk2#19+` and +`+hyperpolymath/idaptik-ums#62+`. On 2026-08-03 both +`+hyperpolymath/...+` names return "`Could not resolve to a +Repository.`" This note establishes where each repo actually went, with +command-level evidence, so later sweeps target the live location. + +=== Method note: the API redirect check was inconclusive — PR-number continuity was decisive + +`+gh api repos/hyperpolymath/ -q .full_name+` is supposed to +follow GitHub’s rename redirect and return the new `+full_name+`. For +both repos it returned a *true HTTP 404* (not a 301/redirect payload — +confirmed with `+gh api ... -i+`), so it gave no positive signal either +way — it is consistent with "`deleted`" and, it turns out, ALSO +consistent with "`transferred to a different owner`" (see below). Do not +treat a 404 from this probe alone as proof of deletion. + +The decisive evidence was *PR-number continuity*: GitHub-assigned PR +numbers, authors, titles and timestamps cannot be recreated by a user — +they are server-assigned and repo-scoped. Finding the exact same PR +number, title, author and creation timestamp at a candidate successor +repo is strong proof of repo lineage (transfer/rename), independent of +whether the `+.full_name+` redirect resolves. + +=== Disposition table + +[width="99%",cols="20%,16%,16%,16%,16%,16%",options="header",] +|=== +|Old name |Finding |Evidence |`+oikosbot.yml+` present? |Sweep PR status +|Follow-up +|`+hyperpolymath/boj-server-mk2+` |*Transferred* to +`+metadatastician/boj-server-mk2+` (public). Repo id `+1310698487+`. +|`+gh api repos/hyperpolymath/boj-server-mk2 -i+` → `+404 Not Found+` +(no redirect). `+gh repo view metadatastician/boj-server-mk2+` → exists, +`+archived=false+`. Decisive: +`+gh pr view 19 -R metadatastician/boj-server-mk2+` → PR #19, title +"`fix(ci): run OikosBot via its published action, not a dead container +pin`", author `+hyperpolymath+` (Jonathan D.A. Jewell), +`+createdAt=2026-07-28T19:31:21Z+`, `+state=MERGED+` — matches the +sweep’s PR #19 exactly. |*Present.* +`+gh api "repos/metadatastician/boj-server-mk2/contents/.github/workflows/oikosbot.yml" -q .name+` +→ `+oikosbot.yml+` |*Travelled with the transfer and is MERGED.* Same +number, same content, at the new location. |Update the estate consumer +list: `+hyperpolymath/boj-server-mk2+` → +`+metadatastician/boj-server-mk2+`. No action needed on the workflow +itself (present + PR already merged); just fix bookkeeping. + +|`+hyperpolymath/idaptik-ums+` |*Transferred* to +`+metadatastician/idaptik-ums+` (public, actively pushed as recently as +2026-08-03T13:28:05Z). Repo id `+1296743295+`. *Not* +`+metadatastician/canonical-ums+` — that is a distinct repo (see +disambiguation below). |`+gh api repos/hyperpolymath/idaptik-ums -i+` → +`+404 Not Found+` (no redirect). +`+gh repo view metadatastician/idaptik-ums+` → exists, +`+archived=false+`. Decisive: +`+gh pr view 62 -R metadatastician/idaptik-ums+` → PR #62, title +"`fix(ci): run OikosBot via its published action, not a dead container +pin`", author `+hyperpolymath+`, `+createdAt=2026-07-28T19:32:14Z+`, +`+state=MERGED+` — matches the sweep’s PR #62 exactly. Cross-check: +`+gh pr view 62 -R metadatastician/canonical-ums+` → +`+Could not resolve to a PullRequest with the number of 62+` — proves +#62 does *not* belong to `+canonical-ums+`. |*Present.* +`+gh api "repos/metadatastician/idaptik-ums/contents/.github/workflows/oikosbot.yml" -q .name+` +→ `+oikosbot.yml+` |*Travelled with the transfer and is MERGED.* Same +number, same content, at the new location. |Update the estate consumer +list: `+hyperpolymath/idaptik-ums+` → `+metadatastician/idaptik-ums+`. +Do not conflate with `+metadatastician/canonical-ums+` (see below). +|=== + +=== Disambiguation: three separate `+*ums+` repos under `+metadatastician+` + +Owner repo-list search +(`+gh repo list metadatastician --limit 400 --json name -q '.[].name' | grep -iE 'boj|ums|idaptik'+`) +turned up four hits, all confirmed distinct entities via their own +`+id+`/`+created_at+`/PR history: + +[width="100%",cols="25%,25%,25%,25%",options="header",] +|=== +|Repo |id |created |Notes +|`+metadatastician/idaptik-ums+` |1296743295 |2026-07-10T17:25:03Z |The +successor of `+hyperpolymath/idaptik-ums+` (PR #62 lineage, proven +above). Description: "`IDApTIK Unified Modding Studio — procedural +generation of characters, levels, objects, and DLC for the game.`" + +|`+metadatastician/canonical-ums+` |1311842310 |2026-07-25T09:14:39Z |*A +different repo*, private. Description: "`Canonical Universal Modding +Studio for IDApTIK.`" This is the rename target of +`+idaptik-ums-canonical+` per prior memory (`+idaptik-ums-canonical+` → +`+metadatastician/canonical-ums+`), *not* of `+idaptik-ums+`. Has its +own, earlier oikosbot sweep PR (`+#6+`, same title), unrelated to +`+hyperpolymath/idaptik-ums#62+`. + +|`+metadatastician/IDApTIK+` |— |— |Third, separate repo (the game +itself, presumably); only noted here so it isn’t confused with the two +`+*-ums+` repos above. + +|`+metadatastician/boj-server-mk2+` |1310698487 |2026-07-24T06:16:28Z +|The successor of `+hyperpolymath/boj-server-mk2+` (PR #19 lineage, +proven above). Description: "`Next-generation architecture for +BoJ-Server… Erlang VM (BEAM)… Idris2 cartridges.`" +|=== + +Note also: `+hyperpolymath/boj-server+` (id `+1171174209+`, distinct +repo, "`The Estate’s primary MCP server`") and +`+hyperpolymath/boj-server-cartridges+` both still resolve fine under +`+hyperpolymath+` and are unrelated to `+boj-server-mk2+` — do not +conflate the `+boj-server+` / `+boj-server-mk2+` pair, per the task +brief’s warning. + +=== Caveat on "`transferred`" vs "`renamed-then-recreated`" + +The `+metadatastician/boj-server-mk2+` repo’s `+created_at+` +(2026-07-24T06:16:28Z) predates the sweep’s PR #19 +(`+2026-07-28T19:31:21Z+`) by four days, and +`+metadatastician/idaptik-ums+`’s `+created_at+` (2026-07-10T17:25:03Z) +predates PR #62 by over two weeks. This is expected and not +contradictory: `+created_at+` reflects the repo’s original creation date +(which transfers do not reset), and the sweep PRs were opened at their +current `+metadatastician/...+` location on 2026-07-28 — consistent with +the sweep running against the already-transferred repos. Nothing here +suggests the repos were deleted-and-recreated; the +PR-number/author/timestamp match is not something a recreation could +reproduce, since GitHub assigns those server-side per-repo. + +=== Recommended follow-up (repeated for clarity) + +[arabic] +. Update the estate consumer manifest / sweep target list: replace +`+hyperpolymath/boj-server-mk2+` with +`+metadatastician/boj-server-mk2+`, and `+hyperpolymath/idaptik-ums+` +with `+metadatastician/idaptik-ums+`. +. No workflow remediation needed at either successor — `+oikosbot.yml+` +is present and the sweep PR is merged at both. +. Flag for the owner: two of the repos in this family (`+idaptik-ums+` +and `+canonical-ums+`) are easy to conflate by name; the estate’s +consumer list should record them as separate rows to avoid a future +sweep double-booking or skipping one. +. If other `+hyperpolymath/*+` consumer entries later 404, re-run this +same PR-number-continuity method rather than trusting the `+.full_name+` +redirect probe alone — it silently 404s on cross-owner transfers. diff --git a/docs/superpowers/notes/2026-08-03-consumer-repo-trace.md b/docs/superpowers/notes/2026-08-03-consumer-repo-trace.md deleted file mode 100644 index 67c84aa..0000000 --- a/docs/superpowers/notes/2026-08-03-consumer-repo-trace.md +++ /dev/null @@ -1,56 +0,0 @@ -# Consumer repo trace: `boj-server-mk2` and `idaptik-ums` - -**Date:** 2026-08-03 -**Why:** on 2026-07-28 an estate sweep opened oikosbot-consumer PRs at -`hyperpolymath/boj-server-mk2#19` and `hyperpolymath/idaptik-ums#62`. On -2026-08-03 both `hyperpolymath/...` names return "Could not resolve to a -Repository." This note establishes where each repo actually went, with -command-level evidence, so later sweeps target the live location. - -## Method note: the API redirect check was inconclusive — PR-number continuity was decisive - -`gh api repos/hyperpolymath/ -q .full_name` is supposed to follow -GitHub's rename redirect and return the new `full_name`. For both repos it -returned a **true HTTP 404** (not a 301/redirect payload — confirmed with -`gh api ... -i`), so it gave no positive signal either way — it is consistent -with "deleted" and, it turns out, ALSO consistent with "transferred to a -different owner" (see below). Do not treat a 404 from this probe alone as -proof of deletion. - -The decisive evidence was **PR-number continuity**: GitHub-assigned PR -numbers, authors, titles and timestamps cannot be recreated by a user — they -are server-assigned and repo-scoped. Finding the exact same PR number, title, -author and creation timestamp at a candidate successor repo is strong proof -of repo lineage (transfer/rename), independent of whether the `.full_name` -redirect resolves. - -## Disposition table - -| Old name | Finding | Evidence | `oikosbot.yml` present? | Sweep PR status | Follow-up | -|---|---|---|---|---|---| -| `hyperpolymath/boj-server-mk2` | **Transferred** to `metadatastician/boj-server-mk2` (public). Repo id `1310698487`. | `gh api repos/hyperpolymath/boj-server-mk2 -i` → `404 Not Found` (no redirect). `gh repo view metadatastician/boj-server-mk2` → exists, `archived=false`. Decisive: `gh pr view 19 -R metadatastician/boj-server-mk2` → PR #19, title "fix(ci): run OikosBot via its published action, not a dead container pin", author `hyperpolymath` (Jonathan D.A. Jewell), `createdAt=2026-07-28T19:31:21Z`, `state=MERGED` — matches the sweep's PR #19 exactly. | **Present.** `gh api "repos/metadatastician/boj-server-mk2/contents/.github/workflows/oikosbot.yml" -q .name` → `oikosbot.yml` | **Travelled with the transfer and is MERGED.** Same number, same content, at the new location. | Update the estate consumer list: `hyperpolymath/boj-server-mk2` → `metadatastician/boj-server-mk2`. No action needed on the workflow itself (present + PR already merged); just fix bookkeeping. | -| `hyperpolymath/idaptik-ums` | **Transferred** to `metadatastician/idaptik-ums` (public, actively pushed as recently as 2026-08-03T13:28:05Z). Repo id `1296743295`. **Not** `metadatastician/canonical-ums` — that is a distinct repo (see disambiguation below). | `gh api repos/hyperpolymath/idaptik-ums -i` → `404 Not Found` (no redirect). `gh repo view metadatastician/idaptik-ums` → exists, `archived=false`. Decisive: `gh pr view 62 -R metadatastician/idaptik-ums` → PR #62, title "fix(ci): run OikosBot via its published action, not a dead container pin", author `hyperpolymath`, `createdAt=2026-07-28T19:32:14Z`, `state=MERGED` — matches the sweep's PR #62 exactly. Cross-check: `gh pr view 62 -R metadatastician/canonical-ums` → `Could not resolve to a PullRequest with the number of 62` — proves #62 does **not** belong to `canonical-ums`. | **Present.** `gh api "repos/metadatastician/idaptik-ums/contents/.github/workflows/oikosbot.yml" -q .name` → `oikosbot.yml` | **Travelled with the transfer and is MERGED.** Same number, same content, at the new location. | Update the estate consumer list: `hyperpolymath/idaptik-ums` → `metadatastician/idaptik-ums`. Do not conflate with `metadatastician/canonical-ums` (see below). | - -## Disambiguation: three separate `*ums` repos under `metadatastician` - -Owner repo-list search (`gh repo list metadatastician --limit 400 --json name -q '.[].name' | grep -iE 'boj|ums|idaptik'`) turned up four hits, all confirmed distinct entities via their own `id`/`created_at`/PR history: - -| Repo | id | created | Notes | -|---|---|---|---| -| `metadatastician/idaptik-ums` | 1296743295 | 2026-07-10T17:25:03Z | The successor of `hyperpolymath/idaptik-ums` (PR #62 lineage, proven above). Description: "IDApTIK Unified Modding Studio — procedural generation of characters, levels, objects, and DLC for the game." | -| `metadatastician/canonical-ums` | 1311842310 | 2026-07-25T09:14:39Z | **A different repo**, private. Description: "Canonical Universal Modding Studio for IDApTIK." This is the rename target of `idaptik-ums-canonical` per prior memory (`idaptik-ums-canonical` → `metadatastician/canonical-ums`), **not** of `idaptik-ums`. Has its own, earlier oikosbot sweep PR (`#6`, same title), unrelated to `hyperpolymath/idaptik-ums#62`. | -| `metadatastician/IDApTIK` | — | — | Third, separate repo (the game itself, presumably); only noted here so it isn't confused with the two `*-ums` repos above. | -| `metadatastician/boj-server-mk2` | 1310698487 | 2026-07-24T06:16:28Z | The successor of `hyperpolymath/boj-server-mk2` (PR #19 lineage, proven above). Description: "Next-generation architecture for BoJ-Server... Erlang VM (BEAM)... Idris2 cartridges." | - -Note also: `hyperpolymath/boj-server` (id `1171174209`, distinct repo, "The Estate's primary MCP server") and `hyperpolymath/boj-server-cartridges` both still resolve fine under `hyperpolymath` and are unrelated to `boj-server-mk2` — do not conflate the `boj-server` / `boj-server-mk2` pair, per the task brief's warning. - -## Caveat on "transferred" vs "renamed-then-recreated" - -The `metadatastician/boj-server-mk2` repo's `created_at` (2026-07-24T06:16:28Z) predates the sweep's PR #19 (`2026-07-28T19:31:21Z`) by four days, and `metadatastician/idaptik-ums`'s `created_at` (2026-07-10T17:25:03Z) predates PR #62 by over two weeks. This is expected and not contradictory: `created_at` reflects the repo's original creation date (which transfers do not reset), and the sweep PRs were opened at their current `metadatastician/...` location on 2026-07-28 — consistent with the sweep running against the already-transferred repos. Nothing here suggests the repos were deleted-and-recreated; the PR-number/author/timestamp match is not something a recreation could reproduce, since GitHub assigns those server-side per-repo. - -## Recommended follow-up (repeated for clarity) - -1. Update the estate consumer manifest / sweep target list: replace `hyperpolymath/boj-server-mk2` with `metadatastician/boj-server-mk2`, and `hyperpolymath/idaptik-ums` with `metadatastician/idaptik-ums`. -2. No workflow remediation needed at either successor — `oikosbot.yml` is present and the sweep PR is merged at both. -3. Flag for the owner: two of the repos in this family (`idaptik-ums` and `canonical-ums`) are easy to conflate by name; the estate's consumer list should record them as separate rows to avoid a future sweep double-booking or skipping one. -4. If other `hyperpolymath/*` consumer entries later 404, re-run this same PR-number-continuity method rather than trusting the `.full_name` redirect probe alone — it silently 404s on cross-owner transfers. diff --git a/docs/superpowers/notes/2026-08-03-round-one-verification.adoc b/docs/superpowers/notes/2026-08-03-round-one-verification.adoc new file mode 100644 index 0000000..b84922e --- /dev/null +++ b/docs/superpowers/notes/2026-08-03-round-one-verification.adoc @@ -0,0 +1,259 @@ +== Round-one verification record: estate economics (telemetry / capability / DEA) + +*Date:* 2026-08-03 *Snapshot under test:* +`+hyperpolymath/oikosbot-estate+` `+snapshots/2026-08-03/+` (commit +`+c196423+`) *Spec:* +`+docs/superpowers/specs/2026-08-03-estate-economics-design.md+`, +"`Verification`" section, six items. *Workspace:* +`+/home/hyperpolymath/developer/hyper-repos/_EXTENSIONS _SET/oikosbot+`, +branch `+feat/estate-round-one+`. + +This note answers each of the spec’s six verification items with the +actual evidence produced by running the pipeline, not by re-deriving +results. + +''''' + +=== 1. Independence + +From `+snapshots/2026-08-03/analysis.json+` → `+independence+`: + +[cols=",",options="header",] +|=== +|pair |pearson r +|`+wall_minutes ~ size_kb+` |*-0.0486* +|`+wall_minutes ~ verified_success_runs+` |0.1060 +|=== + +*Verdict: premise HOLDS.* The falsifier pair, +`+wall_minutes ~ size_kb+`, is essentially zero (-0.0486) — measured +compute (the DEA input, `+wall_minutes+`) is empirically uncorrelated +with the code-volume proxy (`+size_kb+`). This means the DEA model’s +inputs are not secretly measuring the same axis twice. Stated plainly +and without softening: had this coefficient come out high (e.g. |r| > +~0.5), the premise would have failed and that would be reported as a +failure, not rationalised away. It did not — the coefficient is low. + +=== 2. Determinism + +Ran a second `+analyse+` over the *same, static* `+.staging/+` directory +used for snapshot #1 (frozen since Task 13’s collect; nothing +re-collected): + +.... +$ ./target/release/oikosbot estate analyse \ + --staging ~/developer/oikosbot-estate/.staging \ + --snapshot "$CLAUDE_JOB_DIR/tmp/snap-b" +wrote snapshot to /home/hyperpolymath/.claude/jobs/05e3bf6a/tmp/snap-b +real 0m20.699s + +$ diff <(jq -S . ~/developer/oikosbot-estate/snapshots/2026-08-03/analysis.json) \ + <(jq -S . "$CLAUDE_JOB_DIR/tmp/snap-b/analysis.json") +$ echo $? +0 +.... + +*Result: empty diff.* `+analyse+` is byte-for-byte deterministic given a +fixed staging input — no floating-point nondeterminism, no +HashMap-ordering leakage, no timestamp-of-run contamination in the +output JSON. + +*Collection-window caveat (as flagged in the brief):* this only proves +determinism of the _analyse_ step over a frozen input. It does *not* +(and cannot) prove that two independent `+estate collect+` runs against +a live GitHub Actions history would be byte-identical, because the +collection window is a moving target — new runs land, in-flight runs +finalise, between one `+collect+` invocation and the next. Task 13’s own +resumability exercise (deliberately `+kill -9+`-ing a `+collect+` +mid-sweep and re-running the identical command) is the closest available +substitute: the re-run emitted exactly the 7 `+skip (exists)+` lines +expected for the already-landed files and then continued to completion +with 0 errors — i.e. two partially- overlapping collects over the same +short window agreed on every run they both saw, for the repos already +staged. No second live collect was run for this task (that would require +re-hitting `+gh api+` against a shifted window, which is out of scope +for a determinism check); the byte-exact `+analyse+` diff above is the +reproducibility guarantee this task can make, plus the resumability +evidence as the collection-side substitute. + +=== 3. DEA correctness + +Cited, not re-derived — analytic tests in +`+crates/oikosbot-dea/src/lib.rs+`: + +* `+single_io_matches_closed_form+` (line 143) — +single-input/single-output case checked against the closed-form ratio (θ += own_ratio / max_peer_ratio), confirming the LP matches hand-computable +ground truth for DMUs A(2,2)=1.0, B(4,4)=1.0, C(8,4)=0.5. +* `+strong_duality_holds_and_scores_bounded+` (line 152) — for every +DMU, `+|theta_ccr - theta_mult| < 1e-5+` (envelopment LP score equals +multiplier LP score, i.e. *strong duality holds* within numerical +tolerance); `+0 < theta_ccr <= 1 + 1e-9+`; and +`+theta_bcc + 1e-9 >= theta_ccr+` (the BCC frontier is never farther +than CCR, as VRS theory requires). +* `+inefficient_unit_names_frontier_peers+` (line 165) — an inefficient +DMU’s peer set includes the frontier unit with positive envelopment +weight (λ > `+PEER_TOL+` = 1e-6). +* `+two_input_dominated_unit_is_inefficient+` (line 171) — a DMU +strictly dominated on two inputs for the same output scores inefficient. + +All four pass under `+cargo test -p oikosbot-dea+` (see §Full workspace +gate below for the consolidated run). + +*Honest flag, not smoothed over:* in the live snapshot, all 20 "`worst`" +DMUs in `+report.md+`’s off-frontier table score `+theta_ccr = 0.0000+` +with an *empty peers list*. This is mathematically consistent with the +code’s own documented behaviour — `+crates/oikosbot-dea/src/lib.rs+` +line 39, `+const FLOOR: f64 = 1e-6+`, and the comment at line 103, +"`Inputs/outputs are floored at `+1e-6+` before solving (zero +inputs/outputs…)`" — these are repos with `+verified_success_runs = 0+`, +so their DEA output vector is floored to `+1e-6+` rather than true zero, +and the resulting envelopment LP finds no positive-weight peer +combination that dominates them (θ collapses to the floor itself, +effectively unclassifiable as "`closer to`" any single peer). It is not +a bug in the tested LP machinery — the analytic tests above pass on +synthetic DMUs with genuine non-zero outputs — but it means the DEA +scores for verified-zero-output repos carry no discriminative peer +information. This is flagged here for round-two refinement +(e.g. treating zero-verified-output DMUs as a separate X-inefficiency +class rather than running them through CCR at all), per Task 13’s own +concern note. + +=== 4. Ground truth + +____ +*Re-run 2026-08-04 against the shipping code — and the first run’s +method was wrong.* The original gate check grouped runs by +`+(repo, workflow_path)+` in an ad-hoc `+jq+` query, but the code that +shipped grouped by `+workflow_name+`. Those are not the same key: +`+workflow_name+` is free text and two different files can share one +(two workflows both titled "`CI`"), so name-grouping merges their run +counts and can hide a fake-gate candidate behind a same-named _failing_ +workflow. The final whole-branch review caught the mismatch; +`+assess()+` now groups by `+workflow_path+` (unique per file) and +reports paths rather than names, and the gates below were re-run against +that shipping code. The figures moved accordingly and the direction +confirms the defect was real: by path, *1,482 candidates across 313 +repos*, i.e. *212 more* than the by-name run found — those were the ones +name-merging had been concealing. +____ + +Verified against the regenerated `+snapshots/2026-08-03/analysis.json+`: + +[arabic] +. *`+hyperpolymath/oikosbot+` heavy `+startup_failure+` since +2026-07-30* — 137/200 runs (68.5%) are `+startup_failure+`; earliest +timestamp `+2026-07-30T16:38:55Z+` — every `+startup_failure+` in the +200-run window is on/after 07-30, matching the "`epidemic since ~07-30`" +memory note. +. *echidna / kitchenspeak dominated by `+startup_failure+`* — echidna: +186/200 (93%); kitchenspeak: 136/200 (68%, remaining 64 plain +`+failure+`). +. *≥1 infallible-gate candidate, far exceeded* — the shipping +`+assess()+` groups runs by `+workflow_path+` and flags paths with ≥5 +runs and zero failures ever: *1,482 candidate workflows across 313 +repos*. Named examples span already-documented pathologies +(`+guix-policy.yml+` — presence-only gate; `+secret-scanner.yml+`; proof +gates that cannot fail). A candidate is _not_ a proven fake gate: it is +a workflow that has never demonstrated it can fail, which is the honest +claim telemetry alone supports. +. *15 X-inefficiency repos* — `+report.md+`’s X-inefficiency table: +`+wall_minutes > 0+` with `+verified_success_runs = 0+`, i.e. real input +consumed and zero verified output. The count fell from the pre-fix 38 +because `+verified_success_runs+` is now computed per _path_: repos +whose successes had been attributed to a name-merged group are now +correctly credited, so they are no longer counted as producing nothing. +The remaining 15 are the repos where that genuinely holds. + +All four gates PASS against the shipping code. Frontier: 8 repos at +θ_CCR = 1.0 (pre-fix: 9). + +=== 5. Confidence labelling + +Cited from `+crates/oikosbot-telemetry/src/derive.rs+` line 135, +`+confidence_ladder_is_honest+`: + +[source,rust] +---- +assert_eq!(confidence_of("wall_minutes"), Confidence::Measured); +assert_eq!(confidence_of("energy_kwh"), Confidence::Calibrated); +assert_eq!(confidence_of("carbon_g"), Confidence::Estimated); +assert_eq!(confidence_of("imputed_cost_usd"), Confidence::Calibrated); +---- + +Cross-checked against the snapshot’s own `+analysis.json+` → +`+confidence_counts+` block (independently emitted at analysis time, not +just asserted in the unit test): + +[source,json] +---- +{ + "wall_minutes": "Measured", + "energy_kwh": "Calibrated", + "imputed_cost_usd": "Calibrated", + "carbon_g": "Estimated" +} +---- + +Test and live output agree: the confidence ladder is honestly graded +per-metric — `+wall_minutes+` comes straight from GitHub API timestamps +(Measured), `+energy_kwh+`/`+imputed_cost_usd+` are derived via fixed +assumption constants applied to a measured quantity (Calibrated), and +`+carbon_g+` additionally depends on an external, time-varying +grid-intensity figure (Estimated) — the ladder does not claim more +certainty than the derivation chain supports at any step. + +=== 6. Part 0 + +Status of the Part-0 fix PRs (checked live via `+gh pr view+` / +`+gh issue view+` on 2026-08-03, i.e. this task’s own run, not carried +over from the brief which predates these merges landing): + +* `+hyperpolymath/oikosbot#58+` — "`fix(ci): drop illegal +timeout-minutes on uses: jobs (mirror, secret-scanner)`" — *MERGED* +2026-08-03T16:42:30Z. +* `+hyperpolymath/oikosbot#59+` — "`fix: loud builtin-policy warning; +label aspirational config`" — *MERGED* (open at brief-authoring time, +merged since). +* `+metadatastician/enaction-engine#26+` — "`fix(ci): remove stale +OIKOSBOT_ENABLED gate on the oikosbot job`" — *MERGED* (open at +brief-authoring time, merged since). + +All three Part-0 fixes are now merged to their respective `+main+` +branches. + +*CI-verification is blocked estate-wide, not specific to these three +repos*: per the memory note +`+actions-lockfile-enforcement-startup-failure+`, GitHub’s +workflow-lockfile enforcement update produces `+startup_failure+` on +essentially every workflow across the estate (oikosbot, echidna, +squisher, hypatia, trope-checker, UMS, and by the ground-truth +measurement above, oikosbot/echidna/kitchenspeak specifically) as of +~2026-07-30, with the actual failure reason visible only on the GitHub +Actions run HTML page (not in `+gh run view+`/API output). The fix +requires the repo owner to install the `+github/gh-actions-lock+` +extension — an action pending on the owner, outside this task’s scope. +Consequently, none of the three Part-0 PRs above (nor this round-one PR) +can be *CI*-verified right now regardless of correctness; all +verification in this document and in Task 13 was done by direct file +inspection (diffing the illegal-key removal, reading the +`+OIKOSBOT_ENABLED+` gate removal) and by running the relevant +test/build commands *locally* (`+cargo test+`, +`+cargo build --release+`), not by observing green GitHub Actions +checks. + +''''' + +=== Full workspace gate + +Run from the workspace root on branch `+feat/estate-round-one+`: + +.... +$ cargo test --workspace +$ cargo fmt --all --check +$ cargo clippy --workspace --all-targets -- -D warnings +.... + +Results recorded in the commit that adds this note (see git log) — all +three commands green locally at commit time. CI is unable to confirm +this independently per the Part 0 caveat above. diff --git a/docs/superpowers/notes/2026-08-03-round-one-verification.md b/docs/superpowers/notes/2026-08-03-round-one-verification.md deleted file mode 100644 index 7438850..0000000 --- a/docs/superpowers/notes/2026-08-03-round-one-verification.md +++ /dev/null @@ -1,233 +0,0 @@ -# Round-one verification record: estate economics (telemetry / capability / DEA) - -**Date:** 2026-08-03 -**Snapshot under test:** `hyperpolymath/oikosbot-estate` `snapshots/2026-08-03/` (commit `c196423`) -**Spec:** `docs/superpowers/specs/2026-08-03-estate-economics-design.md`, "Verification" section, six items. -**Workspace:** `/home/hyperpolymath/developer/hyper-repos/_EXTENSIONS _SET/oikosbot`, branch `feat/estate-round-one`. - -This note answers each of the spec's six verification items with the actual -evidence produced by running the pipeline, not by re-deriving results. - ---- - -## 1. Independence - -From `snapshots/2026-08-03/analysis.json` → `independence`: - -| pair | pearson r | -|---|---| -| `wall_minutes ~ size_kb` | **-0.0486** | -| `wall_minutes ~ verified_success_runs` | 0.1060 | - -**Verdict: premise HOLDS.** The falsifier pair, `wall_minutes ~ size_kb`, is -essentially zero (-0.0486) — measured compute (the DEA input, `wall_minutes`) -is empirically uncorrelated with the code-volume proxy (`size_kb`). This -means the DEA model's inputs are not secretly measuring the same axis twice. -Stated plainly and without softening: had this coefficient come out high -(e.g. |r| > ~0.5), the premise would have failed and that would be reported -as a failure, not rationalised away. It did not — the coefficient is low. - -## 2. Determinism - -Ran a second `analyse` over the **same, static** `.staging/` directory used -for snapshot #1 (frozen since Task 13's collect; nothing re-collected): - -``` -$ ./target/release/oikosbot estate analyse \ - --staging ~/developer/oikosbot-estate/.staging \ - --snapshot "$CLAUDE_JOB_DIR/tmp/snap-b" -wrote snapshot to /home/hyperpolymath/.claude/jobs/05e3bf6a/tmp/snap-b -real 0m20.699s - -$ diff <(jq -S . ~/developer/oikosbot-estate/snapshots/2026-08-03/analysis.json) \ - <(jq -S . "$CLAUDE_JOB_DIR/tmp/snap-b/analysis.json") -$ echo $? -0 -``` - -**Result: empty diff.** `analyse` is byte-for-byte deterministic given a -fixed staging input — no floating-point nondeterminism, no HashMap-ordering -leakage, no timestamp-of-run contamination in the output JSON. - -**Collection-window caveat (as flagged in the brief):** this only proves -determinism of the *analyse* step over a frozen input. It does **not** (and -cannot) prove that two independent `estate collect` runs against a live -GitHub Actions history would be byte-identical, because the collection -window is a moving target — new runs land, in-flight runs finalise, between -one `collect` invocation and the next. Task 13's own resumability exercise -(deliberately `kill -9`-ing a `collect` mid-sweep and re-running the -identical command) is the closest available substitute: the re-run emitted -exactly the 7 `skip (exists)` lines expected for the already-landed files -and then continued to completion with 0 errors — i.e. two partially- -overlapping collects over the same short window agreed on every run they -both saw, for the repos already staged. No second live collect was run for -this task (that would require re-hitting `gh api` against a shifted window, -which is out of scope for a determinism check); the byte-exact `analyse` -diff above is the reproducibility guarantee this task can make, plus the -resumability evidence as the collection-side substitute. - -## 3. DEA correctness - -Cited, not re-derived — analytic tests in `crates/oikosbot-dea/src/lib.rs`: - -- `single_io_matches_closed_form` (line 143) — single-input/single-output - case checked against the closed-form ratio (θ = own_ratio / - max_peer_ratio), confirming the LP matches hand-computable ground truth - for DMUs A(2,2)=1.0, B(4,4)=1.0, C(8,4)=0.5. -- `strong_duality_holds_and_scores_bounded` (line 152) — for every DMU, - `|theta_ccr - theta_mult| < 1e-5` (envelopment LP score equals multiplier - LP score, i.e. **strong duality holds** within numerical tolerance); - `0 < theta_ccr <= 1 + 1e-9`; and `theta_bcc + 1e-9 >= theta_ccr` (the BCC - frontier is never farther than CCR, as VRS theory requires). -- `inefficient_unit_names_frontier_peers` (line 165) — an inefficient DMU's - peer set includes the frontier unit with positive envelopment weight - (λ > `PEER_TOL` = 1e-6). -- `two_input_dominated_unit_is_inefficient` (line 171) — a DMU strictly - dominated on two inputs for the same output scores inefficient. - -All four pass under `cargo test -p oikosbot-dea` (see §Full workspace gate -below for the consolidated run). - -**Honest flag, not smoothed over:** in the live snapshot, all 20 "worst" -DMUs in `report.md`'s off-frontier table score `theta_ccr = 0.0000` with an -**empty peers list**. This is mathematically consistent with the code's own -documented behaviour — `crates/oikosbot-dea/src/lib.rs` line 39, -`const FLOOR: f64 = 1e-6`, and the comment at line 103, "Inputs/outputs are -floored at `1e-6` before solving (zero inputs/outputs...)" — these are -repos with `verified_success_runs = 0`, so their DEA output vector is -floored to `1e-6` rather than true zero, and the resulting envelopment LP -finds no positive-weight peer combination that dominates them (θ collapses -to the floor itself, effectively unclassifiable as "closer to" any single -peer). It is not a bug in the tested LP machinery — the analytic tests -above pass on synthetic DMUs with genuine non-zero outputs — but it means -the DEA scores for verified-zero-output repos carry no discriminative peer -information. This is flagged here for round-two refinement (e.g. treating -zero-verified-output DMUs as a separate X-inefficiency class rather than -running them through CCR at all), per Task 13's own concern note. - -## 4. Ground truth - -> **Re-run 2026-08-04 against the shipping code — and the first run's method -> was wrong.** The original gate check grouped runs by `(repo, workflow_path)` -> in an ad-hoc `jq` query, but the code that shipped grouped by -> `workflow_name`. Those are not the same key: `workflow_name` is free text -> and two different files can share one (two workflows both titled "CI"), -> so name-grouping merges their run counts and can hide a fake-gate -> candidate behind a same-named *failing* workflow. The final whole-branch -> review caught the mismatch; `assess()` now groups by `workflow_path` -> (unique per file) and reports paths rather than names, and the gates below -> were re-run against that shipping code. The figures moved accordingly and -> the direction confirms the defect was real: by path, **1,482 candidates -> across 313 repos**, i.e. **212 more** than the by-name run found — those -> were the ones name-merging had been concealing. - -Verified against the regenerated `snapshots/2026-08-03/analysis.json`: - -1. **`hyperpolymath/oikosbot` heavy `startup_failure` since 2026-07-30** — - 137/200 runs (68.5%) are `startup_failure`; earliest timestamp - `2026-07-30T16:38:55Z` — every `startup_failure` in the 200-run window - is on/after 07-30, matching the "epidemic since ~07-30" memory note. -2. **echidna / kitchenspeak dominated by `startup_failure`** — echidna: - 186/200 (93%); kitchenspeak: 136/200 (68%, remaining 64 plain - `failure`). -3. **≥1 infallible-gate candidate, far exceeded** — the shipping `assess()` - groups runs by `workflow_path` and flags paths with ≥5 runs and zero - failures ever: **1,482 candidate workflows across 313 repos**. Named - examples span already-documented pathologies (`guix-policy.yml` — - presence-only gate; `secret-scanner.yml`; proof gates that cannot fail). - A candidate is *not* a proven fake gate: it is a workflow that has never - demonstrated it can fail, which is the honest claim telemetry alone - supports. -4. **15 X-inefficiency repos** — `report.md`'s X-inefficiency table: - `wall_minutes > 0` with `verified_success_runs = 0`, i.e. real input - consumed and zero verified output. The count fell from the pre-fix 38 - because `verified_success_runs` is now computed per *path*: repos whose - successes had been attributed to a name-merged group are now correctly - credited, so they are no longer counted as producing nothing. The - remaining 15 are the repos where that genuinely holds. - -All four gates PASS against the shipping code. Frontier: 8 repos at -θ_CCR = 1.0 (pre-fix: 9). - -## 5. Confidence labelling - -Cited from `crates/oikosbot-telemetry/src/derive.rs` line 135, -`confidence_ladder_is_honest`: - -```rust -assert_eq!(confidence_of("wall_minutes"), Confidence::Measured); -assert_eq!(confidence_of("energy_kwh"), Confidence::Calibrated); -assert_eq!(confidence_of("carbon_g"), Confidence::Estimated); -assert_eq!(confidence_of("imputed_cost_usd"), Confidence::Calibrated); -``` - -Cross-checked against the snapshot's own `analysis.json` → -`confidence_counts` block (independently emitted at analysis time, not just -asserted in the unit test): - -```json -{ - "wall_minutes": "Measured", - "energy_kwh": "Calibrated", - "imputed_cost_usd": "Calibrated", - "carbon_g": "Estimated" -} -``` - -Test and live output agree: the confidence ladder is honestly graded -per-metric — `wall_minutes` comes straight from GitHub API timestamps -(Measured), `energy_kwh`/`imputed_cost_usd` are derived via fixed -assumption constants applied to a measured quantity (Calibrated), and -`carbon_g` additionally depends on an external, time-varying grid-intensity -figure (Estimated) — the ladder does not claim more certainty than the -derivation chain supports at any step. - -## 6. Part 0 - -Status of the Part-0 fix PRs (checked live via `gh pr view` / -`gh issue view` on 2026-08-03, i.e. this task's own run, not carried over -from the brief which predates these merges landing): - -- `hyperpolymath/oikosbot#58` — "fix(ci): drop illegal timeout-minutes on - uses: jobs (mirror, secret-scanner)" — **MERGED** 2026-08-03T16:42:30Z. -- `hyperpolymath/oikosbot#59` — "fix: loud builtin-policy warning; label - aspirational config" — **MERGED** (open at brief-authoring time, merged - since). -- `metadatastician/enaction-engine#26` — "fix(ci): remove stale - OIKOSBOT_ENABLED gate on the oikosbot job" — **MERGED** (open at - brief-authoring time, merged since). - -All three Part-0 fixes are now merged to their respective `main` branches. - -**CI-verification is blocked estate-wide, not specific to these three -repos**: per the memory note `actions-lockfile-enforcement-startup-failure`, -GitHub's workflow-lockfile enforcement update produces `startup_failure` on -essentially every workflow across the estate (oikosbot, echidna, squisher, -hypatia, trope-checker, UMS, and by the ground-truth measurement above, -oikosbot/echidna/kitchenspeak specifically) as of ~2026-07-30, with the -actual failure reason visible only on the GitHub Actions run HTML page (not -in `gh run view`/API output). The fix requires the repo owner to install -the `github/gh-actions-lock` extension — an action pending on the owner, -outside this task's scope. Consequently, none of the three Part-0 PRs above -(nor this round-one PR) can be **CI**-verified right now regardless of -correctness; all verification in this document and in Task 13 was done by -direct file inspection (diffing the illegal-key removal, reading the -`OIKOSBOT_ENABLED` gate removal) and by running the relevant test/build -commands **locally** (`cargo test`, `cargo build --release`), not by -observing green GitHub Actions checks. - ---- - -## Full workspace gate - -Run from the workspace root on branch `feat/estate-round-one`: - -``` -$ cargo test --workspace -$ cargo fmt --all --check -$ cargo clippy --workspace --all-targets -- -D warnings -``` - -Results recorded in the commit that adds this note (see git log) — all -three commands green locally at commit time. CI is unable to confirm this -independently per the Part 0 caveat above. diff --git a/docs/superpowers/plans/2026-08-03-estate-economics-round-one.md b/docs/superpowers/plans/2026-08-03-estate-economics-round-one.adoc similarity index 66% rename from docs/superpowers/plans/2026-08-03-estate-economics-round-one.md rename to docs/superpowers/plans/2026-08-03-estate-economics-round-one.adoc index 072019e..b0d848d 100644 --- a/docs/superpowers/plans/2026-08-03-estate-economics-round-one.md +++ b/docs/superpowers/plans/2026-08-03-estate-economics-round-one.adoc @@ -1,106 +1,156 @@ -# Estate Economics Round One — Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Land the Part 0 repairs, then build the read-only estate pipeline: telemetry collector → snapshot → derived metrics → DEA frontier → report. - -**Architecture:** Three new library crates in the existing `oikosbot` Cargo workspace plus an `estate` CLI subcommand. Collection shells out to the authenticated `gh` CLI (no auth code, resumable). Snapshots are Parquet files committed to a new `oikosbot-estate` dataset repo. DEA is solved natively as paired envelopment/multiplier LPs on `good_lp`+HiGHS, giving θ, peer sets, and dual weights with a built-in strong-duality cross-check. Nothing blocks, nothing is enforced. - -**Tech Stack:** Rust (existing workspace), `good_lp` (HiGHS backend, MIT), `parquet`/`parquet_derive`, `serde_json`, `anyhow`, `gh` CLI subprocess. - -**Spec:** `docs/superpowers/specs/2026-08-03-estate-economics-design.md` (committed in Task 1 from `~/.claude/plans/oikosbot-sitrep-effervescent-crescent.md`). - -## Global Constraints - -- Workspace root: `/home/hyperpolymath/developer/hyper-repos/_EXTENSIONS _SET/oikosbot` — **path contains spaces; always quote**. Use `OIKOSBOT="/home/hyperpolymath/developer/hyper-repos/_EXTENSIONS _SET/oikosbot"` in every shell step. -- Crate naming: new crates are `oikosbot-*` (NOT `oikos-*` — that prefix belongs to the accounting DSL per `DISAMBIGUATION.adoc`). The dataset repo is `oikosbot-estate` for the same reason. -- Every new source file starts with `// SPDX-License-Identifier: MPL-2.0` (YAML/MD files: `# SPDX-License-Identifier: MPL-2.0` where the format allows comments). -- `good_lp` MUST be `default-features = false, features = ["highs"]` (the CBC default is EPL-licensed and stale). -- No Python anywhere. No new GitHub Actions in this round (estate Actions are dead pending the owner's lockfile fix; verification is local). -- `cargo fmt --all` and `cargo clippy --workspace --all-targets -- -D warnings` must pass before every commit. -- Confidence labels are load-bearing: money-from-declared-rate and energy are at best `Calibrated`; carbon is at most `Estimated`; only API-sourced quantities are `Measured`. A test asserts this (Task 10). -- Do not touch the uncommitted `.editorconfig`/`.gitignore` edits in the checkout — they are the owner's. -- CI cannot run (estate-wide `startup_failure`); PRs are created but verified by file inspection and local tests. Do not claim CI-green anywhere. - ---- - -## Phase A — Part 0 repairs - -### Task 1: Sync checkout and commit the design spec - -**Files:** -- Create: `docs/superpowers/specs/2026-08-03-estate-economics-design.md` (copy of the approved plan file) -- Create: `docs/superpowers/plans/2026-08-03-estate-economics-round-one.md` (this file) - -**Interfaces:** -- Consumes: approved design at `~/.claude/plans/oikosbot-sitrep-effervescent-crescent.md` -- Produces: branch `feat/estate-round-one` carrying the spec; local `main` in sync with `origin/main` at `7e73ddf` (#57) - -- [ ] **Step 1: Sync main (leave the owner's dirty files alone)** - -```bash +== Estate Economics Round One — Implementation Plan + +____ +*For agentic workers:* REQUIRED SUB-SKILL: Use +superpowers:subagent-driven-development (recommended) or +superpowers:executing-plans to implement this plan task-by-task. Steps +use checkbox (`+- [ ]+`) syntax for tracking. +____ + +*Goal:* Land the Part 0 repairs, then build the read-only estate +pipeline: telemetry collector → snapshot → derived metrics → DEA +frontier → report. + +*Architecture:* Three new library crates in the existing `+oikosbot+` +Cargo workspace plus an `+estate+` CLI subcommand. Collection shells out +to the authenticated `+gh+` CLI (no auth code, resumable). Snapshots are +Parquet files committed to a new `+oikosbot-estate+` dataset repo. DEA +is solved natively as paired envelopment/multiplier LPs on +`+good_lp+`+HiGHS, giving θ, peer sets, and dual weights with a built-in +strong-duality cross-check. Nothing blocks, nothing is enforced. + +*Tech Stack:* Rust (existing workspace), `+good_lp+` (HiGHS backend, +MIT), `+parquet+`/`+parquet_derive+`, `+serde_json+`, `+anyhow+`, `+gh+` +CLI subprocess. + +*Spec:* `+docs/superpowers/specs/2026-08-03-estate-economics-design.md+` +(committed in Task 1 from +`+~/.claude/plans/oikosbot-sitrep-effervescent-crescent.md+`). + +=== Global Constraints + +* Workspace root: +`+/home/hyperpolymath/developer/hyper-repos/_EXTENSIONS _SET/oikosbot+` +— *path contains spaces; always quote*. Use +`+OIKOSBOT="/home/hyperpolymath/developer/hyper-repos/_EXTENSIONS _SET/oikosbot"+` +in every shell step. +* Crate naming: new crates are `+oikosbot-*+` (NOT `+oikos-*+` — that +prefix belongs to the accounting DSL per `+DISAMBIGUATION.adoc+`). The +dataset repo is `+oikosbot-estate+` for the same reason. +* Every new source file starts with +`+// SPDX-License-Identifier: MPL-2.0+` (YAML/MD files: +`+# SPDX-License-Identifier: MPL-2.0+` where the format allows +comments). +* `+good_lp+` MUST be `+default-features = false, features = ["highs"]+` +(the CBC default is EPL-licensed and stale). +* No Python anywhere. No new GitHub Actions in this round (estate +Actions are dead pending the owner’s lockfile fix; verification is +local). +* `+cargo fmt --all+` and +`+cargo clippy --workspace --all-targets -- -D warnings+` must pass +before every commit. +* Confidence labels are load-bearing: money-from-declared-rate and +energy are at best `+Calibrated+`; carbon is at most `+Estimated+`; only +API-sourced quantities are `+Measured+`. A test asserts this (Task 10). +* Do not touch the uncommitted `+.editorconfig+`/`+.gitignore+` edits in +the checkout — they are the owner’s. +* CI cannot run (estate-wide `+startup_failure+`); PRs are created but +verified by file inspection and local tests. Do not claim CI-green +anywhere. + +''''' + +=== Phase A — Part 0 repairs + +==== Task 1: Sync checkout and commit the design spec + +*Files:* - Create: +`+docs/superpowers/specs/2026-08-03-estate-economics-design.md+` (copy +of the approved plan file) - Create: +`+docs/superpowers/plans/2026-08-03-estate-economics-round-one.md+` +(this file) + +*Interfaces:* - Consumes: approved design at +`+~/.claude/plans/oikosbot-sitrep-effervescent-crescent.md+` - Produces: +branch `+feat/estate-round-one+` carrying the spec; local `+main+` in +sync with `+origin/main+` at `+7e73ddf+` (#57) + +* [ ] *Step 1: Sync main (leave the owner’s dirty files alone)* + +[source,bash] +---- OIKOSBOT="/home/hyperpolymath/developer/hyper-repos/_EXTENSIONS _SET/oikosbot" git -C "$OIKOSBOT" fetch origin git -C "$OIKOSBOT" status -sb # expect only .editorconfig/.gitignore dirty git -C "$OIKOSBOT" merge --ff-only origin/main git -C "$OIKOSBOT" log --oneline -1 # expect 7e73ddf (#57) -``` +---- -If `merge --ff-only` fails because the dirty files conflict, STOP and report — do not stash or discard the owner's edits. +If `+merge --ff-only+` fails because the dirty files conflict, STOP and +report — do not stash or discard the owner’s edits. -- [ ] **Step 2: Create the round-one branch and commit spec + plan** +* [ ] *Step 2: Create the round-one branch and commit spec + plan* -```bash +[source,bash] +---- git -C "$OIKOSBOT" switch -c feat/estate-round-one origin/main mkdir -p "$OIKOSBOT/docs/superpowers/specs" "$OIKOSBOT/docs/superpowers/plans" cp ~/.claude/plans/oikosbot-sitrep-effervescent-crescent.md \ "$OIKOSBOT/docs/superpowers/specs/2026-08-03-estate-economics-design.md" git -C "$OIKOSBOT" add docs/superpowers git -C "$OIKOSBOT" commit -m "docs: estate-economics design spec + round-one implementation plan" -``` +---- -### Task 2: Fix the two parse-dead workflows (illegal `timeout-minutes` on `uses:` jobs) +==== Task 2: Fix the two parse-dead workflows (illegal `+timeout-minutes+` on `+uses:+` jobs) -**Files:** -- Modify: `.github/workflows/mirror.yml` (remove one line) -- Modify: `.github/workflows/secret-scanner.yml` (remove one line) +*Files:* - Modify: `+.github/workflows/mirror.yml+` (remove one line) - +Modify: `+.github/workflows/secret-scanner.yml+` (remove one line) -**Interfaces:** -- Produces: PR `fix/uses-job-timeout` against `main`. Merge is the owner's call or post-verification (estate doctrine: Claude merges once verified — but CI cannot verify until the lockfile fix, so leave the PR open with that stated). +*Interfaces:* - Produces: PR `+fix/uses-job-timeout+` against `+main+`. +Merge is the owner’s call or post-verification (estate doctrine: Claude +merges once verified — but CI cannot verify until the lockfile fix, so +leave the PR open with that stated). -- [ ] **Step 1: Branch from origin/main** +* [ ] *Step 1: Branch from origin/main* -```bash +[source,bash] +---- git -C "$OIKOSBOT" switch -c fix/uses-job-timeout origin/main -``` +---- -- [ ] **Step 2: Remove the illegal key from both files** +* [ ] *Step 2: Remove the illegal key from both files* -In `.github/workflows/mirror.yml`, the job currently reads: +In `+.github/workflows/mirror.yml+`, the job currently reads: -```yaml +[source,yaml] +---- jobs: mirror: uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@d135b05bfc647d0c0fbfedc7e80f37ea50f49236 timeout-minutes: 10 secrets: inherit -``` +---- -Delete the `timeout-minutes: 10` line (a job whose body is `uses:` may not carry it; its presence invalidates the whole file — the same defect PR #56 fixed in `scorecard.yml`). Repeat for the `scan:` job in `.github/workflows/secret-scanner.yml`. +Delete the `+timeout-minutes: 10+` line (a job whose body is `+uses:+` +may not carry it; its presence invalidates the whole file — the same +defect PR #56 fixed in `+scorecard.yml+`). Repeat for the `+scan:+` job +in `+.github/workflows/secret-scanner.yml+`. -- [ ] **Step 3: Verify no workflow retains the defect** +* [ ] *Step 3: Verify no workflow retains the defect* -```bash +[source,bash] +---- for f in "$OIKOSBOT"/.github/workflows/*.yml; do awk '/^ uses:/{u=1} /^ timeout-minutes:/{if(u) print FILENAME": job-level timeout-minutes after uses:"} /^ [a-z]/{u=0}' "$f" done -``` +---- Expected: no output. -- [ ] **Step 4: Commit and open the PR** +* [ ] *Step 4: Commit and open the PR* -```bash +[source,bash] +---- git -C "$OIKOSBOT" add .github/workflows/mirror.yml .github/workflows/secret-scanner.yml git -C "$OIKOSBOT" commit -m "fix(ci): drop illegal timeout-minutes on reusable-workflow jobs @@ -111,36 +161,45 @@ git -C "$OIKOSBOT" push -u origin fix/uses-job-timeout gh pr create -R hyperpolymath/oikosbot --head fix/uses-job-timeout \ --title "fix(ci): drop illegal timeout-minutes on uses: jobs (mirror, secret-scanner)" \ --body "Same defect class as #56, which missed these two files. NOTE: repo-wide startup_failure (GitHub lockfile enforcement) means no checks will run on this PR; verified by file inspection. Merge after the owner's gh-actions-lock fix, or on inspection." -``` +---- -### Task 3: Un-gate the proven pilot (enaction-engine) +==== Task 3: Un-gate the proven pilot (enaction-engine) -**Files:** -- Modify (in a fresh clone): `metadatastician/enaction-engine` → `.github/workflows/oikosbot.yml` +*Files:* - Modify (in a fresh clone): +`+metadatastician/enaction-engine+` → `+.github/workflows/oikosbot.yml+` -**Interfaces:** -- Produces: PR to `metadatastician/enaction-engine` removing `if: vars.OIKOSBOT_ENABLED == 'true'` and its stale justification comment. +*Interfaces:* - Produces: PR to `+metadatastician/enaction-engine+` +removing `+if: vars.OIKOSBOT_ENABLED == 'true'+` and its stale +justification comment. -- [ ] **Step 1: Clone into the job tmp dir** +* [ ] *Step 1: Clone into the job tmp dir* -```bash +[source,bash] +---- TMP="$CLAUDE_JOB_DIR/tmp" git clone --depth 1 git@github.com:metadatastician/enaction-engine.git "$TMP/enaction-engine" cd "$TMP/enaction-engine" && git switch -c fix/ungate-oikosbot -``` +---- -- [ ] **Step 2: Remove the gate and the stale comment** +* [ ] *Step 2: Remove the gate and the stale comment* -In `.github/workflows/oikosbot.yml`: delete the `if: vars.OIKOSBOT_ENABLED == 'true'` line and the multi-line comment block above it that claims the image "has never been published" (stale: publish-image went green 2026-07-28; the image is podman-verified and the job already uses the composite action, not the container pull the comment describes). Replace the comment with: +In `+.github/workflows/oikosbot.yml+`: delete the +`+if: vars.OIKOSBOT_ENABLED == 'true'+` line and the multi-line comment +block above it that claims the image "`has never been published`" +(stale: publish-image went green 2026-07-28; the image is +podman-verified and the job already uses the composite action, not the +container pull the comment describes). Replace the comment with: -```yaml +[source,yaml] +---- # Runs the published composite action (hyperpolymath/oikosbot@v0.1.0). # This repo is the proven pilot: 101 SARIF results ingested on PR #21. -``` +---- -- [ ] **Step 3: Commit and open the PR** +* [ ] *Step 3: Commit and open the PR* -```bash +[source,bash] +---- git add .github/workflows/oikosbot.yml git commit -m "fix(ci): remove stale OIKOSBOT_ENABLED gate @@ -151,19 +210,23 @@ git push -u origin fix/ungate-oikosbot gh pr create -R metadatastician/enaction-engine --head fix/ungate-oikosbot \ --title "fix(ci): remove stale OIKOSBOT_ENABLED gate on the oikosbot job" \ --body "Premise ('image has never been published') stale since 2026-07-28. This repo is the proven pilot (101 results ingested, PR #21); the gate turned it off. NOTE: estate-wide startup_failure means checks will not run here until the lockfile fix." -``` +---- -### Task 4: Trace the two unresolvable consumer repos +==== Task 4: Trace the two unresolvable consumer repos -**Files:** -- Create: `docs/superpowers/notes/2026-08-03-consumer-repo-trace.md` (on `feat/estate-round-one`) +*Files:* - Create: +`+docs/superpowers/notes/2026-08-03-consumer-repo-trace.md+` (on +`+feat/estate-round-one+`) -**Interfaces:** -- Produces: written disposition for `hyperpolymath/boj-server-mk2` and `hyperpolymath/idaptik-ums` (renamed → new name; deleted; or transferred), so the consumer list is accurate for later sweeps. +*Interfaces:* - Produces: written disposition for +`+hyperpolymath/boj-server-mk2+` and `+hyperpolymath/idaptik-ums+` +(renamed → new name; deleted; or transferred), so the consumer list is +accurate for later sweeps. -- [ ] **Step 1: Probe both names and likely successors** +* [ ] *Step 1: Probe both names and likely successors* -```bash +[source,bash] +---- for r in hyperpolymath/boj-server-mk2 metadatastician/boj-server-mk2 hyperpolymath/boj-server metadatastician/boj-server \ hyperpolymath/idaptik-ums metadatastician/idaptik-ums metadatastician/canonical-ums hyperpolymath/canonical-ums; do echo "== $r"; gh repo view "$r" --json name,owner,isArchived,url -q '"\(.owner.login)/\(.name) archived=\(.isArchived)"' 2>&1 @@ -171,41 +234,49 @@ done # Renames follow redirects on the API; also check: gh api repos/hyperpolymath/boj-server-mk2 -q .full_name 2>&1 gh api repos/hyperpolymath/idaptik-ums -q .full_name 2>&1 -``` +---- -- [ ] **Step 2: Search the owners' repo lists for near-names** +* [ ] *Step 2: Search the owners’ repo lists for near-names* -```bash +[source,bash] +---- gh repo list hyperpolymath --limit 400 --json name -q '.[].name' | grep -iE 'boj|ums|idaptik' gh repo list metadatastician --limit 400 --json name -q '.[].name' | grep -iE 'boj|ums|idaptik' -``` +---- -- [ ] **Step 3: Record findings and commit** +* [ ] *Step 3: Record findings and commit* -Write the disposition table (old name → found-at / not-found, evidence commands) to `docs/superpowers/notes/2026-08-03-consumer-repo-trace.md`, then: +Write the disposition table (old name → found-at / not-found, evidence +commands) to +`+docs/superpowers/notes/2026-08-03-consumer-repo-trace.md+`, then: -```bash +[source,bash] +---- git -C "$OIKOSBOT" switch feat/estate-round-one git -C "$OIKOSBOT" add docs/superpowers/notes git -C "$OIKOSBOT" commit -m "docs: trace unresolvable oikosbot consumer repos" -``` +---- -### Task 5: Make the Eclexia builtin backend loud, and annotate the over-promising config +==== Task 5: Make the Eclexia builtin backend loud, and annotate the over-promising config -**Files:** -- Modify: `crates/oikosbot-eclexia/src/lib.rs` (the `evaluate_builtin` path) -- Modify: `config/oikos.yaml` (comment annotations only) -- Test: `crates/oikosbot-eclexia/src/lib.rs` (unit test in-module) +*Files:* - Modify: `+crates/oikosbot-eclexia/src/lib.rs+` (the +`+evaluate_builtin+` path) - Modify: `+config/oikos.yaml+` (comment +annotations only) - Test: `+crates/oikosbot-eclexia/src/lib.rs+` (unit +test in-module) -**Interfaces:** -- Consumes: existing `evaluate_policies(dir, results)` flow; existing `evaluate_builtin` dispatch-by-file-stem. -- Produces: a `::warning::` on stderr every time the builtin path is used, naming the file and stating that its contents were not parsed; a pub fn `builtin_warning(policy_stem: &str) -> String` so the text is testable. +*Interfaces:* - Consumes: existing `+evaluate_policies(dir, results)+` +flow; existing `+evaluate_builtin+` dispatch-by-file-stem. - Produces: a +`+::warning::+` on stderr every time the builtin path is used, naming +the file and stating that its contents were not parsed; a pub fn +`+builtin_warning(policy_stem: &str) -> String+` so the text is +testable. -- [ ] **Step 1: Write the failing test** +* [ ] *Step 1: Write the failing test* -In `crates/oikosbot-eclexia/src/lib.rs` tests module: +In `+crates/oikosbot-eclexia/src/lib.rs+` tests module: -```rust +[source,rust] +---- #[test] fn builtin_warning_names_file_and_admits_not_parsing() { let w = builtin_warning("energy_threshold"); @@ -213,19 +284,21 @@ fn builtin_warning_names_file_and_admits_not_parsing() { assert!(w.contains("NOT parsed")); assert!(w.starts_with("::warning::")); } -``` +---- -- [ ] **Step 2: Run it to make sure it fails** +* [ ] *Step 2: Run it to make sure it fails* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test -p oikosbot-eclexia builtin_warning -- --nocapture -``` +---- -Expected: FAIL (`builtin_warning` not found). +Expected: FAIL (`+builtin_warning+` not found). -- [ ] **Step 3: Implement** +* [ ] *Step 3: Implement* -```rust +[source,rust] +---- /// The builtin backend matches policies by FILE STEM and applies hardcoded /// thresholds; the .ecl contents are never read. Until the eclexia parser is /// wired (round two), every builtin evaluation must say so out loud. @@ -236,34 +309,42 @@ pub fn builtin_warning(policy_stem: &str) -> String { with the .ecl text). Install `eclexia` or await the native backend." ) } -``` +---- -In `evaluate_builtin`, before returning its decision, add: +In `+evaluate_builtin+`, before returning its decision, add: -```rust +[source,rust] +---- eprintln!("{}", builtin_warning(stem)); -``` +---- -- [ ] **Step 4: Run test, fmt, clippy** +* [ ] *Step 4: Run test, fmt, clippy* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test -p oikosbot-eclexia && cargo fmt --all && cargo clippy -p oikosbot-eclexia --all-targets -- -D warnings -``` +---- Expected: PASS, clean. -- [ ] **Step 5: Annotate `config/oikos.yaml`** +* [ ] *Step 5: Annotate `+config/oikos.yaml+`* -Above each block the loader parses-and-discards (`integrations:`, `ai_assistants:`, `praxis:`, `reporting:`, `notifications:`, `logging:`, `databases:`, `weights:`, `analysis.carbon:`, `analysis.energy:`, `analysis.complexity:`, `thresholds.eco_standard/eco_excellence`), insert: +Above each block the loader parses-and-discards (`+integrations:+`, +`+ai_assistants:+`, `+praxis:+`, `+reporting:+`, `+notifications:+`, +`+logging:+`, `+databases:+`, `+weights:+`, `+analysis.carbon:+`, +`+analysis.energy:+`, `+analysis.complexity:+`, +`+thresholds.eco_standard/eco_excellence+`), insert: -```yaml +[source,yaml] +---- # ASPIRATIONAL — parsed and DISCARDED by the current loader (config.rs). # Documented for the target design; nothing below this line takes effect yet. -``` +---- -- [ ] **Step 6: Commit and open the honesty PR** +* [ ] *Step 6: Commit and open the honesty PR* -```bash +[source,bash] +---- git -C "$OIKOSBOT" switch -c fix/honesty-punchlist origin/main git -C "$OIKOSBOT" add crates/oikosbot-eclexia config/oikos.yaml git -C "$OIKOSBOT" commit -m "fix: loud builtin-policy warning; mark aspirational config blocks @@ -276,69 +357,87 @@ git -C "$OIKOSBOT" push -u origin fix/honesty-punchlist gh pr create -R hyperpolymath/oikosbot --head fix/honesty-punchlist \ --title "fix: loud builtin-policy warning; label aspirational config" \ --body "Part 0 items 4 and 6 of the estate-economics design. No behaviour change beyond stderr. CI dead estate-wide; verified by local cargo test." -``` +---- ---- +''''' -## Phase B — Round one pipeline +=== Phase B — Round one pipeline -All Phase B work happens on `feat/estate-round-one`. Run `git -C "$OIKOSBOT" switch feat/estate-round-one` before each task. +All Phase B work happens on `+feat/estate-round-one+`. Run +`+git -C "$OIKOSBOT" switch feat/estate-round-one+` before each task. -### Task 6: Scaffold the three crates +==== Task 6: Scaffold the three crates -**Files:** -- Create: `crates/oikosbot-telemetry/Cargo.toml`, `crates/oikosbot-telemetry/src/lib.rs` -- Create: `crates/oikosbot-capability/Cargo.toml`, `crates/oikosbot-capability/src/lib.rs` -- Create: `crates/oikosbot-dea/Cargo.toml`, `crates/oikosbot-dea/src/lib.rs` -- Modify: root `Cargo.toml` ONLY if `members` is an explicit list (check first; if it globs `crates/*`, no change) +*Files:* - Create: `+crates/oikosbot-telemetry/Cargo.toml+`, +`+crates/oikosbot-telemetry/src/lib.rs+` - Create: +`+crates/oikosbot-capability/Cargo.toml+`, +`+crates/oikosbot-capability/src/lib.rs+` - Create: +`+crates/oikosbot-dea/Cargo.toml+`, `+crates/oikosbot-dea/src/lib.rs+` - +Modify: root `+Cargo.toml+` ONLY if `+members+` is an explicit list +(check first; if it globs `+crates/*+`, no change) -**Interfaces:** -- Produces: three empty compiling library crates in the workspace. +*Interfaces:* - Produces: three empty compiling library crates in the +workspace. -- [ ] **Step 1: Check workspace membership style** +* [ ] *Step 1: Check workspace membership style* -```bash +[source,bash] +---- grep -A5 '^\[workspace\]' "$OIKOSBOT/Cargo.toml" -``` +---- -If `members` lists crates explicitly, the three new paths must be appended; if it uses `"crates/*"`, skip. +If `+members+` lists crates explicitly, the three new paths must be +appended; if it uses `+"crates/*"+`, skip. -- [ ] **Step 2: Create the crates (mirror an existing crate's edition/metadata)** +* [ ] *Step 2: Create the crates (mirror an existing crate’s +edition/metadata)* -Each `Cargo.toml` copies the `[package]` metadata style of `crates/oikosbot-pareto/Cargo.toml` (same edition, license MPL-2.0, version 0.1.0). Dependencies: +Each `+Cargo.toml+` copies the `+[package]+` metadata style of +`+crates/oikosbot-pareto/Cargo.toml+` (same edition, license MPL-2.0, +version 0.1.0). Dependencies: -`oikosbot-telemetry`: `serde = { version = "1", features = ["derive"] }`, `serde_json = "1"`, `anyhow = "1"`, `parquet = "56"`, `parquet_derive = "56"`, and `oikosbot-metrics = { path = "../oikosbot-metrics" }`. (Run `cargo add parquet parquet_derive` to resolve the current compatible pair rather than trusting "56"; they must be the same version.) +`+oikosbot-telemetry+`: +`+serde = { version = "1", features = ["derive"] }+`, +`+serde_json = "1"+`, `+anyhow = "1"+`, `+parquet = "56"+`, +`+parquet_derive = "56"+`, and +`+oikosbot-metrics = { path = "../oikosbot-metrics" }+`. (Run +`+cargo add parquet parquet_derive+` to resolve the current compatible +pair rather than trusting "`56`"; they must be the same version.) -`oikosbot-capability`: `serde`, `serde_json`, `anyhow`, `oikosbot-telemetry = { path = "../oikosbot-telemetry" }`. +`+oikosbot-capability+`: `+serde+`, `+serde_json+`, `+anyhow+`, +`+oikosbot-telemetry = { path = "../oikosbot-telemetry" }+`. -`oikosbot-dea`: `anyhow`, `good_lp = { version = "1.15", default-features = false, features = ["highs"] }`. +`+oikosbot-dea+`: `+anyhow+`, +`+good_lp = { version = "1.15", default-features = false, features = ["highs"] }+`. -Each `src/lib.rs` starts as: +Each `+src/lib.rs+` starts as: -```rust +[source,rust] +---- // SPDX-License-Identifier: MPL-2.0 //! (one-line purpose) -``` +---- -- [ ] **Step 3: Verify the workspace builds and commit** +* [ ] *Step 3: Verify the workspace builds and commit* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo check --workspace git add crates/oikosbot-telemetry crates/oikosbot-capability crates/oikosbot-dea Cargo.toml Cargo.lock git commit -m "chore: scaffold oikosbot-telemetry, oikosbot-capability, oikosbot-dea" -``` +---- -### Task 7: Telemetry row types + Parquet snapshot round-trip +==== Task 7: Telemetry row types + Parquet snapshot round-trip -**Files:** -- Create: `crates/oikosbot-telemetry/src/rows.rs` -- Create: `crates/oikosbot-telemetry/src/snapshot.rs` -- Modify: `crates/oikosbot-telemetry/src/lib.rs` (`pub mod rows; pub mod snapshot;`) +*Files:* - Create: `+crates/oikosbot-telemetry/src/rows.rs+` - Create: +`+crates/oikosbot-telemetry/src/snapshot.rs+` - Modify: +`+crates/oikosbot-telemetry/src/lib.rs+` +(`+pub mod rows; pub mod snapshot;+`) -**Interfaces:** -- Produces (relied on by Tasks 8–13): +*Interfaces:* - Produces (relied on by Tasks 8–13): -```rust +[source,rust] +---- pub struct RunRow { pub repo: String, pub run_id: i64, pub workflow_name: String, pub workflow_path: String, pub event: String, pub conclusion: String, pub started_at: String, pub updated_at: String, pub duration_s: i64 } @@ -348,13 +447,14 @@ pub struct ReleaseRow { pub repo: String, pub tag: String, pub published_at: Str // snapshot module: pub fn write_runs(path: &Path, rows: &[RunRow]) -> anyhow::Result<()> pub fn read_runs(path: &Path) -> anyhow::Result> // same pairs for RepoRow, ReleaseRow -``` +---- -- [ ] **Step 1: Write the failing round-trip test** +* [ ] *Step 1: Write the failing round-trip test* -In `snapshot.rs`: +In `+snapshot.rs+`: -```rust +[source,rust] +---- #[cfg(test)] mod tests { use super::*; @@ -376,23 +476,27 @@ mod tests { assert_eq!(back[0].duration_s, 300); } } -``` +---- -- [ ] **Step 2: Run it to make sure it fails** +* [ ] *Step 2: Run it to make sure it fails* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test -p oikosbot-telemetry parquet_roundtrip -``` +---- Expected: FAIL (types/functions not defined). -- [ ] **Step 3: Implement rows and snapshot** +* [ ] *Step 3: Implement rows and snapshot* -`rows.rs`: the three structs above, each `#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, parquet_derive::ParquetRecordWriter)]`. +`+rows.rs+`: the three structs above, each +`+#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, parquet_derive::ParquetRecordWriter)]+`. -`snapshot.rs` — write via `parquet_derive`, read via the stable `RowAccessor` API with column indices matching struct field order: +`+snapshot.rs+` — write via `+parquet_derive+`, read via the stable +`+RowAccessor+` API with column indices matching struct field order: -```rust +[source,rust] +---- use anyhow::{Context, Result}; use parquet::file::properties::WriterProperties; use parquet::file::reader::{FileReader, SerializedFileReader}; @@ -431,39 +535,45 @@ pub fn read_runs(path: &Path) -> Result> { } Ok(out) } -``` +---- -Repeat `write_/read_` pairs for `RepoRow` (bool via `r.get_bool`, size via `get_long`) and `ReleaseRow`. If the `parquet` version's derive/RowAccessor API differs from the above (it moves between majors), adapt to the installed version — the round-trip test is the contract, not this exact code. +Repeat `+write_/read_+` pairs for `+RepoRow+` (bool via `+r.get_bool+`, +size via `+get_long+`) and `+ReleaseRow+`. If the `+parquet+` version’s +derive/RowAccessor API differs from the above (it moves between majors), +adapt to the installed version — the round-trip test is the contract, +not this exact code. -- [ ] **Step 4: Run tests, fmt, clippy; commit** +* [ ] *Step 4: Run tests, fmt, clippy; commit* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test -p oikosbot-telemetry && cargo fmt --all && cargo clippy -p oikosbot-telemetry --all-targets -- -D warnings git add crates/oikosbot-telemetry && git commit -m "feat(telemetry): row types and parquet snapshot round-trip" -``` +---- -### Task 8: Collector over the `gh` CLI (resumable, capped) +==== Task 8: Collector over the `+gh+` CLI (resumable, capped) -**Files:** -- Create: `crates/oikosbot-telemetry/src/collect.rs` -- Modify: `crates/oikosbot-telemetry/src/lib.rs` (`pub mod collect;`) +*Files:* - Create: `+crates/oikosbot-telemetry/src/collect.rs+` - +Modify: `+crates/oikosbot-telemetry/src/lib.rs+` (`+pub mod collect;+`) -**Interfaces:** -- Produces: +*Interfaces:* - Produces: -```rust +[source,rust] +---- pub trait GhRunner { fn api(&self, path: &str) -> anyhow::Result; } pub struct GhCli; // shells out: gh api pub fn list_repos(gh: &dyn GhRunner, owner: &str) -> anyhow::Result> pub fn collect_runs(gh: &dyn GhRunner, repo: &str, max_runs: usize) -> anyhow::Result> pub fn collect_releases(gh: &dyn GhRunner, repo: &str) -> anyhow::Result> -``` +---- -Resumability lives one level up (Task 12): the CLI writes one staging JSON per repo and skips repos whose file already exists. +Resumability lives one level up (Task 12): the CLI writes one staging +JSON per repo and skips repos whose file already exists. -- [ ] **Step 1: Write the failing tests with a fake runner** +* [ ] *Step 1: Write the failing tests with a fake runner* -```rust +[source,rust] +---- #[cfg(test)] mod tests { use super::*; @@ -490,19 +600,21 @@ mod tests { assert_eq!(repos[0].size_kb, 906); } } -``` +---- -- [ ] **Step 2: Run to verify failure** +* [ ] *Step 2: Run to verify failure* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test -p oikosbot-telemetry collect_ -``` +---- Expected: FAIL. -- [ ] **Step 3: Implement** +* [ ] *Step 3: Implement* -```rust +[source,rust] +---- use anyhow::{bail, Context, Result}; use crate::rows::{ReleaseRow, RepoRow, RunRow}; @@ -592,28 +704,30 @@ pub fn collect_releases(gh: &dyn GhRunner, repo: &str) -> Result published_at: r["published_at"].as_str().unwrap_or("").to_string(), }).collect()) } -``` +---- -Add a unit test for `iso_to_epoch` against a known pair: `iso_to_epoch("2026-08-01T00:05:00Z") - iso_to_epoch("2026-08-01T00:00:00Z") == 300`, and `iso_to_epoch("1970-01-01T00:00:00Z") == 0`. +Add a unit test for `+iso_to_epoch+` against a known pair: +`+iso_to_epoch("2026-08-01T00:05:00Z") - iso_to_epoch("2026-08-01T00:00:00Z") == 300+`, +and `+iso_to_epoch("1970-01-01T00:00:00Z") == 0+`. -- [ ] **Step 4: Tests, lint, commit** +* [ ] *Step 4: Tests, lint, commit* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test -p oikosbot-telemetry && cargo fmt --all && cargo clippy -p oikosbot-telemetry --all-targets -- -D warnings git add crates/oikosbot-telemetry && git commit -m "feat(telemetry): gh-cli collector for runs, repos, releases" -``` +---- -### Task 9: Derived metrics with honest confidence labels +==== Task 9: Derived metrics with honest confidence labels -**Files:** -- Create: `crates/oikosbot-telemetry/src/derive.rs` -- Modify: `crates/oikosbot-telemetry/src/lib.rs` (`pub mod derive;`) +*Files:* - Create: `+crates/oikosbot-telemetry/src/derive.rs+` - Modify: +`+crates/oikosbot-telemetry/src/lib.rs+` (`+pub mod derive;+`) -**Interfaces:** -- Consumes: `RunRow`, `oikosbot_metrics::Confidence`. +*Interfaces:* - Consumes: `+RunRow+`, `+oikosbot_metrics::Confidence+`. - Produces: -```rust +[source,rust] +---- pub struct Assumptions { pub power_w: f64, pub pue: f64, pub grid_gco2_per_kwh: f64, pub usd_per_minute: f64 } impl Default for Assumptions { /* documented constants, sources in comments */ } @@ -621,11 +735,13 @@ pub struct DerivedRepo { pub repo: String, pub wall_minutes: f64, pub energy_kwh: f64, pub carbon_g: f64, pub imputed_cost_usd: f64 } pub fn derive_per_repo(runs: &[RunRow], a: &Assumptions) -> Vec pub fn confidence_of(metric: &str) -> oikosbot_metrics::Confidence -``` +---- -- [ ] **Step 1: Write the failing tests — including the honesty assertions from the spec** +* [ ] *Step 1: Write the failing tests — including the honesty +assertions from the spec* -```rust +[source,rust] +---- #[cfg(test)] mod tests { use super::*; @@ -648,13 +764,14 @@ mod tests { assert_eq!(confidence_of("imputed_cost_usd"), Confidence::Calibrated); } } -``` - -- [ ] **Step 2: Verify failure** — `cargo test -p oikosbot-telemetry derive` → FAIL. +---- -- [ ] **Step 3: Implement** +* [ ] *Step 2: Verify failure* — +`+cargo test -p oikosbot-telemetry derive+` → FAIL. +* [ ] *Step 3: Implement* -```rust +[source,rust] +---- use crate::rows::RunRow; use oikosbot_metrics::Confidence; use std::collections::BTreeMap; @@ -713,27 +830,31 @@ pub fn confidence_of(metric: &str) -> Confidence { _ => Confidence::Unknown, } } -``` +---- -(If `Confidence` lacks `PartialEq`, add `#[derive(PartialEq, Eq)]` to it in `oikosbot-metrics` — check first; the pareto crate already compares confidences via `confidence_rank`, so it likely has what's needed.) +(If `+Confidence+` lacks `+PartialEq+`, add `+#[derive(PartialEq, Eq)]+` +to it in `+oikosbot-metrics+` — check first; the pareto crate already +compares confidences via `+confidence_rank+`, so it likely has what’s +needed.) -- [ ] **Step 4: Tests, lint, commit** +* [ ] *Step 4: Tests, lint, commit* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test -p oikosbot-telemetry && cargo fmt --all && cargo clippy -p oikosbot-telemetry --all-targets -- -D warnings git add crates/oikosbot-telemetry && git commit -m "feat(telemetry): derived energy/carbon/cost with honest confidence ladder" -``` +---- -### Task 10: Capability metrics (the output side) +==== Task 10: Capability metrics (the output side) -**Files:** -- Create: `crates/oikosbot-capability/src/lib.rs` (replace scaffold) +*Files:* - Create: `+crates/oikosbot-capability/src/lib.rs+` (replace +scaffold) -**Interfaces:** -- Consumes: `oikosbot_telemetry::rows::{RunRow, ReleaseRow}`. -- Produces: +*Interfaces:* - Consumes: +`+oikosbot_telemetry::rows::{RunRow, ReleaseRow}+`. - Produces: -```rust +[source,rust] +---- pub struct CapabilityRow { pub repo: String, pub runs_total: u64, @@ -746,11 +867,13 @@ pub struct CapabilityRow { pub releases: u64, } pub fn assess(runs: &[RunRow], releases: &[ReleaseRow], min_n: u64) -> Vec -``` +---- -- [ ] **Step 1: Write the failing tests — ground truth from the estate's documented pathologies** +* [ ] *Step 1: Write the failing tests — ground truth from the estate’s +documented pathologies* -```rust +[source,rust] +---- #[cfg(test)] mod tests { use super::*; @@ -784,13 +907,14 @@ mod tests { assert_eq!(c[0].verified_success_runs, 1); // only Real's success counts } } -``` - -- [ ] **Step 2: Verify failure** — `cargo test -p oikosbot-capability` → FAIL. +---- -- [ ] **Step 3: Implement** +* [ ] *Step 2: Verify failure* — `+cargo test -p oikosbot-capability+` → +FAIL. +* [ ] *Step 3: Implement* -```rust +[source,rust] +---- // SPDX-License-Identifier: MPL-2.0 //! Verified capability: output metrics derived from run history alone. //! A gate with N successes and zero failures in its whole history cannot @@ -839,24 +963,24 @@ pub fn assess(runs: &[RunRow], releases: &[ReleaseRow], min_n: u64) -> Vec, pub outputs: Vec } pub struct DeaScore { pub name: String, @@ -868,15 +992,19 @@ pub struct DeaScore { pub output_weights: Vec, // u_r from multiplier form } pub fn dea(dmus: &[Dmu]) -> anyhow::Result> -``` +---- -Inputs/outputs are floored at `1e-6` inside `dea()` (zero inputs/outputs are common in the estate — dead repos — and break LP feasibility). +Inputs/outputs are floored at `+1e-6+` inside `+dea()+` (zero +inputs/outputs are common in the estate — dead repos — and break LP +feasibility). -- [ ] **Step 1: Write the failing analytic tests** +* [ ] *Step 1: Write the failing analytic tests* -Single-input single-output CRS has a closed form: θ_j = (y_j/x_j) / max_k(y_k/x_k). +Single-input single-output CRS has a closed form: θ_j = (y_j/x_j) / +max_k(y_k/x_k). -```rust +[source,rust] +---- #[cfg(test)] mod tests { use super::*; @@ -917,13 +1045,13 @@ mod tests { assert!((d.theta_ccr - 0.5).abs() < 1e-6); } } -``` - -- [ ] **Step 2: Verify failure** — `cargo test -p oikosbot-dea` → FAIL. +---- -- [ ] **Step 3: Implement** +* [ ] *Step 2: Verify failure* — `+cargo test -p oikosbot-dea+` → FAIL. +* [ ] *Step 3: Implement* -```rust +[source,rust] +---- // SPDX-License-Identifier: MPL-2.0 //! Data Envelopment Analysis: input-oriented CCR and BCC, solved as plain //! LPs. Envelopment form yields θ and the peer reference set; the multiplier @@ -1002,42 +1130,53 @@ pub fn dea(dmus: &[Dmu]) -> Result> { peers, input_weights: vw, output_weights: uw }) }).collect() } -``` +---- -(If the installed `good_lp` exposes the HiGHS solver under a different path than `good_lp::solvers::highs::highs`, use what `cargo doc` shows — the solver import is the only unstable point; the LP formulations are the contract.) +(If the installed `+good_lp+` exposes the HiGHS solver under a different +path than `+good_lp::solvers::highs::highs+`, use what `+cargo doc+` +shows — the solver import is the only unstable point; the LP +formulations are the contract.) -- [ ] **Step 4: Tests, lint, commit** +* [ ] *Step 4: Tests, lint, commit* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test -p oikosbot-dea && cargo fmt --all && cargo clippy -p oikosbot-dea --all-targets -- -D warnings git add crates/oikosbot-dea && git commit -m "feat(dea): CCR/BCC via paired envelopment+multiplier LPs on HiGHS" -``` +---- -### Task 12: `estate` CLI subcommand (collect / analyse / report) +==== Task 12: `+estate+` CLI subcommand (collect / analyse / report) -**Files:** -- Create: `crates/oikosbot-cli/src/estate.rs` -- Modify: `crates/oikosbot-cli/src/main.rs` (add `Estate` variant to the clap enum; `mod estate;`) -- Modify: `crates/oikosbot-cli/Cargo.toml` (add path deps on the three new crates) +*Files:* - Create: `+crates/oikosbot-cli/src/estate.rs+` - Modify: +`+crates/oikosbot-cli/src/main.rs+` (add `+Estate+` variant to the clap +enum; `+mod estate;+`) - Modify: `+crates/oikosbot-cli/Cargo.toml+` (add +path deps on the three new crates) -**Interfaces:** -- Consumes: everything produced in Tasks 7–11. -- Produces CLI: +*Interfaces:* - Consumes: everything produced in Tasks 7–11. - Produces +CLI: -``` +.... oikosbot estate collect --owner hyperpolymath --owner metadatastician \ --out [--max-runs 200] oikosbot estate analyse --staging --snapshot oikosbot estate report --snapshot [--format md|json] [-o file] -``` +.... -`collect` writes one `/runs--.json` (serde_json of `Vec`) plus `repos-.json` and `releases--.json` per repo, **skipping any file that already exists** (resumability). `analyse` reads staging, writes `runs.parquet`/`repos.parquet`/`releases.parquet` + `analysis.json` (derived + capability + DEA + independence) into the snapshot dir. `report` renders `analysis.json`. +`+collect+` writes one `+/runs--.json+` +(serde_json of `+Vec+`) plus `+repos-.json+` and +`+releases--.json+` per repo, *skipping any file that +already exists* (resumability). `+analyse+` reads staging, writes +`+runs.parquet+`/`+repos.parquet+`/`+releases.parquet+` + +`+analysis.json+` (derived + capability + DEA + independence) into the +snapshot dir. `+report+` renders `+analysis.json+`. -- [ ] **Step 1: Write the failing test for the analyse core (pure function)** +* [ ] *Step 1: Write the failing test for the analyse core (pure +function)* -In `estate.rs`: +In `+estate.rs+`: -```rust +[source,rust] +---- #[cfg(test)] mod tests { use super::*; @@ -1061,15 +1200,17 @@ mod tests { assert!(a.independence.iter().any(|(pair, _)| pair == "wall_minutes~size_kb")); } } -``` - -- [ ] **Step 2: Verify failure** — `cargo test -p oikosbot-cli analysis_produces` → FAIL. +---- -- [ ] **Step 3: Implement `estate.rs`** +* [ ] *Step 2: Verify failure* — +`+cargo test -p oikosbot-cli analysis_produces+` → FAIL. +* [ ] *Step 3: Implement `+estate.rs+`* -Core pieces (clap wiring mirrors the existing subcommand style in `main.rs`): +Core pieces (clap wiring mirrors the existing subcommand style in +`+main.rs+`): -```rust +[source,rust] +---- // SPDX-License-Identifier: MPL-2.0 use anyhow::Result; use oikosbot_capability::{assess, CapabilityRow}; @@ -1131,41 +1272,60 @@ pub fn run_analysis(runs: &[RunRow], repos: &[RepoRow], releases: &[ReleaseRow], .iter().map(|m| (m.to_string(), format!("{:?}", confidence_of(m)))).collect(); Ok(Analysis { /* map crate types into the *Out mirrors */ }) } -``` - -`collect` (in the same file) loops `list_repos` per owner, then per non-archived repo calls `collect_runs`/`collect_releases`, writing each result to its staging JSON and printing progress; a pre-existing staging file is skipped with a log line (this is the resumability contract). `analyse` loads staging JSONs, writes the three Parquet files via `snapshot::write_*`, runs `run_analysis`, writes `analysis.json`. `report` renders markdown: frontier table (θ=1 repos), worst-20 off-frontier with their peers, X-inefficiency list — the design's headline — as repos with `wall_minutes > 0 && verified_success_runs == 0` under a heading that states plainly: *"X-inefficiency here is our own framing (Leibenstein has no software-engineering literature): real input consumed, zero verified output produced."* End with the independence pairs and the confidence counts, Infracost-style. - -- [ ] **Step 4: Wire clap** — add to the existing `Commands` enum in `main.rs`: - -```rust +---- + +`+collect+` (in the same file) loops `+list_repos+` per owner, then per +non-archived repo calls `+collect_runs+`/`+collect_releases+`, writing +each result to its staging JSON and printing progress; a pre-existing +staging file is skipped with a log line (this is the resumability +contract). `+analyse+` loads staging JSONs, writes the three Parquet +files via `+snapshot::write_*+`, runs `+run_analysis+`, writes +`+analysis.json+`. `+report+` renders markdown: frontier table (θ=1 +repos), worst-20 off-frontier with their peers, X-inefficiency list — +the design’s headline — as repos with +`+wall_minutes > 0 && verified_success_runs == 0+` under a heading that +states plainly: _"`X-inefficiency here is our own framing (Leibenstein +has no software-engineering literature): real input consumed, zero +verified output produced.`"_ End with the independence pairs and the +confidence counts, Infracost-style. + +* [ ] *Step 4: Wire clap* — add to the existing `+Commands+` enum in +`+main.rs+`: + +[source,rust] +---- /// Estate-level telemetry, capability and DEA analysis (read-only) Estate { #[command(subcommand)] cmd: estate::EstateCmd, }, -``` +---- -with `EstateCmd { Collect{..}, Analyse{..}, Report{..} }` defined in `estate.rs` carrying the flags from the interface block. +with `+EstateCmd { Collect{..}, Analyse{..}, Report{..} }+` defined in +`+estate.rs+` carrying the flags from the interface block. -- [ ] **Step 5: Tests, lint, commit** +* [ ] *Step 5: Tests, lint, commit* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test --workspace && cargo fmt --all && cargo clippy --workspace --all-targets -- -D warnings git add crates/oikosbot-cli && git commit -m "feat(cli): estate collect/analyse/report subcommands" -``` +---- -### Task 13: Create the `oikosbot-estate` dataset repo and take snapshot #1 +==== Task 13: Create the `+oikosbot-estate+` dataset repo and take snapshot #1 -**Files:** -- Create (new repo, outside the workspace): `~/developer/oikosbot-estate/README.adoc`, `SCHEMA.adoc`, `snapshots//…` +*Files:* - Create (new repo, outside the workspace): +`+~/developer/oikosbot-estate/README.adoc+`, `+SCHEMA.adoc+`, +`+snapshots//…+` -**Interfaces:** -- Consumes: the `oikosbot estate` CLI from Task 12. -- Produces: `hyperpolymath/oikosbot-estate` on GitHub with the first committed snapshot. +*Interfaces:* - Consumes: the `+oikosbot estate+` CLI from Task 12. - +Produces: `+hyperpolymath/oikosbot-estate+` on GitHub with the first +committed snapshot. -- [ ] **Step 1: Initialise the repo** +* [ ] *Step 1: Initialise the repo* -```bash +[source,bash] +---- mkdir -p ~/developer/oikosbot-estate/snapshots && cd ~/developer/oikosbot-estate git init -b main cat > README.adoc <<'EOF' @@ -1178,13 +1338,19 @@ History is git history; dynamic-efficiency analysis reads consecutive snapshots. Named `oikosbot-estate` (not `oikos-*`, which is the accounting DSL's prefix — see oikosbot's DISAMBIGUATION.adoc). EOF -``` +---- -`SCHEMA.adoc`: one table per Parquet file listing column name/type/meaning, copied from the `rows.rs` doc comments, plus the ledger note: flows (minutes, energy, carbon, cost per snapshot period) and stocks (cumulative totals) reconcile per the SFC discipline — the reconciliation check itself lands with multi-snapshot support in round two, and this file says so explicitly. +`+SCHEMA.adoc+`: one table per Parquet file listing column +name/type/meaning, copied from the `+rows.rs+` doc comments, plus the +ledger note: flows (minutes, energy, carbon, cost per snapshot period) +and stocks (cumulative totals) reconcile per the SFC discipline — the +reconciliation check itself lands with multi-snapshot support in round +two, and this file says so explicitly. -- [ ] **Step 2: Run the first collection (small scope first, then full)** +* [ ] *Step 2: Run the first collection (small scope first, then full)* -```bash +[source,bash] +---- cd "$OIKOSBOT" cargo run -p oikosbot-cli -- estate collect --owner hyperpolymath --out ~/developer/oikosbot-estate/.staging --max-runs 200 cargo run -p oikosbot-cli -- estate collect --owner metadatastician --out ~/developer/oikosbot-estate/.staging --max-runs 200 @@ -1193,66 +1359,89 @@ cargo run -p oikosbot-cli -- estate analyse --staging ~/developer/oikosbot-estat --snapshot ~/developer/oikosbot-estate/snapshots/$DATE cargo run -p oikosbot-cli -- estate report --snapshot ~/developer/oikosbot-estate/snapshots/$DATE \ --format md -o ~/developer/oikosbot-estate/snapshots/$DATE/report.md -``` +---- -(Interrupt/re-run `collect` at least once mid-way to exercise resumability; the second run must skip already-collected repos.) +(Interrupt/re-run `+collect+` at least once mid-way to exercise +resumability; the second run must skip already-collected repos.) -- [ ] **Step 3: Verify against ground truth before committing** +* [ ] *Step 3: Verify against ground truth before committing* -The report MUST show, or the pipeline is wrong (spec §Verification 4): -- `hyperpolymath/oikosbot` with heavy `startup_failure` counts since 07-30; -- known-dead repos (echidna, kitchenspeak) dominated by startup failures; -- at least one infallible-gate candidate somewhere in the estate (documented pathology). +The report MUST show, or the pipeline is wrong (spec §Verification 4): - +`+hyperpolymath/oikosbot+` with heavy `+startup_failure+` counts since +07-30; - known-dead repos (echidna, kitchenspeak) dominated by startup +failures; - at least one infallible-gate candidate somewhere in the +estate (documented pathology). -Also confirm `.staging/` is NOT committed (add to `.gitignore`). +Also confirm `+.staging/+` is NOT committed (add to `+.gitignore+`). -- [ ] **Step 4: Create the GitHub repo and push** +* [ ] *Step 4: Create the GitHub repo and push* -```bash +[source,bash] +---- cd ~/developer/oikosbot-estate echo ".staging/" > .gitignore git add -A && git commit -m "snapshot: first estate telemetry collection ($DATE)" gh repo create hyperpolymath/oikosbot-estate --public --source . --push \ --description "Estate telemetry snapshots for OikosBot's economics pipeline (data only)" -``` +---- -### Task 14: Reproducibility + end-to-end verification, PR +==== Task 14: Reproducibility + end-to-end verification, PR -**Files:** -- Create: `docs/superpowers/notes/2026-08-03-round-one-verification.md` (results record) +*Files:* - Create: +`+docs/superpowers/notes/2026-08-03-round-one-verification.md+` (results +record) -**Interfaces:** -- Consumes: snapshot #1; the full workspace. -- Produces: the round-one PR with the verification record; the spec's six verification items each answered with evidence. +*Interfaces:* - Consumes: snapshot #1; the full workspace. - Produces: +the round-one PR with the verification record; the spec’s six +verification items each answered with evidence. -- [ ] **Step 1: Determinism of analyse** +* [ ] *Step 1: Determinism of analyse* -```bash +[source,bash] +---- cargo run -p oikosbot-cli -- estate analyse --staging ~/developer/oikosbot-estate/.staging --snapshot /tmp/snap-b diff <(jq -S . ~/developer/oikosbot-estate/snapshots/$DATE/analysis.json) <(jq -S . /tmp/snap-b/analysis.json) -``` +---- -Expected: empty diff. (Collection reproducibility over a shifting live window cannot be byte-exact; record instead that two collects within minutes agree on all closed runs — check a sample repo's run set.) +Expected: empty diff. (Collection reproducibility over a shifting live +window cannot be byte-exact; record instead that two collects within +minutes agree on all closed runs — check a sample repo’s run set.) -- [ ] **Step 2: Record the six verification answers** +* [ ] *Step 2: Record the six verification answers* -Write `2026-08-03-round-one-verification.md` answering each spec item with the actual evidence: (1) the independence coefficients from `analysis.json` — state plainly whether `wall_minutes~size_kb` is low (premise holds) or high (premise fails; say so, do not soften); (2) the determinism diff; (3) the DEA analytic tests + duality-gap assertion; (4) the ground-truth detections from Task 13 step 3; (5) the confidence-ladder test; (6) Part 0 status (PRs open, CI-unverifiable, why). +Write `+2026-08-03-round-one-verification.md+` answering each spec item +with the actual evidence: (1) the independence coefficients from +`+analysis.json+` — state plainly whether `+wall_minutes~size_kb+` is +low (premise holds) or high (premise fails; say so, do not soften); (2) +the determinism diff; (3) the DEA analytic tests + duality-gap +assertion; (4) the ground-truth detections from Task 13 step 3; (5) the +confidence-ladder test; (6) Part 0 status (PRs open, CI-unverifiable, +why). -- [ ] **Step 3: Full workspace gate and PR** +* [ ] *Step 3: Full workspace gate and PR* -```bash +[source,bash] +---- cd "$OIKOSBOT" && cargo test --workspace && cargo fmt --all --check && cargo clippy --workspace --all-targets -- -D warnings git add docs/superpowers/notes && git commit -m "docs: round-one verification record" git push -u origin feat/estate-round-one gh pr create -R hyperpolymath/oikosbot --head feat/estate-round-one \ --title "feat: estate economics round one — telemetry, capability, DEA (read-only)" \ --body "Implements the approved design (docs/superpowers/specs/2026-08-03-estate-economics-design.md): oikosbot-telemetry / oikosbot-capability / oikosbot-dea crates + 'oikosbot estate' CLI. Read-only; nothing blocks. First snapshot at hyperpolymath/oikosbot-estate. Verification record in docs/superpowers/notes/. CI dead estate-wide (lockfile enforcement); verified locally: cargo test --workspace green." -``` - ---- - -## Self-Review (done at write time) - -- **Spec coverage:** Part 0 items 1–6 → Tasks 2,3,4,5(×2),1; collector/capability/DEA/CLI/dataset-repo/verification → Tasks 7–14. Deferred per spec: SFC reconciliation check (needs ≥2 snapshots — noted in SCHEMA.adoc), static capability signals, PR decoration, Eclexia join, enforcement. -- **Known API risk, stated:** exact `parquet_derive`/`good_lp` item paths move between versions; the tests are the contract and Steps say adapt to the installed version. -- **Type consistency:** `RunRow`/`RepoRow`/`ReleaseRow` field lists identical in Tasks 7, 8, 10, 12; `Confidence` variants match `oikosbot-metrics`; DEA field names consistent between Task 11 and 12. +---- + +''''' + +=== Self-Review (done at write time) + +* *Spec coverage:* Part 0 items 1–6 → Tasks 2,3,4,5(×2),1; +collector/capability/DEA/CLI/dataset-repo/verification → Tasks 7–14. +Deferred per spec: SFC reconciliation check (needs ≥2 snapshots — noted +in SCHEMA.adoc), static capability signals, PR decoration, Eclexia join, +enforcement. +* *Known API risk, stated:* exact `+parquet_derive+`/`+good_lp+` item +paths move between versions; the tests are the contract and Steps say +adapt to the installed version. +* *Type consistency:* `+RunRow+`/`+RepoRow+`/`+ReleaseRow+` field lists +identical in Tasks 7, 8, 10, 12; `+Confidence+` variants match +`+oikosbot-metrics+`; DEA field names consistent between Task 11 and 12. diff --git a/docs/superpowers/specs/2026-08-03-estate-economics-design.adoc b/docs/superpowers/specs/2026-08-03-estate-economics-design.adoc new file mode 100644 index 0000000..53b4a0d --- /dev/null +++ b/docs/superpowers/specs/2026-08-03-estate-economics-design.adoc @@ -0,0 +1,514 @@ +== OikosBot — Pareto-first estate economics + +=== Context + +Two things prompted this plan. + +*The sitrep.* Asked for a status check on OikosBot, measured live on +2026-08-03. Generation 1 shipped and was verified end-to-end on +2026-07-28 (v0.1.0 tagged, container published, 101 SARIF results +ingested by GitHub code scanning on the `+enaction-engine+` pilot). +Since then three things have gone wrong, two of them genuine defects +rather than environmental noise. They are cheap to fix and are folded in +below as Part 0. + +*The real question.* How should OikosBot grow to match the functionality +of comparable products, while taking its _orientation_ from economic +optimality — Pareto optimality, allocative efficiency, productive +efficiency, dynamic efficiency, opportunity cost — treating the economic +as seriously as the ecological, and using Eclexia as the language for +expressing it. + +Investigation found that the honest answer requires changing the unit of +analysis, not adding features. The detail matters, so it is stated +plainly: + +____ +`+estimate_resources()+` derives energy, duration, carbon and memory +from a single integer — `+complexity+`, a raw AST node count. +`+energy = n × 0.1+`, `+duration = n × 0.5+`, +`+carbon = energy × 1.319e-4+`, `+memory = n × 2KB+`. Four of the five +Pareto objectives are therefore scalar multiples of one another, and a +frontier over collinear axes collapses to a one-dimensional sort. +`+Alloc+` reduces to roughly `+0.35 × complexity+` rank-inverted; +`+Debt+` is `+100 − 0.5 × complexity+`. All three EconScore terms are +the same variable in different clothes. +____ + +The dominance mathematics in `+crates/oikosbot-pareto+` is good work — +ε-tolerant, min-max normalised before any distance is measured, weights +inside the metric, all functions total, conformance-tested against +`+eco_rules.dl+`. The problem is entirely upstream of it, in what gets +fed in. + +A second structural point drove the design: + +____ +Allocative efficiency is not a property of a single change. It means +scarce resources are distributed to their highest-valued competing use; +with one diff in front of you there is nothing to allocate between. +Opportunity cost and marginal analysis have the same requirement. They +need a portfolio. +____ + +=== Decisions taken + +[width="100%",cols="34%,33%,33%",options="header",] +|=== +|# |Question |Ruling +|1 |Unit of analysis |*The estate as a portfolio*, ~400 repos competing +for a shared budget of hours, CI minutes, energy and money. The PR check +survives as one projection of a persistent model, not as the product. + +|2 |Where numbers come from |*CI telemetry becomes the `+Measured+` +tier.* Billable minutes, runner class, wall-clock and queue time from +the GitHub API — retroactive, no instrumentation, every repo. + +|3 |Eclexia’s role |*Bidirectional, with self-hosting as the horizon.* A +shared dominance crate that Eclexia’s inert `+@optimize+` finally +consumes; Eclexia serves as OikosBot’s typed policy surface today; +OikosBot’s allocation LP emits real duals that become Eclexia’s shadow +prices. Owner clarification (2026-08-03): Eclexia is a Turing-complete +general-purpose language implementing the Economics-as-Code paradigm +(per its whitepaper) with applications far beyond this bot — and +OikosBot is eventually to be *implemented in* Eclexia. + +|4 |Output measure |*Verified capability* — tests that genuinely +execute, gates that can genuinely fail, proofs discharged, workflows +that parse, artefacts that publish. + +|5 |Model locus |*Versioned columnar snapshots in a dedicated +`+oikos-estate+` dataset repo.* History is git history. No +infrastructure, no upstream blocker. +|=== + +==== Why these cohere + +Decision 2 breaks the collinearity that makes the present engine +vacuous: money, wall-clock, energy and carbon sourced from telemetry are +mutually independent and independent of node count. Decision 4 supplies +the output term without which every efficiency measure degenerates into +"`delete the code`". Decisions 1, 4 and 5 together make Data Envelopment +Analysis applicable — DEA compares units with multiple incommensurable +inputs _and_ outputs, requires no prices, and returns both a frontier +and a distance-to-frontier per unit. DEA is a linear program, and its +dual yields virtual weights on each input and output: marginal scarcity +derived from the estate’s own data. Those duals are decision 3’s shadow +prices. One LP produces the efficiency verdict and the prices Eclexia +consumes. + +The payoff of decision 4 deserves emphasis. The estate’s documented +pathologies — gates that cannot fail, workflows that never parsed, proof +checks that exit 0 without a prover, `+eclexiaiser.toml+` manifests +pointing at a template’s files — all become a single _economic_ fact: +real input consumed, zero output produced. That is X-inefficiency, and +OikosBot would be measuring and pricing it rather than merely listing +it. + +==== What the landscape says (surveyed live, 2026-08-03) + +Full survey in the session record; the points that bind the design: + +* *Hardware energy counters do not exist on hosted CI.* GitHub runners +are Azure VMs; the kernel deliberately does not load the RAPL powercap +driver in virtualised environments — an absent-file problem, not +permissions. Only two honest strategies exist: deterministic +instrumentation (Valgrind-derived, the CodSpeed approach) or modelled +estimation from utilisation (the *Eco-CI* approach: power curves over +SPECpower-trained models, vhost ratio 0.03125 for `+ubuntu-latest+`). +This _confirms_ decision 2 as the only honest route, and makes Eco-CI +required reading before writing the energy model. +* *The unoccupied market square is a working gate with honest numbers.* +Eco-CI, Green Metrics Tool and Impact Framework measure but never block; +Creedengo blocks but on static rules with thin empirical grounding. +Nobody gates on measured sustainability + economics. Meanwhile the +_universal_ table stake OikosBot lacks is *PR decoration* — every +surveyed product posts a comment or check run. Pattern to copy: +Infracost’s single updated-in-place comment with a one-line verdict +header and an honest "`M estimated, K not estimated`" footnote. +* *DEA needs no dependency.* No Rust DEA crate exists; input-oriented +CCR is ~150 lines of LP on `+good_lp+` +(`+default-features = false, features = ["highs"]+` — HiGHS is MIT; +avoid the stale EPL CBC default). BCC adds one constraint; scale +efficiency falls out of the ratio. *Allocative efficiency has a standard +LP definition* (cost efficiency ÷ technical efficiency, given input +prices), and the *Malmquist index* — four LPs over two periods, +decomposing productivity change into efficiency-change × +technical-change — is the textbook formalism for base-vs-head +comparison. SFA is ruled out (MLE research project; DEA is deterministic +and sufficient). +* *Vocabulary: SCI (ISO/IEC 21031:2024).* Prose-only standard, no schema +— so OikosBot defines the JSON in SCI’s own terms (`+E+`, `+I+`, `+M+`, +`+R+`) and inherits ISO’s authority. SARIF idiom: numbers as +value+unit+method triples in hierarchical camelCase property bags; +classification via `+taxonomies+`, not tags; `+partialFingerprints+` +hash the finding’s _shape_, never the metric value; human-readable +tables into `+help.markdown+` (GitHub ignores unknown properties). +* *Data sources for round one*, by licence and reliability: GitHub +*Billing Usage API* (GA 2026-06-04) for actual consumed minutes; +published per-minute prices (~40% cut effective 2026-01-01 — verify live +rates before hardcoding); vendored *CCF coefficients* (Apache-2.0: +per-family min/max watts + embodied kgCO2e — the clean-licence +indirection over SPECpower, which is a licence hazard to touch +directly); *UK NESO carbon-intensity API* (free, CC-BY, verified live) +plus *GSF Real Time Cloud CSV* for regions; *Boavizta* as an AGPL +_sidecar_ (never linked) for embodied hardware carbon; *EPA scghg CSV +vendored* for the social cost of carbon. deps.dev (CC-BY, no auth) when +dependency data is wanted. +* *The Social Cost of Carbon is now a political hazard.* The US federal +SC-GHG framework was dismantled in 2025 (IWG disbanded, estimates +withdrawn, EPA rescission). Any SCC-denominated finding is a scientific +claim, not a compliance one. Ruling: SCC is a configurable preset — +value + source + vintage + discount rate recorded in the SARIF +properties — shipping EPA-2023 ($190/tCO2 at 2% Ramsey) as a _labelled_ +preset alongside UK DESNZ and EU ETS prices, never as "`the official +value`". +* *Debt gets two independent dollar denominators* to cross-check: +SonarQube’s published remediation model (30 min/line default, A–E bands +at 5/10/20/50% — adopting the band grid costs nothing and buys +legibility) and COCOMO via `+scc+` (Go binary, shell out). +`+rust_finprim+` (MIT, maintained) turns debt-as-interest into real +discounted-cash-flow arithmetic. The Technical Debt Dataset (31 Apache +projects, SQLite) is the calibration corpus. +* *Flag against the Scallop ruling* (recorded, not overturned — the port +is a later phase): Scallop is not on crates.io, needs nightly, has no +`+license+` field in its manifest, and last released 2024-08. If the +policy layer stays deterministic, crisp Datalog (`+ascent+`, stable +Rust, on crates.io) suffices; Scallop is warranted only if probabilistic +reasoning is genuinely wanted. Decide at that phase; if Scallop, vendor +and SHA-pin. +* *Two honesty notes*: X-inefficiency and Baumol’s cost disease have +_no_ software-engineering literature — using the terms is our framing +and must say so. And `+config/oikos.yaml+` currently declares PR +comments, a dashboard, OTel and Slack integrations that do not exist in +code (added to Part 0). + +==== Eclexia’s trajectory: OikosBot as reference implementation + +Per the owner’s clarification and the whitepaper (_Economics-as-Code: A +Novel Programming Paradigm for Sustainable Computing_ — first-class +scarcity, trade-offs, opportunity cost and multi-objective optimisation, +with formal semantics and type-safety proofs), the relationship is not +"`bot with a config language`". It is: *Eclexia is the paradigm’s +language; OikosBot is its first serious application and, eventually, a +program written in it.* + +Consequences for this design: + +[arabic] +. *Rust OikosBot is the reference implementation, built to emigrate.* +Each component is a candidate for migration into Eclexia as the language +matures, and each migration is a live test of the paradigm claim — can +this actually be said in Economics-as-Code? The migration order suggests +itself: dominance/ frontier maths → Eclexia stdlib (making `+@optimize+` +semantically real for _every_ Eclexia program, not just this one); DEA’s +LP and its duals → the real solver behind `+shadow_price()+`, replacing +hand-set constants and discharging the ShadowPrices.v axioms (eclexia +#43) by implementation; telemetry ingestion → the runtime’s known gap +"`metrics not wired to real OS metrics`". +. *Contributions upstream are language work, not bot plumbing.* The +shared dominance crate, money literals, and measured shadow prices all +serve Eclexia’s general ambitions; OikosBot merely needs them first. +. *Keep the boundary honest meanwhile.* Until Eclexia can express these +components, the Rust implementation must not pretend otherwise — the +current filename-matching `+.ecl+` backend is exactly the pretence to +eliminate. The whitepaper’s evaluation numbers (20–40% energy etc.) are +projections by its own admission ("`no measured benchmarks`"); the +estate telemetry programme is, incidentally, the first chance to replace +those projections with measurements. + +==== The accounting DSL (`+oikos-economics-accounting-dsl+`) + +Assessed at the owner’s suggestion, with the caution that it is "`not +massively developed but could have some value`". Measured state: a real +parser front-end (Logos + Chumsky, 71 passing tests) plus five shallow +AST lint passes — and no typechecker, no compiler, no CLI, no evaluator; +nothing in it computes a number. The advertised guarantee ("`a model +that violates any accounting identity does not compile`") is +unimplemented: the enforcement path is a commented-out call to a stub +returning `+Err(EphapaxNotAvailable)+`. The Godley check counts sign +glyphs and ignores amounts; the README’s own example fails its own +checker; `+Stock+`/`+Flow+` is a parsed keyword no checker reads; ΔStock += Σflows exists nowhere. Development stopped ~2026-06-20. + +*Ruling on its role.* Adopt the _discipline_, not the dependency, and +invert the relationship: + +[arabic] +. *The estate ledger is stock-flow consistent by construction.* In the +snapshot schema, every flow (minutes, £, joules, gCO2e per period) +debits one account and credits another; every stock (cumulative spend, +cumulative carbon, debt principal) must reconcile against its +accumulated flows at each snapshot. This is implemented as identity +checks over real numbers inside the round-one crates — a few hundred +lines, and precisely the substance the DSL is missing. +. *Borrow its vocabulary, not its checker.* `+oikos-syntax+` is a clean, +serde-serialisable AST for sectors / accounts / periods / +transaction-flow matrices; naming the snapshot schema in its terms costs +nothing and keeps the two projects convergent. Its instrument typestate +checker (~115 lines, correct) and its pipe-table `+godley { }+` syntax +are worth lifting if a human-writable matrix surface is ever wanted. +. *OikosBot becomes the DSL’s first real testbed, not its consumer.* A +live, numerically-real SFC model of the estate is exactly the proving +ground the DSL lacks. When its Ephapax lowering eventually lands, the +estate ledger is the model waiting to compile — the dependency points +forward, and nothing in this design blocks on a stalled language +project. + +Two side-findings for the estate log: the DSL checkout is 1 commit ahead +of origin (a CodeQL re-pin — verify the SHA before pushing, per the +phantom-SHA landmine), and its stated Ephapax blocker ("`parser not yet +complete`") looks stale — `+ephapax-parser+` is now ~5.4k LOC and +Ephapax’s README marks most components complete; the assumption has not +been retested since June. + +=== Part 0 — repairs before any new work + +These are independent of the design and should land first. + +[arabic] +. *`+timeout-minutes+` on a `+uses:+` job kills two workflows.* +`+.github/workflows/mirror.yml+` and +`+.github/workflows/secret-scanner.yml+` both carry +`+timeout-minutes: 10+` on a job whose body is `+uses:+`. That key is +illegal there and invalidates the whole file — the distinguishing +signature is a run whose conclusion is `+failure+` at 0s and whose _name +is the file path_ rather than the workflow’s `+name:+`. PR #56 fixed +exactly this in `+scorecard.yml+` on 07-30 and missed these two. +Verified by comparison: governance, hypatia-scan and scorecard have zero +job-level `+timeout-minutes+`; these two have one each. The standards +pin `+d135b05bfc64+` is real, so this is not a phantom-SHA case. +. *The proven pilot has been switched off.* +`+metadatastician/enaction-engine+`’s oikosbot job now carries +`+if: vars.OIKOSBOT_ENABLED == 'true'+`, and the repo has no variables +set — so the one repo that demonstrably ingested 101 results never runs. +Its inline justification claims the image "`has never been published`", +which is stale: publish-image went green on 07-28 and the image was +podman-verified. It is the only one of the 14 reachable consumers gated +this way; the other 13 pin `+oikosbot@bb95ab50…+` cleanly with no +`+|| echo+`. Either set the variable or remove the gate and correct the +comment. +. *Two consumer repos no longer resolve.* +`+hyperpolymath/boj-server-mk2+` and `+hyperpolymath/idaptik-ums+` +return "`could not resolve to a Repository`" — renamed, transferred, or +deleted. Trace them; `+metadatastician/canonical-ums+` shows the estate +has done such renames before. +. *The Eclexia policy path is a fake gate.* `+oikosbot-eclexia+`’s +default backend, `+evaluate_builtin+`, dispatches on the `+.ecl+` *file +stem* and runs hardcoded Rust thresholds; the file’s contents are never +read. The thresholds contradict the files they claim to implement — +`+energy_threshold.ecl+` declares `+> 50.0 J+` per function, the builtin +fires at `+> 1000 J+` total. The `+eclexia-native+` backend cannot +compile (parser and interp are not dependencies). This is resolved by +the Eclexia work below, but should be made loud immediately rather than +left silently wrong. +. *Local checkout is 7 PRs stale* — HEAD `+3f1e6e6+` (#50) against +origin `+7e73ddf+` (#57), with uncommitted `+.editorconfig+` and +`+.gitignore+` edits. +. *`+config/oikos.yaml+` over-promises.* It declares PR comments, a +dashboard on :8080, OTel export, Slack/email notification and a praxis +loop, none of which exist in code; the config loader silently discards +them. A new adopter reading the reference config will believe OikosBot +does things it does not. Annotate the file to mark aspirational blocks, +or trim them. + +*Blocking constraint.* Every Actions workflow in the estate currently +dies at startup with a 0-second `+startup_failure+`, root-caused on +2026-08-03 to GitHub’s new workflow-lockfile enforcement. It is not +repairable from inside any repo: it needs +`+gh extension install github/gh-actions-lock+`, run once by the owner. +Nothing below can be CI-verified until that lands — which is precisely +why decision 5 puts collection in a local CLI reading server-side +history, so the work is not gated on it. + +=== Architecture + +Round one is a read-only pipeline. Nothing blocks, nothing is enforced, +and no existing behaviour changes. + +.... +gh API ──► collect ──► snapshot ──► derive ──► DEA ──► report + (crate) (parquet) (crate) (crate) (md/json) +.... + +==== New crates + +*`+oikos-telemetry+`* — the collector. Reads, per repo, from the GitHub +REST and GraphQL APIs: workflow run history (conclusion, duration, +`+run_started_at+`, runner labels), `+timing+` per run for billable +milliseconds by runner class, Actions billing, releases, packages, and +repository metadata. Writes rows, not verdicts. Rate-limit aware and +resumable, because ~400 repos with history is a lot of calls; it must be +safe to interrupt and re-run. + +*`+oikos-capability+`* — the output measure, deliberately derived from +run history rather than source analysis in round one. The signals are +cheap and hard to fake: + +* _Does the workflow parse?_ A run that is `+startup_failure+`, or whose +name is its own file path, produced nothing from real input. +* _Can the gate fail?_ A required check with N successful runs and +*zero* failures in its entire history is a fake-gate candidate. This is +a pure telemetry signal — no code analysis at all — and it +operationalises a pathology the estate has documented repeatedly. +* _Do tests execute?_ Test counts extracted from run logs, versus jobs +that report success with no test output. +* _Do artefacts publish?_ Releases and package versions actually +produced. + +Static signals (proof bodies containing `+sorry+`/`+axiom+`/`+admit+`, +`+just+` recipes that exit 0 without a prover) are deferred to round +two; they are valuable but they are estimates, and round one is about +establishing a Measured floor. + +*`+oikos-dea+`* — Data Envelopment Analysis, implemented natively (~150 +lines of LP per model on `+good_lp+` with the HiGHS backend, MIT +throughout; no Rust DEA crate exists and none is needed). Input-oriented +CCR solved per repo, returning three things: the efficiency score θ, the +*peer reference set* (the frontier repos this one is measured against — +the most actionable output DEA gives, because it names a concrete +exemplar rather than a number), and the *dual variables*, the virtual +weights on each input and output. Those duals are the future shadow +prices; round one computes and records them without anything consuming +them yet. BCC (one added convexity constraint) gives pure technical +efficiency, and θ_CCR/θ_BCC separates scale effects. Later rounds get +standard definitions for free: allocative efficiency = cost efficiency ÷ +technical efficiency once input prices are attached, and the *Malmquist +index* (four LPs over two snapshots) becomes the principled base-vs-head +productivity comparison. + +*Ledger discipline.* The snapshot schema is a stock-flow-consistent +ledger: every flow debits one account and credits another; every stock +reconciles against its accumulated flows at each snapshot, and the +reconciliation is a hard check in `+analyse+`. Named in the +`+oikos-syntax+` vocabulary (sectors / accounts / periods / +transaction-flow matrix) per the DSL ruling above. + +*Reporting vocabulary.* Metrics are named and disclosed in SCI terms +(ISO/IEC 21031) — energy `+E+`, intensity `+I+`, embodied `+M+`, per +functional unit `+R+` — with every number carried as a value + unit + +method triple. Confidence follows the existing ladder, and every report +states the Estimated/Calibrated/ Measured counts the way Infracost +discloses unpriced resources. + +*`+oikosbot-pareto+`* is reused unchanged. Extracting it into a crate +shared with Eclexia belongs to round two. + +==== The confidence ladder does real work here + +This is the part that keeps the design honest, and it uses machinery the +codebase already has rather than adding any. + +[width="100%",cols="34%,33%,33%",options="header",] +|=== +|Quantity |Source |Confidence +|Billable minutes, wall-clock, queue time |GitHub API, directly +|*Measured* + +|Money |minutes × published per-minute rate |*Measured* + +|Verified-capability signals from run history |GitHub API, directly +|*Measured* + +|Energy |minutes × runner TDP × PUE |*Calibrated* — a coefficient over a +measurement + +|Carbon |energy × grid intensity |*Estimated* — GitHub does not expose +the runner’s region, so intensity rests on a declared assumption +|=== + +That stratification is a feature, not an apology. It means the economic +axes are the _strongest_ ones — money and time are directly measured — +which is exactly the "`economic as strongly as the ecological`" +requirement, and it is the first time anything in this system has been +able to claim `+Measured+`. It also means that when enforcement arrives +in a later round, it can legitimately block on cost and waste while +carbon remains advisory until better data exists. The existing gate +already implements precisely this rule: only `+Measured+` or +`+Calibrated+` may block. + +==== Where things live + +A new `+oikos-estate+` repository holds versioned snapshots (Parquet), +one per collection run, plus the schema. Analysis code stays in +`+oikosbot+`. Keeping the data out of `+oikosbot+` avoids the tool’s own +history becoming a data-publishing concern, and keeps OikosBot from +silently measuring a corpus that contains itself. + +==== CLI surface + +`+oikosbot estate collect | analyse | report+`, mirroring the existing +`+check+`/`+report+` conventions and honouring `+.oikos.yml+`. +Collection runs locally against the API today and can move into CI once +the lockfile blocker clears, without changing the code. + +=== Roadmap beyond round one + +*Ruled (2026-08-03): round two is decided by round one’s evidence, not +pre-committed.* The plan records the candidates and the decision +criterion: + +* *Candidate A — the Eclexia join*: export DEA duals as shadow prices, +parse `+.ecl+` for real, patch Eclexia’s selector to consume +`+@optimize+` via the shared dominance crate. Chosen if the independence +check passes cleanly and the duals are stable across snapshots (unstable +prices should not be exported). +* *Candidate B — enforcement + calibration (#48)*: wire +`+calibration.rs+`, let `+compare --check+` block on Measured/Calibrated +axes. Chosen if the telemetry proves reliable enough to gate on and +consumers are ready for a gate. Per the landscape survey, a _working_ +gate with honest numbers is the unoccupied market square — but per +estate doctrine, it ships working or ships disabled-and-says-so; never +in between. +* *Candidate C — substrate deepening*: SFC identity checks live, static +capability signals (proof-body scans, fake-gate source analysis), longer +backfill. Chosen if round one’s DEA population or data quality is too +thin to support A or B. + +*Decision criterion*: the independence matrix, dual stability across ≥2 +snapshots, and coverage (fraction of repos with non-degenerate +telemetry) — all three reported by round one itself. + +*PR decoration* (single updated-in-place comment, verdict header, honest +estimated/measured counts — the one table stake every comparable product +has) attaches to whichever round first produces per-PR output; it is +blocked on nothing and may be pulled forward if a quick win is wanted. + +*Later, sequenced by the above*: Scallop-vs-ascent decision at the +policy-engine port (evidence recorded in the landscape section); +Malmquist index for base-vs-head once ≥2 snapshots exist; debt-as-DCF +via `+rust_finprim+` with the two dollar denominators cross-checked; +language coverage beyond Rust/JS/Python (the analyzer gap is moot for +round one, since telemetry is language-agnostic — one reason the +estate’s Zig/Idris repos are finally measurable at all); +OikosBot-in-Eclexia migrations as the language matures. + +=== Verification + +Round one is verifiable _without CI_, which is the point of sequencing +it first. + +[arabic] +. *Independence check — the falsifier for the whole premise.* Compute +the correlation matrix across the collected axes (money, wall-clock, +energy, carbon, node count). If money and time are not substantially +independent of node count, the collinearity problem has not been solved +and the design is wrong. This test must be run and reported before any +DEA result is trusted. +. *Reproducibility.* Two collection runs over the same time window must +produce identical snapshots. Re-running `+analyse+` on a committed +snapshot must be deterministic. +. *DEA correctness.* Validate against a published worked example with +known efficiency scores; verify θ ∈ (0,1], that every frontier unit +scores exactly 1, and that duals satisfy complementary slackness. A DEA +implementation that has not been checked against a known answer is not +evidence. +. *Ground truth on known cases.* The estate has documented fake gates +and dead workflows. The capability detector must find them. If it does +not flag repos already known to have gates that cannot fail, it is not +measuring capability. +. *Confidence labelling.* Assert that no quantity is labelled +`+Measured+` unless it came directly from the API, and specifically that +carbon never claims better than `+Estimated+`. +. *Part 0 repairs* are verified by workflow-file inspection now, and by +an actual green run once the lockfile fix lands — not before. diff --git a/docs/superpowers/specs/2026-08-03-estate-economics-design.md b/docs/superpowers/specs/2026-08-03-estate-economics-design.md deleted file mode 100644 index 50d2ae8..0000000 --- a/docs/superpowers/specs/2026-08-03-estate-economics-design.md +++ /dev/null @@ -1,429 +0,0 @@ -# OikosBot — Pareto-first estate economics - -## Context - -Two things prompted this plan. - -**The sitrep.** Asked for a status check on OikosBot, measured live on 2026-08-03. -Generation 1 shipped and was verified end-to-end on 2026-07-28 (v0.1.0 tagged, -container published, 101 SARIF results ingested by GitHub code scanning on the -`enaction-engine` pilot). Since then three things have gone wrong, two of them -genuine defects rather than environmental noise. They are cheap to fix and are -folded in below as Part 0. - -**The real question.** How should OikosBot grow to match the functionality of -comparable products, while taking its *orientation* from economic optimality — -Pareto optimality, allocative efficiency, productive efficiency, dynamic -efficiency, opportunity cost — treating the economic as seriously as the -ecological, and using Eclexia as the language for expressing it. - -Investigation found that the honest answer requires changing the unit of analysis, -not adding features. The detail matters, so it is stated plainly: - -> `estimate_resources()` derives energy, duration, carbon and memory from a single -> integer — `complexity`, a raw AST node count. `energy = n × 0.1`, -> `duration = n × 0.5`, `carbon = energy × 1.319e-4`, `memory = n × 2KB`. Four of -> the five Pareto objectives are therefore scalar multiples of one another, and a -> frontier over collinear axes collapses to a one-dimensional sort. `Alloc` -> reduces to roughly `0.35 × complexity` rank-inverted; `Debt` is -> `100 − 0.5 × complexity`. All three EconScore terms are the same variable in -> different clothes. - -The dominance mathematics in `crates/oikosbot-pareto` is good work — ε-tolerant, -min-max normalised before any distance is measured, weights inside the metric, all -functions total, conformance-tested against `eco_rules.dl`. The problem is -entirely upstream of it, in what gets fed in. - -A second structural point drove the design: - -> Allocative efficiency is not a property of a single change. It means scarce -> resources are distributed to their highest-valued competing use; with one diff -> in front of you there is nothing to allocate between. Opportunity cost and -> marginal analysis have the same requirement. They need a portfolio. - -## Decisions taken - -| # | Question | Ruling | -|---|---|---| -| 1 | Unit of analysis | **The estate as a portfolio**, ~400 repos competing for a shared budget of hours, CI minutes, energy and money. The PR check survives as one projection of a persistent model, not as the product. | -| 2 | Where numbers come from | **CI telemetry becomes the `Measured` tier.** Billable minutes, runner class, wall-clock and queue time from the GitHub API — retroactive, no instrumentation, every repo. | -| 3 | Eclexia's role | **Bidirectional, with self-hosting as the horizon.** A shared dominance crate that Eclexia's inert `@optimize` finally consumes; Eclexia serves as OikosBot's typed policy surface today; OikosBot's allocation LP emits real duals that become Eclexia's shadow prices. Owner clarification (2026-08-03): Eclexia is a Turing-complete general-purpose language implementing the Economics-as-Code paradigm (per its whitepaper) with applications far beyond this bot — and OikosBot is eventually to be **implemented in** Eclexia. | -| 4 | Output measure | **Verified capability** — tests that genuinely execute, gates that can genuinely fail, proofs discharged, workflows that parse, artefacts that publish. | -| 5 | Model locus | **Versioned columnar snapshots in a dedicated `oikos-estate` dataset repo.** History is git history. No infrastructure, no upstream blocker. | - -### Why these cohere - -Decision 2 breaks the collinearity that makes the present engine vacuous: money, -wall-clock, energy and carbon sourced from telemetry are mutually independent and -independent of node count. Decision 4 supplies the output term without which every -efficiency measure degenerates into "delete the code". Decisions 1, 4 and 5 -together make Data Envelopment Analysis applicable — DEA compares units with -multiple incommensurable inputs *and* outputs, requires no prices, and returns both -a frontier and a distance-to-frontier per unit. DEA is a linear program, and its -dual yields virtual weights on each input and output: marginal scarcity derived -from the estate's own data. Those duals are decision 3's shadow prices. One LP -produces the efficiency verdict and the prices Eclexia consumes. - -The payoff of decision 4 deserves emphasis. The estate's documented pathologies — -gates that cannot fail, workflows that never parsed, proof checks that exit 0 -without a prover, `eclexiaiser.toml` manifests pointing at a template's files — -all become a single *economic* fact: real input consumed, zero output produced. -That is X-inefficiency, and OikosBot would be measuring and pricing it rather than -merely listing it. - -### What the landscape says (surveyed live, 2026-08-03) - -Full survey in the session record; the points that bind the design: - -- **Hardware energy counters do not exist on hosted CI.** GitHub runners are Azure - VMs; the kernel deliberately does not load the RAPL powercap driver in - virtualised environments — an absent-file problem, not permissions. Only two - honest strategies exist: deterministic instrumentation (Valgrind-derived, the - CodSpeed approach) or modelled estimation from utilisation (the **Eco-CI** - approach: power curves over SPECpower-trained models, vhost ratio 0.03125 for - `ubuntu-latest`). This *confirms* decision 2 as the only honest route, and makes - Eco-CI required reading before writing the energy model. -- **The unoccupied market square is a working gate with honest numbers.** Eco-CI, - Green Metrics Tool and Impact Framework measure but never block; Creedengo - blocks but on static rules with thin empirical grounding. Nobody gates on - measured sustainability + economics. Meanwhile the *universal* table stake - OikosBot lacks is **PR decoration** — every surveyed product posts a comment or - check run. Pattern to copy: Infracost's single updated-in-place comment with a - one-line verdict header and an honest "M estimated, K not estimated" footnote. -- **DEA needs no dependency.** No Rust DEA crate exists; input-oriented CCR is - ~150 lines of LP on `good_lp` (`default-features = false, features = ["highs"]` - — HiGHS is MIT; avoid the stale EPL CBC default). BCC adds one constraint; - scale efficiency falls out of the ratio. **Allocative efficiency has a standard - LP definition** (cost efficiency ÷ technical efficiency, given input prices), - and the **Malmquist index** — four LPs over two periods, decomposing - productivity change into efficiency-change × technical-change — is the textbook - formalism for base-vs-head comparison. SFA is ruled out (MLE research project; - DEA is deterministic and sufficient). -- **Vocabulary: SCI (ISO/IEC 21031:2024).** Prose-only standard, no schema — so - OikosBot defines the JSON in SCI's own terms (`E`, `I`, `M`, `R`) and inherits - ISO's authority. SARIF idiom: numbers as value+unit+method triples in - hierarchical camelCase property bags; classification via `taxonomies`, not tags; - `partialFingerprints` hash the finding's *shape*, never the metric value; - human-readable tables into `help.markdown` (GitHub ignores unknown properties). -- **Data sources for round one**, by licence and reliability: GitHub **Billing - Usage API** (GA 2026-06-04) for actual consumed minutes; published per-minute - prices (~40% cut effective 2026-01-01 — verify live rates before hardcoding); - vendored **CCF coefficients** (Apache-2.0: per-family min/max watts + embodied - kgCO2e — the clean-licence indirection over SPECpower, which is a licence - hazard to touch directly); **UK NESO carbon-intensity API** (free, CC-BY, - verified live) plus **GSF Real Time Cloud CSV** for regions; **Boavizta** as an - AGPL *sidecar* (never linked) for embodied hardware carbon; **EPA scghg CSV - vendored** for the social cost of carbon. deps.dev (CC-BY, no auth) when - dependency data is wanted. -- **The Social Cost of Carbon is now a political hazard.** The US federal SC-GHG - framework was dismantled in 2025 (IWG disbanded, estimates withdrawn, EPA - rescission). Any SCC-denominated finding is a scientific claim, not a - compliance one. Ruling: SCC is a configurable preset — value + source + - vintage + discount rate recorded in the SARIF properties — shipping EPA-2023 - ($190/tCO2 at 2% Ramsey) as a *labelled* preset alongside UK DESNZ and EU ETS - prices, never as "the official value". -- **Debt gets two independent dollar denominators** to cross-check: SonarQube's - published remediation model (30 min/line default, A–E bands at 5/10/20/50% — - adopting the band grid costs nothing and buys legibility) and COCOMO via `scc` - (Go binary, shell out). `rust_finprim` (MIT, maintained) turns debt-as-interest - into real discounted-cash-flow arithmetic. The Technical Debt Dataset (31 - Apache projects, SQLite) is the calibration corpus. -- **Flag against the Scallop ruling** (recorded, not overturned — the port is a - later phase): Scallop is not on crates.io, needs nightly, has no `license` - field in its manifest, and last released 2024-08. If the policy layer stays - deterministic, crisp Datalog (`ascent`, stable Rust, on crates.io) suffices; - Scallop is warranted only if probabilistic reasoning is genuinely wanted. - Decide at that phase; if Scallop, vendor and SHA-pin. -- **Two honesty notes**: X-inefficiency and Baumol's cost disease have *no* - software-engineering literature — using the terms is our framing and must say - so. And `config/oikos.yaml` currently declares PR comments, a dashboard, OTel - and Slack integrations that do not exist in code (added to Part 0). - -### Eclexia's trajectory: OikosBot as reference implementation - -Per the owner's clarification and the whitepaper (*Economics-as-Code: A Novel -Programming Paradigm for Sustainable Computing* — first-class scarcity, -trade-offs, opportunity cost and multi-objective optimisation, with formal -semantics and type-safety proofs), the relationship is not "bot with a config -language". It is: **Eclexia is the paradigm's language; OikosBot is its first -serious application and, eventually, a program written in it.** - -Consequences for this design: - -1. **Rust OikosBot is the reference implementation, built to emigrate.** Each - component is a candidate for migration into Eclexia as the language matures, - and each migration is a live test of the paradigm claim — can this actually be - said in Economics-as-Code? The migration order suggests itself: dominance/ - frontier maths → Eclexia stdlib (making `@optimize` semantically real for - *every* Eclexia program, not just this one); DEA's LP and its duals → the real - solver behind `shadow_price()`, replacing hand-set constants and discharging - the ShadowPrices.v axioms (eclexia #43) by implementation; telemetry ingestion - → the runtime's known gap "metrics not wired to real OS metrics". -2. **Contributions upstream are language work, not bot plumbing.** The shared - dominance crate, money literals, and measured shadow prices all serve Eclexia's - general ambitions; OikosBot merely needs them first. -3. **Keep the boundary honest meanwhile.** Until Eclexia can express these - components, the Rust implementation must not pretend otherwise — the current - filename-matching `.ecl` backend is exactly the pretence to eliminate. The - whitepaper's evaluation numbers (20–40% energy etc.) are projections by its own - admission ("no measured benchmarks"); the estate telemetry programme is, - incidentally, the first chance to replace those projections with measurements. - -### The accounting DSL (`oikos-economics-accounting-dsl`) - -Assessed at the owner's suggestion, with the caution that it is "not massively -developed but could have some value". Measured state: a real parser front-end -(Logos + Chumsky, 71 passing tests) plus five shallow AST lint passes — and no -typechecker, no compiler, no CLI, no evaluator; nothing in it computes a number. -The advertised guarantee ("a model that violates any accounting identity does not -compile") is unimplemented: the enforcement path is a commented-out call to a stub -returning `Err(EphapaxNotAvailable)`. The Godley check counts sign glyphs and -ignores amounts; the README's own example fails its own checker; `Stock`/`Flow` is -a parsed keyword no checker reads; ΔStock = Σflows exists nowhere. Development -stopped ~2026-06-20. - -**Ruling on its role.** Adopt the *discipline*, not the dependency, and invert the -relationship: - -1. **The estate ledger is stock-flow consistent by construction.** In the snapshot - schema, every flow (minutes, £, joules, gCO2e per period) debits one account and - credits another; every stock (cumulative spend, cumulative carbon, debt - principal) must reconcile against its accumulated flows at each snapshot. This - is implemented as identity checks over real numbers inside the round-one crates - — a few hundred lines, and precisely the substance the DSL is missing. -2. **Borrow its vocabulary, not its checker.** `oikos-syntax` is a clean, - serde-serialisable AST for sectors / accounts / periods / transaction-flow - matrices; naming the snapshot schema in its terms costs nothing and keeps the - two projects convergent. Its instrument typestate checker (~115 lines, correct) - and its pipe-table `godley { }` syntax are worth lifting if a human-writable - matrix surface is ever wanted. -3. **OikosBot becomes the DSL's first real testbed, not its consumer.** A live, - numerically-real SFC model of the estate is exactly the proving ground the DSL - lacks. When its Ephapax lowering eventually lands, the estate ledger is the - model waiting to compile — the dependency points forward, and nothing in this - design blocks on a stalled language project. - -Two side-findings for the estate log: the DSL checkout is 1 commit ahead of origin -(a CodeQL re-pin — verify the SHA before pushing, per the phantom-SHA landmine), -and its stated Ephapax blocker ("parser not yet complete") looks stale — -`ephapax-parser` is now ~5.4k LOC and Ephapax's README marks most components -complete; the assumption has not been retested since June. - -## Part 0 — repairs before any new work - -These are independent of the design and should land first. - -1. **`timeout-minutes` on a `uses:` job kills two workflows.** - `.github/workflows/mirror.yml` and `.github/workflows/secret-scanner.yml` both - carry `timeout-minutes: 10` on a job whose body is `uses:`. That key is illegal - there and invalidates the whole file — the distinguishing signature is a run - whose conclusion is `failure` at 0s and whose *name is the file path* rather - than the workflow's `name:`. PR #56 fixed exactly this in `scorecard.yml` on - 07-30 and missed these two. Verified by comparison: governance, hypatia-scan - and scorecard have zero job-level `timeout-minutes`; these two have one each. - The standards pin `d135b05bfc64` is real, so this is not a phantom-SHA case. - -2. **The proven pilot has been switched off.** - `metadatastician/enaction-engine`'s oikosbot job now carries - `if: vars.OIKOSBOT_ENABLED == 'true'`, and the repo has no variables set — so - the one repo that demonstrably ingested 101 results never runs. Its inline - justification claims the image "has never been published", which is stale: - publish-image went green on 07-28 and the image was podman-verified. It is the - only one of the 14 reachable consumers gated this way; the other 13 pin - `oikosbot@bb95ab50…` cleanly with no `|| echo`. Either set the variable or - remove the gate and correct the comment. - -3. **Two consumer repos no longer resolve.** - `hyperpolymath/boj-server-mk2` and `hyperpolymath/idaptik-ums` return - "could not resolve to a Repository" — renamed, transferred, or deleted. Trace - them; `metadatastician/canonical-ums` shows the estate has done such renames - before. - -4. **The Eclexia policy path is a fake gate.** - `oikosbot-eclexia`'s default backend, `evaluate_builtin`, dispatches on the - `.ecl` **file stem** and runs hardcoded Rust thresholds; the file's contents are - never read. The thresholds contradict the files they claim to implement — - `energy_threshold.ecl` declares `> 50.0 J` per function, the builtin fires at - `> 1000 J` total. The `eclexia-native` backend cannot compile (parser and interp - are not dependencies). This is resolved by the Eclexia work below, but should be - made loud immediately rather than left silently wrong. - -5. **Local checkout is 7 PRs stale** — HEAD `3f1e6e6` (#50) against origin - `7e73ddf` (#57), with uncommitted `.editorconfig` and `.gitignore` edits. - -6. **`config/oikos.yaml` over-promises.** It declares PR comments, a dashboard on - :8080, OTel export, Slack/email notification and a praxis loop, none of which - exist in code; the config loader silently discards them. A new adopter reading - the reference config will believe OikosBot does things it does not. Annotate - the file to mark aspirational blocks, or trim them. - -**Blocking constraint.** Every Actions workflow in the estate currently dies at -startup with a 0-second `startup_failure`, root-caused on 2026-08-03 to GitHub's -new workflow-lockfile enforcement. It is not repairable from inside any repo: it -needs `gh extension install github/gh-actions-lock`, run once by the owner. Nothing -below can be CI-verified until that lands — which is precisely why decision 5 puts -collection in a local CLI reading server-side history, so the work is not gated on -it. - -## Architecture - -Round one is a read-only pipeline. Nothing blocks, nothing is enforced, and no -existing behaviour changes. - -``` -gh API ──► collect ──► snapshot ──► derive ──► DEA ──► report - (crate) (parquet) (crate) (crate) (md/json) -``` - -### New crates - -**`oikos-telemetry`** — the collector. Reads, per repo, from the GitHub REST and -GraphQL APIs: workflow run history (conclusion, duration, `run_started_at`, -runner labels), `timing` per run for billable milliseconds by runner class, -Actions billing, releases, packages, and repository metadata. Writes rows, not -verdicts. Rate-limit aware and resumable, because ~400 repos with history is a -lot of calls; it must be safe to interrupt and re-run. - -**`oikos-capability`** — the output measure, deliberately derived from run history -rather than source analysis in round one. The signals are cheap and hard to fake: - -- *Does the workflow parse?* A run that is `startup_failure`, or whose name is its - own file path, produced nothing from real input. -- *Can the gate fail?* A required check with N successful runs and **zero** failures - in its entire history is a fake-gate candidate. This is a pure telemetry signal — - no code analysis at all — and it operationalises a pathology the estate has - documented repeatedly. -- *Do tests execute?* Test counts extracted from run logs, versus jobs that report - success with no test output. -- *Do artefacts publish?* Releases and package versions actually produced. - -Static signals (proof bodies containing `sorry`/`axiom`/`admit`, `just` recipes -that exit 0 without a prover) are deferred to round two; they are valuable but they -are estimates, and round one is about establishing a Measured floor. - -**`oikos-dea`** — Data Envelopment Analysis, implemented natively (~150 lines of -LP per model on `good_lp` with the HiGHS backend, MIT throughout; no Rust DEA -crate exists and none is needed). Input-oriented CCR solved per repo, returning -three things: the efficiency score θ, the **peer reference set** (the frontier -repos this one is measured against — the most actionable output DEA gives, -because it names a concrete exemplar rather than a number), and the **dual -variables**, the virtual weights on each input and output. Those duals are the -future shadow prices; round one computes and records them without anything -consuming them yet. BCC (one added convexity constraint) gives pure technical -efficiency, and θ_CCR/θ_BCC separates scale effects. Later rounds get standard -definitions for free: allocative efficiency = cost efficiency ÷ technical -efficiency once input prices are attached, and the **Malmquist index** (four LPs -over two snapshots) becomes the principled base-vs-head productivity comparison. - -**Ledger discipline.** The snapshot schema is a stock-flow-consistent ledger: -every flow debits one account and credits another; every stock reconciles against -its accumulated flows at each snapshot, and the reconciliation is a hard check in -`analyse`. Named in the `oikos-syntax` vocabulary (sectors / accounts / periods / -transaction-flow matrix) per the DSL ruling above. - -**Reporting vocabulary.** Metrics are named and disclosed in SCI terms -(ISO/IEC 21031) — energy `E`, intensity `I`, embodied `M`, per functional unit -`R` — with every number carried as a value + unit + method triple. Confidence -follows the existing ladder, and every report states the Estimated/Calibrated/ -Measured counts the way Infracost discloses unpriced resources. - -**`oikosbot-pareto`** is reused unchanged. Extracting it into a crate shared with -Eclexia belongs to round two. - -### The confidence ladder does real work here - -This is the part that keeps the design honest, and it uses machinery the codebase -already has rather than adding any. - -| Quantity | Source | Confidence | -|---|---|---| -| Billable minutes, wall-clock, queue time | GitHub API, directly | **Measured** | -| Money | minutes × published per-minute rate | **Measured** | -| Verified-capability signals from run history | GitHub API, directly | **Measured** | -| Energy | minutes × runner TDP × PUE | **Calibrated** — a coefficient over a measurement | -| Carbon | energy × grid intensity | **Estimated** — GitHub does not expose the runner's region, so intensity rests on a declared assumption | - -That stratification is a feature, not an apology. It means the economic axes are -the *strongest* ones — money and time are directly measured — which is exactly the -"economic as strongly as the ecological" requirement, and it is the first time -anything in this system has been able to claim `Measured`. It also means that when -enforcement arrives in a later round, it can legitimately block on cost and waste -while carbon remains advisory until better data exists. The existing gate already -implements precisely this rule: only `Measured` or `Calibrated` may block. - -### Where things live - -A new `oikos-estate` repository holds versioned snapshots (Parquet), one per -collection run, plus the schema. Analysis code stays in `oikosbot`. Keeping the -data out of `oikosbot` avoids the tool's own history becoming a data-publishing -concern, and keeps OikosBot from silently measuring a corpus that contains itself. - -### CLI surface - -`oikosbot estate collect | analyse | report`, mirroring the existing -`check`/`report` conventions and honouring `.oikos.yml`. Collection runs locally -against the API today and can move into CI once the lockfile blocker clears, -without changing the code. - -## Roadmap beyond round one - -**Ruled (2026-08-03): round two is decided by round one's evidence, not -pre-committed.** The plan records the candidates and the decision criterion: - -- **Candidate A — the Eclexia join**: export DEA duals as shadow prices, parse - `.ecl` for real, patch Eclexia's selector to consume `@optimize` via the shared - dominance crate. Chosen if the independence check passes cleanly and the duals - are stable across snapshots (unstable prices should not be exported). -- **Candidate B — enforcement + calibration (#48)**: wire `calibration.rs`, let - `compare --check` block on Measured/Calibrated axes. Chosen if the telemetry - proves reliable enough to gate on and consumers are ready for a gate. Per the - landscape survey, a *working* gate with honest numbers is the unoccupied market - square — but per estate doctrine, it ships working or ships disabled-and-says-so; - never in between. -- **Candidate C — substrate deepening**: SFC identity checks live, static - capability signals (proof-body scans, fake-gate source analysis), longer - backfill. Chosen if round one's DEA population or data quality is too thin to - support A or B. - -**Decision criterion**: the independence matrix, dual stability across ≥2 -snapshots, and coverage (fraction of repos with non-degenerate telemetry) — all -three reported by round one itself. - -**PR decoration** (single updated-in-place comment, verdict header, honest -estimated/measured counts — the one table stake every comparable product has) -attaches to whichever round first produces per-PR output; it is blocked on -nothing and may be pulled forward if a quick win is wanted. - -**Later, sequenced by the above**: Scallop-vs-ascent decision at the policy-engine -port (evidence recorded in the landscape section); Malmquist index for base-vs-head -once ≥2 snapshots exist; debt-as-DCF via `rust_finprim` with the two dollar -denominators cross-checked; language coverage beyond Rust/JS/Python (the analyzer -gap is moot for round one, since telemetry is language-agnostic — one reason the -estate's Zig/Idris repos are finally measurable at all); OikosBot-in-Eclexia -migrations as the language matures. - -## Verification - -Round one is verifiable *without CI*, which is the point of sequencing it first. - -1. **Independence check — the falsifier for the whole premise.** Compute the - correlation matrix across the collected axes (money, wall-clock, energy, carbon, - node count). If money and time are not substantially independent of node count, - the collinearity problem has not been solved and the design is wrong. This test - must be run and reported before any DEA result is trusted. -2. **Reproducibility.** Two collection runs over the same time window must produce - identical snapshots. Re-running `analyse` on a committed snapshot must be - deterministic. -3. **DEA correctness.** Validate against a published worked example with known - efficiency scores; verify θ ∈ (0,1], that every frontier unit scores exactly 1, - and that duals satisfy complementary slackness. A DEA implementation that has - not been checked against a known answer is not evidence. -4. **Ground truth on known cases.** The estate has documented fake gates and dead - workflows. The capability detector must find them. If it does not flag repos - already known to have gates that cannot fail, it is not measuring capability. -5. **Confidence labelling.** Assert that no quantity is labelled `Measured` unless - it came directly from the API, and specifically that carbon never claims better - than `Estimated`. -6. **Part 0 repairs** are verified by workflow-file inspection now, and by an - actual green run once the lockfile fix lands — not before. -