fix: report the real failure — harness diagnostics, Qoder stdout, and mode validation - #725
Open
jackeduzan-oss wants to merge 3 commits into
Open
Conversation
The csv harness restored sys.stdout inside its except branch and then
called sys.stdout.getvalue() on it. Any exception in the scored snippet
therefore surfaced as:
AttributeError: '_io.TextIOWrapper' object has no attribute 'getvalue'
so a missing pandas and a genuinely broken model answer produced the same
inscrutable harness traceback. Hold the buffer in its own name, restore
stdout in finally, and report the exception the snippet actually raised.
exec() had the matching problem from the other side: it kept only stderr,
while every harness prints its "FAIL: output was ..." line to stdout and
exits 1. The one useful diagnostic was unreachable, leaving failures as a
bare "Command failed: python3 /tmp/...". It now carries a message field
preferring stderr then stdout; stderr stays on the result for any other
caller.
Also skips the pandas case when pandas is absent. The subject under test is
the checker, not pandas, and `npm test` went red on any machine without it.
CI installs pandas, so the case still executes there.
The report-only branch wrote its status line, and then the Qoder block wrote the ruleset — whose first line is that same "PONYTAIL MODE ACTIVE — level: X" string. Two concatenated JSON objects landed on stdout, which parses as neither. The mode-switch branch below already guards with !isQoder for exactly this reason; report-only was missed. Same guard, and nothing is lost because the ruleset write already opens with the line being suppressed.
Two inconsistencies with the normalizers this file ships beside. getDefaultMode did its own toLowerCase() without trimming, while normalizeMode() trims and validates against RUNTIME_MODES. So PONYTAIL_DEFAULT_MODE=" lite " — one stray space in a shell profile or a CI yaml — silently resolved to the built-in default instead of lite, with no error. Reuse normalizeMode for both the env var and the config field. readMode returned the flag file verbatim. A hand-edited or truncated file made `/ponytail` announce "level: banana" while getPonytailInstructions had already normalized it back to full — the status line contradicted the ruleset actually in force. The OpenCode reader already calls normalizePersistedMode on the same value; this one now does too. Drops the unused getConfigDir import while touching that line.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four small correctness fixes found while reading the hooks and the correctness benchmark. Each is one commit, so they can be taken separately or partially. Every one is verified by a regression test, and
npm testis green (87/86 pass + 1 skipped, 23, 3), as arecheck-rule-copiesandcheck-versions.The theme is the same in all four: a failure should say what actually went wrong, and a value should be validated where it enters.
1. The correctness harness reported its own crash instead of the failure
benchmarks/correctness.js, csv task:Any exception in the scored snippet produced:
So a missing pandas and a genuinely wrong model answer both surfaced as the same inscrutable harness traceback. The buffer now has its own name and stdout is restored in
finally, and the exception the snippet raised is reported.exec()had the mirror problem: it kept onlystderr, while every harness prints itsFAIL: output was ...line to stdout and exits 1. That diagnostic was unreachable, so failures read as a bareCommand failed: python3 /tmp/.... It now carries amessagefield preferring stderr then stdout.stderris still on the result object for any other caller.Before / after on the same input:
The pandas case is now skipped when pandas is absent — the subject under test is the checker, not pandas, and
npm testwent red locally on any machine without it. CI installs pandas, so the case still executes there. A new test proves the checker itself is not pandas-dependent by passing a stdlibcsvsolution.2. Qoder received two JSON objects from a bare
/ponytailhooks/ponytail-mode-tracker.js. The report-only branch wrote its status line, then the Qoder block wrote the ruleset — whose first line is that samePONYTAIL MODE ACTIVE — level: Xstring. stdout carried two concatenated JSON objects, which parses as neither:The mode-switch branch below already guards with
!isQoderfor exactly this reason; report-only was missed. Same guard applied, and nothing is lost — the ruleset write already opens with the suppressed line.3.
PONYTAIL_DEFAULT_MODE=" lite "silently resolved tofullgetDefaultMode()did its owntoLowerCase()without trimming, while thenormalizeMode()it ships beside trims and validates againstRUNTIME_MODES. One stray space in a shell profile or a CI yaml gave you the built-in default with no error. Now reusesnormalizeModefor both the env var and the config field — same validation, including the#377rule thatreviewcan never be a default.4. A corrupt flag file was reported as the active level
readMode()returned the file verbatim, so/ponytailannouncedlevel: bananawhilegetPonytailInstructionshad already normalized it back tofull— the status line contradicted the ruleset actually in force. The OpenCode reader already callsnormalizePersistedModeon the same value (.opencode/plugins/ponytail.mjs:35); this one now does too. Also drops the unusedgetConfigDirimport on the line being touched.Verified end to end after the fixes:
/ponytailPONYTAIL_DEFAULT_MODE=" lite "litebananalevel: full, the mode really in force(raised ModuleNotFoundError: No module named 'pandas')Happy to split this into separate PRs or drop any commit if you'd prefer them apart.