diff --git a/PILOT.md b/PILOT.md index 43ebea0..c31ef42 100644 --- a/PILOT.md +++ b/PILOT.md @@ -1867,3 +1867,8 @@ A full audit accounted for every VCR identifier used by the acceptance and task Version 0.1.23 is a packaging release, not an acceptance result. It publishes the explicit strategy, test-order and triage commands, diagnostic-cost metadata, the typed-decision cache and five opt-in skills; per-change details and boundaries are in CHANGELOG.md. Pre-publication evidence at the release revision: 858 offline tests pass on Linux, `git diff --check` is clean, the exact-tree wheel and sdist pass `tools/check_distribution.py`, and the registry was rechecked (latest published 0.1.22; 0.1.23 unused). Hosted CI and the tag-gated publish workflow remain the merge and release gates. This release claims no speed, quality, cost, host-delivery or platform benefit; the frozen cohorts remain censored or mixed and fresh Desktop delivery stays unverified. Published receipts: PR #310 passed hosted CI and squash-merged as `d5b4772`; tag-gated publish run 36792180928 passed the suite, build, distribution check and Trusted Publishing; a fresh registry pipx install of published 0.1.23 passed `doctor` with both advisory hooks and a keyless local triage smoke with a diagnostic cost resolved locally (no remote call). Artifact digests are recorded in TASKS.md VCR410. These validate packaging, publication and local setup only; they do not measure advice delivery or task benefit, and overall acceptance remains not established. + + +## VCR411 — Doctor strategy-advice variant fix (diagnostics only) + +Doctor previously reported the `UserPromptSubmit` advisor as unregistered whenever the supported `--strategy-advice` opt-in appended its flag to the hook command, failing its own installer output. The check now also accepts that exact product variant. No hook, advice, privacy or acceptance behavior changed; one regression test plus the full suite pass locally, and hosted CI remains the merge gate. diff --git a/TASKS.md b/TASKS.md index 57914f1..5dda01e 100644 --- a/TASKS.md +++ b/TASKS.md @@ -1271,3 +1271,8 @@ A full audit accounted for every VCR identifier across both evidence ledgers. Tw Version 0.1.23 publishes the three explicit coding-decision commands with their local-routing and provenance features, caller-verified diagnostic costs, the opt-in typed-decision cache, and the fifth bundled skill; `pyproject.toml` and the package `__version__` are bumped to 0.1.23 and CHANGELOG.md carries the full change list. Pre-publication local evidence at the release revision: the full offline suite passes (858 tests, Linux), `git diff --check` is clean, `uv build` produced the wheel and sdist, and `tools/check_distribution.py` passed after its expected bundled-skill set was corrected to enumerate `bundled_skills/*/SKILL.md` (five skills) instead of a stale two-name list. The PyPI registry was rechecked before the release claim: the latest published version is 0.1.22 and 0.1.23 is unused. Hosted CI on the release pull request is required before merge; the publish workflow rebuilds from the exact tag, reruns the suite and distribution check, and publishes through Trusted Publishing. No trial was run; no efficacy, delivery or platform claim is made. Release receipts: PR #310 passed hosted CI and squash-merged as `d5b4772`; annotated tag `v0.1.23`. Tag-gated publish run 36792180928 passed the version/tag check, the full Python 3.11 suite, `python -m build`, `tools/check_distribution.py` and Trusted Publishing. PyPI 0.1.23 reports wheel SHA-256 `5293e33c3879129756712132bae42aa8955884f3bc2c289984c71ffc2e87c87b` and sdist SHA-256 `bacbe3a3ac8d657802dc197155f41de561aea0ea45af26d2246e51d778135f6f`. A fresh registry pipx install of published 0.1.23 (Python 3.14.7) reported `JevCompass 0.1.23`, passed `doctor` with both advisory hooks and no PreToolUse gate, and a keyless local triage smoke with `--diagnostic-cost` returned `no-remote-choice` with `local_abstention`. The GitHub release publishes the same notes. This is packaging, publication and local-setup evidence only; no task-benefit claim is made. + + +## VCR411 — Doctor recognizes the strategy-advice prompt hook variant + +`jevcompass install --strategy-advice` registers the `UserPromptSubmit` advisor with a `--strategy-advice` argument, but `doctor` compared the registered command against the plain form only and reported the hook as unregistered — failing its own supported opt-in configuration. Doctor now additionally accepts the product prompt hook carrying the installer's strategy-advice flag; `SubagentStart` and spawn validation are unchanged. One focused regression test covers the strategy variant, and the full 859-test suite passes locally with `git diff --check` clean. Hosted CI remains required before merge. This is a diagnostics correction only; no hook, advice or acceptance behavior changed. diff --git a/src/jevcompass/cli.py b/src/jevcompass/cli.py index 5f46d28..f1f496a 100644 --- a/src/jevcompass/cli.py +++ b/src/jevcompass/cli.py @@ -17,6 +17,7 @@ from .installer import ( SPAWN_ADVICE_MATCHER, SUBAGENT_MATCHER, + _has_strategy_advice_flag, _is_legacy_gate, _is_product_hook, install, @@ -171,6 +172,8 @@ def doctor(*, test_jev: bool = False) -> dict[str, Any]: registered = [] for event_name in ("UserPromptSubmit", "SubagentStart"): if any(handler.get("command") == hook_command + or (event_name == "UserPromptSubmit" + and _has_strategy_advice_flag(handler.get("command"))) for group in hooks.get(event_name, []) if event_name != "SubagentStart" or group.get("matcher") == SUBAGENT_MATCHER for handler in group.get("hooks", [])): diff --git a/tests/test_doctor.py b/tests/test_doctor.py index 8273cf1..4cb7f5e 100644 --- a/tests/test_doctor.py +++ b/tests/test_doctor.py @@ -27,6 +27,25 @@ def test_cli_version_matches_project_metadata(self): self.assertEqual(ended.exception.code, 0) self.assertEqual(output.getvalue().strip(), f"JevCompass {expected}") + def test_doctor_recognizes_strategy_advice_prompt_hook_variant(self): + with tempfile.TemporaryDirectory() as directory: + codex_home = Path(directory) / "codex" + codex_home.mkdir() + (codex_home / "hooks.json").write_text(json.dumps({"hooks": { + "UserPromptSubmit": [{"hooks": [{ + "command": f"{advisor.hook_command()} --strategy-advice", + }]}], + "SubagentStart": [{"matcher": SUBAGENT_MATCHER, "hooks": [{"command": advisor.hook_command()}]}], + }})) + with mock.patch.dict(os.environ, {"CODEX_HOME": str(codex_home)}), \ + mock.patch.object(cli, "model_status", return_value="available"), \ + mock.patch.object(cli, "catalog_snapshot", return_value=([{"id": "exec_command"}], {"curated_entries": 1, "available_tools": 1, "available_skills": 0, + "configured_mcp_servers": 0, "discovered_skills": 0, "unavailable_entries": 0})): + result = cli.doctor() + self.assertEqual(result["hooks_json"]["registered_advisory_hooks"], + ["UserPromptSubmit", "SubagentStart"]) + self.assertTrue(result["hooks_json"]["ok"]) + def test_doctor_separates_registered_hooks_from_safe_observed_metric(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory)