I've been building AnchorStrategy, a multi-agent pipeline that takes meeting notes and turns them into a strategy report: it reads the minutes, has several AI agents split up research on competitors, customers, and cross-industry analogies, then a strategist agent pulls it into a SWOT and a set of strategic options.
I'd been validating it against Japanese business-school case studies (past exams from a Japanese small-business consultant certification), which worked, but left me unable to tell whether a weakness I was seeing was specific to Japanese-language input or a real flaw in the pipeline itself. So I built an English case to re-run the same validation and find out.
Building a clean case
Copyright was the first snag. Harvard Business School cases are cheap to buy for personal use (under $10), but publishing AI-generated output based on one needs separate licensing. I ended up using data from NIST's Baldrige National Quality Award — a US quality-award program. NIST's own 2-page profile of a winning company is clean to use but way too thin for a case study. The company's own ~50-page award application has plenty of detail, but it's the company's copyrighted work.
So I pulled only the facts out of that application — numbers, org structure, the business issues — and wrote an entirely new case narrative from scratch, using none of the original wording, headings, or product names. Facts aren't copyrightable; only the specific expression is, which is the same principle newsrooms and textbook writers rely on. The result: a fictional metal-nameplate and label manufacturer, "Company A," Dallas, Texas — family-owned since the 1940s, won a quality award in the late '90s.
The setup
Generation and grading run on different vendors (Gemini for generation, Claude for grading) so the grader isn't just validating its own house style. The grader never sees my prompt or intent — only the question, the official scoring rationale, a checklist of required elements, and the full generated report. It's judging one thing only: does the report touch the facts it needs to, not whether the strategy itself is any good.
The first result looked fine
Five runs, same case, same settings: average 78.0/100, range 73.5–86.5. That's roughly the same run-to-run variance I'd seen on the Japanese cases — nothing that looked alarming.
Until I broke it down by question. Question 1 (a 3C analysis plus a call to identify a structural weakness, worth 40 points) averaged around 30 — not bad on the surface. But that question has four separate scoring elements (E1–E4), and one of them — "references a competitor-related fact" (Q1-E2, worth 10) — scored zero in 4 of 5 runs. Everything else scored near-full marks, so the total buried a failure that was happening almost every time.
I nearly missed it myself. If I'd only tracked the total, this would have filed under "decent."
What was disappearing
The case narrative states the company's competitive strengths plainly:
Company A is a founding member of its industry's national trade association... To the company's knowledge, no other chemical-etching business operates in Texas; the nearest direct competitor is in a neighboring state. Unlike most of its competitors, Company A carries essentially no debt, maintains its own on-site metal inventory... It formally benchmarks seven of its leading competitors every year through a mystery-shopper program.
(No in-state competitor / debt-free, in-house inventory and tooling / benchmarks 7 competitors annually.)
But in the first run's output, the section that should have covered this read:
[HYPOTHESIS VALIDATION] The President & CEO's suspicion that reliance on spreadsheets is a major barrier to efficiency is supported by the operational bottleneck created by manual data entry between production, sales, and accounting...
No trace of the competitive picture — just a validation of the CEO's offhand comment about spreadsheets. In other runs the report went further and stated outright, "No individual competitor data is available in the input," when the data was very much there.
Tracing it back
I walked the pipeline's intermediate state, node by node, to find where this dropped out.
At the very first node — the one that summarizes the minutes (minutes_ingest) — this competitive information was already missing from the extracted key facts. It never even reached the dedicated competitor-analysis node (market_competitor); it was gone before that.
Digging further: the competitor-analysis node's prompt sorted incoming information into two buckets — "industry averages" and "named-competitor data." But "a fact about our own relative competitive strength, stated without naming a specific competitor" (no in-state competitor, debt-free, in-house capabilities) fit neither bucket. It fell through the gap in the classification scheme and was dropped before extraction even happened.
The fix
I widened what the minutes-summarization node extracts — from "numbers and named entities" to include causal statements and third-party business-structure context generally. I added a new category to the competitor-analysis node: "relative competitive position, without naming a specific competitor" — constrained to only fire when the input explicitly states it, so it doesn't start inventing unsupported claims.
Midway through, I found a side effect I hadn't planned for: a downstream node that formats the final report had the category list hardcoded, so adding a new category upstream didn't actually surface it in the output. Fixed that too.
Re-ran the same case 3 times after the fix. Competitor analysis scored full marks all 3 times.
Company A is the only chemical-etching business in Texas; the nearest direct competitor is in a neighboring state.
That line held, every time. Average total jumped to 89.3.
What's still broken
Not a clean sweep. One more fact — "benchmarks 7 competitors annually via a mystery-shopper program" — stayed missing in all 3 post-fix runs. It didn't affect the score, since that question only required any one of three facts, but it doesn't fit the three categories I just expanded (numbers/named-entities, causal statements, third-party structure), so there's likely a fourth pattern I haven't classified yet. One data point isn't enough to fix it properly, so I logged it and left it for next time.
Patching the classification scheme every time a gap surfaces is inherently reactive — I went from 3 categories to 4 this round, and I'd bet a 5th pattern turns up eventually.
Separately: the guardrail that blocks fabricated content (citation fidelity) tripped in 3 of 5 runs on this sample — a higher rate than I've seen on the Japanese cases. Not sure yet if that's an English-specific weak spot or just noise from a small sample.
The actual takeaway
The bug wasn't the interesting part. What stuck with me was that I almost didn't catch it, because I was reading the average. Sum several scoring elements together and one of them can fail systematically, every single run, and still get buried under everything else scoring well. The agent looks fine on the surface metric while consistently missing one specific thing — which is exactly the pattern AnchorStrategy is supposed to catch in a human's business analysis. Finding it in my own grading pipeline first was not lost on me.
Try it: I'm running an early-access program. Registration takes just an email address — apply here, and I'll follow up with access.
Request access
Full score matrices, the complete generated reports (before/after the fix), and the grading rationale for every checklist item are public here, if you want to check my work:
Validation archive
Exactly. The issue isn’t always that the AI can’t see the information—it’s that the information never gets captured, structured, and stored in a way the system can use later. Great reminder that observation and memory are two very different things.
The post-fix lift is compelling, especially because you kept the same case and isolated the dropped category. I’d separate two checks going forward: (1) a coverage invariant at each node so required facts retain source spans, and (2) a small untouched holdout set to distinguish generalization from patching known failures. For the benchmark fact that still disappears, could an “unclassified but retained” lane be safer than adding another bucket?
The end-to-end fixture is the piece I'd prioritize. Keep one case with required facts and assert at each node that the facts and source spans survive, then run it on every prompt or schema change. I would also hold out a few unseen cases so a fix does not just memorize the failures you already found.
The category-by-category patching is going to keep finding new gaps forever — there's no reason the space of "kinds of facts that matter" is finite, and you've already found a 4th pattern that doesn't fit the first 3. Might be worth inverting the whole approach: instead of classifying facts into buckets and keeping only what's classified, default to keeping everything from the minutes and define a much smaller "safe to drop" list (boilerplate, exact restatement, obviously irrelevant procedural notes). An unclassified fact then survives by default instead of silently disappearing, which matches what you actually want from something whose whole job is not losing information.
Good point. Did you test that with users before committing to it?
Solid point. Validating user demand before getting too deep into the architecture saves so much time.
Good point. Did you test that with users before committing to it?
The classification-gap diagnosis is right, but the 3→4 categories trajectory is a signal about the architecture, not just the taxonomy. Every classification scheme is a lossy filter — whatever doesn't fit a bucket gets dropped silently. For the ingest node specifically, retention beats classification: keep every fact with its source line, tag nothing, and let the nodes that actually know the question do the filtering downstream. Ingest doesn't yet know which facts will matter; the grader does. And on almost missing it: per-element scores saved you because the total is a summary designed to hide exactly this class of bug — per-element failure rates deserve to be the default view, not a drill-down.
Good point. Did you test that with users before committing to it?
Aggregate scores can hide the one failure that matters. I’d pair the overall score with minimum gates for critical criteria, so a systematic zero can’t be averaged away. That’s a principle we use in Agiloop’s assessments: the score summarizes, but the evidence and individual findings drive the decision.
That's the same direction I'm already moving in — minimum gates per criterion, not just an overall number. What made this one persuasive is that the summarized score genuinely read as "seems fine," and it took slicing by question instead of by the total to find the one gate failing almost every run. Curious how Agiloop decides which criteria get a hard gate versus which stay folded into the summary score — is that a fixed list per assessment type, or tuned per engagement?
That gap between perceiving something and actually persisting it is such a subtle failure mode. Did you trace it back to a memory/context issue or something in how the agent decides what's worth recording?
Not memory/context — nothing was truncated or fell out of a window. It's squarely the second one: how the agent decides what's worth recording in the first place. The minutes-summarization step had an extraction schema tuned for numbers and named entities, and this fact (no in-state competitor, stated without naming one) didn't match either. It wasn't dropped after being noticed — it was never flagged as recording-worthy to begin with. Same story one step downstream, too: the competitor-analysis node's own classification only had two buckets (industry averages / named-competitor data), and a fact about relative position without naming a competitor fit neither. So less a memory failure, more a taxonomy failure, twice in a row.
This is one of the best build-in-public debugging write-ups I've read here, and the meta-lesson is the real contribution: aggregate scores bury systematic failures. Three pushes: 1. The reactive category patching is the actual unsolved problem. Instead of widening the extraction schema each time a gap surfaces, invert it: extract everything candidate-relevant into an unstructured pool first, then classify — with an explicit "uncategorized" bucket that gets reviewed, not silently dropped. Silent drops are the failure mode; an uncategorized queue is at least visible. 2. Watch your grader. The citation-fidelity guardrail tripping 3 of 5 runs on the English case vs rarely on Japanese is worth investigating — but note the confound: you wrote the English case. Blind the grader to pre/post-fix and case authorship. 3. n=5 → n=3 is thin for the claims. "Full marks all 3 times" is encouraging, not conclusive — with that variance, three clean runs could be luck. Report the range alongside the mean every time. Publishing the full score matrices and grading rationale is exactly right — that's what turns a blog post into something the community cites.
This is a great comment, going through each point:
The extract-everything-into-a-pool-then-classify-with-an-uncategorized-bucket approach is better than what I've been doing. My current method is additive — widen the schema each time a gap surfaces — which means anything that doesn't fit yet gets silently dropped, and I only find out by manually walking the intermediate state after something already looks wrong. An uncategorized queue makes the gap visible immediately instead of requiring me to go looking for it. That's a real fix for the "there's probably a 5th pattern" problem, not just a patch. Reworking extraction around this instead of hand-adding a 4th and 5th category.
Fair catch, and sharper than the doubt I already had. I flagged the English-vs-Japanese trip rate as "not sure if it's language-specific or noise," but hadn't isolated that I wrote the English case myself — that's a real confound, separate from sample size. The grader doesn't see my prompt or intent, but I honestly can't say right now whether it's blind to case authorship or pre/post-fix status the way it should be. Need to check that before I put any weight on the 3-of-5 number.
Agreed on n=3. "Full marks all 3 times" reads more conclusive than it is — should be reporting the range next to the mean by default, and probably shouldn't have called it "fixed" off three runs.
And appreciate the note on the matrices — that's the whole point of publishing them, glad it's landing as something to actually check rather than just a claim to take on faith.
Glad the uncategorized-bucket idea landed. One thing I'd add on the review cadence: give the bucket a promotion rule, not just a review — e.g. if the same item type shows up uncategorized 3+ times in a week, it automatically becomes a category candidate. Otherwise the queue becomes a junk drawer you stop opening.
On the grader confound: the cleanest fix is having someone else write fresh cases, but even without that you can blind the grader to pre/post-fix by shuffling case IDs and running both versions through the same grading pass. That at least separates 'the fix works' from 'I know which runs are the fix.'
And for the thin-n problem: pre-register the pass criteria before running. 'Three consecutive full marks, declared in advance' turns three clean runs from 'maybe luck' into an actual test. The discipline that caught the bug is worth applying to the fix.
All three land.
Good point on the promotion rule — a review-only queue is exactly the kind of thing I'd stop opening after week three. A concrete threshold (3+ occurrences in a week → category candidate) turns it into something that surfaces itself instead of something I have to remember to check.
Shuffling case IDs and running both versions through the same grading pass is the actionable version of "blind the grader" — I don't have a second person to write fresh cases, but shuffling IDs is something I can just do. Going to rerun the existing before/after set that way before I trust the comparison at all.
Fair, and it stings a little because it's obviously right — I declared "fixed" after seeing the result, not before. Pre-registering the pass bar (e.g., "3 consecutive full marks on this specific check" stated before running, not after) is the same discipline that found the bug in the first place, and I wasn't applying it symmetrically to the fix. Adopting that for the next round.
Interesting approach. What was the hardest part to get right?
Tracing the drop, honestly — not fixing it, finding it. The fact wasn't just misclassified somewhere visible, it vanished before it ever reached the node whose job was to handle it. Had to walk the intermediate state output node by node until I found the first point where it was already gone, which turned out to be the very first summarization step. If I'd only looked at the node "responsible" for competitor analysis, I'd have never found it, because by the time execution got there the information simply didn't exist anymore.
The useful design distinction here is between a score and a coverage contract. For each case, keep a small set of must-survive facts (with source spans) and fail the run if any disappear before the final report. That turns “the agent saw it but dropped it” into a testable handoff failure, rather than another downstream prompt patch. I’d version that contract separately from the prompts.
Tracing the drop, honestly — not fixing it, finding it. The fact wasn't just misclassified somewhere visible, it vanished before it ever reached the node whose job was to handle it. Had to walk the intermediate state output node by node until I found the first point where it was already gone, which turned out to be the very first summarization step. If I'd only looked at the node "responsible" for competitor analysis, I'd have never found it, because by the time execution got there the information simply didn't exist anymore.
Really relatable. How much time do you put into this each week?
This is a side project around a full-time day job, so realistically it's evenings and weekends — not a fixed number, more like whatever's left after work on a given week. Some weeks that's a few focused hours on one bug (like the one in this post), other weeks it's mostly writing up what I found rather than building. Not sustainable at startup-hours pace, but enough to keep the validation loop moving.
Such a good catch. Aggregate scores can hide a repeatable failure for a long time. I would add tiny invariants at every node so a required fact disappearing fails loudly. The hardcoded downstream list is painfully real too.
Yeah, "fail loudly" is the key phrase — right now a disappearing fact fails silently, it just quietly lowers an average three questions later. Invariants at every node instead of one end check is the fix. And glad it's not just me — the hardcoded downstream list is the kind of bug that makes you feel dumb for an afternoon and then relieved when you realize it's a common shape of mistake, not a personal one.
Averages can hide exactly the failure you care about. I like treating each intermediate transform as a contract: list required facts, assert they survive every node, and keep one end-to-end fixture. Score coverage and fidelity separately from final usefulness. That makes regressions visible without patching prompts ad hoc.
Per-node contracts is a sharper version of what I've been doing by hand — tracing intermediate state after I already suspect something's off, instead of asserting it automatically at each step. That's exactly why the minutes_ingest drop sat unnoticed as long as it did: nothing flagged it, I only found it because I went looking.
And splitting coverage / fidelity / final usefulness into separate scores instead of one blended number is a real correction. I've been treating "did the required fact survive" and "was it represented correctly" as basically the same signal, and they're clearly not — the mystery-shopper fact failed on coverage, but a fact that survives and gets subtly distorted downstream is a different failure mode entirely, and right now I'd have no way to tell those apart. Building the fixture set with that split in mind from the start, not bolting it on after.
The "averages hide systematic failure" discovery is the most useful thing in here. Sum several scoring elements and one can fail every single run while the total still looks acceptable. That pattern is everywhere in multi-agent pipelines.
I hit something similar building Genie. Intent classification was scoring fine on aggregate accuracy but failing completely on one edge case category that made up 12% of real production requests. The category wasn't in the eval breakdown so it never flagged. I only caught it when I sliced the failures by type instead of reading the aggregate pass rate.
The hardcoded category list in the downstream formatter is the part that always catches people off guard. Fix the upstream extraction, re-run, wonder why the output didn't change. The dependency is invisible until you trace the intermediate state manually.
On the reactive patching problem: have you considered writing a coverage test that explicitly asserts each fact type against a canonical case? That way a new classification gap surfaces as a failing test rather than a production scoring drop.
The Genie example is a really clean parallel — 12% of real traffic silently failing because the category wasn't even in the eval breakdown is the same shape of bug, just found from the production side instead of the eval side. Good reminder that this isn't a "my pipeline" problem, it's a "aggregate metrics" problem.
And yes, hit that exact hardcoded-formatter trap — fixed the upstream extraction, reran, output didn't change, spent longer than I'd like to admit assuming I'd made an error before tracing it down to a downstream node silently dropping the new category. Invisible until you manually walk the intermediate state, agreed.
On the coverage test — that's exactly where I'm heading next: a canonical case per fact type, asserted explicitly, so a new gap shows up as a red test instead of a quietly-lower average three weeks later. Don't have it built yet, but this thread is basically talking me into prioritizing it over the next validation round instead of after it.
Nice work shipping it. What has been the biggest challenge since launch?
Honestly, "since launch" undersells where I'm at — it's still invite-only, no public launch yet. So the biggest challenge so far isn't a launch-day fire, it's the gap this thread already poked at: getting from "passes my own rubric" to "actually useful for someone who isn't me." Everything's been validated against exam scoring criteria, not against a real person using it for their own work, and I don't yet know how those two diverge.
Thanks for sharing the numbers, that makes it much easier to follow.
Thanks for reading it — glad the numbers made it easier to follow rather than just more noise to wade through.
The real finding here is not about agents, it is about averages. A 78 total hiding one element that fails 4 runs out of 5 is the same trap as a healthy blended CAC hiding one channel that never converts, and you only catch it in the line items. I would stop scoring on totals and make each element a pass/fail gate, so a run with 90 points and a zero anywhere still counts as a failed run.
You're right, and this is actually the direction I'm already moving toward — tracking pass/fail per required element instead of a summed score, so a 90 with one hard zero doesn't get averaged into "looks fine." The CAC analogy is a good one; that's exactly the kind of blending that let this sit unnoticed for longer than it should have. Going to make the gate explicit rather than something I have to remember to check manually each time.
Good point. Did you test that with users before committing to it?
Good point. Did you test that with users before committing to it?
Not with real users, no — everything so far has been tested against exam rubrics and scoring criteria, not against someone actually using it to do their job. That's the gap I'm aware of and haven't closed yet. The early-access program is partly meant to fix that: get it in front of a few people who'd actually use this kind of report, see where it breaks in practice rather than on paper.
The post-fix score is encouraging, but how will you distinguish genuine generalization to unseen cases from improving performance on failure patterns you've already identified?
Good question — honestly the part I'm least confident about. The 3 post-fix runs were on the same case I used to find the bug in the first place, so this doesn't rule out just overfitting to that one pattern. What would actually convince me is running the same fix against a completely different case (different industry, different competitive structure) and checking the miss rate holds up there too — haven't done that yet, it's next on the list, along with building a proper held-out test set instead of eyeballing individual runs. Will post an update once I have real numbers instead of "seems better."
This is useful. How are you finding your first users so far?
Thanks, glad it was useful! Honestly still very early on the user side — no public launch yet, just reaching out directly to a handful of people who actually write this kind of report for a living (mostly independent consultants) and asking them to try it. Wanted the failure modes ironed out first before pushing on distribution. This post is partly that too — seeing if the story holds up with people outside my own bubble before I put more effort into finding users.
Links didn't come through when I pasted this in — here they are:
Request access: https://docs.google.com/forms/d/e/1FAIpQLSdvwMaqaD6RHX0TCYpd8Rd0tEBuriHXTtDwfyeejTm8QAE1AA/viewform
Validation archive: https://claude.ai/artifact/FUL7q7BpSybodkHwKNVX5t