case-data-material-planner · git:20260908.9e6261b · 2026-09-08 · sha256 a03fbd9cd8b73f2f

case-data-material-planner git:20260908.9e6261bA

Immutable. This exact content is served forever at /api/v1/blob/a03fbd9cd8b73f2f.

---
name: case-data-material-planner
description: >-
  Prepares executable case preconditions and case-executable.md from a
  requirement or written cases. Internal case-material engine; enter
  through testdata-generation so SKILL_DIR and adapters resolve.
license: Apache-2.0
---

# case-data-material-planner

One-stop material engine between case authoring and case execution.

- Build an optional business-context graph from requirements
- Parse entities and actions from each case
- Construct data and bind commands through adapters
- Emit `case-executable.md`

## How the work is divided

`scripts/pipeline.ts` owns orchestration. You supply judgment at three points
only — `parse-case`, `knowledge-build`, and `select-tool` — and the script does
construction, binding, linting, and writeback. This split exists because those
three stages need reading comprehension while the rest must be reproducible: a
case re-run tomorrow has to yield the same manifest, which is impossible if a
model re-derives each step. So when the runner exits asking for one of the three,
launch that Agent, merge its patch, and `--resume`; do not take over a stage the
script already implements (`invoke_entity.ts`, `bind_action.ts`,
`slot_render.ts`), and with 2+ cases go through the dispatcher so each case gets
its own `pipeline.ts` process.

## Hard constraints

These are the ones that silently corrupt output rather than failing loudly,
which is why they are worth stating:

- **Do not invent business IDs, amounts, or config values.** A plausible-looking
  ID makes a case that fails at execution time with no trace of where the value
  came from. When unsure, record `failReason` and let the gate surface it.
- **Parse before constructing.** Data needs come from the case's preconditions
  and steps; skipping parse means constructing something the case never asked
  for.
- **Keep `case-executable.md` business-only.** No `node` / `python` / `bash`
  invoke lines, no `skillRoot`, cwd, host, or mock-server ports — those live in
  `manifest.json` (`invokeCmd` / `filledCmd`). The case document is read by
  people and replayed in other environments, where local paths and ports are
  wrong or meaningless.
- **Persist every non-envelope business field**, not just the primary ID. A
  downstream step usually needs the attributes that came back with it, and
  re-fetching them is not always possible.
- **Respect the gates.** C3 stays blocking when lint-gate is abnormal, and the
  lint-gate loop runs at most 3 rounds before it must hand the decision to the
  user — an unbounded loop burns the run without converging.
- **During knowledge-build, describe the business, not a shopping list.** The
  graph answers "what does this domain look like"; deciding what data to prepare
  is the parse stage's job and doing it early biases the parse.

---

## Entry routes

After receiving the request, classify the input:

### Path A: requirement / design doc (+ cases)

1. Read `planner.md` and run `scripts/pipeline.ts`
2. Exit 12 → knowledge-build Agent, merge, `--resume`
3. Then parse (exit 11) or continue if cases were already parsed

### Path B: cases only (no requirement doc)

1. Read `planner.md` and run `scripts/pipeline.ts --case-id … --source …`
2. Reuse `./testdata/case-materials/business-context.json` via `--context` when it exists
3. Otherwise knowledge-build is skipped by the runner
4. Handle exit 11 (parse-case) then `--resume`. Construction, binding, and
   writeback stay inside `pipeline.ts` — do not take those stages over.

### Path C: batch (2+ cases)

When the user supplies **2 or more cases**:

1. Read `case-dispatcher.md` and follow it
2. One `pipeline.ts` process per case (parallel). Shared knowledge-build once
3. The user talks to the dispatcher, not a per-case Planner Agent

**Rule:**

- case count ≥ 2 → Path C
- case count == 1 → Path A or B

### Input sources (highest first)

| Priority | Source | How | Notes |
|---|---|---|---|
| 0 | Caller-supplied `sources[]` | Passed in the Task / delegate prompt | Each item is `{caseId, sourceType, sourcePath}`; skip interactive collection |
| 1 | Document URL | `doc_source.get(<ref>)` | Requirement / design / knowledge-base page |
| 2 | `planId` | `workspace_context.get_plan(<id>)` + `api_catalog.search_plan_changes(<id>)` | Plan detail and change APIs |
| 3 | Local file path | Read the file | |
| 4 | Pasted text | Use as-is | |

Priority 0 is an optional fast path. `sourceType` values:
`remote-case | local-file | planId | paste`.
They map to the four legal `caseSource.type` values.
Callers that omit `sources[]` still use priorities 1–4 interactively.

## Material gate (case source)

The prerequisite material is **the case pack**, not executor fields.

- **No case source** → ask for a file, paste, `planId`, or document URL. Do not invent case text. Do not start parse or construct.
- **Path A without cases** → finish knowledge-build, then **wait** for cases. Do not jump to invoke.
- **Path B** → at least one case is mandatory. `knowledge-build` may be skipped; parse may not.
- **Batch (2+)** → every listed case needs a resolvable source. Do not silently drop a case.
- After the user supplies cases, `--resume` `pipeline.ts`. Do not skip parse, invoke, or writeback.
- Executor / tool params inside pipelines follow the original fill rules (defaults for optional fields; do not invent core IDs). C3 / lint-gate stay blocking.

---

## Constraints

- Execution logic lives in `scripts/pipeline.ts`; `planner.md` is the host protocol
- LLM Agents allowed: `parse-case`, `knowledge-build`, `select-tool` only
- Do not skip C3; the happy path has zero human gates
- Lint-gate loop is at most 3 rounds
- Entities and actions are equal tracks; an empty track short-circuits
- `targetLocation` is the only bind to the original case text
- Action params: `paramsFromEntities` / `paramsFromGenerators` / `paramsFromPriorActions`
- Tool verify uses `tool_registry.query_input_list` (see `references/param-source-spec.md`)
- `businessContext = null` is a valid degraded mode
- Entities follow `constructionStrategy`: `tool-build` → `invoke_entity.ts`,
  `config` → `generate_config_commands.ts`, `static-value` → filled at parse,
  `runtime` → deferred to execution
- New scenes: drop a slot under pack `slots/` or `workspace.slot_roots`.
  `select_tool.ts` binds from `slot.yaml` (or inferred executors). Do not edit the selector

## Path roots

Two roots, and mixing them up is the most common failure here:

| Variable | Points at | Owns |
|---|---|---|
| `SUBSKILL_DIR` | directory of this file (`$SKILL_DIR/references/case-data-material-planner/`) | `scripts/`, `agents/`, `templates/`, `references/` — every relative path in this sub-skill |
| `SKILL_DIR` | the parent skill root that contains the top-level `SKILL.md` | `scripts/adapters/`, `scripts/search_data_build.ts`, `slots/` |

There is no `adapters/` folder under `SUBSKILL_DIR/scripts/`. Adapter calls go to
`$SKILL_DIR/scripts/adapters/cli.ts`.

## File layout

```
SKILL.md                    entry routing (this file)
planner.md                  host protocol for pipeline.ts
case-dispatcher.md          multi-case: one pipeline.ts per case
agents/                     LLM specs (parse / knowledge-build / select-tool)
  knowledge-build.md
  parse-case.md
  select-tool.md
  writeback.md              slot_render render rules, applied by the script
scripts/                    pipeline.ts + construct/writeback scripts
templates/                  manifest / cache / context templates
references/                 thresholds and contracts
```

## Outputs

- `./testdata/case-materials/business-context.json`
- `./testdata/case-materials/tool-binding-cache.json`
- `./testdata/case-materials/{case-id}/manifest.json`
- `./testdata/case-materials/{case-id}/case-executable.md`

## Dependencies

No private CLIs. All I/O goes through the parent skill's adapters
(`$SKILL_DIR/scripts/adapters/cli.ts`):

| Need | Adapter |
|---|---|
| Discover / install domain skills | `skill_marketplace` |
| Query / execute / publish tools | `tool_registry` |
| Discover HTTP APIs | `api_catalog` |
| Proven methods | `experience_store` |
| Documents | `doc_source` |
| Test-plan context | `workspace_context` |
| Case writeback | `case_writeback` |
| Config and SQL | `config_store`, `data_store` |

## Collaboration

This sub-skill is hosted by `testdata-generation`. `select_tool.ts` reuses
`scripts/parallel_search.ts`, which calls the same adapters. Enterprise tools
are added through those adapters or a new slot under `slots/` /
`workspace.slot_roots` — `invoke_entity.ts` only dispatches on `toolType`.