Skip to content

refactor: one canonical Heart-owned arcticpy CI install for every CTI repo #170

Description

@Jammy2211

Overview

import autocti hard-requires arcticpy, which is not a pip dependency: its sdist is source-only C++ (needs libgsl-dev + a toolchain + Cython) and its own requirements downgrade numpy below 2.0. Every CTI repo that runs CI therefore carries its own copy of the install recipe, there is no single owner, and the documented recipe is incomplete for a clean environment. This task makes one canonical arcticpy install step owned by PyAutoHeart (the organ that owns the reusable CI workflows), repoints every consumer at it, and fixes the human/agent docs so they match CI.

Audit result (this supersedes the prompt's site list)

The prompt flagged PyAutoCTI's own lib-tests CI as UNREAD. Audited:

  • PyAutoCTI/.github/workflows/main.yml is a thin caller of PyAutoHeart/.github/workflows/lib-tests.yml@main. It contains no arcticpy recipe of its own.
  • That reusable workflow carries two further copies, one per job: unittest (L81–90) and unittest-nojax (L180–186).

So there are four shell copies, not two:

# Site setuptools wheel?
1 PyAutoHeart/.github/workflows/lib-tests.yml — job unittest yes
2 PyAutoHeart/.github/workflows/lib-tests.yml — job unittest-nojax yes
3 autocti_workspace_test/.github/scripts/smoke_install.sh no
4 autocti_assistant/.github/workflows/wiki-currency.yml yes

autocti_workspace has no .github/ directory at all — it is a docs-only consumer.

Correction to the "latent failure" framing

The prompt says smoke_install.sh "relies on the runner image's ambient setuptools" and is one runner-image change from breaking. That is not accurate as written. PyAutoHeart's smoke-tests.yml runs

- name: Install (base + the workspace's own epilogue)
  run: |
    pip install --upgrade pip setuptools wheel
    pip install pyyaml
    bash workspace/.github/scripts/smoke_install.sh

so setuptools is present when the epilogue runs. The real defect is weaker but still worth fixing: the epilogue's correctness depends implicitly on a preceding step in a different repository's workflow, with nothing stating the dependency. Reordering or trimming that Heart step would silently break CTI smoke. Making the canonical step self-contained removes the coupling.

Docs are worse than CI

  • PyAutoCTI/AGENTS.md §arcticpy — the note both other repos cite as canonical. Says "install it after numpy is in place" and then the bare pip install arcticpy line. Omits both Cython and setuptools.
  • autocti_workspace/AGENTS.md §arcticpy — "install it after numpy". Omits Cython and setuptools.
  • autocti_workspace_test/AGENTS.md — "after numpy+cython". Omits setuptools.
  • autocti_assistant/skills/ac_setup_environment.md — numpy + cython, no setuptools.

Plan

  • Add a canonical composite action install-arcticpy to PyAutoHeart, version-pinned in one place, ending in an import assertion.
  • Give it a self-test workflow in PyAutoHeart so a change to the recipe is proven to build before it can break four repos.
  • Repoint all four shell copies at it and delete the local recipes.
  • Fix the four documented recipes so humans and agents get the same working commands CI uses.
  • Leave the build-cache idea unimplemented; record it as a measured follow-up rather than guessing.
Detailed implementation plan

Affected Repositories

  • PyAutoHeart (primary)
  • autocti_workspace_test
  • autocti_assistant
  • PyAutoCTI
  • autocti_workspace

Branch Survey

Repository Current Branch Dirty?
./PyAutoHeart main clean
./autocti_workspace_test main clean
./autocti_assistant main clean
./PyAutoCTI main clean
./autocti_workspace main clean

Suggested branch: feature/arcticpy-install-standardisation

Shape: composite action, not a shell script

PyAutoHeart has no .github/actions/ today, so this is a new convention for the repo. It is the right one:

  • PyAutoHeart is public, so uses: PyAutoLabs/PyAutoHeart/.github/actions/install-arcticpy@main resolves from any consumer repo with no checkout of Heart required — the same reach the reusable workflows already have, and the same @main reference style every caller already uses.
  • A shell script in Heart would need each consumer to fetch it first (curl or a checkout), which is more machinery and a worse pin.

smoke_install.sh is a bash script and cannot uses: an action. Rather than leave copy #3 in place, the arcticpy install moves out of the workspace epilogue and into Heart's smoke-tests.yml as a gated step. This matches the existing precedent inside Heart: lib-tests.yml already gates its arcticpy step on if: inputs.package == 'autocti'.

Implementation Steps

  1. PyAutoHeart/.github/actions/install-arcticpy/action.yml (new) — composite action:

    • input version, default 2.6 (the single pin).
    • input sudo, default true, so a container without sudo can opt out of the apt leg.
    • sudo apt-get update && sudo apt-get install -y libgsl-dev
    • python -m pip install --upgrade pip setuptools wheel — self-contained, no reliance on a caller's preceding step.
    • python -m pip install numpy cython
    • python -m pip install "arcticpy==${version}" --no-build-isolation --no-deps
    • python -c "import arcticpy; print(arcticpy.__version__)" — the assertion. Today a broken arcticpy surfaces much later as a confusing import autocti failure in an unrelated job.
  2. PyAutoHeart/.github/workflows/arcticpy-action.yml (new) — self-test. Runs on PRs touching the action or the workflow, plus workflow_dispatch. Uses the local path ./.github/actions/install-arcticpy so it exercises the branch's version, then asserts add_cti produces a trail on a single bright pixel. Without this, a @main reference means a recipe change cannot be validated before merge.

  3. PyAutoHeart/.github/workflows/lib-tests.yml — replace both inline blocks (jobs unittest, unittest-nojax) with the action, keeping the if: inputs.package == 'autocti' gate.

  4. PyAutoHeart/.github/workflows/smoke-tests.yml — add an arcticpy boolean input (default false); when true, run the action after Python setup and before the workspace epilogue. Default-false keeps every non-CTI workspace caller byte-identical.

  5. autocti_workspace_test — caller .github/workflows/smoke_tests.yml passes arcticpy: true; delete the recipe from .github/scripts/smoke_install.sh, leaving it to do only what is genuinely workspace-specific (the chain install and the autonerves re-pin).

  6. autocti_assistant/.github/workflows/wiki-currency.yml — replace the inline recipe in "Install the stack" with the action, and delete the "Recipe mirrors …smoke_install.sh" comment that acknowledged the copy-paste.

  7. Docs — fix all four, each to the verified-complete recipe, and point them at the canonical action so the CI/doc link is explicit:

    • PyAutoCTI/AGENTS.md §arcticpy (the cited canonical note; keep the no-root header workaround)
    • autocti_workspace/AGENTS.md §arcticpy
    • autocti_workspace_test/AGENTS.md
    • autocti_assistant/skills/ac_setup_environment.md (note: the assistant's .claude/skills/*.md are symlinks into skills/, git mode 120000 — one edit covers both surfaces)

Verified recipe (measured 2026-08-24, clean Ubuntu container, Python 3.12 venv)

apt-get install -y libgsl-dev          # g++/gcc/make already present on the image
pip install numpy scipy setuptools wheel Cython matplotlib
pip install arcticpy==2.6 --no-build-isolation --no-deps

Each missing build dep failed the build naming the next one: setuptoolsCython → builds. matplotlib is a runtime import (arcticpy/read_noise.py) needed only because --no-deps suppresses it; the CTI stack installs it anyway, so it is not a build dep and does not belong in the canonical step.

Deliberately not done

Caching the built wheel (actions/cache keyed on arcticpy version + Python version + runner image). arcticpy compiles from source on every CI run in every CTI repo, so there is real time to reclaim — but the prompt asks for a measured proposal with before/after timings rather than an assumed win, and cache-invalidation complexity is a genuine cost. Filed as a follow-up, not guessed at here.

Key Files

  • PyAutoHeart/.github/actions/install-arcticpy/action.yml — new canonical step
  • PyAutoHeart/.github/workflows/arcticpy-action.yml — new self-test
  • PyAutoHeart/.github/workflows/lib-tests.yml — copies 1 and 2
  • PyAutoHeart/.github/workflows/smoke-tests.yml — new gated step for copy 3
  • autocti_workspace_test/.github/scripts/smoke_install.sh — copy 3
  • autocti_assistant/.github/workflows/wiki-currency.yml — copy 4

Original Prompt

Click to expand starting prompt

PyAutoMind/draft/maintenance/ci/arcticpy_install_standardisation.md, filed 2026-08-24 — reproduced verbatim:

Standardise the arcticpy CI install across every CTI repo

Type: maintenance / Target: pyautoheart / Difficulty: medium / Autonomy: supervised / Priority: normal

import autocti hard-requires arcticpy, which is not a pip dependency: its sdist is source-only (needs libgsl-dev + a C++ toolchain + Cython) and its own requirements downgrade numpy below 2.0. Every CTI repo that runs CI therefore carries its own copy of the install recipe. There is no single owner, the copies have already diverged in a way that matters, and the recipe as documented is incomplete for a clean environment.

Make one canonical arcticpy install step, owned by the organ that owns the reusable CI workflows, and have every CTI repo consume it.

Work: (1) put the canonical step in PyAutoHeart alongside the reusable workflows the CTI repos already call — check PyAutoHeart's actual layout and conventions before choosing the shape; (2) add the missing setuptools wheel; (3) assert the install worked with python -c "import arcticpy; print(arcticpy.__version__)"; (4) repoint every consumer and delete the local copies; (5) fix the documented recipe in autocti_workspace/AGENTS.md, autocti_assistant/skills/ac_setup_environment.md and PyAutoCTI/AGENTS.md; (6) single pin — bumping arcticpy==2.6 should become a one-line change.

Optional, decide deliberately: caching the built wheel. Raise it as a measured proposal with before/after timings rather than assuming it is worth the cache-invalidation complexity.

Why now: two tasks in the preceding sequence were deferred on the belief that arcticpy could not be built outside a prepared machine — a belief that turned out to be false, and cost real work. A canonical, documented, CI-owned install is what stops that assumption forming again.

Unblocks draft/test/autocti/phase5_smoke_reenable_ordered_trap_scripts.md (CTI epic Phase 5) and helps draft/bug/autocti/wiki_currency_baseline_drift.md, whose baseline regeneration needs the same stack.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions