git:20260820.098938c to git:20260820.9936d96

92 added, 259 removed. Audit A to A.

---
name: deepline-plays
description: 'Use for Deepline GTM work that searches, enriches, scores, collects signals, or automates a workflow: find companies or people, enrich a CSV, find emails or LinkedIn, compare providers, build a waterfall, create a webhook or cron, or write a Play. For live information work, run a small heterogeneous experiment, exploit the observed winner, and reopen misses. Skip pure copywriting and non-GTM research.'
---
# Deepline Plays
## Quick Start
```bash
npm install -g deepline
# Fallback for secure sandboxes: mkdir -p "$HOME/.local" && npm config set prefix "$HOME/.local" && export PATH="$HOME/.local/bin:$PATH" && npm install -g deepline --registry https://code.deepline.com/api/v2/npm/
deepline auth register --wait auto
deepline auth wait --timeout 120 # completes Cowork/browser approval; no-op if already connected
deepline auth status
deepline -h
```
## CLI resolution
Run `deepline` when it is available. If the shell reports that command is missing, use `<workspace-root>/.deepline/runtime/bin/deepline` (or the npm-created `.cmd` shim on Windows). If neither exists, follow `https://code.deepline.com/INSTALL.md` to set up Deepline.
- The job defines truth. The catalog supplies executable moves. The Play records what worked.
-
```text
- contract → compare → exploit → recover → export
+ contract → compare → exploit → recover → export → price
```
- This is ordinary TypeScript. Do not invent a DSL, an agent runtime, or a provider wrapper. A `SearchProgram` is one small function that calls a tool, fetch, child Play, private connector, or local artifact and returns a typed attempt. `runSearchExperiment` owns the fair pilot, ranked waterfall, holdout, gap-only retries, and cost/coverage report.
-
- ## Map the source terrain before code
-
- For an open-world, thin-data, or costly job, read
- `references/strategy-pre-research.md` **before** opening the scaffold. Start
- with where the fact could live, not with which provider to call. This is the
- execution-sized pre-research pass: catalog actions, public sources, private
- data, and existing Plays are all candidate ways to materialize the same claim.
-
- Write a one-screen source map for each stage. It is complete when the stage's
- acceptance fact has three plausible, differently-shaped places to look and a
- first small probe for each:
-
- | Claim shape | Places the fact may live | Different route geometries |
- | ------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------- |
- | Company and qualification | entity index, registry/filing, first-party page, internal account data | filter query, registry page, direct source traversal |
- | Current person and role | people index, leadership directory, professional profile, CRM | company/domain join, title/function query, profile/roster extraction |
- | Revenue, event, or signal | structured data, dated public record, company document, internal event data | entity lookup, filing/document retrieval, event/date query |
-
- Use `tools list` and `tools describe` to map current catalog capabilities. Use
- search to find the public source or dataset itself; once a source is known, map,
- fetch, or parse that source directly. The map becomes 6–12 route cards across
- the relevant stages. It is not a provider shopping list: each card says which
- corpus, join, or failure mode makes it valuable when another route misses.
-
- Treat the first catalog request as a preflight decision. If it fails and the
- job needs a Deepline tool or durable runtime, record the exact error in the
- source map and stop with the scaffold still unbound. Do not expand it into
- throwing placeholder routes: they look like an experiment to a structural check
- but cannot compare, exploit, or recover anything. A route based solely on an
- already-confirmed local artifact or direct source may continue; otherwise report
- `needs catalog/schema` rather than producing a pretend Play or CSV.
-
- For supplied, trusted rows, skip company discovery but still map two independent
- ways to complete each missing fact. For an actual source map, scout assignment,
- and card format, read `references/strategy-pre-research.md`.
-
- ## The only live-information rule
-
- Do not present a live result as complete until its durable Play has:
-
- 1. Compared two materially different routes on the same unit.
- 2. Used an observed valid route on a later unit.
- 3. Tried an eligible dormant route only for real unresolved gaps, or recorded why none remained within the credit cap.
- 4. Exported accepted and unresolved rows from the completed run.
-
- When the user has not supplied company rows, the company-stage rows are source
- partitions such as query shards, registry pages, or geography × category. The
- company experiment produces the cohort. A literal array is appropriate only for
- user-supplied known rows, because otherwise it hides the discovery decision the
- Play is meant to test.
-
- ## Score a run; evaluate a concept
-
- A **score** chooses a route inside one live Play. `runSearchExperiment` already
- scores only accepted evidence: complete rows, verified required claims,
- supported evidence, source misses, adapter failures, observed calls, and
- observed Deepline credits. It exposes a cost/coverage frontier rather than
- letting a model trade unsupported answers for a better-looking number.
-
- An **eval** asks whether an approach generalizes beyond that one run. A concept
- is an information geometry such as “registry page → operator evidence,” not a
- provider name or a prompt variant. Run an eval when a workflow will recur, a
- source map/scaffold/skill changed, or an economic claim needs evidence. A
- single pilot plus untouched holdout is the smallest useful eval. A repeated
- task needs a frozen case set that includes normal, sparse, and likely-miss
- units.
-
- Freeze these before comparing concepts:
-
- 1. The contract and denominator. Do not lower a required claim to make a
- candidate look better.
- 2. Cases, source/date snapshot where relevant, credit ceiling, and acceptance
- verifier. Each concept sees the same cases and its own receipts.
- 3. The decision order: hard truth and cohort gates first, then verified
- coverage, then marginal Deepline credits/calls and latency. Record unknown
- cost as unknown, never zero.
-
- Export the run's result dataset **and** route scorecard. Across repeated evals,
- compare the same scorecard columns by concept and failure slice. A route that
- wins one easy pilot but loses sparse rows or produces adapter failures is a
- local winner, not a reusable default. Read `references/evaluating.md` when
- designing a repeated evaluation or changing this skill.
-
- ## Establish the execution boundary
-
- Create the durable Play before sourcing final rows. It is the only artifact
- allowed to produce the user-facing list: it keeps the pilot comparable, records
- which route supplied each fact, and makes a later gap actionable. Direct calls
- are valuable only as sentinel probes for an input or response seam; their rows
- do not enter the final cohort.
-
- The output receipt is concrete: before writing final rows, run
- `run-and-export-search-experiment.py` and obtain its `{ ok: true, runId,
- output }` response. Until that receipt exists, work is a source map or a probe,
- not a deliverable. Writing a CSV from remembered values or direct-call results
- hides which route won and erases the unresolved frontier, so it cannot close the
- job.
-
- The completed live artifact has four receipts:
-
- 1. Two materially different routes compared on the same unit.
- 2. An observed valid route used on a later untouched unit.
- 3. A dormant route challenged only for an unresolved gap, or an explicit reason
- no eligible route remains within the frozen Deepline-credit ceiling.
- 4. An export of accepted and unresolved rows from that completed Play run.
+ Ordinary TypeScript, no DSL. A `SearchProgram` is one function that calls a tool,
+ a fetch, a child Play, a connector, or a local artifact and returns a typed
+ attempt. `runSearchExperiment` owns the pilot, ranked waterfall, holdout,
+ gap-only retries, and cost/coverage report.
- Use `tools list` and `tools describe` to seed cards without spending a row
- budget. If a sentinel probe exposes a bad payload or adapter, repair or replace
- that route in the Play before calling it again. Start the scaffold before a
- third live route probe. This keeps discovery from becoming an unscored terminal
- waterfall whose results cannot be exploited or recovered.
+ ## Deliverable
- ## Run the frontier loop
+ | Part | Contents |
+ | ------------------- | ----------------------------------------------------------------------- |
+ | **Result line** | rows in / accepted / marginal credits per accepted row / run id |
+ | **CSV** | the user's exact headers, per-claim source, `miss_reason` on every null |
+ | **Unresolved rows** | in the same file; a null carries an absence receipt |
+ | **Route table** | initial and final waterfall, cost and completions per route |
+ | **COST RECEIPT** | the block `run-and-export-search-experiment.py` prints, verbatim |
+ | **Next actions** | dormant routes and what each would buy, at measured cost deltas |
- A single direct tool call is allowed only as a sentinel probe for an input or getter. If it errors, repair or replace that route in the Play. Do not issue more row-by-row terminal calls. A broken adapter is not a source miss.
+ - Marginal, never amortized. Total ÷ successes reported 1.51 credits/email for a
+ route whose real marginal cost was 0.21.
+ - Pass the printed block through. Do not recompute credits in prose.
+ - A catalog ceiling stops the run; it is not spend. Label it. A 120-credit
+ ceiling truncated recovery at ~12 credits actual, and two apparent logic
+ regressions were budget artifacts.
- ## Decide the topology first
+ ## Read one job page
- Write one sentence before touching tools:
+ Read the row that matches this job, and only that row. Each page is complete for
+ its job: source geometry, route ladder, pilot sizing, stop conditions.
- ```text
- unit + decision + required facts + scale
- ```
+ | The job | Page |
+ | ----------------------------------------------- | --------------------- |
+ | Companies or people that are not rows yet | `jobs/finding.md` |
+ | Columns to fill on rows you already have | `jobs/enriching.md` |
+ | Claims that need attributable evidence | `jobs/researching.md` |
+ | A trigger, review gate, or external side effect | `jobs/automating.md` |
- | JTBD | Unit | Required completion facts |
- | ------ | --------------------- | --------------------------------------- |
- | Search | market partition | identity + qualifying evidence |
- | Enrich | frozen supplied row | requested claim for that exact identity |
- | Signal | subject × time window | subject, change, dated source |
- | Score | accepted row | bound facts + reproducible verdict |
+ Two lookups, consulted on a trigger rather than read up front:
+ `shared/authoring.md` for Play syntax outside the scaffold, and
+ `references/debugging.md` for a failed, empty, or misshapen run.
- Keep requested fields required. Do not make contacts, titles, LinkedIn, dates, or evidence optional merely to promote a run. A null requires an absence receipt: materially different routes tried, typed outcomes retained.
+ **If your configuration forbids subagents, say so before starting serial work.**
+ Resolving that conflict silently cost one run ~30 minutes.
- Choose one shape:
+ ## Topology
- - **Known rows:** one experiment enriches the supplied rows.
- - **Open-world discovery:** rows are query/page/geography/registry partitions, never remembered companies. The experiment emits accepted companies.
- - **Company → person:** two stages. Company programs compete first; only `companyExperiment.finalResults` become contact rows. Company and people tools are sequential stages, not consensus.
- - **End-to-end:** compare only when every program can produce the same complete final row from the same starting seam.
+ Write `unit + decision + required facts + scale` before touching tools.
+ Requested fields stay required; demoting one to promote a run is not a pass. A
+ null needs an absence receipt: materially different routes tried, typed outcomes
+ retained.
- ## Browse just enough catalog
+ One shape. **Known rows:** one experiment over the supplied rows. **Open-world
+ discovery:** rows are query/page/geography/registry partitions, never remembered
+ companies. **Company → person:** two sequential stages, not consensus; only
+ `companyExperiment.finalResults` become contact rows. **End-to-end:** compare
+ only when every program produces the same complete final row from the same seam.
- Start with information roles, not a remembered vendor. Search, inspect the returned categories, then describe the two active and two possible dormant routes. Do not dump the full catalog or browse for minutes before coding.
+ ## Catalog
```bash
deepline tools search "<information role and controls>" --json
+ deepline tools grep "<substring>" --json # ranked search has returned the same
+ # irrelevant hits for three different queries
deepline tools list <returned-category> --json
- deepline tools describe <tool-id> --json \
- | python3 <skill-root>/scripts/show-declared-getters.py
- ```
-
- The getter view can also read a saved contract without re-querying:
- Also look for public primary sources, user-owned data, local artifacts, and
- prebuilt Plays. The source terrain step above is required for open-world work;
- assign 3–6 short-lived scouts by information geometry when the map needs more
- than one perspective.
-
- ```bash
- python3 <skill-root>/scripts/show-declared-getters.py "$WORKDIR/<tool-id>.json"
- ```
-
- `tools describe` is the authoring contract. For every `ctx.tools.execute`, bind a named declared `playExpression` from the response that made that call:
-
- ```ts
- const response = await ctx.tools.execute({
- id: 'route_a',
- tool: '...',
- input,
- description: '...',
- });
- const value = response.extractedValues.described_value?.get() ?? null;
- ```
-
- For lists, map provider-shaped rows through the durable handle's `keys`, not a guessed raw preview path:
-
- ```ts
- const list = response.extractedLists.companies;
- if (!list) throw new Error('GETTER_REQUIRED: companies');
- const rows = await list.get().peek(10);
- const domainKey = list.keys.company_domain;
- const domain = domainKey ? rows[0]?.[domainKey] : null;
+ deepline tools describe <tool-id> --json | python3 <skill-root>/scripts/show-declared-getters.py
+ python3 <skill-root>/scripts/show-declared-getters.py "$WORKDIR/<tool-id>.json" # saved contract
```
- Use `toolResponse.raw` only to bind a value back to an exact source excerpt, to debug, or for an explicitly undeclared field after one sentinel probe. Never cast it into an invented `Company[]` or `Person[]`. If a declared list lacks a needed field, that is an adapter seam: add an evidence route or bind the observed path after the probe.
-
- ## Write the first real Play now
+ `tools describe` is the authoring contract and can disagree with runtime: a
+ declared getter has been absent, and a tool documenting one scalar has returned a
+ full list. Bind a named declared `playExpression` and sentinel-probe one row
+ before scaling. `toolResponse.raw` is for an exact source excerpt, debugging, or
+ an undeclared field after that probe — never a cast into an invented `Company[]`.
- Scaffold once, edit the first two routes, then run it. Do not read the whole reference library first.
+ Cover source classes before provider names — index, SERP, primary document,
+ registry, event feed, first-party data, aggregator, validator. Two vendors
+ reaching the same terminal corpus are one evidence lineage.
- ```bash
- python3 <skill-root>/scripts/scaffold-search-experiment.py \
- ./deepline/data/<task-slug> --name <task-slug>
- ```
+ Record each route's pricing basis: per call, per returned result, or unknown. A
+ confirmed-uncharged miss justifies a broader challenge wave, not a narrower one.
- For an open-world company → person job, use the stage-aware scaffold instead.
- It is deliberately one Play, not a company collector plus a separate
- `contact-lookup.play.ts`: that separate shape is how a direct loop bypasses the
- pilot, winner selection, recovery, and accepted-company handoff.
+ ## Build and run
```bash
python3 <skill-root>/scripts/scaffold-search-experiment.py \
- ./deepline/data/<task-slug> --name <task-slug> \
- --topology company-to-person
+ ./deepline/data/<task-slug> --name <task-slug> --input-csv <rows.csv>
```
- Change only these seams on the first pass:
-
- 1. `rows` and the required claim contract.
- 2. The incumbent's literal mechanism, declared getter, evidence binding, and canonical entity key.
- 3. One heterogeneous challenger that can satisfy the same stage contract.
- 4. The final row mapping.
+ Read its printed `next` list: it carries the four seams, `tools: [...]`,
+ `coherenceChecks`, and the company→person handoff at the point you edit them.
+ `--input-csv` also writes a stratified `fixture.csv`. Iterate route code against
+ that; use the full cohort only for a scored run.
Keep the top-level `definePlay` description short and concrete. The UI shows it
below the Play identifier. Catalog categories are derived from the registered
tools used by the Play; do not author category metadata on the Play itself.
- The final mapping is the CSV contract. Use the field names the user asked for
- exactly: an internal `company_domain` claim can export as `domain`,
- `contact_title` as `title`, and `contact_linkedin` as `linkedin_url`. A useful
- row with renamed headers still breaks downstream imports and hides coverage
- checks.
-
- Give each retained program an honest information shape and terminal lineage. Two vendors that return the same page may compete on coverage/cost, but cannot form independent evidence consensus. Several query variants of one provider can be a coverage fallback only when their observed miss behavior differs.
-
- When five or more routes are viable, bind at least four before the first batch: two active, two dormant. This does **not** call them all per row. The helper fans the active pair out in parallel, exploits the winner, then spends dormant routes only on gaps. A catalog of twenty tools followed by two handwritten calls is not broad exploration.
-
- One program must not put competing routes in `Promise.all` or a dataset column. The helper already parallelizes programs and needs separate outcomes to rank. If one mechanism truly has dependent calls, assign each result a named response and consume its own getter sequentially.
-
- Use `boundClaim`, `found`, and `attempt` from the copied helpers. `boundClaim`
- uses `bindResearchEvidenceToSource`: the literal returned value must occur in
- the source receipt. Candidates are not final claims. A finder plus verifier is
- a candidate seam plus an acceptance seam; a validator rejection reopens only
- that row/claim.
-
- For company → person, call `runSearchExperiment` twice. Derive the second stage from accepted company results, not a raw/handpicked side array:
-
- ```ts
- const contactRows = companyExperiment.finalResults
- .filter((result) => result.complete)
- .map((result) => ({
- domain: verifiedSearchClaimValue<string>(result, 'company_domain'),
- company_name: verifiedSearchClaimValue<string>(result, 'company_name'),
- }));
- ```
-
- If stage one yields zero accepted rows, stop before stage two. Inspect that run, repair one mapping/claim seam or replace one route, then rerun stage one. Do not substitute direct web research or a manual cohort.
-
- ## Run, inspect, recover
-
- Use this one command for the first output. It runs the structural check, Play check, completed Play, and run-bound export; it refuses to overwrite a CSV.
-
```bash
+ deepline billing balance --json
python3 <skill-root>/scripts/run-and-export-search-experiment.py \
- ./deepline/data/<task-slug>/<task-slug>.play.ts \
- --input '{}' --out ./results.csv
+ ./deepline/data/<task-slug>/<task-slug>.play.ts --input '{}' --out ./results.csv
+ python3 <skill-root>/scripts/cost-receipt.py <run-id> --scorecard <scorecard>.csv # already-run
```
- For the stage-aware company → person scaffold, its printed command already has
- `--company-to-person`. That enforces two experiments, live company discovery,
- and the accepted-company handoff before a run can export.
-
- Then inspect the run. If target coverage is unmet, repair a malformed getter, then test an unused heterogeneous route only for unresolved units. Do not declare `programs_exhausted` because two easy routes were tried while catalog options remain unbound. Stop when the contract is met, the frozen credit cap is met, or distinct eligible routes yield a real absence receipt.
- The generated `run-and-export-search-experiment.py` command is the output gate.
- Use it for the first result instead of calling `deepline plays run` directly: it
- checks the experiment shape, runs the completed Play, and exports that run's
- dataset. This is what prevents a syntactically valid ordinary Play from quietly
- becoming an unscored batch or a hand-authored CSV.
-
- Bind each retained route as one named `SearchProgram` with an honest hypothesis,
- lineage, maximum calls, cost ceiling when paid, and expected miss. Add every
- bound route to `boundProgramIds`. `explorationProgramCount` is the parallel
- first wave, not the pool size. The runtime supplies parallelism across programs;
- putting competing routes in one `Promise.all` hides their receipts and prevents
- ranking.
+ `run-and-export` does the structural check, Play check, completed Play, run-bound
+ export of both the results dataset and the route scorecard, then the COST
+ RECEIPT. Its `{ok: true, runId, output}` is the completion receipt: before it the
+ work is a probe, and a CSV written from remembered values hides which route won.
- Quality gates precede economics. Among valid results prefer fewer observed Deepline credits, then fewer calls. Use run receipts when present; otherwise use a catalog ceiling and label it unknown. Never expose provider spend.
+ Receipt labels:
- Report: output path, run ID, initial/final waterfall, pilot and holdout result, recovery attempts, unresolved/absence receipts, and whole-run Deepline cost.
+ - **CUT CANDIDATE** — spent credits, completed nothing. Cut it. One route at 3.95
+ credits/call, 200× a search, ran ten rounds for zero results because the
+ scorecard reported no cost at all.
+ - **NEVER REACHED** — never invoked, so its zero results are not a ceiling and not
+ a source miss. `maxFallbacks` bounds the dependency-closed waterfall and
+ defaults to 2; raise it (up to 4, scaled to pool size) or drop the route.
+ - **cached calls** — reruns of the same inputs reuse tool receipts. Quote the
+ marginal rate, not this run's total.
- ## Subagents and references
+ Quality gates precede economics; among valid results prefer fewer observed
+ credits, then fewer calls. Never expose provider spend.
- Use one or two short-lived subagents only when there are several genuinely different source geometries. Give each the same contract and one source lane. They return one strategy card and ordinary TypeScript block; the parent binds, runs, and judges routes. They do not execute providers or decide truth.
+ Reusing a route across jobs is an eval, not a score: freeze the contract,
+ verifier, cases and ceiling, and stratify the case set (normal, sparse,
+ likely-miss, collision-prone) rather than picking easy rows after seeing results.
+ A concept is an information geometry, never a vendor.
- Read only the matching detail when needed:
+ ## Subagents
- | Need | Read |
- | -------------------------------------------- | ------------------------------------- |
- | Repeated strategy evaluation or skill change | `references/evaluating.md` |
- | Open-world company or people discovery | `jobs/finding.md` |
- | Known-row enrichment or scoring | `jobs/enriching.md` |
- | Automation or activation | `jobs/automating.md` |
- | Tool choice and source geometry | `references/provider-navigation.md` |
- | Source-first ideation or subagent lanes | `references/strategy-pre-research.md` |
- | Failed/empty/misshapen run | `references/debugging.md` |
- | General Play syntax | `shared/authoring.md` |
+ One or two, only when several source geometries are plausible: same contract, one
+ source lane each, returning a strategy card and ordinary TypeScript. The parent
+ binds, runs, and judges. Verification fans out the same way — four defects found
+ in four sequential rounds of eyeballing output fit in one pass over row batches.