Skip to content

Add C8: an EXTEND-only consumer agent a domain pack can declare - #8

Merged
JSv4 merged 1 commit into
mainfrom
feat/domain-consumer-agent
Aug 21, 2026
Merged

JSv4 merged 1 commit into
mainfrom
feat/domain-consumer-agent

Conversation

@JSv4

@JSv4 JSv4 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

The gap

A domain pack can install a corpus group and a GLOBAL orchestrator. It cannot install the agent that actually answers a question — the one scoped to the corpus holding the user's documents, which needs both that corpus's persona and reach across the group.

That configuration existed only as a script run by hand against one database. Anyone installing the pack got the orchestrator-only shape.

Measured on the us-export-control gold set:

shape grounded sections contributed by retrieval
orchestrator only (what an install gives you today) 6/15 7/15
corpus agent + this increment 13/15 14/15

The design

The pack supplies the text. The operator supplies the binding.

The pack can author the instructions because the group slug the text must name is the pack's own invention. It cannot supply the binding, because which corpus consumes a domain is unknowable at authoring time — so that is install_domain_pack --consumer-corpus <pk>, not a manifest field.

Neither half is useful alone, and each gets its own diagnostic: a manifest block with no --consumer-corpus is reported as not applied (C5) rather than passing silently; the flag with no block is an error.

Why mode: EXTEND is required rather than recommended

This is what keeps the feature inside the existing prohibition rather than an exception to it.

Depend on a consuming corpus's persona to make its tools reachable…

That clause was written when REPLACE was the only instructions mode, so "contribute instructions to a consuming corpus" and "substitute for that corpus's persona" were necessarily the same act. EXTEND separates them. A consumer_agent is:

  • self-contained — it names the group slug itself, so its tools are reachable on its own terms (the same rule C3 holds the orchestrator to, now applied here);
  • additive — the corpus's persona survives, single-sourced on the corpus.

A REPLACE consumer agent would violate the clause as originally written, and is refused for exactly that reason. DOMAIN_PACKS.md now states both sides of the principle — may add, may not depend on or replace — with a note recording why the clause reads as it does, so the next reader doesn't have to reconstruct it.

What's checked where

validate_domain.py checks the file-decidable half and says explicitly that it cannot check the binding:

  • mode is EXTEND
  • instructions_file present, inside the domain dir, non-empty
  • tools are grantable
  • group slug named when search_across_corpora is declared

Eight new self-test cases — 18/18 pass, including two that must stay clean rather than error:

ok: consumer_agent is optional
ok: a well-formed consumer_agent validates
ok: C8: consumer_agent.mode REPLACE is rejected
ok: C8: consumer_agent with no mode is rejected
ok: C8: consumer_agent whose instructions_file is missing is rejected
ok: C8: consumer_agent that never names the group slug is rejected
ok: C8: unknown consumer_agent tool is rejected
ok: a consumer_agent with no tools need not name the slug

The last one matters: an agent declaring no reach is a legitimate shape (answering rules only), so the slug rule — which exists solely to make search_across_corpora callable — must not fire.

About consumer_agent.txt

It is the project's reach block and answering rules verbatim. Editing them would invalidate the measurement above, so I concatenated rather than rewrote, adding only a header comment.

Worth knowing when reviewing: it deliberately quotes no regulatory text. Anything quoted there is reproducible from the prompt alone, which makes it worthless as evidence that retrieval happened — and the project's scorer filters out spans the prompt supplies, so quoted text would score as nothing. That property should survive future edits.

Companion PR

Platform half — Corpus.default_agent, the resolution path, and the installer's --consumer-corpus: Open-Source-Legal/OpenContracts#2274. Neither PR is useful alone; the schema needs an installer that reads it, and the installer needs a schema to read.

Backward compatible: consumer_agent is optional, and a pack without it installs exactly as before.

A domain pack could install a corpus group and a GLOBAL orchestrator. It could
not install the agent that actually answers a question -- the one scoped to the
corpus holding the user's documents, which needs BOTH that corpus's persona and
reach across the group. That configuration existed only as a script someone ran
by hand, so an installed pack reproduced the orchestrator-only shape.

Measured on the us-export-control gold set: orchestrator-only grounded 6/15
questions with retrieval contributing sections on 7/15; the corpus agent
carrying persona + this increment reached 13/15 and 14/15.

The split is the design. The PACK supplies the text, because the group slug the
text must name is the pack's own invention. The OPERATOR supplies the binding
(`--consumer-corpus <pk>`), because no pack can know which corpus will consume
it. Neither half is useful alone, and each gets its own diagnostic rather than
a silent no-op.

mode: EXTEND is required, not recommended. This is what keeps the feature
INSIDE the existing prohibition on coupling to a consuming corpus's persona
rather than an exception to it: REPLACE would overwrite text the pack does not
own, EXTEND appends and leaves the corpus persona single-sourced. The
prohibition was written when REPLACE was the only mode available, so
"contribute instructions" and "substitute for the persona" were necessarily the
same act; EXTEND separates them. DOMAIN_PACKS.md now states both sides of that
principle -- may add, may not depend on or replace -- with a note recording why
the clause reads the way it does.

validate_domain.py checks the file-decidable half (EXTEND, file present and
non-empty, tools grantable, group slug named when search_across_corpora is
declared) and says explicitly that the binding is not checkable here. Eight new
self-test cases, including two that must stay CLEAN: consumer_agent is optional,
and one declaring no tools need not name the slug.

us-export-control gains the block and consumer_agent.txt. That file is the
project's reach block and answering rules VERBATIM -- editing them would
invalidate the measurement above. It deliberately quotes no regulatory text:
anything quoted there is reproducible from the prompt alone and so worthless as
evidence that retrieval happened.

Platform half: Open-Source-Legal/OpenContracts#2274.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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