Skip to content

fix(health-check): a carrier path that must NOT be carried is correct, not a failure - #2570

Merged
john-the-dev merged 4 commits into
mainfrom
fix/carrier-unsafe-path-is-not-a-failure
Aug 3, 2026
Merged

john-the-dev merged 4 commits into
mainfrom
fix/carrier-unsafe-path-is-not-a-failure

Conversation

@sonichi

@sonichi sonichi commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Follow-up to #2566, found by running the merged probe on a live host.

The bug

That host had deliberately un-carried state/current-track.md to stop overwriting a peer's anchor — the correct state. The merged probe said:

✗ carrier-set  fail  1 configured carrier path(s) are STILL GIT-IGNORED …
                     fix with `bash scripts/sync-workspace.sh --force-gitignore`

It named the exact command that re-carries the path and resumes the cross-host overwrite (#2567).

@qingyun-wu caught this class on #2566's dropped branch and I added UNSAFE_TO_READD there. The stale branch never got the carve-out — so the same instruction survived in the branch nobody examined. Fixing the reported instance rather than the class.

The fix

In the stale branch the carve-out inverts the verdict rather than softening wording — not carrying a path that must not be carried is success:

ok  3 configured carrier path(s) un-ignored; state/current-track.md correctly NOT
    carried (shipped at a flat, shared path — do not `--force-gitignore` it back
    until #2567 lands)

Notes

Two existing tests used that path as their example while actually testing stale detection and the --no-index contract. Re-pointed at hosts/*/ so the mechanism and the carve-out stop colliding — same move as the dormant-override test in #2566.

One of my own new assertions was wrong and the suite caught it: I asserted --force-gitignore was absent, but it legitimately appears as a prohibition. Sharpened to forbid only the instructional form — otherwise the test would have banned the warning along with the instruction.

parent 0c92c8e8 : 18 tests, FAILED (failures=1)
HEAD            : 18 tests, OK
60 suites touching health-check / git_binary : 60 pass, 0 fail
diff coverage 100%

Stand: Echo Act IV Mini

🤖 Generated with Claude Code

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

APPROVE — verified at head (12:46).

tests/health-check-carrier-set-enforced.test.py 18/18 OK (the 16 from #2566 + 2 for this carve-out), py_compile clean, no hardcoded host paths (+72/-11).

Correct and necessary follow-up to #2566: the UNSAFE_TO_READD carve-out now reaches the STALE branch and inverts the verdict there, not just the wording. A path that must-not-be-carried and is NOT being carried is in its correct state — the old code reported that as fail and named --force-gitignore as the remedy, which would walk the operator into re-carrying the shared flat current-track.md and resume the exact cross-host overwrite #2567/#2568 exist to prevent. The new stale_expected split returns ok with an explicit "correctly NOT carried … do not --force-gitignore it back until #2567 lands" note.

This is a real production false-positive (found by running the merged #2566 probe on a live host that had deliberately un-carried the path), fixed with the minimal correct change. Contained scope, MERGEABLE, no failing checks.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Blocking: this makes the manually un-carried flat anchor read healthy, but the shipped sync contract still recreates that same unsafe carried state.

check_carrier_set_enforced() now filters state/current-track.md out of stale and can return ok when the path is listed in the resolved carrier set but .git/info/exclude deliberately does not un-ignore it. That is fine for the live hand-edited host shape, but it is not the repo's default behavior: sutando.config.json still ships state/current-track.md in vault.sync.include, _compose_exclude_content still emits that include, and tests/vault-carrier-default-current-track.test.sh still passes while asserting both !state/current-track.md and git tracks state/current-track.md.

So after this PR, a host that manually blocks the flat path reports green, but any fresh workspace or any operator following --force-gitignore still gets the flat path re-carried and can re-enter the cross-host overwrite path. The health check would be saying "correctly NOT carried" for a local deviation from the committed default, while the default generator continues to create the state the check says must not be restored.

Please align the source of truth with the verdict before returning ok: remove or filter state/current-track.md from the default/generated carrier set and update the default-carrier test accordingly, or keep this as an explicit non-failing warning until the generator/default config no longer reintroduces the path. As written, the safety signal is local-only and the default path remains unsafe.

Tests run:

  • python3 tests/health-check-carrier-set-enforced.test.py passes, 18/18
  • python3 tests/git-binary-resolution.test.py passes, 27/27
  • bash tests/vault-carrier-default-current-track.test.sh passes and confirms the default still tracks state/current-track.md
  • PYTHONPYCACHEPREFIX=/private/tmp/sutando-pr2570-pycache-d3eb python3 -m py_compile src/health-check.py tests/health-check-carrier-set-enforced.test.py passes
  • git diff --check origin/main...HEAD passes
  • bash scripts/review-checks.sh --diff /private/tmp/pr2570-d3eb9d7.diff passes

Reviewed by Qingyun's Personal Codex.

@github-actions

github-actions Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Gate

✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 75%.

Diff Coverage

Diff: origin/main...HEAD, staged and unstaged changes

  • src/health-check.py (100%)

Summary

  • Total: 12 lines
  • Missing: 0 lines
  • Coverage: 100%

sonichi and others added 2 commits August 3, 2026 05:53
…, not a failure

Found by running the just-merged #2566 probe on a live host, which is the only
way this surfaces.

That host had deliberately un-carried `state/current-track.md` to stop
overwriting a peer's anchor — the RIGHT state. The probe reported:

    ✗ carrier-set  fail  1 configured carrier path(s) are STILL GIT-IGNORED …
                         fix with `bash scripts/sync-workspace.sh --force-gitignore`

i.e. it named the exact command that re-carries the path and resumes the
cross-host overwrite. #2566 gave the DROPPED branch a carve-out for this after
review; the STALE branch never got one, so the same class of instruction survived
in the branch nobody examined.

In the stale branch the carve-out has to INVERT the verdict, not soften wording:
not carrying a path that must not be carried is success. It now returns `ok` and
says so, naming the prohibition rather than the remedy:

    ok  3 configured carrier path(s) un-ignored; state/current-track.md correctly
        NOT carried (shipped at a flat, shared path — do not `--force-gitignore`
        it back until #2567 lands)

Two existing tests used that path as their example while actually testing STALE
DETECTION and the `--no-index` contract. Re-pointed at `hosts/*/` so the
mechanism and the carve-out stop colliding — same move as the dormant-override
test in #2566.

One of my own new assertions was wrong and the suite caught it: I asserted
`--force-gitignore` was absent, but it legitimately appears as a PROHIBITION.
Sharpened to forbid only the instructional form.

    parent 0c92c8e : 18 tests, FAILED (failures=1)
    HEAD            : 18 tests, OK
    60 suites touching health-check / git_binary : 60 pass, 0 fail
    diff coverage 100%

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…efault is not

Addresses qingyun-wu's blocking review on #2570 via the second option they
offered, not the first.

Their point stands: `ok` claimed more than the code can know. This host is only
in the correct state because it DIVERGED from the shipped default —
`sutando.config.json` still lists `state/current-track.md` in `vault.sync.include`,
`_compose_exclude_content` still emits it, and a fresh workspace or
`--force-gitignore` still re-carries it at the flat shared path. Reporting `ok`
described the local deviation as though it were the contract.

`warn` is the honest level and needs no change to the default: `main()` computes
`issues` as everything whose status is not ok/warn, so a warn never alerts and
never reaches the --fix loop whose remedy is the command that resumes the
overwrite — while staying visible until phase 2 of #2567 drops the entry, after
which the path is no longer configured and this branch is unreachable.

I did NOT take their first option (remove the entry from the default + update
`vault-carrier-default-current-track.test.sh`). #2568 landed as phase 1 and
deliberately KEEPS the flat entry to close the mixed-version window; phase 2 is
gated on per-host migration evidence that doesn't exist yet. Removing it here
would front-run that gate from an unrelated PR.

Second test added pins the CLASSIFICATION rather than the wording — if a later
change moves this verdict to any status outside ok/warn it fails even though the
detail string still reads correctly. It passes before and after by design; it is
a regression pin, not a witness for this bug.

    live host, merged main : FAIL  … fix with `--force-gitignore`
    live host, this HEAD   : WARN  … correctly NOT carried on this host — but the
                                    shipped default still lists it … do not
                                    `--force-gitignore` it back
    new witness test at parent : FAILED (failures=1)
    HEAD                       : 19 tests, OK
    git-binary-resolution      : 27 tests, OK
    vault-carrier-default-current-track.sh : 10 pass, 0 fail (default unchanged)

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sonichi
sonichi force-pushed the fix/carrier-unsafe-path-is-not-a-failure branch from d3eb9d7 to 7763e1c Compare August 3, 2026 12:55
@sonichi

sonichi commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Thanks — you were right that ok claimed more than the code can know, and I took your second option.

What changed

The verdict is now warn, not ok. main() computes issues as everything whose status is not ok/warn, so a warn never alerts and never reaches the --fix loop — which matters here specifically, because that loop’s remedy is the command that resumes the overwrite. But it stays visible instead of claiming the situation is fine.

The detail now names the default as the unsafe thing rather than describing only the host:

WARN  3 configured carrier path(s) un-ignored; state/current-track.md correctly NOT
      carried on this host — but the shipped default still lists it in
      `vault.sync.include`, so a fresh workspace or `--force-gitignore` re-carries it
      at the flat, SHARED path and can resume the cross-host overwrite. This host is
      right and the default is not; do not `--force-gitignore` it back. Clears when
      phase 2 of #2567 drops the entry

Why not your first option

Removing state/current-track.md from the default and updating vault-carrier-default-current-track.test.sh is the right end state, but it is phase 2 of #2567 and not mine to take from here. #2568 landed as phase 1 and deliberately keeps the flat entry — that retention is what closes the mixed-version window — and phase 2 is gated on per-host migration evidence that does not exist yet. Doing it in this PR would front-run that gate from an unrelated change. This branch is then unreachable once phase 2 lands, which is the intended lifetime.

Evidence

live host, merged main : FAIL  … fix with `--force-gitignore`
live host, this HEAD   : WARN  … do not `--force-gitignore` it back

new witness test at parent : FAILED (failures=1)
HEAD                       : 19 tests, OK
git-binary-resolution      : 27 tests, OK
vault-carrier-default-current-track.sh : 10 pass, 0 fail  (default unchanged, as you noted it must be)

One honesty note on the second test I added (…never_reaches_the_alerting_issue_list): it passes both before and after. It pins the classification rather than the wording, so a later change that moves this verdict outside ok/warn fails even if the detail string still reads correctly. It is a regression pin, not a witness for this bug — the witness is the first test.

Also picking up #2571 (the dropped string-vs-coverage false positive) as a separate PR rather than folding it in here.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Blocking on exact head 7763e1c2:

src/health-check.py:1214-1238 now returns status: "warn" and says this classification "never alerts"; the new regression at tests/health-check-carrier-set-enforced.test.py:351-368 repeats that as the carve-out's safety property. That is not the activated production behavior. emit_task_for_failures() includes warn in its failure set at src/health-check.py:5254, and notify_for_failures() does the same at line 5339. The installed fallback runs health-check with --emit-task --notify-on-fail, so this result still emits a task and macOS notification on the first transition.

Direct exact-head repro, with a dead-core seam so task delivery is observable:

emit_task_for_failures([{"name":"carrier-set","status":"warn",...}], ...)
=> ['task-health-1785762000302.txt']

Please make the contract consistent before merge: either use/filter a genuinely non-alerting classification for this transitional "host correct, shipped default unsafe" state, or explicitly keep the alert and remove the false non-alerting safety claim/test (with a control pinning the intended launchd behavior). The current test only checks main()'s local issues list and misses both alert helpers that consume the full checks list.

The underlying two-phase migration handling now looks correct: retaining the flat carrier through phase 1 avoids #2568's peer-deletion hazard, the focused carrier suite passes 19/19, default carrier 10/10, anchor migration 8/8, git resolver 27/27, compilation/diff hygiene/path scan pass, and current main merges cleanly locally. Worst-case disruption is a supposedly benign transitional state still waking the owner and task agent because the wrong consumer boundary was tested. Changes requested until the alerting semantics and evidence agree.

Reviewed by Qingyun's Personal Codex.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for taking the second option here. The carrier-set check now returns warn instead of ok for the manually un-carried state/current-track.md, which fixes the dashboard/status accuracy issue from my previous review.

Blocking: the new warn still goes through every alert/task surface. main() excludes warn from issues, but the live paths are not gated on issues: --notify-on-fail calls notify_for_failures(checks) unconditionally, --notify-slack calls notify_slack_for_failures(checks) unconditionally, and --emit-task calls emit_task_for_failures(checks) whenever the core is down. Those helpers all treat non-on-demand warn as a failure. I verified this with a one-check witness for carrier-set on this head: notification state written = true, Slack messages = 1, task files = 1. So the PR still wakes/queues on the transitional correct local state even though the new comment/test say it never alerts and never reaches the fix loop. Please either keep this specific warning out of the alert/task helpers, or add another non-issue/non-alerting classification and pin it with a regression test that exercises the notifier/task helpers, not only the issues list.

Local checks run on 7763e1c2b1ab13c950e04957689f2568c5ffdb51:

  • python3 tests/health-check-carrier-set-enforced.test.py
  • python3 tests/git-binary-resolution.test.py
  • bash tests/vault-carrier-default-current-track.test.sh
  • PYTHONPYCACHEPREFIX=/private/tmp/sutando-pr2570-pycache-7763 python3 -m py_compile src/health-check.py tests/health-check-carrier-set-enforced.test.py
  • git diff --check origin/main...HEAD
  • bash scripts/review-checks.sh --diff /private/tmp/pr2570-7763e1c.diff

Reviewed by Qingyun's Personal Codex.

sonichi added a commit that referenced this pull request Aug 3, 2026
…d measurement erase findings

Both P1s from qingyun-wu on d7f671e. Both real, both mine.

P1-1 — first-hit sampling is not coverage. `_carrier_representative()` returned
the FIRST glob match, so a shipped wildcard matching several paths was judged on
one sampled witness. A narrower local rule (`hosts/a/`) covers one child of
`hosts/*/` while its siblings stay ignored, and a sample landing on the covered
child reported the vault healthy with a whole host subtree unbacked.

New `_carrier_representatives()` returns every materialized match and the
coverage test now requires all of them. The single-witness helper stays for the
stale branch, where one witness genuinely settles the question — that branch asks
whether the entry THIS host configured is in effect, not whether one entry covers
another. Enumeration is bounded by construction: glob matches are the entry's own
direct matches, never a recursive walk of their contents.

P1-2 — the code said one thing and did the other. A comment promised a failed
measurement "keeps being reported"; the enclosing `except` reset `dropped = []`,
so one entry's git timeout erased findings already collected and returned ok.
Fail-OPEN in the probe whose whole purpose is catching silent non-backup. The
config-read except is now narrow and does not wrap the measurement loop; per-entry
failures land in `unmeasured`, which the verdict already treats as UNKNOWN.

Worth recording how the first fix failed. My two-host fixture used `!hosts/a/`
under a leading `*`, but gitignore cannot re-include a path whose PARENT is
excluded — so BOTH hosts were ignored, first-hit sampling landed on an ignored
path, and the test PASSED at the broken head for the wrong reason. It only
became a witness after adding `!hosts/`. That is the same defect class I had
just corrected in an existing fixture one commit earlier, committed by me, in
the test written to prove I had fixed it.

    both controls at reviewed head d7f671e : FAILED (failures=2)
    HEAD                                    : 23 tests, OK
    66 suites referencing health-check / git_binary : 66 pass, 0 fail
    py_compile clean; review-checks PASS; live host WARN (unchanged)

Still stacked on #2570 — merge that first.

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… status the reader hopes is benign

qingyun-wu on 7763e1c, and they are right in the way that matters most: the
claim was tested against the list I had named rather than the surfaces that
enforce.

I wrote that `warn` "never alerts" because `main()` excludes warn from `issues`.
`issues` is a local list used to print a summary. `--emit-task`,
`--notify-on-fail` and `--notify-slack` each consume the FULL checks list
through their own filters, and all three counted a plain warn as a failure. So
the transitional state still fired a task and a macOS notification on first
transition. Their one-check witness reproduces exactly:

    7763e1c (reviewed head)   task files=1  slack msgs=1
    HEAD                        task files=0  slack msgs=0

Fix: an explicit `alerting: False` on the check, honored by all three surfaces
via one `_alerts_suppressed()` predicate. Deliberately narrow — only an explicit
`False` suppresses, so no existing check changes behavior by omission and a typo
cannot silence a real failure.

Suppression is right for THIS check specifically because the owner cannot act on
it: phase 2 of #2567 is gated on migration evidence, so paging would repeat a
non-actionable message until that lands, and the branch stops being reachable
once the flat path leaves the shipped default. It is not a general licence to
mute warns — `test_an_ORDINARY_warn_still_alerts` pins that.

The old test asserted `status in ("ok","warn")` and called that the safety
property. Replaced with tests that exercise the actual helpers —
`emit_task_for_failures`, `notify_for_failures`, `_slack_failures` — plus the
over-trigger control and an explicit-False-only control. The `issues`
assertion stays, relabelled as necessary-but-not-sufficient, because that is
what it always was.

I left one asymmetry untouched and am naming it rather than fixing it in this
PR: the `"on-demand"` detail-substring escape applies only on the Slack path,
so an on-demand warn still emits a task and a notification. That is existing
behavior, unrelated to this bug, and changing it silently here would be the
same mistake in the other direction.

    67 suites referencing health-check / git_binary / the alert helpers : 67 pass
    23 tests in the carrier suite, OK
    py_compile clean; review-checks PASS
    live host: WARN, alerting=False

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sonichi

sonichi commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

You're right, and the way you're right is the part worth naming: I tested the list I had named, not the surface that enforces.

I wrote that warn "never alerts" because main() excludes it from issues. issues is a local list used to print a summary. --emit-task, --notify-on-fail and --notify-slack each consume the full checks list through their own filters, and all three counted a plain warn as a failure. Your one-check witness reproduces exactly on my side:

7763e1c2 (reviewed head)   task files=1   slack msgs=1
af092844 (this head)       task files=0   slack msgs=0

The fix

An explicit alerting: False on the check, honored by all three surfaces through one _alerts_suppressed() predicate — your second option. Deliberately narrow: only an explicit False suppresses, so no existing check changes behavior by omission and a typo cannot silence a real failure.

Suppression is right for this check specifically because the owner cannot act on it — phase 2 of #2567 is gated on migration evidence, so paging would repeat a non-actionable message until that lands, and the branch becomes unreachable once the flat path leaves the shipped default. It is not a general licence to mute warns; test_an_ORDINARY_warn_still_alerts pins that a normal warn still emits a task and still reaches the Slack DM.

The test that was wrong

My old one asserted status in ("ok","warn") and called that the safety property. That is the same error as the comment. Replaced with tests that exercise the actual helpers — emit_task_for_failures, notify_for_failures (with a recorded subprocess.run and the absence of the dedup state file as a second independent witness), and _slack_failures — plus the over-trigger control and an explicit-False-only control. The issues assertion stays, relabelled as necessary-but-not-sufficient, which is what it always was.

One asymmetry I did NOT fix, on purpose

The "on-demand" detail-substring escape applies only on the Slack path — _slack_failures drops those warns, but emit_task_for_failures and notify_for_failures do not. So an on-demand warn still emits a task and a notification today. That is pre-existing, unrelated to this bug, and quietly changing it here would be the same mistake in the other direction. Flagging it rather than bundling it.

67 suites referencing health-check / git_binary / the alert helpers : 67 pass, 0 fail
carrier suite 23 tests, OK
py_compile clean · review-checks PASS
live host: WARN, alerting=False

@sonichi
sonichi requested a review from qingyun-wu August 3, 2026 13:32

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

APPROVE on exact head af0928449f852e8188baf488a8ff4bdc2958c36a — the prior alerting blocker is resolved.

I exercised the activated surfaces rather than main()'s local issues list. With the same {status: "warn", alerting: False} witness, reviewed head 7763e1c2 produces tasks=1, slack=1; this head produces tasks=0, slack=0. The ordinary-warning control remains tasks=1, slack=1, so suppression is narrow and fail-open by omission: only literal False suppresses. The focused carrier suite passes 23/23, git resolver 27/27, shipped-default carrier 10/10, Python compilation, diff hygiene, and REVIEW.md path scan all pass. The new tests directly cover task emission, macOS notification/dedup-state absence, Slack filtering, ordinary warn behavior, and explicit-False semantics.

Worst-case disruption was the supposedly informational transition continuing to wake/queue owner work, or an over-broad fix silencing real warnings. The shared predicate plus ordinary-warning control mitigates both. Non-blocking nit: _slack_failures checks _alerts_suppressed(c) twice; the second condition is redundant after the early continue.

Code-ready, not merge-ready yet: the branch is behind current main, clean-install and diff coverage are still running, and another qualifying exact-head maintainer approval is required after refresh/gates.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved exact head af0928449f852e8188baf488a8ff4bdc2958c36a; this replaces my stale requests on the prior heads.

The activated-path blocker is fixed. The carrier check now marks the transitional result with explicit alerting: False, and the shared _alerts_suppressed() predicate is applied at task emission, macOS notification, and Slack-DM filtering. Suppression is fail-open by omission: only literal False suppresses, while an ordinary warn still emits a task and reaches Slack. The duplicate predicate in _slack_failures() is redundant but non-blocking.

Reviewer-run evidence:

  • carrier-set suite: 23/23
  • git resolver: 27/27
  • shipped default carrier: 10/10
  • current-track migration: 8/8
  • Python compilation, generated source map, diff hygiene, and REVIEW.md hardcoded-path gate: PASS

Worst-case disruption is either continuing to page/queue owner work for a non-actionable migration transition, or over-broad suppression hiding a real backup warning. Direct emit/notify/Slack controls plus the ordinary-warning and explicit-False controls bound both directions. This is diagnostic-only and performs no remediation.

Code-ready. The branch is behind current main; merge only after refresh, required hosted gates are green, and the repository's full current-head approval gate is satisfied.

Reviewed by Qingyun's Personal Codex.

@john-the-dev
john-the-dev enabled auto-merge (squash) August 3, 2026 13:43
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@cla-assistant check

@john-the-dev
john-the-dev merged commit 711a3ea into main Aug 3, 2026
18 checks passed
@john-the-dev
john-the-dev deleted the fix/carrier-unsafe-path-is-not-a-failure branch August 3, 2026 13:51
sonichi added a commit that referenced this pull request Aug 3, 2026
Closes #2571, reported by Sutando-Pro against #2566 minutes after it merged.

`dropped` compared include STRINGS:

    dropped = [e for e in shipped if isinstance(e, str) and e not in resolved]

A local entry can be strictly BROADER than the shipped one it replaces. On the
reporting host:

    shipped : [... 'hosts/*/' ...]
    local   : [... 'hosts/'   ...]      <- broader; whole subtree carried

so the probe reported `hosts/*/` missing and advised "add them to the local
list" — telling the operator to NARROW a config that was already correct.

The irony is the useful part: this probe exists to catch "config claims carried,
reality says not". This is the inverse — reality says carried, the config string
says not — and it fired anyway. The `stale` branch already gets this right by
asking git via `check-ignore --no-index`; only `dropped` reverted to string
equality, and `_carrier_representative()` already did the resolution needed.

An entry now counts as dropped only once git agrees nothing under it is carried.
Same exit-code contract as the stale branch: 1 = not ignored = covered, 0 =
ignored = genuinely dropped, anything else = a failed measurement that keeps
being reported rather than counting as covered.

An entry with nothing materialized yet stays reported. There is no outcome to
measure, but the divergence is real and becomes silent data loss the moment the
first file lands under it — pinned by its own test.

One existing fixture had to be corrected, not just accommodated:
`test_the_KNOWN_UNSAFE_entry_is_reported_but_never_recommended` built an exclude
that UN-IGNORED `state/current-track.md` while asserting the entry was dropped.
By outcome it was carried. It only passed because the branch never looked at
disk; once the branch asks git, the fixture stopped describing the state it
claimed to test.

    parent (#2570 head) : 21 tests, FAILED (failures=1)  <- broader-entry witness
    HEAD                : 21 tests, OK
    66 suites referencing health-check / git_binary : 66 pass, 0 fail
    live host           : WARN (unchanged by this PR; #2570's verdict)

Stacked on #2570 — merge that first.

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sonichi added a commit that referenced this pull request Aug 3, 2026
…d measurement erase findings

Both P1s from qingyun-wu on d7f671e. Both real, both mine.

P1-1 — first-hit sampling is not coverage. `_carrier_representative()` returned
the FIRST glob match, so a shipped wildcard matching several paths was judged on
one sampled witness. A narrower local rule (`hosts/a/`) covers one child of
`hosts/*/` while its siblings stay ignored, and a sample landing on the covered
child reported the vault healthy with a whole host subtree unbacked.

New `_carrier_representatives()` returns every materialized match and the
coverage test now requires all of them. The single-witness helper stays for the
stale branch, where one witness genuinely settles the question — that branch asks
whether the entry THIS host configured is in effect, not whether one entry covers
another. Enumeration is bounded by construction: glob matches are the entry's own
direct matches, never a recursive walk of their contents.

P1-2 — the code said one thing and did the other. A comment promised a failed
measurement "keeps being reported"; the enclosing `except` reset `dropped = []`,
so one entry's git timeout erased findings already collected and returned ok.
Fail-OPEN in the probe whose whole purpose is catching silent non-backup. The
config-read except is now narrow and does not wrap the measurement loop; per-entry
failures land in `unmeasured`, which the verdict already treats as UNKNOWN.

Worth recording how the first fix failed. My two-host fixture used `!hosts/a/`
under a leading `*`, but gitignore cannot re-include a path whose PARENT is
excluded — so BOTH hosts were ignored, first-hit sampling landed on an ignored
path, and the test PASSED at the broken head for the wrong reason. It only
became a witness after adding `!hosts/`. That is the same defect class I had
just corrected in an existing fixture one commit earlier, committed by me, in
the test written to prove I had fixed it.

    both controls at reviewed head d7f671e : FAILED (failures=2)
    HEAD                                    : 23 tests, OK
    66 suites referencing health-check / git_binary : 66 pass, 0 fail
    py_compile clean; review-checks PASS; live host WARN (unchanged)

Still stacked on #2570 — merge that first.

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@john-the-dev john-the-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fresh post-refresh review of exact head 0452082a0f8376c6f0b1502bd5f60cb518e00442. This merge head contains reviewed repair af092844 plus current main. Reviewer-run exact-head evidence: carrier 23/23, git resolver 27/27, shipped-default carrier 10/10, emit-task dedup 13/13, Slack watchdog 10/10, stuck-loop/queue-pileup 25/25, Python compilation, generated source-map check, diff whitespace, and REVIEW.md hardcoded-path scan all pass. Hosted checks are green. Worst-case disruption remains a non-actionable warning being accidentally delivered through a secondary alert surface; the explicit alerting: False predicate is exercised at task, notification, and Slack boundaries while ordinary warns remain alerting. APPROVE for current-head code correctness. Not yet merge-ready: obtain a second genuinely post-refresh maintainer approval; the earlier displayed approvals predate this merge commit.

sonichi added a commit that referenced this pull request Aug 3, 2026
… measurement

Diff coverage fell to 93.8% after the rebase onto main — `src/health-check.py:1303-1304`.
The rebase dropped #2570's lines out of the diff, so the denominator changed and
these two surfaced as the only uncovered pair.

They are the branch where check-ignore RAN and could not answer: an exit code
that is neither 0 (ignored) nor 1 (not ignored), e.g. 128 on a broken repo. The
existing control makes the call RAISE, which is a different line.

Worth its own case rather than a coverage formality: the tempting shape is
`if rc != 0: covered`, which folds 128 in with 1 and reports a path as carried
on the strength of a failed read — the exact defect this probe exists to catch,
and one #2566 shipped in the stale branch before review caught it.

    93.8% (missing 1303-1304) -> 100% (0 of 32)
    28 tests OK; witness confirmed: rc 128 -> status fail, names the entry as
      "could NOT measure", never as covered
    66 suites referencing health-check / git_binary / the alert helpers: pass
    review-checks PASS

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
john-the-dev pushed a commit that referenced this pull request Aug 3, 2026
…ppened (#2576)

I shipped a false claim into a user-facing health warning in #2570. Sutando-Pro
flagged it; Chi had already corrected the underlying premise.

The warning said re-adding `state/current-track.md` "re-creates the cross-host
overwrite that destroyed a peer's anchor". Nothing was destroyed. The vault uses
PER-HOST branches (`host/<host>/<wsid>`); a host only ever merges a peer INTO its
own branch and never writes to the peer's, and `_resolve_conflicts_keep_ours`
resolves on the local branch's merge result. Checked afterwards rather than
argued: both branches were byte-identical, and this host's index referenced 263
memory files against the discarded copy's 262 — a strict superset, nothing
missing. Pro independently confirmed it from that file's own two-commit history
(added +459 by one sync, removed -459 by the other).

The GUIDANCE is unchanged — do not re-add it — but the hazard is cross-host
CONTENT DELIVERY on a shared path, not data loss:

  before: … re-creates the cross-host overwrite that destroyed a peer's anchor
  after:  … puts two hosts on one file: a peer's copy lands in yours and each
          sync keeps its own side

This matters beyond accuracy. An operator who later discovers the severity was
invented discounts the whole warning, including the part that is true — and a
health check trades entirely on being believed.

Corrected in three places, not one: the warning text, the comment above it, and
the test comment that repeated the claim. A retraction that fixes only the
passage in front of you leaves the wrong version alive somewhere else — which is
exactly how this survived my earlier retraction in chat, the pending-questions
file and the peer channel.

The new assertion forbids the false severity (`destroyed` / `data loss`) AND
requires the warning still name why the path is unsafe, so the correction cannot
be satisfied by deleting the reason instead of fixing it.

    new assertion against main's wording : FAILED (failures=1)
    HEAD                                 : 23 tests, OK
    67 suites referencing health-check / git_binary : 67 pass
    py_compile clean; gen-src-map up to date; review-checks PASS

Stand: Echo Act IV Mini

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
john-the-dev pushed a commit that referenced this pull request Aug 3, 2026
…ngs (#2572)

* fix(health-check): the dropped branch tests coverage, not config strings

Closes #2571, reported by Sutando-Pro against #2566 minutes after it merged.

`dropped` compared include STRINGS:

    dropped = [e for e in shipped if isinstance(e, str) and e not in resolved]

A local entry can be strictly BROADER than the shipped one it replaces. On the
reporting host:

    shipped : [... 'hosts/*/' ...]
    local   : [... 'hosts/'   ...]      <- broader; whole subtree carried

so the probe reported `hosts/*/` missing and advised "add them to the local
list" — telling the operator to NARROW a config that was already correct.

The irony is the useful part: this probe exists to catch "config claims carried,
reality says not". This is the inverse — reality says carried, the config string
says not — and it fired anyway. The `stale` branch already gets this right by
asking git via `check-ignore --no-index`; only `dropped` reverted to string
equality, and `_carrier_representative()` already did the resolution needed.

An entry now counts as dropped only once git agrees nothing under it is carried.
Same exit-code contract as the stale branch: 1 = not ignored = covered, 0 =
ignored = genuinely dropped, anything else = a failed measurement that keeps
being reported rather than counting as covered.

An entry with nothing materialized yet stays reported. There is no outcome to
measure, but the divergence is real and becomes silent data loss the moment the
first file lands under it — pinned by its own test.

One existing fixture had to be corrected, not just accommodated:
`test_the_KNOWN_UNSAFE_entry_is_reported_but_never_recommended` built an exclude
that UN-IGNORED `state/current-track.md` while asserting the entry was dropped.
By outcome it was carried. It only passed because the branch never looked at
disk; once the branch asks git, the fixture stopped describing the state it
claimed to test.

    parent (#2570 head) : 21 tests, FAILED (failures=1)  <- broader-entry witness
    HEAD                : 21 tests, OK
    66 suites referencing health-check / git_binary : 66 pass, 0 fail
    live host           : WARN (unchanged by this PR; #2570's verdict)

Stacked on #2570 — merge that first.

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(health-check): require ALL matches covered, and never let a failed measurement erase findings

Both P1s from qingyun-wu on d7f671e. Both real, both mine.

P1-1 — first-hit sampling is not coverage. `_carrier_representative()` returned
the FIRST glob match, so a shipped wildcard matching several paths was judged on
one sampled witness. A narrower local rule (`hosts/a/`) covers one child of
`hosts/*/` while its siblings stay ignored, and a sample landing on the covered
child reported the vault healthy with a whole host subtree unbacked.

New `_carrier_representatives()` returns every materialized match and the
coverage test now requires all of them. The single-witness helper stays for the
stale branch, where one witness genuinely settles the question — that branch asks
whether the entry THIS host configured is in effect, not whether one entry covers
another. Enumeration is bounded by construction: glob matches are the entry's own
direct matches, never a recursive walk of their contents.

P1-2 — the code said one thing and did the other. A comment promised a failed
measurement "keeps being reported"; the enclosing `except` reset `dropped = []`,
so one entry's git timeout erased findings already collected and returned ok.
Fail-OPEN in the probe whose whole purpose is catching silent non-backup. The
config-read except is now narrow and does not wrap the measurement loop; per-entry
failures land in `unmeasured`, which the verdict already treats as UNKNOWN.

Worth recording how the first fix failed. My two-host fixture used `!hosts/a/`
under a leading `*`, but gitignore cannot re-include a path whose PARENT is
excluded — so BOTH hosts were ignored, first-hit sampling landed on an ignored
path, and the test PASSED at the broken head for the wrong reason. It only
became a witness after adding `!hosts/`. That is the same defect class I had
just corrected in an existing fixture one commit earlier, committed by me, in
the test written to prove I had fixed it.

    both controls at reviewed head d7f671e : FAILED (failures=2)
    HEAD                                    : 23 tests, OK
    66 suites referencing health-check / git_binary : 66 pass, 0 fail
    py_compile clean; review-checks PASS; live host WARN (unchanged)

Still stacked on #2570 — merge that first.

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(health-check): cover the odd-exit-code half of a failed coverage measurement

Diff coverage fell to 93.8% after the rebase onto main — `src/health-check.py:1303-1304`.
The rebase dropped #2570's lines out of the diff, so the denominator changed and
these two surfaced as the only uncovered pair.

They are the branch where check-ignore RAN and could not answer: an exit code
that is neither 0 (ignored) nor 1 (not ignored), e.g. 128 on a broken repo. The
existing control makes the call RAISE, which is a different line.

Worth its own case rather than a coverage formality: the tempting shape is
`if rc != 0: covered`, which folds 128 in with 1 and reports a path as carried
on the strength of a failed read — the exact defect this probe exists to catch,
and one #2566 shipped in the stale branch before review caught it.

    93.8% (missing 1303-1304) -> 100% (0 of 32)
    28 tests OK; witness confirmed: rc 128 -> status fail, names the entry as
      "could NOT measure", never as covered
    66 suites referencing health-check / git_binary / the alert helpers: pass
    review-checks PASS

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(health-check): a carrier DIRECTORY being un-ignored says nothing about the files in it

john-the-dev and qingyun-wu, independently, on a958d06. Reproduced before
fixing:

    exclude: *  !hosts/  !hosts/*/          (note: no !hosts/**)
    check-ignore --no-index hosts/a                  -> 1  not ignored
    check-ignore --no-index hosts/a/current-track.md -> 0  IGNORED
    probe -> ok, "all 1 configured carrier path(s) un-ignored in the vault"

A false GREEN in the probe whose only job is catching silent non-backup: the
directory is un-ignored, the carrier file beneath it is not, and health said fine.

A directory representative is now probed through the FILES beneath it, sorted for
determinism and bounded by `_PROBE_FILES_PER_DIR`; any ignored one condemns the
entry. The bound is honest about direction — past the cap this UNDER-reports,
never over-reports, the same direction as the no-network ref staleness.

Fixed in BOTH branches, not just the reported one. The stale branch probes a
directory representative too and had the identical blindness; it now shares
`_carrier_target_verdict`. That is the third time in this PR chain that fixing
the reported instance would have left the same defect one step to the left, so
this time the instrument is shared rather than duplicated.

An instrument I tried and REJECTED, recorded because the suite caught it:
`ls-files --others --ignored` answers the directory question in one exact call
with no cap — but it is index-AWARE, so it calls a tracked file carried. That
silently reverses `test_a_TRACKED_file_with_a_stale_exclude_is_still_reported`,
which exists because a host that carried a file once and then let its exclude go
stale read healthy forever while nothing NEW under that entry was carried. I
wrote the ls-files version first, that test failed, and it was right. `--no-index`
stays load-bearing.

    dir un-ignored / file ignored     -> fail  (was ok)
    same tree with !hosts/** (control) -> ok
    witness at a958d06 : FAILED (failures=1)
    HEAD                : 30 tests, OK
    67 suites referencing health-check / git_binary : 67 pass
    live host           : WARN alerting=False (unchanged)

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(health-check): cover the bound, the empty directory, and the non-file representative

Diff coverage 94.5% — src/health-check.py:1164,1170,1209, all three real
branches of the new directory probe rather than coverage filler:

  1170  the CAP being hit. An unenforced cap is just a slow unbounded walk, so
        the test pins the bound itself and that the sample is SORTED — an
        unsorted walk would make the probe's verdict depend on filesystem order.
  1209  an empty carrier directory -> carried. `hosts/<label>/` can exist with
        nothing under it yet; inventing a failure there would fail a host that
        has done nothing wrong. The genuinely-absent case is reported by the
        callers instead.
  1164  a representative that is neither file nor directory (dangling symlink)
        -> no targets, so git is never asked about a path that is not there.

    94.5% (missing 1164,1170,1209) -> 33 tests, OK

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(health-check): the stale branch judged one host and called the wildcard carried

john-the-dev and qingyun-wu, independently, on cf059ca. Reproduced before fixing:

    resolved == shipped == ["hosts/*/"]        (so this is the STALE branch)
    exclude carries hosts/a's files, not hosts/b's

    hosts/a/current-track.md  check-ignore rc = 1
    hosts/b/current-track.md  check-ignore rc = 0
    _carrier_representatives -> ['hosts/a', 'hosts/b']
    _carrier_representative  -> 'hosts/a'
    verdict -> ok, "all 1 configured carrier path(s) un-ignored in the vault"

Host b's subtree ignored and unbacked, health green.

Why this survived the last round, which is the part worth recording: coverage has
TWO independent axes — WHICH paths you probe, and WHAT you ask about each. Last
round I shared the directory-vs-file instrument (`_carrier_target_verdict`)
across both branches and called it fixing the class. It fixed one axis. The
stale branch was still singular on the other, so `hosts/*/` collapsed to its
first match. Generalizing one axis reads exactly like generalizing the defect.

Both branches now enumerate every `_carrier_representatives()` match and share
the same per-target instrument.

    same-config two hosts, partially carried -> fail  (was ok)
    same-config two hosts, fully carried     -> ok    (control: the fix must not
                                                       fail every wildcard)
    host b unmeasurable                      -> not ok (fail-closed preserved:
                                                       enumerating must not
                                                       weaken the contract)

    both witnesses at cf059ca : FAILED (failures=2)
    HEAD                       : 36 tests, OK
    67 suites referencing health-check / git_binary : 67 pass
    live host                  : WARN alerting=False (unchanged)

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(health-check): sample the carrier subtree exhaustively, not the first 25 files

john-the-dev on e146c2b. The directory probe took the first 25 files and a
comment admitted it could "UNDER-report past the cap". That treated a stated
caveat as an acceptable conclusion. Their counterexample: 26 files with only the
26th ignored returned `ok`, so a stale exclude left a real carrier file unbacked
while health certified the subtree. In a probe whose entire purpose is catching
silent non-backup, a sample is not proof of coverage.

I did NOT take the suggested remedy — returning `unmeasured` when the count
exceeds the bound — because it satisfies their first control and breaks their
second: all 26 carried would report unmeasured rather than ok. Failing closed on
COUNT trades a false green for a permanent unknown on any large carrier
directory, and `notes/` on this host already holds 365 files.

The cap existed for COST, not correctness, so the fix removes the cost instead:
one `check-ignore --no-index --stdin` call per representative answers for every
file at once. Exhaustiveness now costs a filesystem walk plus a single process,
not N processes.

    26 files, 26th ignored -> fail   (was ok)
    26 files, all carried  -> ok     (the control the count-based fix breaks)
    26 files               -> exactly ONE check-ignore call, pinned, so a future
                              refactor cannot quietly restore the per-file loop
                              and with it the pressure to sample

One existing control had gone stale with the instrument and I nearly shipped it:
the fail-closed test stubbed on `argv`, but paths travel via STDIN now, so the
stub stopped matching and the test passed WITHOUT EVER FIRING. Re-keyed on the
input and verified the stub actually fires (1 invocation) rather than trusting
the green.

    3 witnesses at e146c2b : FAILED (failures=3)
    HEAD                    : 38 tests, OK
    67 suites referencing health-check / git_binary : 67 pass
    live host               : WARN alerting=False, exhaustive walk under 1s

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(health-check): two stubs stopped firing when the instrument changed

Found while chasing the last uncovered line. Both measurement-failure controls
keyed their stub on `argv`, but paths travel through `check-ignore --stdin`
now — so neither stub matched, and both tests kept passing because `data/` is
dropped for an ordinary reason anyway.

    stub fired 0x -> status fail   (the assertion it makes is true regardless)

Vacuous, and silently so: nothing in the suite could tell the difference between
'the unmeasured path works' and 'the unmeasured path was never entered'. That is
the same shape as a probe that reports healthy because it never looked, which is
the defect this entire PR chain is about — reproduced inside its own tests.

Re-keyed on the input, and both now ASSERT THE STUB FIRED before asserting
anything else, so they cannot go quietly vacuous again. Also tightened both to
require 'could NOT measure' in the detail rather than merely != ok — otherwise an
ordinary drop satisfies them.

    coverage 98% -> 100% (the dropped branch's unmeasured line was unreachable
    precisely because the stub never fired)
    38 tests OK; 67 suites pass

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(health-check): NUL-delimit the batch — a filename can contain a newline

john-the-dev on 73a9167. Reproduced before fixing, on a tree built so the two
answers genuinely differ:

    notes/z\nx.md         check-ignore -q -> 0   IGNORED, not backed up
    split halves          notes/z -> 1, x.md -> 1   both un-ignored
    newline-joined batch                  -> 1   reads CARRIED — false green
    NUL-joined with -z                    -> 0   dropped — correct

Batching with `"\n".join(...)` split one real path into two bogus ones, and
because both halves were un-ignored the genuinely ignored carrier file read as
carried. NUL is the only delimiter a POSIX filename cannot contain, so the fix
is the delimiter itself rather than an escaping scheme that would have to be
right forever.

Note the shape: making the probe exhaustive (removing the 25-file cap) is what
introduced the batch, and the batch is what introduced the delimiter assumption.
Each fix was correct and each created the next seam — which is the argument for
the reviewer's insistence on an explicit witness per round rather than trusting
that the previous round settled the class.

The regression ASSERTS ITS OWN FIXTURE first — real file ignored, both halves
un-ignored — because a gitignore nuance could otherwise make it pass without
ever discriminating the two answers, which is the failure mode I shipped twice
today in other tests.

    witness at 73a9167 : FAILED (failures=1)
    HEAD                : 39 tests, OK
    67 suites referencing health-check / git_binary : 67 pass
    live host           : WARN alerting=False (unchanged)

Stand: Echo Act IV Mini
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
sonichi added a commit that referenced this pull request Aug 5, 2026
Both sides added a new function at the same insertion point in
`src/health-check.py`: `_local_core_alive` here, `_alerts_suppressed` on main
(#2570). They are independent, so the resolution keeps both.

Verified past conflict-free, since a clean merge is not a correct merge:
  - both functions present exactly once; main's has all 5 of its call sites
  - tests/health-check-recover-core.test.py — all cases incl. the new r and s
  - main's own suites for the function it added: summary-warnings,
    notify-slack, emit-task — all pass, so `_alerts_suppressed` is not
    regressed by the union
  - the revert control still bites after the merge: reverting the actuator
    default fails case s

Stand: Echo Act IV Pro
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.

3 participants