Skip to content

feat(amico-run): attribute CPU cost to principal and Lab from the solve unit cgroup, not process.cpuUsage() #2133

Closed

Description

@aarontrowbridge

Why

Central-hub cost ledger (ADR 0041 section 5). It replaces the approach in #2121, which measured process.cpuUsage(): that counts only the engine process, not the Julia child, so every solve would be costed at about zero. Since A1, each solve runs in its own transient unit amico-run-<id>.service, whose cgroup accounts the solve's real CPU including all child threads.

What

  1. Add principal, lab and cpu_seconds to ReceiptRecord (and ledger-record.schema.json), plus a costTotals rollup by principal and Lab. Unattributed runs roll up under (unattributed).
  2. Measure CPU from the unit's cgroup. On Linux read cpu.stat usage_usec of the run's cgroup (find it via the A1 run.lock unit name, or systemctl --user show -p ControlGroup), read before the unit is torn down and again at settle; the delta is cpu_seconds. Where no cgroup is available (macOS, a run not in a unit), fall back to child rusage if obtainable, else record cpu_seconds absent and cost_basis: unmeasured. NEVER fall back to process.cpuUsage().
  3. Cost = cpu_seconds x a rate read from a human-edited file (~/.amico/amicode/rates.json), default 0, so an unconfigured install records seconds and no invented dollars. Label every dollar figure basis: list_price_estimate.
  4. Never fail a run because accounting failed.

Acceptance Criteria

  • a test runs a real child process that burns CPU in a transient cgroup or unit (or a faithful temp cgroup fixture) and asserts cpu_seconds reflects the CHILD's CPU.
  • a test proves process.cpuUsage() is never used: a run whose engine process is idle but whose child burns 1+ s must record at least 1 s.
  • no rate configured => seconds recorded and no cost_usd; rate configured => cost_usd = seconds x rate with basis list_price_estimate.
  • unattributed runs roll up under (unattributed); principal and Lab rollups sum to the total.
  • an accounting failure never fails a run or changes its exit code.
  • the schema validates the new fields.

Activity

  1. added
    0.4.1Fixes-only maintenance lane after 0.4.0 (v1 overlay frozen)
    on Nov 4, 2026
  2. added a commit that references this issue on Nov 4, 2026
  3. aarontrowbridge commented on Nov 4, 2026

    @aarontrowbridge
    ContributorAuthor

    Merged via #2134. Independently verified against real transient units: workloads of 2/8/0 CPU-seconds measured 2.01/8.04/0.00.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    0.4.1Fixes-only maintenance lane after 0.4.0 (v1 overlay frozen)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions