Skip to content

Add: test-category axis so a suite-only change runs only its own suite - #1590

Merged
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:ci/test-category-axis
Jul 30, 2026
Merged

Add: test-category axis so a suite-only change runs only its own suite#1590
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:ci/test-category-axis

Conversation

@ChaoWao

@ChaoWao ChaoWao commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #1589. That PR collapses the two NON_CODE vocabularies; this one adds a third axis on top. Review/merge #1589 first — the diff here is the second commit only.

Why

tests/st/ + examples/ and tests/ut/ execute disjoint trees:

Suite Command
scene tests pytest examples tests/st ...
unit tests pytest tests/ut ... + ctest --test-dir tests/ut/cpp/build

And no unit test reads examples/ or tests/st/. The only cross-references are a faked nodeid string in tests/ut/py/test_resource_failure_summary.py and a comment in test_error_code_names.cpp — neither opens a file.

So a diff confined to one suite cannot break the other, and until now every such diff ran both. Measured on the last 60 commits to main: 8 scene-test/example-only, 3 unit-test-only — about 18% paying for a suite that could not have regressed, including time on the self-hosted a2a3 and a5 pools.

Shape

st_affected / ut_affected copy the existing arch pattern exactly — a partition is unaffected only when every changed file belongs exclusively to a sibling partition, with NON_CODE subtracted first:

ST_ONLY='^(tests/st/|examples/)'
UT_ONLY='^tests/ut/'

ST_REMAINING=$(echo "$FILES" | grep -vE "$UT_ONLY" | grep -vE "$NON_CODE" || true)
UT_REMAINING=$(echo "$FILES" | grep -vE "$ST_ONLY" | grep -vE "$NON_CODE" || true)

Copying the shape is what buys the two properties that matter:

  • Fail-safe direction — an unrecognised path survives both filters, lands in REMAINING, and turns the flag on. A new directory over-runs CI rather than silently skipping it.
  • Shared infrastructure resolves without enumeration — the root conftest.py, pyproject.toml, simpler_setup/, tests/lint/ belong to no single partition, so they match no sibling pattern and flip every flag true. Nothing has to list them.

Resulting gates

Job family Gate
st-sim-*, st-onboard-* <arch>_changed && st_affected
profiling-flags-smoke (a2a3_changed || a5_changed) && st_affected — it runs pytest examples/{arch}/.../vector_example/, so it is st-family
ut, ut-a2a3, ut-a5 non_code_only != true && ut_affected
packaging-matrix non_code_only != true (unchanged)

The UT jobs stay off the arch axis. docs/ci.md principle 5 argues that unit tests cover shared contracts and a falsely-skipped regression outweighs the minutes — that is a judgement about silicon attribution and it still holds. Test category is a different question and the answer differs.

packaging-matrix keeps non_code_only alone. Neither tests/ nor examples/ ships in the wheel (wheel.packages = ["simpler_setup", "python/simpler"]), so it over-runs on a tests-only diff — cheap, GitHub-hosted, not worth a fourth condition. Stated in docs/ci.md rather than left implicit.

Testing

Simulated the full three-axis filter against eleven file lists:

Input st_affected ut_affected Jobs reached
tests/st/... only true false scene tests + packaging
examples/a2a3/... only true false a2a3 scene tests + packaging
examples/a5/... only true false a5 scene tests + packaging
tests/ut/py/... only false true unit tests + packaging
tests/ut/cpp/a5/... only false true unit tests + packaging
src/common/log/host_log.cpp true true everything
root conftest.py true true everything
pyproject.toml true true everything
simpler_setup/scene_test.py true true everything
st + ut mixed true true everything
docs + mkdocs.yml false false pre-commit + docs only
  • yaml.safe_load on ci.yml parses
  • mkdocs build --strict — exit 0
  • markdownlint-cli2 — 0 errors on docs/ci.md and the rule
  • Every if: re-read after rewiring; the || in profiling-flags-smoke is parenthesised so && binds as intended

Per the rule's own §6, the check that matters is what ran: this PR touches ci.yml, so all three axes report "affected" and the full matrix should fire here.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e3769e99-7b94-4e46-859d-10fc1f9cdef7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

CI change detection now includes separate scene-test and unit-test affected flags. Workflow jobs use these flags alongside existing non-code and architecture gates, and the CI documentation describes the updated scheduling behavior.

Changes

CI test-category gating

Layer / File(s) Summary
Layered detection axes
.claude/rules/ci-change-detection.md
Defines non-code, architecture, and test-category axes with sibling-partition evaluation and fail-safe handling.
Affected-category output computation
.github/workflows/ci.yml
Adds st_affected and ut_affected outputs, handles empty diffs, and classifies ST-only and UT-only paths.
Workflow gates and CI documentation
.github/workflows/ci.yml, docs/ci.md
Applies category gates to scene-test, profiling, and unit-test jobs and documents the updated scheduling rules.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ChangedFiles
  participant DetectChanges
  participant CIJobs
  ChangedFiles->>DetectChanges: changed paths
  DetectChanges->>DetectChanges: compute st_affected and ut_affected
  DetectChanges->>CIJobs: publish category outputs
  CIJobs->>CIJobs: evaluate job conditions
Loading

Possibly related PRs

Poem

A bunny checks the changed-file trail,
ST and UT flags hop without fail.
Scene jobs run when scene paths appear,
Unit jobs wait for unit paths near.
Empty diffs make every gate glow—
The full CI garden gets room to grow.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a test-category axis so suite-only changes run only their own suite.
Description check ✅ Passed The description is directly related to the CI change detection and job-gating updates described in the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ChaoWao
ChaoWao force-pushed the ci/test-category-axis branch 2 times, most recently from f637218 to 8889108 Compare July 30, 2026 03:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 760-761: Update the workflow step that computes affected files to
capture git diff output separately while preserving its exit status, including
when the pull-request base or head SHA is unavailable or invalid. Use only git
diff’s status to select the empty FILES fallback, then always emit st_affected
and ut_affected outputs through the existing output-writing logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c9cab668-9d2d-4227-a4d8-907674fb9e37

📥 Commits

Reviewing files that changed from the base of the PR and between f260e7d and 8889108.

📒 Files selected for processing (3)
  • .claude/rules/ci-change-detection.md
  • .github/workflows/ci.yml
  • docs/ci.md

Comment thread .github/workflows/ci.yml
`tests/st/` + `examples/` and `tests/ut/` execute disjoint trees — `pytest
examples tests/st` versus `pytest tests/ut` plus the C++ ctest — and no unit
test reads `examples/` or `tests/st/`. The only cross-references are a faked
nodeid string in `tests/ut/py/test_resource_failure_summary.py` and a comment
in `test_error_code_names.cpp`; neither opens a file. So a diff confined to one
suite cannot break the other, and until now every such diff ran both.

Over the last 60 commits on main, 8 were scene-test/example-only and 3 were
unit-test-only — around 18% paying for a suite that could not have regressed,
including time on the self-hosted a2a3 and a5 pools.

`st_affected` and `ut_affected` are written in the same shape as the existing
arch flags: a partition is unaffected only when EVERY changed file belongs
exclusively to a sibling partition, with `NON_CODE` subtracted first. Copying
the shape is what buys the two properties that matter. Unrecognised paths
survive both filters and turn the flag on, so a new directory over-runs CI
rather than silently skipping it. And shared test infrastructure — the root
`conftest.py`, `pyproject.toml`, `simpler_setup/`, `tests/lint/` — belongs to
no single partition, so it matches no sibling pattern and flips every flag
true, without needing to be enumerated anywhere.

The fail-open guard also learns that a *failed* `git diff` is the same as an
empty one. `run:` is `bash -e`, so a non-zero diff — unresolved base/head SHA —
aborted the step before the guard and emitted no outputs at all; a failed
`detect-changes` then leaves every downstream `needs:` unsatisfied, which SKIPS
the matrix rather than running it. Fail-closed, the opposite of the intent. Only
the exit status decides now, and a failure folds into the empty case.

The UT jobs stay off the arch axis. `docs/ci.md` principle 5 argues that unit
tests cover shared contracts and a falsely-skipped regression outweighs the
minutes saved, which is a judgement about silicon attribution and still holds.
Test category is a different question, and the answer differs.

`packaging-matrix` keeps `non_code_only` alone. Neither `tests/` nor
`examples/` ships in the wheel, so it over-runs on a tests-only diff, but it is
cheap and GitHub-hosted and a fourth condition buys little.

Simulated against thirteen file lists before pushing. `tests/st/`-only and
`examples/`-only now reach scene tests alone; `tests/ut/py/`-only and
`tests/ut/cpp/a5/`-only reach unit tests alone; `src/common/`, the root
`conftest.py`, `pyproject.toml`, `simpler_setup/`, a mixed st+ut diff, an empty
diff and an unresolvable-SHA diff all still reach both.

`docs/ci.md` and `.claude/rules/ci-change-detection.md` move with it — the rule
now describes three layered axes, states the shared shape as the reason a
fourth axis must copy it rather than invent one, and covers the failed-diff
case under "fail open once, at the top".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChaoWao
ChaoWao merged commit 72148a2 into hw-native-sys:main Jul 30, 2026
18 checks passed
@ChaoWao
ChaoWao deleted the ci/test-category-axis branch July 30, 2026 12:16
ChaoWao added a commit to ChaoWao/simpler-fork that referenced this pull request Jul 31, 2026
The SDMA quarantine was a pair of `--ignore=<path>` arguments. `pytest
--ignore` on a path that no longer exists **exits 0 and says nothing** —
measured — so moving either demo directory would have dropped the quarantine
silently and landed both tests back beside the `aicore_op_timeout`
fault-injection test. That collision is the whole hazard: provisioning the
SDMA workspace creates 48 device-only STARS streams that sit in the device
fault domain, so a later AICore fault on that device costs ~306 s instead of
~0.3 s (hw-native-sys#1425, contained in hw-native-sys#1406).

`@pytest.mark.sdma` replaces the path matching, and carries the whole
consequence rather than one arbitrary piece of it. One declaration now drives
three things:

- **The Worker is built with `enable_sdma=True`.** Both pytest construction
  sites in `conftest.py` and both standalone sites in `scene_test.py` read it,
  the latter from `cls.pytestmark` so `python test_x.py` behaves like the
  pytest path.
- **The L2 Worker pool stops mixing capabilities.** The key gains the flag and
  reuse tests it, because an enable_sdma Worker holds its STARS streams for
  life: handing it to a test that did not ask for them would spread the
  teardown hazard to every later L2 case on that device. The existing
  same-device retire loop then performs the swap, so no new teardown path.
- **SDMA sorts last.** `sort_key` gains a term, keyed off the marker rather
  than the class because the fault-injection tests are plain functions with no
  `_st_level`. Fault injection therefore always runs on a device that has never
  provisioned — which is what actually addresses the interaction, rather than
  merely quarantining it.

With the capability expressible, `prefetch_async_demo` becomes an ordinary L2
`@scene_test` class. It was a hand-rolled Worker only because `CASES` had no
channel to `Worker.__init__`, and it paid for that by forfeiting golden
comparison, case parametrization, `--rounds`, `--case` and the dispatcher's
device allocation. 160 lines become 89, and its verification —
`torch.equal(out, src)` — is now a one-line `compute_golden`. The framework's
default orchestration includes turned out to be sufficient, so no new
passthrough was needed there.

`sdma_async_completion_demo` keeps its hand-rolled L3 Worker for now and takes
the marker for ordering and CI selection only; converting it means expressing a
comm domain through `CASES`, which is a larger change and independent of this
one.

CI selects with `-m sdma` / `-m "not sdma"`, so the sweep and the dedicated
step can no longer drift apart. **The dedicated step stays until hw-native-sys#1425 is
fixed**: in-session ordering separates the cases, but a fault on a device that
has already provisioned still costs minutes, so they must not share a device.

Separately, `docs/capability-survey.md` carried four `ci.yml:<line>` citations,
three already invalidated by this session's edits — `:607` lands on a blank
line, `:884` on a `cmake --build`, `:628-643` on a dep_gen comment, after the
qwen step was deleted in hw-native-sys#1601 and `detect-changes` reworked in hw-native-sys#1589 / hw-native-sys#1590 /
hw-native-sys#1607. Line numbers into a file this active cannot be maintained, so all four
become greppable anchors. `grep -rIn 'ci\.yml:[0-9]'` is now empty repo-wide.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ChaoWao added a commit to ChaoWao/simpler-fork that referenced this pull request Jul 31, 2026
The SDMA quarantine was a pair of `--ignore=<path>` arguments. `pytest
--ignore` on a path that no longer exists **exits 0 and says nothing** —
measured — so moving either demo directory would have dropped the quarantine
silently and landed both tests back beside the `aicore_op_timeout`
fault-injection test. That collision is the whole hazard: provisioning the
SDMA workspace creates 48 device-only STARS streams that sit in the device
fault domain, so a later AICore fault on that device costs ~306 s instead of
~0.3 s (hw-native-sys#1425, contained in hw-native-sys#1406).

`@pytest.mark.sdma` replaces the path matching, and carries the whole
consequence rather than one arbitrary piece of it. One declaration now drives
three things:

- **The Worker is built with `enable_sdma=True`.** Both pytest construction
  sites in `conftest.py` and both standalone sites in `scene_test.py` read it,
  the latter from `cls.pytestmark` so `python test_x.py` behaves like the
  pytest path.
- **The L2 Worker pool stops mixing capabilities.** The key gains the flag and
  reuse tests it, because an enable_sdma Worker holds its STARS streams for
  life: handing it to a test that did not ask for them would spread the
  teardown hazard to every later L2 case on that device. The existing
  same-device retire loop then performs the swap, so no new teardown path.
- **SDMA sorts last.** `sort_key` gains a term, keyed off the marker rather
  than the class because the fault-injection tests are plain functions with no
  `_st_level`. This is what will make merging the dedicated CI step back into
  the sweep safe once hw-native-sys#1425 is fixed; today `-m` already separates them, so it
  matters for local full runs.

With the capability expressible, `prefetch_async_demo` becomes an ordinary L2
`@scene_test` class. It was a hand-rolled Worker only because `CASES` had no
channel to `Worker.__init__`, and it paid for that by forfeiting golden
comparison, case parametrization, `--rounds`, `--case` and the dispatcher's
device allocation. 160 lines become 89, and its verification —
`torch.equal(out, src)` — is now a one-line `compute_golden`. The framework's
default orchestration includes turned out to be sufficient, so no new
passthrough was needed there. The conversion also moves it from the Resource
phase to the L2 phase, which runs after it, so it provisions on a device the
fault-injection cases have already finished with.

`sdma_async_completion_demo` keeps its hand-rolled L3 Worker for now and takes
the marker for ordering and CI selection only; converting it means expressing a
comm domain through `CASES`, which is a larger change and independent of this
one.

CI selects with `-m sdma` / `-m "not sdma"`, so the sweep and the dedicated
step can no longer drift apart. **The dedicated step stays until hw-native-sys#1425 is
fixed**: the two phases run their jobs in parallel across devices, so only
separate device pools keep an SDMA provisioning away from a fault injection.

`.claude/skills/testing/SKILL.md` moves with the mechanism throughout — not
only the mirror-CI command, but the two instructions that told readers to
extract `--ignore` sets from `ci.yml` and to grep it when a test passes alone
and fails in the sweep. Both now name the marker; following the skill
reproduces CI rather than a mechanism that no longer exists.

Separately, `docs/capability-survey.md` carried four `ci.yml:<line>` citations,
three already invalidated by this session's edits — `:607` lands on a blank
line, `:884` on a `cmake --build`, `:628-643` on a dep_gen comment, after the
qwen step was deleted in hw-native-sys#1601 and `detect-changes` reworked in hw-native-sys#1589 / hw-native-sys#1590 /
hw-native-sys#1607. Line numbers into a file this active cannot be maintained, so all four
become greppable anchors. `grep -rIn 'ci\.yml:[0-9]'` is now empty repo-wide.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ChaoWao added a commit to ChaoWao/simpler-fork that referenced this pull request Jul 31, 2026
The SDMA quarantine was a pair of `--ignore=<path>` arguments. `pytest
--ignore` on a path that no longer exists **exits 0 and says nothing** —
measured — so moving either demo directory would have dropped the quarantine
silently and landed both tests back beside the `aicore_op_timeout`
fault-injection test. That collision is the whole hazard: provisioning the
SDMA workspace creates 48 device-only STARS streams that sit in the device
fault domain, so a later AICore fault on that device costs ~306 s instead of
~0.3 s (hw-native-sys#1425, contained in hw-native-sys#1406).

`@pytest.mark.sdma` replaces the path matching, and carries the whole
consequence rather than one arbitrary piece of it. One declaration now drives
three things:

- **The Worker is built with `enable_sdma=True`.** Both pytest construction
  sites in `conftest.py` and both standalone sites in `scene_test.py` read it,
  the latter from `cls.pytestmark` so `python test_x.py` behaves like the
  pytest path.
- **The L2 Worker pool stops mixing capabilities.** The key gains the flag and
  reuse tests it, because an enable_sdma Worker holds its STARS streams for
  life: handing it to a test that did not ask for them would spread the
  teardown hazard to every later L2 case on that device. The existing
  same-device retire loop then performs the swap, so no new teardown path.
- **SDMA sorts last.** `sort_key` gains a term, keyed off the marker rather
  than the class because the fault-injection tests are plain functions with no
  `_st_level`. This is what will make merging the dedicated CI step back into
  the sweep safe once hw-native-sys#1425 is fixed; today `-m` already separates them, so it
  matters for local full runs.

With the capability expressible, `prefetch_async_demo` becomes an ordinary L2
`@scene_test` class. It was a hand-rolled Worker only because `CASES` had no
channel to `Worker.__init__`, and it paid for that by forfeiting golden
comparison, case parametrization, `--rounds`, `--case` and the dispatcher's
device allocation. 160 lines become 89, and its verification —
`torch.equal(out, src)` — is now a one-line `compute_golden`. The framework's
default orchestration includes turned out to be sufficient, so no new
passthrough was needed there. The conversion also moves it from the Resource
phase to the L2 phase, which runs after it, so it provisions on a device the
fault-injection cases have already finished with.

`sdma_async_completion_demo` keeps its hand-rolled L3 Worker for now and takes
the marker for ordering and CI selection only; converting it means expressing a
comm domain through `CASES`, which is a larger change and independent of this
one.

CI selects with `-m sdma` / `-m "not sdma"`, so the sweep and the dedicated
step can no longer drift apart. **The dedicated step stays until hw-native-sys#1425 is
fixed**: the two phases run their jobs in parallel across devices, so only
separate device pools keep an SDMA provisioning away from a fault injection.

`.claude/skills/testing/SKILL.md` moves with the mechanism throughout — not
only the mirror-CI command, but the two instructions that told readers to
extract `--ignore` sets from `ci.yml` and to grep it when a test passes alone
and fails in the sweep. Both now name the marker; following the skill
reproduces CI rather than a mechanism that no longer exists.

Separately, `docs/capability-survey.md` carried four `ci.yml:<line>` citations,
three already invalidated by this session's edits — `:607` lands on a blank
line, `:884` on a `cmake --build`, `:628-643` on a dep_gen comment, after the
qwen step was deleted in hw-native-sys#1601 and `detect-changes` reworked in hw-native-sys#1589 / hw-native-sys#1590 /
hw-native-sys#1607. Line numbers into a file this active cannot be maintained, so all four
become greppable anchors. `grep -rIn 'ci\.yml:[0-9]'` is now empty repo-wide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ChaoWao added a commit that referenced this pull request Jul 31, 2026
…#1609)

The SDMA quarantine was a pair of `--ignore=<path>` arguments. `pytest
--ignore` on a path that no longer exists **exits 0 and says nothing** —
measured — so moving either demo directory would have dropped the quarantine
silently and landed both tests back beside the `aicore_op_timeout`
fault-injection test. That collision is the whole hazard: provisioning the
SDMA workspace creates 48 device-only STARS streams that sit in the device
fault domain, so a later AICore fault on that device costs ~306 s instead of
~0.3 s (#1425, contained in #1406).

`@pytest.mark.sdma` replaces the path matching, and carries the whole
consequence rather than one arbitrary piece of it. One declaration now drives
three things:

- **The Worker is built with `enable_sdma=True`.** Both pytest construction
  sites in `conftest.py` and both standalone sites in `scene_test.py` read it,
  the latter from `cls.pytestmark` so `python test_x.py` behaves like the
  pytest path.
- **The L2 Worker pool stops mixing capabilities.** The key gains the flag and
  reuse tests it, because an enable_sdma Worker holds its STARS streams for
  life: handing it to a test that did not ask for them would spread the
  teardown hazard to every later L2 case on that device. The existing
  same-device retire loop then performs the swap, so no new teardown path.
- **SDMA sorts last.** `sort_key` gains a term, keyed off the marker rather
  than the class because the fault-injection tests are plain functions with no
  `_st_level`. This is what will make merging the dedicated CI step back into
  the sweep safe once #1425 is fixed; today `-m` already separates them, so it
  matters for local full runs.

With the capability expressible, `prefetch_async_demo` becomes an ordinary L2
`@scene_test` class. It was a hand-rolled Worker only because `CASES` had no
channel to `Worker.__init__`, and it paid for that by forfeiting golden
comparison, case parametrization, `--rounds`, `--case` and the dispatcher's
device allocation. 160 lines become 89, and its verification —
`torch.equal(out, src)` — is now a one-line `compute_golden`. The framework's
default orchestration includes turned out to be sufficient, so no new
passthrough was needed there. The conversion also moves it from the Resource
phase to the L2 phase, which runs after it, so it provisions on a device the
fault-injection cases have already finished with.

`sdma_async_completion_demo` keeps its hand-rolled L3 Worker for now and takes
the marker for ordering and CI selection only; converting it means expressing a
comm domain through `CASES`, which is a larger change and independent of this
one.

CI selects with `-m sdma` / `-m "not sdma"`, so the sweep and the dedicated
step can no longer drift apart. **The dedicated step stays until #1425 is
fixed**: the two phases run their jobs in parallel across devices, so only
separate device pools keep an SDMA provisioning away from a fault injection.

`.claude/skills/testing/SKILL.md` moves with the mechanism throughout — not
only the mirror-CI command, but the two instructions that told readers to
extract `--ignore` sets from `ci.yml` and to grep it when a test passes alone
and fails in the sweep. Both now name the marker; following the skill
reproduces CI rather than a mechanism that no longer exists.

Separately, `docs/capability-survey.md` carried four `ci.yml:<line>` citations,
three already invalidated by this session's edits — `:607` lands on a blank
line, `:884` on a `cmake --build`, `:628-643` on a dep_gen comment, after the
qwen step was deleted in #1601 and `detect-changes` reworked in #1589 / #1590 /
#1607. Line numbers into a file this active cannot be maintained, so all four
become greppable anchors. `grep -rIn 'ci\.yml:[0-9]'` is now empty repo-wide.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant