Skip to content

13 min read

Two workers research the same company because the brief let them

On this page

The weekly competitive brief covers six companies — Northbeam, Corveil and Palisade on the infrastructure side, Tessera, Marlowe and Aventine on the applications side — and it is produced by a two-level tree. An orchestrator delegates to two sector supervisors, each supervisor runs three research workers, and each worker researches one company. The two supervisors receive their instructions in a delegation brief, and the field of that brief which says what a supervisor owns is called its scope.

How that scope is written decides whether the brief comes out with every company in it exactly once. A scope can be an enumeration — a list of the company names this delegate owns — or a theme, a sentence describing a category that the delegate is expected to resolve into names itself. The two look interchangeable in a system prompt and behave completely differently, because a theme has to be interpreted, and the interpreting is done separately by each supervisor with no view of what the other one decided.

A theme is resolved twice, independently, with no way to compare

Start with what a supervisor is being asked to do when its scope is a theme. The brief says something like "application-layer companies in the competitive set". To dispatch anything, the supervisor must first turn that sentence into a list of names, which means deciding, company by company, whether each one belongs to its category. It makes that decision from what the model knows and what its retrieval layer shows it, and it makes it alone.

Now run the same step in the other supervisor. It has a different sentence and the same task, and it resolves its own theme into its own list. Nothing in either process consults the other. So the two lists are produced by two independent judgements over the same underlying set of companies, and nothing requires those judgements to be consistent with each other.

Two failures follow directly, and they are the only two available. A company that satisfies both themes lands on both lists, and gets researched twice. A company that satisfies neither lands on no list, and gets researched by nobody. Neither outcome requires a mistake by anyone: both supervisors did exactly what their briefs said. What is missing is a partition — a split in which every company falls to exactly one supervisor and none falls to two — and the tree never had a place where one could be asserted.

The trace shows both failures in about ten seconds, which is the payoff for having put boundary_id and parent_id in the delegation brief rather than reconstructing the tree from timestamps afterwards.

jsonl
{"run_id":"wk39","boundary_id":"root>infra","parent_id":null,"scope_text":"infrastructure companies in the competitive set","dispatched":["northbeam","corveil","palisade"]}
{"run_id":"wk39","boundary_id":"root>apps","parent_id":null,"scope_text":"application-layer companies in the competitive set","dispatched":["tessera","marlowe","corveil"]}
{"run_id":"wk39","boundary_id":"infra>corveil","parent_id":"root>infra","tool_calls":9,"input_tokens":31402,"latency_ms":18730}
{"run_id":"wk39","boundary_id":"apps>corveil","parent_id":"root>apps","tool_calls":11,"input_tokens":36180,"latency_ms":22105}
{"run_id":"wk39","boundary_id":"root>apps","aventine":"not dispatched"}

Read the first two lines together and the overlap is visible without any tooling: corveil appears in both dispatched arrays, under two different scope_text sentences. Read the last line and the gap is visible too. Corveil was researched twice, at a combined cost of about 68,000 input tokens and 41 seconds of summed worker latency across two workers. Both totals come straight off the trace: the two input_tokens fields add to roughly 68,000, and the two latency_ms fields add to about 41 seconds.

The second of those figures is a sum, and it is worth separating from wall-clock, which is the time that elapses on a clock while the run waits, as distinct from the summed compute of agents running side by side. The two Corveil workers hang off different supervisors and run at the same time, so the duplication costs the full 41 seconds of paid worker time and only the longer of the two in elapsed time. The waste is real either way; it is the spend that doubles, not the clock.

Only one of the two failures showed up in the brief. Run 39 went out with Corveil in it twice: the infrastructure section put its annual recurring revenue at $310M as of the FY2026 Q2 filing, and the applications section, four hundred words later, put it at $287M as of the same filing, sourced to a different page of the same document. Aventine was researched by nobody, and nobody noticed, because a brief that is missing a company reads like a brief about five companies.

Corveil satisfied both themes for a reason outside the service: it shipped an application-layer product in Q3 and had been describing itself that way in its own marketing ever since. Aventine, a data-infrastructure company that sells only to application teams, plausibly satisfies neither theme depending on which sentence of its website you read. Neither fact is under the team's control, and neither appears in the team's changelog.

That is the part of the mechanism worth following all the way through. A theme is not stored anywhere as a list; it is re-derived by a model on every single run. The model's reading of the world moves when the world moves — a homepage rewrite, a product launch, whatever the retrieval layer surfaces that week. So the membership of each supervisor's scope is recomputed from current evidence every Monday, without a line of code changing. The defect rate of a themed partition is therefore a moving quantity rather than a property you measure once and sign off.

The workers did nothing wrong, and neither did the supervisors

It is tempting to treat this as a worker problem, and the fix that suggests itself is a line in the worker prompt: before researching, check whether another worker has already covered this company.

That instruction cannot be followed, and the reason is structural rather than a matter of wording. Workers in this tree have no channel to each other and no view of the run. An instruction to check something is only executable if the thing is observable, and coverage by a sibling is not observable from inside a worker. A worker asked to check it will either ignore the instruction or hallucinate compliance with it, and the second failure mode is worse than the first because it produces a confident sentence in the report saying coverage was verified.

The same reasoning rules out fixing it at the supervisor. Each sector supervisor was handed a sentence describing its sector and asked to enumerate the companies in it. Given "application-layer companies in the competitive set" and a model that has read Corveil's positioning, dispatching a worker on Corveil is the correct action. A supervisor is only as well-scoped as its brief, and this brief left the boundary between the two sectors to be decided independently by two agents that could not compare notes. That is not a judgment failure. It is an underspecified partition, and it will produce overlap on some runs and gaps on others depending on which company happens to sit near the seam that week.

Where you can intervene, and what each intervention actually changes

Four responses were proposed in the retro for run 39, and they are worth laying side by side, because three of them make the brief look correct next week and only one of them makes the tree correct.

ResponseWhat it changesWhat it leaves in placeCost per run
Deduplicate by company name at the merge stepCorveil appears once in the outputBoth workers still run; the surviving figure is whichever the merge saw first~$0.30 and ~20s of wasted work, now invisible
Ask the orchestrator to reconcile conflicting figuresConflicts get an explicit resolutionDuplicate research still happens; gaps like Aventine still silentOne extra inference call, plus the same waste
Tell workers to skip companies "already covered"Nothing — the check is unobservable to a workerEverything, plus a false compliance claim in the reportPrompt tokens and a new lie surface
Enumerate a non-overlapping scope in each delegationNeither supervisor can claim the other's company; unassigned companies fail loudlyNothing relevant to this failureZero at run time; one assertion at dispatch

The middle column is the one to read, and reading the table that way is what separates the four. Column two says what each response improves, and on that column the first three all look like progress. Column three says what survives each response, and there the first three are nearly identical: the duplicate research still happens, the gap is still silent, and the money is still spent. Only the last row has "nothing relevant to this failure" in that column, and the gap between that cell and the three above it is the whole comparison.

The first row is the one teams reach for, because the artefact that embarrassed everyone was the brief and deduplication removes the embarrassment. It is also the row that guarantees the failure recurs in a form nobody looks at: after deduplication the tree still spends twice on Corveil every week, still resolves the ARR conflict arbitrarily, and still drops Aventine.

The reason deduplication cannot reach the gap is worth stating exactly, because it is the tempting belief in this chapter and it is tempting for a good reason. Deduplication is a function of the output list. It compares entries against each other and removes a repeat. An absent company contributes no entry, so it changes nothing about the list a deduplicator sees. The quantity that stays constant under deduplication is the thing everyone was looking at — the brief, which now reads correctly. The quantity that varies underneath it is coverage, which the brief cannot show, because a document about five companies looks exactly like a document about the five companies it was supposed to cover.

An enumerated scope, asserted before anything is dispatched

The fix is to make the partition a property of the run rather than a judgment made twice, independently, by two delegates. The orchestrator already knows the six companies; it is the only agent in the tree that does. Assigning each to exactly one supervisor, and checking that assignment before a single delegation goes out, turns a class of silent output defects into a loud dispatch-time error.

python
UNIVERSE = ["northbeam", "corveil", "palisade", "tessera", "marlowe", "aventine"]

def partition_scopes(assignment: dict[str, list[str]]) -> dict[str, list[str]]:
    """assignment maps boundary_id -> the companies that boundary owns."""
    seen: dict[str, str] = {}
    for boundary_id, companies in assignment.items():
        for c in companies:
            if c in seen:
                raise ScopeOverlap(f"{c} claimed by {seen[c]} and {boundary_id}")
            seen[c] = boundary_id
    missing = set(UNIVERSE) - seen.keys()
    if missing:
        raise ScopeGap(f"unassigned: {sorted(missing)}")
    return assignment

ASSIGNMENT = partition_scopes({
    "root>infra": ["northbeam", "corveil", "palisade"],
    "root>apps":  ["tessera", "marlowe", "aventine"],
})

That function asserts exactly two set facts, and they are the two halves of what "partition" means. The loop asserts that no company appears in two lists, by refusing to write a name into seen twice. The missing check asserts that the union of the lists is the whole of UNIVERSE, by subtracting the names that were claimed from the names that exist and requiring nothing to be left over. Overlap is the first violated; a gap is the second.

What the pair licenses is precise, and worth not overstating: both of run 39's defects become impossible rather than unlikely, and they become impossible before any tokens are spent. Corveil is now in exactly one list, and the fact that it is a defensible member of either sector has stopped being a decision the system makes — it is a decision a human made once, wrote down, and can revisit deliberately. Aventine cannot be dropped, because dropping it means the assignment does not cover UNIVERSE and the run fails.

The residual is equally precise. The assertion says every company is claimed once; it says nothing about whether the company was claimed by the right supervisor, nothing about whether UNIVERSE lists the right companies, and nothing about what the worker then finds. Twelve lines of set arithmetic can close a coverage question and cannot open a correctness one.

The delegation brief from chapter 1 already has the field this belongs in, so nothing new needs to be threaded through the tree:

python
briefs = [
    DelegationBrief(
        run_id="wk40",
        boundary_id=boundary_id,
        parent_id=None,
        scope=companies,                 # enumerated, disjoint, exhaustive
        constraints=BRIEF_CONSTRAINTS,   # unchanged at every hop
        return_schema=SECTOR_RETURN_SCHEMA,
    )
    for boundary_id, companies in ASSIGNMENT.items()
]

The supervisor's system prompt then names its three companies rather than describing a category, and — this is the part that is easy to skip — the return contract can be checked against the same list. A sector summary whose companies array does not have exactly the names in scope is a contract violation, whether it invented a seventh company or silently returned two. That check costs a set comparison and catches the class of failure where a supervisor reconciles three worker reports into a summary that mentions two.

Two of the choices there are decisions rather than requirements, and separating them keeps the argument honest later. Requiring the union to equal UNIVERSE is what makes coverage checkable at all; without a declared universe there is nothing to subtract from. Writing ASSIGNMENT as a constant in the source, rather than having the orchestrator produce it and then validating it, is a decision — one that trades the orchestrator's flexibility for a partition that cannot drift between runs.

Open-ended tasks have no list to partition

Scope enumeration works because the unit of work is nameable before the run starts. Six companies are known on Friday; the brief is about those six and no others. A large share of hierarchical tasks are not like that. If the top-level ask were "identify the three most credible competitive threats that emerged this quarter", there is no list to partition, because producing the list is the work. Handing two supervisors a theme is then unavoidable, and the mechanism has to change: either a single dispatcher enumerates candidates first and only then partitions them, or delegates claim work items from a shared ledger and the claim is what makes coverage exclusive. Both of those reintroduce coordination the tree was supposed to avoid, which is a real cost of hierarchy on open-ended tasks and not a defect in this chapter's fix.

Nor does a clean partition make the two ARR numbers agree. Both figures in run 39 were real numbers that appear in that document; one is total ARR and one is subscription ARR net of professional services. Neither worker was wrong about what it read, and neither supervisor had any way to notice, because neither of them saw the other's output. A partition guarantees that only one worker reads Corveil's filing, which means the brief will now contain exactly one figure — and if the worker picks subscription ARR when the reader wanted total ARR, the brief is confidently, singularly wrong.

That is the sharpest limit on the fix, and it points the other way from the usual instinct. Removing a contradiction is not the same as being right, and a tree that returns one number where it used to return two has removed the only signal that a choice was being made at all. The orchestrator saw both figures in run 39 and did what a model given two sector summaries will do — it laid them out in the order it received them — but at least both were on the page.

The partition assertion, the enumerated scope and the contract check give the tree something it did not have in chapter 1: at every boundary, a specific claim about what should have crossed it, which makes it possible to say afterwards whether the crossing went well. That phrase — the boundary was clean — is about to start doing a great deal of work. Every reliability figure in the rest of this topic is a count of clean boundaries over total boundaries, and nobody has yet said what a clean one is.

Hierarchical / supervisor trees — read the current version at https://daviddominguez.dev/learn/en/hierarchical-supervisor-trees/two-workers-research-the-same-company-because-the-brief-let-them

Back to top