feat(adapters): systematic-debugging scenario pack - #254
Conversation
6c7e135 to
9316a17
Compare
|
Thanks for the re-review. Addressed the ordered-sequence concern (commit 9316a17):
Note: I added an opt-in --compare-baseline real-harness run (score delta of the candidate skill vs the same scenario without it), but I could not execute it here - this PR was developed without an authenticated Claude/CLI on a POSIX host. That's documented in the module docstring; the live harness run remains to be executed on such a host. |
|
Re-reviewed In if mt != last.get(str(p), mt):
last[str(p)] = mtFor an unseen path the comparison is always The shipped suite is green (1516 passed, 9 skipped), because the order tests hand-write audit events rather than exercising the watcher that produces them. Please initialize/update the per-path snapshot on every scan, explicitly define handling of new source files, and add a watcher-to-judge integration regression. Also put watcher shutdown/join in |
|
Yifan Yang (@Yif-Yang) — fixed on
|
|
Thanks for The production watcher still cannot establish the claimed event ordering reliably. I added deterministic watcher-to-judge tests using real on-disk source edits and controlled scheduling between scans, with the same result-line format emitted by the pytest shim:
Both negative assertions fail; the valid Please tie edit evidence to synchronized source snapshots/test invocation boundaries, reconcile the final source state before scoring, and fail closed when the required order cannot be established. Add these producer-to-judge cases rather than only hand-writing already-correctly-ordered edit logs. Reducing the poll interval alone does not establish the invariant. This is a remaining correctness blocker for the new ordered-process score, not post-merge cleanup. Please also refresh the PR description: it still says the change does not touch the evidence machinery, but it now adds and changes the event producer and ordering judgment. |
|
Thanks for the detailed review Yifan Yang (@Yif-Yang). I've reworked the ordering so it no longer trusts the polling watcher's append order. Fix (
Your two cases are now both rejected, plus a valid control accepted:
These drive real on-disk edits through One caveat I can't clear here: the bash shims and live harness need a POSIX host, so I validated the producer/judge logic offline. The 25 deterministic tests in this file are green; the shim's |
|
One more correctness refinement from a self-review ( Rationale: a well-behaved agent that creates an unrelated auxiliary file (e.g. a scratch helper) before reproducing would have flipped the previous whole-tree hash, so its failing run no longer matched the
The shim's 26 deterministic tests in the file are green; the remaining failures in |
Add a systematic-debugging skill scenario pack to the Superpowers adapters.SuperpowersEvaluator, alongside verification-before-completion. Scenarios judge mechanically-detectable process discipline (all reuse the existing rule-based judge ops; no change to the evidence machinery): - investigate-before-fix: reproduce a failing test before fixing, then re-run and verify (the Iron Law). - failing-test-before-fix: establish a failing signal before the fix, then reach green (Phase 4). - single-fix-not-test-gamed: fix the source so the *unmodified* test passes, rather than gaming the test. Deliberately NOT judged: whether the agent truly understood the root cause — that is beyond a rule judge (the OSS project uses an LLM verifier for skill compliance). Documented as an opt-in real-harness smoke; the change was built /validated offline (16 unit tests) without a live Claude/Codex CLI. Refs microsoft#132.
Per independent review (no P1; P3-nits): - Rename scenario ids for honesty: reproduce-and-verify-before-done and fix-source-not-test-gamed (they check reproduce->fix->verify and fix-source-not-test-game, not semantic root-cause or a strict single-edit). - Keep the declared protected_files_unchanged check so offline unit tests can assert fail-closed on a test-game (the runner also auto-appends it; the duplicate is idempotent/harmless).
Make the existing opt-in real-harness caveat explicit and current: the --compare-baseline baseline-versus-skill run and the ordered reproduce-before-fix live evidence were validated with offline fixtures + adversarial-order unit tests only; the real-harness runs require a POSIX host with an authenticated claude CLI and were not executed here.
_watch_edits compared mt != last.get(p, mt) for an unseen path -> always False, so no entry was ever baselined and no edit was ever logged; the judge then failed closed for every real run. Now cache the first-sight mtime as a baseline and log only on a subsequent change (new source files baselined on first sight). Also move watch_stop.set()/join() into a finally so the timeouts/exceptions in _run_scenario no longer leak a daemon watcher thread. Added a watcher-to-judge integration regression (real mtime change -> edit logged).
The ordered-event judge relied on the polling watcher's append order,
which cannot establish the reproduce->fix->verify invariant reliably: two
distinct edits within one scan interval coalesce into one observation, and
a final edit after the last pass (or after the watcher stopped) is omitted.
Maintainer cases 1 and 2 were both incorrectly accepted.
Replace that with synchronous source snapshots tied to test boundaries:
- The pytest shim writes a one-line \{nonce} snap <content-hash>\ at every
invocation (before running), so each test carries its authoritative source.
- \_run_scenario\ writes \{nonce} start <hash>\ before the agent (the
original/not-yet-edited state) and \{nonce} end <hash>\ after it (final
reconciliation in the finally path).
- \_pytest_reproduce_fix_order\ now asserts: the first failing run is on the
start state (reproduce-before-fix), a later passing run is on a different
(edited) source (verify-after-fix), and the last verified snapshot equals the
final end state (no unverified trailing edit). It fails closed when the
baseline, a fail/pass pair, or the reconciliation cannot be established.
- Fails closed if the shim's \snap\ line is missing for a result.
The shim's snap and the harness's start/end run the exact same fingerprint
snippet, so producer and judge agree on one authoritative hash. Added
producer-to-judge regressions that drive real on-disk edits through
_source_fingerprint, covering the maintainer's two rejected cases plus the
valid control.
…files The snapshot fingerprint previously hashed EVERY *.py under the project dir. A well-behaved agent that creates an unrelated auxiliary file (e.g. a scratch helper) before reproducing would flip the hash, so its failing run no longer matched the start snapshot and the run was wrongly rejected. Scope the fingerprint to the scenario's source-under-test: every setup file EXCEPT the protected ones (typically the tests). The pytest shim and the harness's start/end now hash exactly those files, so: - adding a new unrelated .py before reproduce is invisible (accepted), and - editing the actual code under test still flips the hash (rejected as edit-before-reproduce). _added_ test_aux_file_added_before_reproduce_accepted covering the regression (aux file does not change the scoped hash; editing the source does).
…oritative The order judge no longer reads the watcher's edit lines — the source state at each test boundary is captured authoritatively by the shim's snap line plus the harness start/end snapshots. Keeping the polling watcher running (and its mtime edit lines) was dead weight: it added a background thread and an event stream nothing consumes, and its append order was exactly the unreliable behavior the snapshot model replaced. Remove _watch_edits, its _run_scenario thread, the now-unused hreading import, and the mtime-watcher regression test. The event producer is now single-path: snap (shim) + start/end (harness), all computed by the same scoped fingerprint snippet.
37869f9 to
a9ac49a
Compare
Add ponytail comments tracking two deliberate simplifications with a known ceiling and upgrade path, so a future reader knows they are intentional: - snap/result pairing follows append order; concurrent pytest shim runs can interleave and mis-pair (fails closed; per-pid correlation is the upgrade). - the fingerprint is scoped to the setup source files, so a fix living only in a newly-added module (original source unchanged) is invisible (rejected).
…ario pack The baseline-vs-candidate flag is not part of the systematic-debugging scenario pack and its semantics (evaluate(skill, None) = " without the
The upstream OSS repo has no 'ponytail' convention; replace the two personal workflow notes with plain-English comments describing the same ceilings (snap/result pairing by log order; scoped fingerprint misses fixes that live in a newly-added module). No behavior change.
The offline scenario tests feed hand-authored logs to the judge, so the real bash shim producer was never exercised (the exact gap the reviewer's watcher bug exposed). This POSIX-only smoke drives pytest through the actual shim (reproduce -> edit -> verify) and asserts the judge accepts the real emitted snap/start/end log; a second case asserts edit-before-reproduce still fails closed. It skips on non-POSIX and runs in Linux CI, failing if the shim's snap hash ever diverges from the harness snapshot.

Summary
Adds a
systematic-debuggingscenario pack to the existing Superpowers evaluation adapter (skillopt_sleep/adapters/superpowers.py), alongsideverification-before-completion. Refines issue #132 by extending the adapter to a second checkable skill.What it does
The three scenarios judge mechanically-detectable process discipline:
reproduce-and-verify-before-done- observe a failing run, then re-run and verify after editing (guards against fix-without-reproduce / no-verify).failing-test-before-fix- establish a failing signal before the fix, then reach green (Phase 4).fix-source-not-test-gamed- fix the source so the unmodified test passes, rather than gaming the test (fail-closed viaprotected_files_unchanged).Ordered evidence (changed)
pytest_reproduce_fix_orderties edit evidence to synchronous source snapshots at each test boundary (the old polling_watch_editsis removed - its append order could coalesce two edits into one observation or omit a final edit after the last pass):{nonce} snap <content-hash>at every invocation (before it runs), so each test carries its authoritative source state._run_scenariowrites{nonce} start <hash>before the agent (the original, un-edited state) and{nonce} end <hash>after it (final reconciliation).snapand the harness'sstart/endrun the exact same fingerprint snippet, so producer and judge agree on one hash. The hash is scoped to the scenario's source-under-test (every setup file except the protected ones), so adding an unrelated auxiliary.pydoes not count as editing the source.pytest_reproduce_fix_orderaccepts only when: the first failing run is on thestartstate (reproduce-before-fix), a later passing run is on a different/edited source (verify-after-fix), and the last verified snapshot equals the finalendstate (no unverified trailing edit). Fails closed when a baseline, a fail/pass pair, or the reconciliation cannot be established.Both reviewer-flagged gaps are now rejected:
edit -> fail -> edit -> pass(edit before reproduction) andfail -> edit -> pass -> final edit(no verification after the last edit).How to review / validate
python -m pytest tests/test_systematic_debugging_scenarios.py. 25 deterministic tests drive real on-disk edits through_source_fingerprintand reach the judge (producer-to-judge, not hand-authored logs).python -m pytest tests/test_systematic_debugging_shim_harness.py. Drives the real bash pytest shim throughreproduce -> edit -> verifyand asserts the order judge accepts the emittedsnap/start/endlog - closing the producer<->judge gap, since the offline tests feed hand-authored logs. A second case assertsedit-before-reproducestill fails closed.claudeonly):python -m skillopt_sleep.adapters.superpowers --skill systematic-debugging. The shim's own producer path now has CI coverage (above), but the full_run_scenarioagent path (realclaude) remains the one unverified live path - it needs a Posix host with an authenticated CLI, which the submitting environment lacked. Thetest_superpowers_scenarios.pyfailures on this Windows host are exactly that (bash shim needs Posix), not logic failures.Honest boundaries
Scope
skillopt_sleep/adapters/superpowers.py,tests/test_systematic_debugging_scenarios.py,tests/test_superpowers_scenarios.py,tests/test_systematic_debugging_shim_harness.py. Later commits also removed the obsolete_watch_editswatcher (net -100 lines) and rewrote the twoponytail:-style comments into project-tone notes.Refs #132.