Repository navigation
feature: honour the prompt header keys the ranker is documented to act on - #217
Merged
Merged
Conversation
…t on PyAutoMind REFERENCE.md states the contract: Intake persists Difficulty: from the shared sizing faculty "so the value shown up front is the one the Feature Agent later acts on". It was not acted on. _feature.py mentioned Difficulty once, where it PRINTED its own re-derived score, and read no header key at all — not Difficulty:, not Status:, not Priority:, not Blocked-by:/Closes-when:. The visible failure: `feature select` recommended a prompt declaring Status: blocked, Priority: low AND an explicit Blocked-by: gate whose body says "do not start before the queue runner has run cleanly". Parsing lands in the sizing faculty, beside the derivation it overrides, so declared and derived stay in one place and the bug/refactor conductors get the same reading for free: - declared_header() reads Difficulty/Status/Priority/Blocked-by/Closes-when, skipping fenced blocks. That skip is load-bearing, not tidiness: the bug prompt driving this fix QUOTES the offending header in a ```-block, so without it the bug report would declare itself blocked. Same rule and same reason as PyAutoMind lifecycle.py:draft_gate_refs. - trailing `# note` comments are stripped, splitting on " #" so a Repo#123 ref survives intact (the live backlog has both forms on one line). - an unrecognised Difficulty: value is ignored rather than trusted. The ranker then honours them: declared difficulty overrides derived (with the disagreement REPORTED, not silently resolved — the gap is evidence about the heuristic); Priority: orders the shortlist above the difficulty term; and a prompt declaring Status: blocked or an unresolved Blocked-by: sinks below everything and can never be the recommended pick. It stays listed in its own labelled band so a human can see and override, and next_action says do not start. declared_blocked() is deliberately conservative: this agent is offline and cannot tell whether a gate has since closed, so an unresolved Blocked-by: reads as blocked and the output points at `lifecycle.py issues --drafts`, which does resolve refs against GitHub. Being wrongly held back is cheap and visible; being wrongly recommended is the failure this exists to stop. Measured on the live backlog: the blocked prompt drops from rank 1 to last of 27, correctly labelled, and two further blocked prompts surface that had not been noticed by hand. draft_staleness_detection_signals moves from too-large #26 to medium #5 — it derived too-large purely on file length, and length is a bad size proxy because a prompt grows as it accumulates findings. New tests are hermetic and pin the regression by the offending prompt's HEADER SHAPE rather than the live file, so fixing the backlog cannot silently retire them. Verified they actually bite: reverting only _feature.py fails 7 of the 10. Fixtures use invented repo names, so the file adds no tenant-firewall instance facts. Prompt: PyAutoMind draft/bug/pyautobrain/feature_ranker_ignores_header_keys.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WSCA1h6cSKMNwFmWEsxuCL
Jammy2211
added a commit
that referenced
this pull request
Aug 24, 2026
Third instance of one family: a conductor derives a value from prose and ignores the header key the author declared. (1) the Feature Agent's ranker, fixed #217; (2) the Bug Agent; (3) the Intake Agent's Difficulty:. Intake never read a declared Difficulty at all. It hand-built the dict it passes the sizing faculty, omitting the declared_header() keys the file-loading path folds in, so estimate_difficulty ran with no declared key present, nothing reconciled derived against declared, and the derived level was persisted into the header — silently replacing what the input asked for. Reported case: raw text declaring `Difficulty: medium` filed as `too-large` (score 11), unchanged when the full header block was prepended, hand-corrected after --apply. The heuristic's biggest input is length, and length is a bad size proxy — a prompt is long when it carries a design, which is exactly what declaring a level exists to correct. Per the prompt, the precedence rule moves INTO the faculty both conductors already call rather than being re-implemented per conductor — three reconciliations were three chances to forget one, and two forgot: - `_sizing.effective_difficulty` is now the single rule: declared wins, derived level + score returned alongside so a disagreement is reported, not silently resolved. `_feature` drops its local copy and imports it; `_bug` and `_intake` now go through it. - `declared_header` reads `Type:` and `Autonomy:` too (it already read Difficulty/Priority/Status/Blocked-by), and `declared_inline` is its counterpart for unheadered conception prose — the ideas.md idiom "Difficulty large, supervised." included. Neither reads a fenced block: a prompt quoting a header documents it, rather than declaring it. - Intake reads both, keeps the declaration out of the derived title/slug (it was leaking into the filename), and honours a declared `Type:` over its prose classifier — the filing-note leg, where the prompt naming itself a bug throughout was classified `feature`. - The Bug Agent no longer re-homes a prompt that declares `Type: bug` on prose keywords: a defect report legitimately says "refactor" or "documentation" while describing a crash. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RXdgVSW8bUhVdEXEz6AAJ
This was referenced Aug 24, 2026
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.
PyAutoMind
REFERENCE.mdstates the contract:It was not acted on.
_feature.pymentionedDifficultyexactly once, where it printed its own re-derived score, and read no header key at all — notDifficulty:, notStatus:, notPriority:, notBlocked-by:/Closes-when:.The visible failure:
feature selectrecommendeddraft/feature/autonomy/10_scheduled_runs.mdas the next task to start — a prompt declaringStatus: blocked,Priority: lowand an explicitBlocked-by:gate whose body reads "Do not start before the queue runner has completed several supervised interactive runs cleanly."Prompt:
PyAutoMind:draft/bug/pyautobrain/feature_ranker_ignores_header_keys.mdWhere the parsing lives
In the sizing faculty, beside the derivation it overrides — so declared and derived stay in one place, and the
bug/refactorconductors (which shareparse_prompt) get the same reading for free.declared_header()reads the five keys and skips fenced blocks. That skip is load-bearing, not tidiness: the bug prompt driving this fix quotes the offending header in a ```-block, so without it the bug report would declare itself blocked. Same rule and same reason asPyAutoMind/lifecycle.py:draft_gate_refs.Two smaller traps handled: trailing
# notecomments are stripped by splitting on" #"so aRepo#123ref survives intact (the live backlog has both on one line), and an unrecognisedDifficulty:value is ignored rather than trusted.What the ranker now does
Difficulty:Priority:Status: blocked/ unresolvedBlocked-by:next_actionsays do not start.Blocked prompts stay listed in their own labelled band so a human can see and override.
declared_blocked()is deliberately conservative: this agent is offline and cannot tell whether a gate has since closed, so an unresolvedBlocked-by:reads as blocked and the output points atlifecycle.py issues --drafts, which does resolve refs against GitHub. Being wrongly held back is cheap and visible; being wrongly recommended is the failure this exists to stop.Measured on the live backlog
BLOCKED — Status: blocked; priority low; declared medium vs derived small.ep_analytic_updates,oversampled_psf_dataset_adoption).draft_staleness_detection_signalsmoves fromtoo-largeFlatten the skill chain: short dispatchers, plan_branches folded, mandatory-load budget #26 tomediumModernise and re-home PyAuto workflow skills for PyAutoBrain #5. It derivedtoo-largepurely on file length — and length is a bad size proxy, because a prompt grows as it accumulates findings. Documented inAGENTS.mdas the reason the declared key exists.Verification
289 passed(was 279; the 2test_skill_install.pyfailures are pre-existing and environment-only — they do not occur in CI).New tests are hermetic and pin the regression by the offending prompt's header shape, not the live file, so fixing the backlog cannot silently retire them. Confirmed they actually bite: reverting only
_feature.pyfails 7 of the 10 (the 3 survivors are puredeclared_headerunit tests, which is coherent). Fixtures use invented repo names, so the file adds no tenant-firewall instance facts — verified againstrepos_sync.py --check.🤖 Generated with Claude Code
https://claude.ai/code/session_01WSCA1h6cSKMNwFmWEsxuCL
Generated by Claude Code