Skip to content

fix: tool output quality — query_context cap, list_concerns noise, deliver-phase detection, resolve_concern nudge - #5

Merged
Koed00 merged 11 commits into
mainfrom
feature/tool-output-quality
Jun 20, 2026
Merged

Koed00 merged 11 commits into
mainfrom
feature/tool-output-quality

Conversation

@Koed00

@Koed00 Koed00 commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

Bundled regression-recovery round fixing 4 independent defects in the already-shipped lore-mcp MCP tools, all found via live dogfooding immediately after list-concerns shipped:

  1. query_context had no size cap at all — a single feature's response could balloon past 97,000 characters with no truncation, no warning.
  2. list_concerns was noisy — generic nWave section headers ("Decisions", "Summary", "Mode") cluttered the candidate list with zero topic signal.
  3. list_features couldn't tell if DELIVER actually happened — it reported phases based on directory existence, not whether any work was committed.
  4. resolve_concern's CONCERN_NOT_FOUND didn't chain to list_concerns — despite the two tools being designed to work together.

What changed

  • query_context now applies a per-file cap (8000 chars, reusing the existing capSnippetAtHeadingBoundary from resolve_concern) and a total-response cap (60000 chars) that prioritizes a feature's own content over repo-wide ADR/brief.md content when truncating.
  • list_concerns filters a stoplist of generic, convention-driven section headers from heading-text candidates only — never from feature directory names or ADR titles.
  • list_features now requires deliver/execution-log.json to contain at least one COMMIT-phase entry before reporting the deliver phase, not mere directory existence.
  • resolve_concern's CONCERN_NOT_FOUND message now mentions list_concerns() as the next step.
  • A 5th, smaller fix found along the way: query_context no longer emits a spurious "file could not be read" warning for every feature's deliver phase (DELIVER produces execution-log.json, never wave-decisions.md).

What a reviewer should know

  • A core DESIGN-wave assumption turned out to be false, found only by re-running live data, not by the test suite. DESIGN assumed "a single result's snippet is already bounded by an existing per-file cap" — but query_context had no per-file cap at all. brief.md alone (49,501 chars) was returned whole on every query. No total-response number alone could have fixed the original bug. The fix grew into two layered mechanisms, both verified against this repo's own ab-mcp feature (97,705 chars → 55,708 chars) before being called done.
  • The total-response truncation priority (feature-specific content before repo-wide ADRs) is documented in a new ADR-007.
  • Mutation testing initially regressed concern-matcher.ts to 75.21% (below the 80% per-file gate) due to this round's new GENERIC_HEADING_STOPLIST code — closed to 86.04% with added tests, no production changes.
  • Full nWave wave trace under docs/feature/tool-output-quality/. Root-cause writeup for the mid-implementation finding: docs/feature/tool-output-quality/distill/upstream-issues.md. Evolution summary: docs/evolution/tool-output-quality-evolution.md.

Test plan

  • 520/520 tests passing (npm test)
  • npm run build (tsc strict) clean
  • npm run check:arch (dependency-cruiser core/shell boundary) clean
  • Mutation testing: concern-matcher.ts 86.04%, format-response.ts 87.65%, project-wide 87.92% (threshold 80%)
  • Live dogfood re-confirmed all 4 KPIs against this repo's own docs:
    • list-concerns feature's query_context response: 97,705 → 55,708 chars
    • list_concerns() candidates: 96 → 78, generic headers gone
    • deliver phase correctly shown for all 4 features with committed DELIVER waves
    • resolve_concern's nudge text confirmed present
  • DES integrity verification: 4/4 roadmap steps with complete TDD traces

Ilan Steemers and others added 11 commits June 20, 2026 18:25
Bundled regression-recovery round fixing 4 defects found via live
dogfooding of the already-shipped lore-mcp tools: query_context has no
size cap (97,705-char bug), list_concerns is noisy with generic
headers, list_features can't detect a completed DELIVER wave, and
resolve_concern's error doesn't chain to list_concerns.

DISCUSS, DESIGN (ADR-007), and DISTILL waves completed and peer-reviewed
(approved). Acceptance tests confirmed RED (not BROKEN) against current
production code -- 7 of 8 assertions genuinely RED, 4 of 8 scenarios
pass trivially as expected regression-safety behavior.
Layers two truncation mechanisms, discovered through live dogfooding
to both be necessary:

1. Per-file cap (8000 chars, reusing the existing capSnippetAtHeadingBoundary
   from resolve_concern) -- query_context previously had ZERO per-file cap,
   so a single file like brief.md (49,501 chars) or a large ADR was
   returned whole on every query.

2. Total-response cap (60000 chars) with feature-specific-content priority
   over repo-wide ADR/CLAUDE.md content -- query_context includes ALL
   ADRs + the full architecture brief on every per-feature query by design,
   so as those grow, pure oldest-first truncation was dropping a feature's
   OWN foundational decisions before dropping unrelated repo-wide content.

Both findings surfaced via dogfooding against this repo's own deepest
feature (ab-mcp, 15 results / 135,434 uncapped chars) -- see
docs/feature/tool-output-quality/distill/upstream-issues.md for the
full root-cause analysis and how the DESIGN-wave assumptions were
corrected mid-implementation.

Step-ID: 01-01
… reads

query_context now spuriously warned "deliver/wave-decisions.md could not
be read (file may have been removed)" for every feature, once
list_features started correctly detecting the deliver phase (tool-output-
quality step 01-03). DELIVER produces execution-log.json, never
wave-decisions.md, so this was a guaranteed TOCTOU "failure" on every
query, not an actual file-removal signal. Found via live dogfooding
during this round's post-merge integration gate.
Document the intentional duplication of SNIPPET_MAX_CHARS (format-response.ts
vs concern-matcher.ts) and the "deliver" phase literal (classify-structure.ts
vs server.ts's DELIVER_PHASE_NAME), consistent with the project's existing
core/shell duplication-with-comment convention. No behavior change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…her.ts to >=80%

concern-matcher.ts was at 75.21% file-level mutation score, below the
project's per-feature 80% gate. Added targeted unit tests pinning the
GENERIC_HEADING_STOPLIST entries, isStructuralAncestor/excludeStructuralAncestors
boundary conditions, findSectionEnd/findFirstChildHeadingLineIndex edge cases,
splitIntoParagraphs filter precision, and ADR regex anchoring/exactness.
Also added a format-response.ts test exercising the per-file-truncation
warning branch (perFileTruncations > 0) that previously had no test forcing
a truncated individual snippet through formatQueryContextResponse.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Koed00
Koed00 merged commit 70cfd63 into main Jun 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant