Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,25 @@ JOB=$(ade parse -d doc.pdf --wait 0 --id-only) # submit and return, exit 3
ade parse -d doc.pdf --id-only # re-run to resume; same id
```

## Build a schema

No schema yet? `ade schema build JOB_ITEM_ID` hands a parsed document to
an AI-agent session running the
[build-schema skill](https://github.com/landing-ai/claude-skills), which
authors and validates the schema through real extractions before
delivering it. On an interactive terminal a finished parse also asks what
you want next — build a schema, open the viewer, or exit — and exiting
prints both commands so you can run them yourself later.
You describe what to extract; the session negotiates accuracy
targets with you and iterates until the schema converges or is legibly
blocked. It needs an agent CLI on your PATH — Claude Code (`claude`,
ideally with the `landingai-ade` plugin) or Codex (`codex`); any other
agent can be declared in `~/.ade/config.json` as
`{"agent": {"name": "...", "command": ["..."]}}`. The delivered schema
lands in the named workspace as `deliverable/schema.json`, ready for
`ade extract`; set `{"schema_prompt": false}` to silence the post-parse
offer.

## Extract

| command | what it does |
Expand Down
10 changes: 10 additions & 0 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@ Prefer the explicit two-step (`parse -d`, then `extract JOB_ITEM_ID`) when
you want the same parse to feed several schemas — the id makes the reuse
visible.

## Schema authoring is not for agents driving this CLI

`ade schema build` (and the offer a human sees after an interactive
parse) launches a *foreground AI-agent session* running the build-schema
skill — it requires a real terminal on every stream and never fires
under `--json`, `--id-only`, or inside an agent host or CI. If you are
an agent and the user needs a schema authored, invoke the **build-schema
skill** (the `landingai-ade` plugin of landing-ai/claude-skills)
directly rather than shelling out to `ade schema build`.

## Pending and resume

Wait expiry is a normal outcome, not an error. If the poll budget
Expand Down
91 changes: 91 additions & 0 deletions docs/adr/0010-post-parse-agent-handoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# The schema gap is bridged by handing off to an agent CLI, not by ade authoring schemas

## Context

The workflow is parse → schema → extract, and the middle step was entirely
the user's: hand-author a JSON Schema, then pass it to `extract --schema`.
The build-schema skill (the `landingai-ade` plugin of
[landing-ai/claude-skills](https://github.com/landing-ai/claude-skills),
mirrored from `schema-loop` where it is benchmark-gated) already automates
that step properly — an AI-agent session drives this very CLI through
measured iteration (real extractions, reviewer-built ground truth,
converged-or-blocked stops) and delivers the final schema at
`<workspace>/deliverable/schema.json`. The CLI and the skill each knew
nothing of the other, so the natural flow — parse, build the schema,
extract — required the user to know the skill exists and wire it up
themselves.

## Decision

ade bridges to the skill by **launching an agent CLI as a foreground
subprocess**, in two entry points sharing one body (`schema_build.py`):

- **`ade schema build JOB_ITEM_ID`** — the standalone command, usable on
any stored parse.
- **A post-parse offer** — after a parse summary (fresh or cached hit) on
a real terminal, an arrow-key menu (ADR-0002's pattern) asks *what
next*, with three choices: build a schema, open the viewer, or exit.
Those are the two things a human actually does with a fresh parse, plus
the way out.

**Exit is last and the default**, so bare Enter preserves the old
behavior keystroke-for-keystroke. It is not a dead end: exiting (and
aborting with Esc, and finding no agent installed) prints both commands
with a runnable short id, because by then the summary's `next:` line has
scrolled behind a menu, and the person who chose Exit is exactly the one
who needs them restated. The `next:` line itself remains the
machine-mode teaching.

The viewer choice is the same subprocess-of-self re-exec the extract
handoff uses. A door that cannot open (no agent CLI installed) is
non-fatal — the parse already succeeded and billed; a door that opens
and then fails ends with that command's exit code.

ade collects only what it already knows plus the extraction **intent**
(and optional existing-schema / ground-truth files); per-field accuracy
targets are deliberately *not* collected — the skill's own intake
negotiates them in conversation. The seeded prompt carries the full parse
job item id, source, workspace (`<ade-home>/schema-runs/<slug>-<item-id>-<date>/`
by default — the item id, a content hash, keys it so two documents that
share a filename never share a workspace or each other's deliverable,
while re-running the same document resumes
by default), and skill-acquisition instructions. When the session ends,
ade looks for `deliverable/schema.json`, names it, and offers to run the
extract — a re-exec of this CLI (`update.reexec_argv`, shared with the
viewer daemons), which is mostly a free cache hit since the skill's final
iteration already ran it.

**Agent adapters, not one agent.** A small table (`agents.py`) knows
`claude` (Claude Code — triggers the skill natively via the plugin) and
`codex`; detection is a PATH lookup through the injected `which` port. A
menu choice is remembered as `agent.default` in `config.json`; a custom
`agent.command` entry launches any other CLI. Every adapter receives the
seeded prompt as its final positional argument.

**ade stays network-free here.** It never fetches, pins, or updates the
skill: non-native agents are told to use a local copy or shallow-clone
the marketplace repo themselves. A missing agent CLI fails legibly with
install instructions (non-fatally on the offer path — the parse
succeeded).

**Gating.** The offer fires only when stdin, stdout, and stderr are all
terminals, never under `--json`/`--id-only`, never inside an agent host
or CI (surface.py detection — an agent driving `ade parse` must never
meet a menu), and `schema_prompt: false` silences it. The standalone
command requires a real terminal even under `--json` (the child session
inherits the streams; a piped stdout would be corrupted by its TUI) and
then requires `--intent`, resolves the agent without a menu, and never
auto-runs the extract.

## Consequences

- The interactive flow gains a doorway the machine contract cannot see:
stdout stays payload-only, all prompts ride stderr, and every existing
`--json` consumer is byte-identical.
- Subprocess launch and PATH lookup are ports (`run_agent`, `which`), so
the suite tests the whole handoff offline — no real process ever runs.
- Agents driving ade should invoke the build-schema skill directly; the
root SKILL.md says so. `ade schema build` is for humans at terminals.
- The deliverable contract (`deliverable/schema.json`) is the one seam
ade trusts from the skill; if the skill's output layout ever changes,
this is the single place to follow it.
99 changes: 97 additions & 2 deletions docs/reference/help.json
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,91 @@
},
"band": "network verbs \u2014 the ADE job contracts"
},
{
"name": "schema build",
"usage": "ade schema build JOB_ITEM_ID [options] [--json]",
"summary": "Build an extraction schema for a parsed document by handing it to\nan AI-agent session running the build-schema skill (measured\niteration: real extractions, reviewer verdicts, converged-or-blocked\nstops). Ends by naming the delivered schema and offering the extract.\n\nBills credits: the skill validates every schema version with a real\n`ade extract` run, so a converging loop costs one extraction per\niteration per document (the extraction offered at the end is normally\na free cache hit \u2014 the last iteration already ran it). The session\nreports what it spent; `ade history list` itemises every run.",
"arguments": [
{
"name": "JOB_ITEM_ID",
"required": true,
"help": "A completed parse job item id (or unambiguous prefix)."
}
],
"flags": [
{
"flags": "--intent",
"metavar": "TEXT",
"required": false,
"default": null,
"help": "What to extract, in plain language (prompted interactively when omitted; required with --json)."
},
{
"flags": "--existing-schema",
"metavar": "FILE",
"required": false,
"default": null,
"help": "A schema to improve: the skill runs it unchanged first as the regression floor."
},
{
"flags": "--ground-truth",
"metavar": "FILE",
"required": false,
"default": null,
"help": "Golden ground truth seeding the skill's eval set (JSON keyed by document filename, or flat CSV)."
},
{
"flags": "--workspace",
"metavar": "PATH",
"required": false,
"default": null,
"help": "Skill workspace directory (default: <ade-home>/schema-runs/<source>-<item-id>-<date>/; an existing one resumes)."
},
{
"flags": "--agent",
"metavar": "TEXT",
"required": false,
"default": null,
"help": "Agent CLI to launch (claude, codex, or a config-declared name); default: the configured or only available one."
}
],
"supports_json": true,
"result": {
"shape": "object",
"keys": [
{
"key": "status",
"what": "'schema_built'"
},
{
"key": "job_item_id",
"what": "the parse item the schema was built against"
},
{
"key": "agent",
"what": "the agent CLI that hosted the session"
},
{
"key": "workspace",
"what": "the skill workspace (eval set, iteration log, dispositions live alongside the schema)"
},
{
"key": "schema",
"what": "absolute path of the delivered schema.json"
},
{
"key": "extract_hint",
"what": "the ready-to-run extract invocation"
},
{
"key": "extracted",
"what": "always false \u2014 the extract run is offered interactively, never performed silently"
}
],
"note": "Interactive terminals only: the command launches a foreground AI-agent session (claude, codex, or a config-declared CLI) seeded with the parse and your intent \u2014 that session runs the build-schema skill, which BILLS CREDITS: it validates every schema version with a real extract run, so a converging loop costs roughly one extraction per iteration per document. The extraction offered at the end is normally a free cache hit (the last iteration already ran it). Agents should invoke the build-schema skill directly instead of calling this."
},
"band": "schema authoring \u2014 the agent handoff"
},
{
"name": "history list",
"usage": "ade history list [options] [--json]",
Expand Down Expand Up @@ -1335,6 +1420,8 @@
"body": [
"parse \u2500\u252c\u2500> find \u2500\u2500> crop look at an element as an image",
" \u251c\u2500> view grounded page images + markdown",
" \u251c\u2500> schema build author the extract schema with an",
" \u2502 AI-agent session (build-schema skill)",
" \u2514\u2500> extract \u2500\u2500> view schema-shaped data, grounded",
"",
"1. parse ensure a document is parsed. Prints a job item id;",
Expand All @@ -1344,10 +1431,18 @@
" type, page, box, text.",
"3. crop render element regions as PNGs: one --element-id, or a",
" batch by find's own filters (--type figure, --all).",
"4. extract ensure a schema extraction exists for a parse job item",
"4. schema build author the extract schema when you don't have",
" one: hands the parse to an interactive AI-agent",
" session running the build-schema skill, then offers",
" the extract it delivers. The skill bills credits \u2014",
" it proves each schema version with a real extract",
" run, so expect one extraction per iteration per",
" document (interactive terminals only \u2014 agents should",
" run the skill directly instead).",
"5. extract ensure a schema extraction exists for a parse job item",
" (or bring-your-own markdown). It becomes its own job",
" item, referencing the parse \u2014 artifacts never copied.",
"5. view build the self-contained HTML viewer for a parse or an",
"6. view build the self-contained HTML viewer for a parse or an",
" extract item; --element-id emits a deep link to one",
" element \u2014 the citation contract.",
"",
Expand Down
Loading
Loading