Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .console/backlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,44 @@

_Durable work inventory. Update after each meaningful chunk of progress._

## Up Next

### Push Custodian 5ef3f0f, or the Windows find_tool fix stays unpinnable
- `5ef3f0f fix(adapters): make find_tool's venv-first preference work on Windows`
exists only in the local Custodian checkout (branch `claude/reconcile-june-2026-08-03`,
upstream gone). `origin/main` is at `7a780b7`, which OC now pins.
- Until it is pushed, a Windows Custodian run resolves linters off PATH rather than
a venv. That produced 1222 phantom ruff findings on 2026-08-03 (ruff 0.16 default
rule set vs OC's pinned 0.15.13) before the local checkout picked the commit up.

## Done

### 2026-08-03: Clear the 10 vulture findings blocking the pre-push gate (✅ COMPLETE)
- **Objective**: Re-enabling vulture left 10 genuine findings holding
`custodian-multi --fail-on-findings` RED. Resolve them by removing dead code, not
by whitelisting.
- **Status**: ✅ COMPLETE — gate clean at 0 findings, exit 0.
- **Correction**: the earlier claim that "`layers` and `full` in the same command ARE
read" was wrong — `cmd_observe_and_validate` reads only `quiet`. Those names escaped
the report because vulture matches on bare NAME and they are used by other commands.
The real scope was four stub commands whose entire option lists were ignored while
`--help` and two user guides advertised them.
- **Changes**:
- `observer/cli.py` — stripped `observe-and-validate`, `compare`, `import`, `cleanup`
to `--quiet` only; the planned interface stays in `docs/design/STAGE0_CLI_SPECIFICATION.md`.
Deleted `list --filter` (could never work — nothing caches a validation status).
Fixed `cleanup` exiting EXIT_SUCCESS while deleting nothing (not a vulture finding;
same fail-open shape, found while editing).
- `pr_review_watcher/main.py` — removed `pending_checks` from two functions + 16 call
sites; neither body read it.
- Docs — both user guides relabelled to "Planned Options (not accepted today)";
removed the runnable `cleanup` examples; corrected the spec's `list` line.
- New test `test_unimplemented_stubs_reject_planned_flags` pins that stubs REJECT
planned flags rather than swallowing them.
- **Verification**: vulture reports nothing; `custodian-multi --fail-on-findings` exits 0;
ruff clean; full suite 10345 passed with the same 6 pre-existing sandbox/timing
failures, each reproduced on an unmodified checkout. `.vulture_whitelist.py` unchanged.

### 2026-07-15: Stage 4 — Refactor existing code to use the new shared helper (✅ COMPLETE)
- **Objective**: Independently re-verify Stage 2's migration against the "refactor existing
code" acceptance bar (identified/updated all relevant callsites, replaced redundant
Expand Down
140 changes: 140 additions & 0 deletions .console/log.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,143 @@
## 2026-08-04 — fix(ci): bump the audit workflow's Custodian pin in lockstep with pyproject

`.github/workflows/custodian-audit.yml` hardcodes its OWN Custodian SHA, separate
from pyproject's, and its comment explicitly requires the two move together. The
vulture fail-open fix bumped pyproject d6ba8ab -> 7a780b7 but missed the
workflow, so CI would have kept installing the old adapter — leaving the
fail-open alive in the one place it matters most, the required `audit` gate.

This also explains an observation in #492, which landed on main today: it noted
"the Custodian audit reported 1222 findings (the ruff group alone — vulture was
clean in CI)". Vulture WAS installed in CI. It was not clean: on d6ba8ab the
adapter builds `vulture <src> --min-confidence=N <tests>`, an argument order
vulture's argparse rejects (exit 2, empty stdout), and the empty output was read
as "no dead code". This repo had 621 findings at vulture's default confidence
the whole time. Independent corroboration of the fail-open from a different
author on a different day.

Also dropped the workflow's unpinned `pip install vulture`. vulture is a dev
dependency now, so `.[dev]` pins it (2.16) beside ruff and ty — removing the
moving part rather than relocating it, which is exactly the argument #492's own
comment makes one level down about ruff.

## 2026-08-03 — fix(observer): retire the CLI flags the gate's vulture pass exposed

Follow-up to closing the vulture fail-open earlier today. That left 10 genuine
findings holding the pre-push gate red; this clears them. Gate is now clean at
0 findings under a custodian that actually runs vulture (it reported 621 before).

Correction to the earlier write-up, which claimed "`layers` and `full` in the
same command ARE read, so parameter-usage detection is working". That was wrong.
`cmd_observe_and_validate`'s body reads ONLY `quiet` — `layers` and `full` are
equally unread there. They escaped the report because vulture matches on bare
NAME and those names are used by other commands in the tree. The real finding
was bigger than 8 stray flags: FOUR commands (`observe-and-validate`, `compare`,
`import`, `cleanup`) are stubs whose entire option lists are ignored, and
`--help` plus two user guides advertised them as though they worked.

Decision per flag, per the "implement or delete" bar:

* The four stubs are documented as PLANNED (`docs/design/STAGE0_CLI_SPECIFICATION.md`
§"Secondary Commands (Planned Future)"; both user guides carry "not yet
implemented" notes). So deleting the commands was wrong — but so was keeping
parameters they discard. Stripped each stub to `--quiet` only. The planned
interface stays in the spec, which is where a design belongs; a half-declared
signature that typer advertises in `--help` is not a spec, it is a promise the
command breaks. Deleting `import`'s required input path is deliberate: taking
a file and dropping it is indistinguishable from importing it and failing.
* `list --filter valid|invalid` — deleted. It could never have worked: the
listing walks snapshot directories and never loads or caches a validation
status to filter on (its observed_at column is a literal "—"). Implementing it
needs the caching layer the help text presumed, not a flag.

Also fixed while in `cmd_cleanup`, and NOT one of the vulture findings: it
exited EXIT_SUCCESS while deleting nothing. A scheduled `cleanup --days 30`
therefore reported success and silently retained every snapshot forever, with no
way for the caller to tell a working cleanup from a stub. Now exits non-zero.
Same fail-open shape as the vulture bug itself — a green signal that means
nothing — which is why it was worth fixing rather than leaving for later. The
guide's two runnable `cleanup` examples were removed; the option tables in both
guides are relabelled "Planned Options (not accepted today)".

`pending_checks` removed from `_update_check_history` and `_should_escalate_ci_wait`
in pr_review_watcher, plus 16 call sites. Neither body ever read it. Note the
tests passed `pending_checks=["audit"]` in two places, implying behaviour that
could not exist — those assertions were passing for the wrong reason.

New test pins the intent: `test_unimplemented_stubs_reject_planned_flags` asserts
each stub REJECTS the planned flags rather than swallowing them, so nobody
re-adds an ignored option without a failing test.

Verification: `vulture src tests .vulture_whitelist.py --min-confidence=80`
reports nothing; `custodian-multi --fail-on-findings` exits 0 (clean); ruff
check/format clean; full suite 10345 passed with the same 6 pre-existing
sandbox/timing failures, each reproduced on an unmodified checkout. Nothing was
added to .vulture_whitelist.py — every finding was resolved by removing the dead
code, not by suppressing the report.

## 2026-08-03 — fix(custodian): close the vulture fail-open in the pre-push gate

The pre-push Custodian gate reported "0 findings, clean" on this repo while a
Windows box running a newer Custodian reported hundreds. Windows was the correct
side; the green gate was a FALSE CLEAN, and had been for as long as the pin has
been in place.

Three things had to line up to hide it:

1. `.custodian/config.yaml` sets `tools.vulture: true` — the detector is meant
to run.
2. `pyproject.toml` never declared `vulture` in the dev extra, so
`uv pip install -e .[dev]` never installed it. The fleet venv has no vulture
and none is on PATH.
3. The custodian pin `d6ba8ab` PREDATES Custodian 261bbb5, "fix(vulture): put
paths before options, and stop reading a failed run as clean". On that pin
the adapter built `vulture <src> --min-confidence=N <tests>`, which vulture's
argparse rejects — exit 2, empty stdout — and the empty output was read as
"no dead code".

So even had vulture been installed, the pinned adapter could not have produced a
finding: the invocation itself was malformed and the failure was swallowed. The
detector has never once run. Fixed by bumping the pin to 7a780b7 (origin/main,
contains 261bbb5) and declaring `vulture==2.16` alongside the existing ruff/ty
pins. The two must land together — after 261bbb5 a missing vulture fails LOUDLY,
so bumping the pin alone would red the gate on "vulture not found".

Threshold set explicitly to `tools.vulture_min_confidence: 80`. Custodian's
adapter registry falls back to 60 while its own config loader documents 80 as
the intended default; relying on whichever wins is how this stays surprising. On
this repo the difference is stark: 60 yields 621 findings (essentially all
UNUSED_METHOD/attribute heuristics), 80 yields 32, every one at 100% confidence.

Of those 32, 22 are names an external contract forces us to accept — the
`__exit__` protocol, pytest's `pytest_sessionfinish` hookspec, fixtures
requested purely for a side effect, lambda stubs that must mirror the callee
they replace — plus two compat shims the source already documents as deliberate
(`max_rewrite_attempts` carries `# noqa: ARG002 — kept for signature compat`,
`queue_threshold` carries `# kept for config compat, not used in logic`). Those
are listed in a new `.vulture_whitelist.py`, which Custodian's adapter picks up
automatically when present. The whitelist matches on bare NAME, not location, so
it is kept minimal and each entry carries its justification.

The remaining 10 are real and are deliberately NOT whitelisted:

* `observer/cli.py` ×8 — `--format`, `--skip-validation`, `--output`,
`--filter-status`, `--signals-only`, `--input`, `--validate-after`, `--keep`
are declared as typer options and never read. `layers` and `full` in the same
command ARE read, which is what makes these stand out rather than look like a
vulture blind spot. Passing `--format yaml` today silently yields JSON.
* `pr_review_watcher/main.py:2508,2543` — `pending_checks` parameter threaded
through two call sites and never used.

CONSEQUENCE, stated plainly: merging this turns the gate red on those 10 until
they are triaged. That is the intended effect — the gate was previously green by
accident. Deciding whether each observer flag should be wired up or deleted is
product work and is not guessed at here.

Also found, not fixable from this repo: the Custodian commit that makes
`find_tool` prefer a venv on Windows (5ef3f0f) exists only in the local checkout
and was never pushed, so it cannot be pinned. Without it a Windows run resolves
linters off PATH; that cost 1222 phantom ruff findings earlier today until the
local checkout picked the commit up mid-session.
## 2026-08-03 — fix(hooks): pre-push resolved the wrong workspace root inside a git worktree

`.hooks/pre-push` locates the boundary disclosure artifact by globbing sibling
Expand Down
6 changes: 6 additions & 0 deletions .custodian/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,12 @@ maintenance:
tools:
ruff: true
vulture: true
# Custodian's adapter registry falls back to 60 when this is unset, but its
# own config loader documents 80 as the intended default — so set it here
# rather than inherit whichever wins. On this repo 60 yields 621 findings,
# essentially all UNUSED_METHOD/attribute heuristics; 80 yields 32, all at
# 100% confidence. Raise deliberately, not by accident.
vulture_min_confidence: 80
# AI3 is now expressed as a Semgrep rule (no Python AST walker).
# See .custodian/rules/semgrep/ai3_no_directory_scanning.yaml.
semgrep:
Expand Down
16 changes: 13 additions & 3 deletions .github/workflows/custodian-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,23 @@ jobs:
# emitting a phantom finding fleet-wide despite OC's `r1_enabled: false`,
# red-failing the audit on every repo between two PRs. Bump this SHA in
# lockstep with pyproject when intentionally adopting newer detectors.
#
# Bumped d6ba8ab -> 7a780b7 in lockstep with pyproject, as this comment
# requires. d6ba8ab predates Custodian 261bbb5, which fixed the vulture
# adapter building `vulture <src> --min-confidence=N <tests>` — an order
# vulture's argparse rejects (exit 2, empty stdout) that was then read as
# "no dead code". That is why this gate has reported vulture clean while
# vulture was installed: the run failed every time and the failure was
# swallowed. Same vacuous-green failure mode the step below warns about.
run: |
python -m pip install --upgrade pip
pip install "custodian[tools] @ git+https://github.com/ProtocolWarden/Custodian.git@d6ba8ab245c6f4e79e9f8fffd4e4221bfaf266e8"
pip install vulture
pip install "custodian[tools] @ git+https://github.com/ProtocolWarden/Custodian.git@7a780b7845337810a235111e378e78fb06361dd5"

- name: Install repo and its pinned lint toolchain
# `.[dev]` (not plain `.`) so the adapters run OC's OWN pinned ruff/ty.
# `.[dev]` (not plain `.`) so the adapters run OC's OWN pinned ruff/ty/vulture.
# vulture used to be installed unpinned in the step above — the same moving
# part this comment objects to. It is a dev dependency now, so `.[dev]`
# pins it (2.16) alongside ruff and ty.
# The reproducibility argument in the step above applies one level down:
# pinning Custodian while installing `ruff` unpinned just moves the moving
# part. It floated to 0.16.1 and this gate reported 1222 findings against a
Expand Down
49 changes: 49 additions & 0 deletions .vulture_whitelist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (C) 2026 ProtocolWarden
"""Vulture whitelist — names vulture reports as dead that a signature requires.

Custodian's vulture adapter passes this file as an extra scan path when it
exists (see `custodian/adapters/vulture.py`), so every name referenced here
counts as used. Vulture flags unused *parameters* at 100% confidence, which is
correct for genuinely dead code but wrong whenever an external contract dictates
the signature — a protocol method, a pytest hook, a fixture requested purely for
its side effect, or a lambda that must mirror the callee it replaces.

Scope discipline: this file is for names a contract forces us to accept, plus
compat shims the source itself already documents as deliberate. Genuinely unused
parameters are NOT listed here — they are left visible so the gate reports them.

Vulture matches on the bare NAME, not the location, so an entry suppresses that
identifier repo-wide. Keep the list minimal and justified for that reason.
"""

# --- Language / stdlib protocols ------------------------------------------
# `__exit__(self, exc_type, exc_val, tb)` — the context-manager protocol fixes
# the signature; this implementation only needs to release the lock.
exc_val # noqa: B018 # src/operations_center/audit_dispatch/locks.py:103

# --- pytest hook specifications -------------------------------------------
# `pytest_sessionfinish(session, exitstatus)` — pytest calls hooks by keyword
# against its own hookspec, so the parameter must exist whether or not it is read.
exitstatus # noqa: B018 # tests/conftest.py:301, observer/pytest_flaky_plugin.py:91

# --- pytest fixtures requested for their side effects ---------------------
# Naming the fixture in the signature is what activates it; the body has no
# reason to reference the value.
valid_console_dir # noqa: B018 # tests/unit/detectors/test_r{1,2}_console_*.py
no_cl_env # noqa: B018 # tests/unit/execution/test_coordinator_cl_wrap.py:78
monkeypatch_modules # noqa: B018 # tests/unit/execution/test_workspace_cov.py:695

# --- Test doubles that must mirror the signature they replace -------------
# monkeypatch/lambda stubs are called with the real callee's arguments, so they
# have to accept them even when the stub ignores them.
lg # noqa: B018 # lambda pm, rr, lg — test_repo_graph_factory_cov.py
indent # noqa: B018 # lambda indent=2 mirroring model_dump_json — *_cov.py
expected_kind # noqa: B018 # def _raise(_path, expected_kind=None) — graph_doctor

# --- Compat shims the source already documents as deliberate --------------
# Both carry an in-source comment stating the parameter is retained on purpose
# to avoid churning callers; one already suppresses ruff ARG002. Listed here so
# the two linters agree rather than one of them staying permanently red.
max_rewrite_attempts # noqa: B018 # spec_author/phase_orchestrator.py:141
queue_threshold # noqa: B018 # spec_author/trigger.py:26
4 changes: 3 additions & 1 deletion docs/design/STAGE0_CLI_SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,9 @@ operations-center-observer-snapshot observe-and-validate \
#### `list`
List available snapshots in storage
```bash
operations-center-observer-snapshot list [--filter recent|all] [--format table|json]
operations-center-observer-snapshot list [--limit N] [--order recent|oldest|name] [--format table|json]
# Note: `list` is implemented. A --filter flag was declared but
# never worked (no validation status is cached to filter on) and was removed.
```

#### `compare`
Expand Down
15 changes: 3 additions & 12 deletions docs/user-guides/CLI_QUICK_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ operations-center-observer-snapshot list [OPTIONS]
```bash
--limit N Max snapshots to list [default: 10]
--order ORDER Sort order (recent|oldest|name) [default: recent]
--filter STATUS Filter (valid|invalid)
--format FORMAT Output format (table|json|csv) [default: table]
--storage-root PATH Storage directory
--verbose, -v Include file size, checksum
Expand Down Expand Up @@ -223,22 +222,14 @@ operations-center-observer-snapshot export snapshot-id export.jsonl
operations-center-observer-snapshot cleanup [OPTIONS]
```

### Options
**Not yet implemented.** This command accepts only `--quiet`; it exits non-zero without doing any work. The planned options below are a design target recorded in `docs/design/STAGE0_CLI_SPECIFICATION.md`, not flags the CLI currently accepts.

### Planned Options (not accepted today)
```bash
--days N Delete snapshots older than N days [default: 30]
--keep-count N Keep at least N most recent [default: 50]
--dry-run/--no-dry-run Preview changes (default: true)
--storage-root PATH Storage directory
--quiet, -q Minimal output
```

### Examples
```bash
# Preview cleanup
operations-center-observer-snapshot cleanup --days 30 --keep-count 50

# Actually delete (not dry-run)
operations-center-observer-snapshot cleanup --days 30 --keep-count 50 --no-dry-run
```

---
Expand Down
Loading
Loading