Skip to content

fix: conductors derive over a declared header key (difficulty, type) #274

Closed

Description

@Jammy2211

Overview

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 classifying on prose keywords over a declared Type: bug; (3) the Intake Agent persisting its derived Difficulty: over the declared one.

analyse() hand-built the prompt-shaped dict it hands the sizing faculty, omitting the declared_header(text) keys the file-loading path (_sizing.py:369) does fold in. So estimate_difficulty ran with no declared key present, nothing reconciled derived against declared, and the derived level was written into the header — silently replacing what the input asked for. Reported 2026-08-24: raw text declaring Difficulty: medium classified too-large (score 11), unchanged when the full header block was prepended; the header had to be hand-corrected after --apply.

This matters because the derived score weights prompt length, and length is a bad size proxy — a prompt is long when it carries a design and the sibling tasks' traps, not when the work is large. That inversion is already on the record in complete/2026/08/feature-ranker-ignores-header-keys.md ("that is what a declared Difficulty: is for"). A declared key is the documented escape hatch, and on this path it did nothing.

Plan

  • Put the precedence rule in the sizing faculty — one place all three conductors already call — instead of re-implementing it per conductor. Rule, stated explicitly: declared wins, and the derived level is reported alongside it when they disagree, which keeps the heuristic honest and visible.
  • Give the faculty one definition of "what counts as a declaration": header lines for filed prompts, prose for unheadered conception input; never a fenced block.
  • Fix leg (2) in the same pass: a declared Type: bug outranks the Bug Agent's prose keywords.
  • Stop the declaration leaking into intake's derived title and filename.
Detailed implementation plan

Affected Repositories

  • PyAutoBrain (primary)
  • PyAutoMind (prompt lifecycle only)

Branch

claude/intake-declared-difficulty-icstba (remote web-github session; no task worktree)

Implementation Steps

  1. agents/faculties/sizing/_sizing.py
    • effective_difficulty(p) -> (level, score, factors, derived_level) — the single precedence rule, moved here from _feature.py.
    • declared_header also reads Type: and Autonomy: (it already read Difficulty/Priority/Status/Blocked-by).
    • declared_inline(text) -> (fields, spans) — the same keys read out of unheadered prose, including the ideas.md idiom Difficulty large, supervised.; code fences and inline spans masked first, matching declared_header's rule.
    • strip_declarations(text, spans) — for title derivation only; the prompt body stays verbatim.
  2. agents/conductors/feature/_feature.py — drop the local effective_difficulty, import the faculty's. No behaviour change.
  3. agents/conductors/bug/_bug.py — analyse_bug/select_bug go through effective_difficulty; the BugDecision gains difficulty_declared/difficulty_derived/difficulty_disagreement; re_home_check returns None for a prompt that declares Type: bug.
  4. agents/conductors/intake/_intake.py — read both declaration sources (header lines win a tie), size through effective_difficulty, honour a declared Type: over the prose classifier, keep the declaration out of the derived title/slug, and report provenance (difficulty_source, work_type_source, autonomy_source, priority_source). formalise's _derive_fields takes the same path.
  5. Docs: the sizing faculty's AGENTS.md + module docstring (it now owns the rule), intake AGENTS.md/INTAKE_TAXONOMY.md, skills/intake/intake.md, BUG_TAXONOMY.md.
  6. tests/test_declared_header_precedence.py — the family's lock: rule-level tests plus the reported reproduction, the prepended-header case, the title/slug leak, declared Type: at intake and in the Bug Agent's re-home check, and "no declaration → still derives".

Key Files

  • agents/faculties/sizing/_sizing.py — the rule and both readers
  • agents/conductors/{intake,bug,feature}/ — consumers
  • tests/test_declared_header_precedence.py — new

Validation

  • pytest tests/ -q → 491 passed
  • python3 PyAutoMind/scripts/repos_sync.py --check --only "tenant firewall (organ code)" → OK

Original Prompt

The authoritative prompt is PyAutoMind active/intake_ignores_declared_difficulty.md (filed on main as c1927d5, advanced draft/ → active/ on issue creation).

Activity

  1. changed the title [-]fix: intake ignores a difficulty declared in the raw text[/-] [+]fix: conductors derive over a declared header key (difficulty, type)[/+] on Aug 24, 2026
  2. added a commit that references this issue on Aug 24, 2026
  3. Jammy2211 commented on Aug 24, 2026

    @Jammy2211
    ContributorAuthor

    Shipped

    • PyAutoBrain#275 — merged (979fac5)
    • PyAutoMind#308 — merged (243a871), lifecycle state only

    The precedence rule now lives in the sizing faculty, not in each conductor: effective_difficulty reconciles declared against derived (declared wins; the derived level and score come back alongside so a disagreement is reported), declared_header reads Type:/Autonomy: as well, and declared_inline reads the same keys out of unheadered conception prose — neither from a fenced block. The Feature Agent's local copy is gone; the Bug Agent and Intake now go through the shared rule.

    Both legs of the family are closed:

    • Difficulty — a declared level is honoured at conception and in formalise, and no longer leaks into the derived title/slug.
    • Type — Intake honours a declared Type: over its prose classifier, and the Bug Agent no longer re-homes a prompt declaring Type: bug on prose keywords.

    On the prompt's own file, intake now reports Work-type: bug (declared) and Difficulty: medium (declared); it previously called it feature and re-sized it.

    CI: pytest (3.12) and pytest (3.13) green on the merged head (491 tests, 15 new in tests/test_declared_header_precedence.py), tenant-firewall leg green. Heart was not reachable from this web-github session, so ship_library's documented fallback — the repo's own suite as the gate — applied.

    Scope note: the prompt asked for leg (2) in the same pass, so this branch is wider than intake alone. One root cause, one shared rule — splitting would have meant two PRs editing the same function.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions