|
| 1 | +- completed: 2026-08-24 |
| 2 | +- issue: https://github.com/PyAutoLabs/autocti_workspace/issues/29 |
| 3 | +- prs: |
| 4 | + - https://github.com/PyAutoLabs/autocti_workspace/pull/30 (merged) |
| 5 | +- summary: | |
| 6 | + CTI CI standardisation Phase 6, task 1 of 3 (HIGH). autocti_workspace was |
| 7 | + listed in PyAutoHeart's `workspaces` group, whose required workflows are |
| 8 | + ["Smoke Tests", "Navigator Check"], but carried only the former — and a |
| 9 | + required workflow with no runs never satisfies `all_green`, so the repo |
| 10 | + rolled up as permanently `in_progress`: able to go red, never able to go |
| 11 | + green. Added the missing workflow AND the catalogue it gates on. |
| 12 | + |
| 13 | +## The prompt's premise was wrong, and it was load-bearing |
| 14 | + |
| 15 | +The prompt said the repo "has root-level catalogue files the assistant's |
| 16 | +citation checks already lean on (`llms.txt`, `llms-full.txt`, |
| 17 | +`workspace_index.json`)". **It has none of the three** — `git ls-files` finds |
| 18 | +nothing. The line it was reading, in `autocti_assistant/.github/workflows/ |
| 19 | +wiki-currency.yml`, is a comment explaining git's *cone-mode* behaviour |
| 20 | +("root-level files … are always included automatically"), not an assertion that |
| 21 | +those files exist. |
| 22 | + |
| 23 | +This is the second consecutive CTI task whose prompt asserted a file that was |
| 24 | +never there (Phase 5: "add them back to the workspace `smoke_tests.txt`" — there |
| 25 | +was no `smoke_tests.txt`). Worth noticing as a pattern in how these prompts get |
| 26 | +written, not just as two separate corrections. |
| 27 | + |
| 28 | +Why it mattered rather than being a detail: the staleness job ends in |
| 29 | + |
| 30 | +``` |
| 31 | +git diff --exit-code llms-full.txt workspace_index.json |
| 32 | +``` |
| 33 | + |
| 34 | +and on **untracked** paths `git diff` exits **0**. Verified directly. So adding |
| 35 | +the workflow alone would have produced a permanently green job that checked |
| 36 | +nothing — the same class of vacuous gate the task set out to close. |
| 37 | + |
| 38 | +## What Navigator Check actually is |
| 39 | + |
| 40 | +Not a Heart reusable. `PyAutoHeart/.github/workflows/` has no |
| 41 | +`navigator_check.yml`; the name appears only in `config/repos.yaml` and |
| 42 | +`heart/checks/ci_status.py`. Each workspace owns a ~30-line thin caller of |
| 43 | +`PyAutoLabs/PyAutoHands/.github/workflows/navigator_check.yml@main` whose only |
| 44 | +input is `project:`. Confirmed identical in shape across `autofit_workspace` |
| 45 | +(`autofit`), `autogalaxy_workspace` (`autogalaxy`), `autolens_workspace` |
| 46 | +(`autolens`) and `HowToLens` (`howtolens`). |
| 47 | + |
| 48 | +The reusable workflow runs three jobs: a path/banner lint |
| 49 | +(`check_navigator.py`), an unbatched multi-start search guard |
| 50 | +(`check_search_memory.py`), and a staleness job that regenerates the catalogue |
| 51 | +and diffs it. `autocti` was already registered in `autohands/navigator.py`'s |
| 52 | +`WORKSPACE_TITLES`, so no PyAutoHands change was needed. |
| 53 | + |
| 54 | +## The 12 docstring underlines |
| 55 | + |
| 56 | +Twelve scripts — both `extract.py`s, four `data_preparation/examples/`, and the |
| 57 | +whole six-part `overview/` series — opened with a `-----` title underline rather |
| 58 | +than `=====`. `navigator._parse_header` only treats a line as a title underline |
| 59 | +when `set(underline) == {"="}`, so for all twelve the dashes became the *summary*, |
| 60 | +and the generated catalogue read `[Extract](scripts/dataset_1d/extract.py): -------`. |
| 61 | + |
| 62 | +Fixed here rather than committing a knowingly-broken artifact: one line per file, |
| 63 | +docstring-only. Called out explicitly in the PR body as a deliberate widening of |
| 64 | +a CI PR into `scripts/`, with an offer to split it out. |
| 65 | + |
| 66 | +**Trap, caught before pushing:** six of the twelve files are CRLF. The first pass |
| 67 | +read them with `open(p).read()` (universal newlines) and wrote back LF, producing |
| 68 | +a 1,558-line diff across six files instead of six one-line changes. Redone in |
| 69 | +binary mode preserving each line's own terminator; `git diff --stat` then showed |
| 70 | +exactly `12 files changed, 12 insertions(+), 12 deletions(-)`. |
| 71 | + |
| 72 | +## Verified |
| 73 | + |
| 74 | +All three reusable jobs run locally against the branch before pushing, then green |
| 75 | +in CI: |
| 76 | + |
| 77 | +| job | local | CI | |
| 78 | +|---|---|---| |
| 79 | +| `check_navigator.py --banners=fail` | OK | ✅ Navigator paths + banner lint | |
| 80 | +| `check_search_memory.py` | OK | ✅ Unbatched multi-start search check | |
| 81 | +| `regenerate_navigator.py autocti` + `git diff --exit-code` | OK, byte-identical across two runs | ✅ Catalogue staleness | |
| 82 | + |
| 83 | +Plus `smoke (3.12)` and `smoke (3.13)` green — six checks, no reruns. All 12 |
| 84 | +edited scripts byte-compile; none is in `smoke_tests.txt`. |
| 85 | + |
| 86 | +## Work item 4 — the general case, surveyed not guessed |
| 87 | + |
| 88 | +The prompt asked whether other repos have the same silent-never-green defect. |
| 89 | +Surveyed all 15 repos across the four groups carrying `required_workflows`: |
| 90 | + |
| 91 | +| group | required | missing a file | |
| 92 | +|---|---|---| |
| 93 | +| `libraries` (6) | `Tests` | none | |
| 94 | +| `workspaces` (4) | `Smoke Tests`, `Navigator Check` | **autocti_workspace** | |
| 95 | +| `workspaces_test` (4) | `Smoke Tests` | none | |
| 96 | +| `howto` (3) | `Smoke Tests`, `Navigator Check` | none | |
| 97 | + |
| 98 | +Exactly one instance, now closed. The proposal for a Heart-side drift check was |
| 99 | +posted on the issue rather than implemented, per the prompt's instruction not to |
| 100 | +widen: the failure mode is silent by construction (a missing gate reads as |
| 101 | +*pending*, indistinguishable from *a run is in flight*), and it reopens on every |
| 102 | +edit to `repos.yaml` that adds a repo to a group or a workflow to a group's |
| 103 | +requirements. Suggested shape: a deep-tier (not per-tick) check matching each |
| 104 | +group's required names against the parsed `name:` field of each repo's workflow |
| 105 | +files, reported as a *configuration* finding rather than red CI. |
| 106 | + |
| 107 | +## Follow-ups deliberately not taken |
| 108 | + |
| 109 | +- **A hand-curated `llms.txt`.** Every sibling carries one — a hand-maintained |
| 110 | + routing layer the generator never writes and `check_navigator.py` skips when |
| 111 | + missing. Doc-authoring, not CI. |
| 112 | +- **13 scripts catalogue as `(no summary in script docstring)`** — they have a |
| 113 | + title but no prose paragraph. A documentation gap; nothing gates on it. |
| 114 | +- **Notebook regeneration is blocked for this workspace entirely.** |
| 115 | + `autohands/generate.py` exits on any project absent from |
| 116 | + `build_util.COLAB_PROJECTS`, and `autocti` is not there (nor in PyAutoNerves' |
| 117 | + `setup_colab.py` `_PROJECTS`). So `generate.py autocti` cannot run at all — |
| 118 | + discovered while documenting the regeneration command, and recorded in |
| 119 | + `AGENTS.md` so the next person does not try. `regenerate_navigator.py` is |
| 120 | + unaffected; it never touches that registry. Worth its own task. |
| 121 | + |
| 122 | +## Heart gate |
| 123 | + |
| 124 | +`pyauto-brain vitals` → **STALE**, score 65. Every reason was the same |
| 125 | +organism-scope evidence gap — `gh: command not found` in this cloud session, so |
| 126 | +`ci_status` could not be queried for any repo. Nothing known-bad. STALE passes |
| 127 | +the dev-ship gate by design (PyAutoHeart `AGENTS.md`; `AUTONOMY.md` leg 4) |
| 128 | +because the gap is organism-scope rather than branch-scope; branch-scope |
| 129 | +evidence was the local job table above. |
| 130 | + |
| 131 | +## Original prompt |
| 132 | + |
| 133 | +# autocti_workspace has no Navigator Check, so its CI can never roll up green |
| 134 | + |
| 135 | +Type: maintenance |
| 136 | +Target: autocti_workspace |
| 137 | +Repos: |
| 138 | +- @autocti_workspace |
| 139 | +- @PyAutoHeart |
| 140 | +Difficulty: medium |
| 141 | +Autonomy: supervised |
| 142 | +Priority: high |
| 143 | +Status: formalised |
| 144 | +Filed: 2026-08-24 |
| 145 | +Issued: 2026-08-24 |
| 146 | + |
| 147 | +`PyAutoHeart/config/repos.yaml` lists `autocti_workspace` in the `workspaces` |
| 148 | +group, whose required workflows are: |
| 149 | + |
| 150 | +```yaml |
| 151 | +required_workflows: |
| 152 | + workspaces: ["Smoke Tests", "Navigator Check"] # smoke_tests.yml + navigator_check.yml |
| 153 | +``` |
| 154 | +
|
| 155 | +`Smoke Tests` now exists (autocti_workspace#27/#28, 2026-08-24 — the repo's first |
| 156 | +CI). **`Navigator Check` does not.** That is not cosmetic. |
| 157 | + |
| 158 | +## Why this blocks readiness, not just tidiness |
| 159 | + |
| 160 | +`heart/checks/ci_status.py` rolls a repo up over its *required* workflows. A |
| 161 | +required workflow with **no runs at all** is not scored as a failure — it simply |
| 162 | +never satisfies `all_green`. Verified directly against the real function: |
| 163 | + |
| 164 | +``` |
| 165 | +required for `workspaces`: ['Smoke Tests', 'Navigator Check'] |
| 166 | + |
| 167 | +Smoke green, Navigator MISSING -> {'conclusion': '', 'status': 'in_progress'} |
| 168 | +both green -> {'conclusion': 'success', 'status': 'completed'} |
| 169 | +smoke red, Navigator MISSING -> {'conclusion': 'failure', 'status': 'completed'} |
| 170 | +``` |
| 171 | +
|
| 172 | +So `autocti_workspace` rolls up as **permanently `in_progress`** — never green, |
| 173 | +never red. It cannot reach `conclusion: success` no matter how healthy it is, |
| 174 | +which means the readiness/release gate can never see this repo as CI-clean. |
| 175 | +Adding Smoke Tests was necessary but not sufficient. |
| 176 | + |
| 177 | +Note the asymmetry in that table: a *red* Smoke Tests still reports failure |
| 178 | +correctly. So the repo can go red but can never go green — the worst shape for a |
| 179 | +gate to be in. |
| 180 | + |
| 181 | +## Work |
| 182 | + |
| 183 | +1. **Find out what `Navigator Check` actually is.** It is not a Heart reusable |
| 184 | + workflow — `PyAutoHeart/.github/workflows/` has no `navigator_check.yml`; the |
| 185 | + name appears only in `config/repos.yaml` and `heart/checks/ci_status.py`. |
| 186 | + Each workspace owns its own `.github/workflows/navigator_check.yml`. Read a |
| 187 | + sibling that has one (`autolens_workspace`, `autogalaxy_workspace`, |
| 188 | + `autofit_workspace`, or a `HowTo*` repo) and mirror it. Do **not** invent a |
| 189 | + check from the name. |
| 190 | +2. **Add it to `autocti_workspace`**, adapted to this repo's actual navigator |
| 191 | + surface. autocti_workspace has root-level catalogue files the assistant's |
| 192 | + citation checks already lean on (`llms.txt`, `llms-full.txt`, |
| 193 | + `workspace_index.json` are sparse-checked out by |
| 194 | + `autocti_assistant/.github/workflows/wiki-currency.yml`), so there is a real |
| 195 | + navigator surface here to validate. |
| 196 | +3. **Confirm the roll-up flips to `success`** once both workflows are green on |
| 197 | + `main` HEAD — that is the actual acceptance criterion, not "the workflow |
| 198 | + passes". |
| 199 | +4. **Consider the general case.** If other repos in a required group are missing |
| 200 | + a required workflow, they have the same silent-never-green defect. A Heart-side |
| 201 | + drift check ("every repo in a required group has a workflow file for each of |
| 202 | + its required workflows") would catch this class rather than this instance. |
| 203 | + Raise it as a proposal with findings; do not widen this task unilaterally. |
| 204 | + |
| 205 | +## Why it was left out of the smoke task |
| 206 | + |
| 207 | +autocti_workspace#28 was scoped to the ordered-trap smoke coverage (CTI epic |
| 208 | +Phase 5). Adding a second, unrelated workflow would have widened that PR beyond |
| 209 | +its task. Filed here instead, deliberately. |
| 210 | + |
| 211 | +## Context |
| 212 | + |
| 213 | +`PyAutoMind/complete/2026/08/phase5-smoke-ordered-trap-scripts.md` — how the |
| 214 | +repo got its first CI, and the `arcticpy: true` caller convention it uses. |
0 commit comments