Skip to content
Back to skills

Propose Price

ASecurity

Chooses the sale price for one model from the candidates the pricing script allows, with a written rationale, and has the script verify the choice. Use as the price stage once quote.json, comparables.json, viability.json and the preview drafts exist. Not for computing a price (price_candidates.py), not for market research (research-comparables), not for copy.

  • 6 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 16, 2026
ai-agentspythongobashapi

Works with

  • api

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned October 16, 2026

npx -y skills add 3DAPI-Inc/dapilot --skill propose-price --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Propose Price?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Propose Price
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/3dapi-inc-propose-price/badge)](https://www.skillsdirectory.com/skills/3dapi-inc-propose-price)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: propose-price
description: Chooses the sale price for one model from the candidates the pricing script allows, with a written rationale, and has the script verify the choice. Use as the price stage once quote.json, comparables.json, viability.json and the preview drafts exist. Not for computing a price (price_candidates.py), not for market research (research-comparables), not for copy.
allowed-tools: Read, Bash, Write
---

# propose-price

## When
A brief with `stage: price`. The work dir has `quote.json`,
`comparables.json`, `viability.json` and `renders/draft_00.png`, and no
`price.json`.

## Inputs
- `price.candidates.json`, written by step 2: the only prices this skill may
  choose from, `skipped` with its reasons, `p75_sellers` (who holds the
  weight at or above the p75), and `offer`: the unit of sale. Every
  candidate is the price of one purchase of `offer.units` printed units
  (the market's usual pack, six coasters; one for most products), costed
  at `offer.cost`, and carries `per_unit`. The band in `market` is per
  unit; `market.offer` is the same band at the pack
- `comparables.json`: the listings, their prices, sales, favourites, shop
  and market weight (estimated units sold per day, from the shop's recent
  reviews; `sales_est` shows the reviews and window behind it), heaviest
  first
- `viability.json`: `market.basis`, `market.thin`, the band, the verdict
  with `verdict_reason`, `market.outliers` (prices fenced out of the band,
  with what they were), `market.demand`,
  and `identity`: what research decided the object is and how sure it was
  (`confidence` high, medium or low). A `low` identity means the band may
  be another product's band; the proposal still stands on it, and the
  rationale and the summary say so first, so the person who approves reads
  the doubt before the number
- `renders/draft_00.png` to `draft_07.png`: what ours looks like
- the comparables' `images` URLs, only if a Read of them is needed to settle
  finish or complexity

Environment: `DAPILOT_STORE_DIR`, for the pricing policy. The script fails
with a message when it is missing; pass it on.

## Procedure
1. If `price.json` exists, print a `cached:true` result and stop.
2. Run `$PY ${CLAUDE_PLUGIN_ROOT}/scripts/price_candidates.py --workdir $WORKDIR`.
   - Exit 0: `price.candidates.json` holds one to three candidates
     (`markup`, `median`, `p75`) above the floor and the minimum margin;
     `skipped` says why `p75` is absent when it is. Continue.
   - Exit 2: `price.rejected.json` is written; no candidate is allowed.
     Return `ok:true`, outputs `["price.rejected.json"]`, `needs_human` set
     to one line: "<product>: no price clears the floor <floor> at the
     minimum margin; skip, or override with a price above the floor". Stop.
   - Exit 1: `ok:false`, reason from stderr.
3. Judge: pick one candidate. Read `price.candidates.json`, `comparables.json`,
   `viability.json` and the drafts, then answer one question: which of the
   allowed prices will the buyer who sees ours next to those listings
   accept? `reference/positioning.md` gives the tests. The three p75 tests:
   (a) `market.basis` is `weighted`;
   (b) the drafts show ours at least as good as the median listing: same
       object, no fewer parts, no worse finish;
   (c) `p75_sellers` shows the p75 is not one shop's price:
       `distinct_named_shops` is 2 or more, `top_share` is under 0.6 and
       `unnamed_share` is under 0.2. A null `p75_sellers` means (c) is
       unknown and fails.
   Which case applies is read from the candidates, in this order:
   - `verdict` is `unreliable`: the script has already put `median` and
     `p75` under `rejected` with `unreliable_band` and `band_reason`, so
     only `markup` and `margin` (the lowest price at the minimum margin,
     listed since 2026-10-12 for this case as for no market) can stand.
     Propose the lowest of them with position `cost_anchored` and a
     rationale that repeats `band_reason`: the band was one shop's price,
     or nobody is known to buy, or too few listings remained once clones
     were folded. Neither listed means the script wrote
     `price.rejected.json`; step 2 covers it.
   - `market.thin` is true: `markup` when it is a candidate. Otherwise the
     `median` when it is one, otherwise the sole candidate. The rationale
     says the band is thin, as printed. A thin band never picks `p75` over
     an allowed `median`.
   - `median` and `p75` are both candidates: `p75` when (a), (b) and (c)
     all hold, else `median`. `markup` alongside them is the cost-anchored
     fallback for an unreliable band, not a third market position.
   - `p75` is the sole candidate: the question is whether it is defensible
     in the band, as the guide's one-candidate section says. Propose it
     with position `premium`, and the rationale states which of (a), (b),
     (c) held and which failed. A person decides at publish; the rationale
     is written for that person.
   - `median` is the sole candidate: propose it. `markup` alone: propose it
     and say the market gave nothing else.
   Write `price.json`:
   ```json
   {"price": 19.99, "currency": "USD", "candidate": "p75",
    "position": "premium",
    "offer": {"units": 1},
    "rationale": "three sentences: what the comparables sell at and how well, what ours has or lacks, which of (a) (b) (c) held or failed and why this candidate",
    "runner_up": {"candidate": "median", "price": 13.99, "why_not": "one sentence"},
    "floor": 8.99, "verdict": "viable"}
   ```
   `price` is copied from the candidate, never typed; `offer.units` is
   copied from the candidates file, and when it is more than one the
   rationale's first sentence says what one purchase is ("a set of six at
   24.99, 4.17 a coaster"), so the person approving reads the pack before
   the number. `runner_up` is null when there was one candidate.
4. Run `$PY ${CLAUDE_PLUGIN_ROOT}/scripts/price_candidates.py --workdir $WORKDIR --check`.
   Exit 0: continue. Any other exit: delete `price.json`, return `ok:false`
   with the check's `errors` as the reason. Do not adjust the price to pass.
5. Append the log paragraph: floor, band basis, whether the band is thin,
   candidates with margins, the choice, the position, which p75 tests held
   or failed, one line of why. Remove `.lock`. Print the result line with
   the price and margin in `summary`.

## Outputs
- `price.candidates.json`, by the script, unchanged.
- `price.json`, schema above, only when a candidate was chosen and the check
  passed. With a person's `price.approved.json` it is what lets design, copy
  and publish run.
- `price.rejected.json`, by the script, when nothing is allowed.

## Gate
Auto to write the proposal. A person approves or overrides it afterwards,
which writes `price.approved.json`; the design stage requires that file, so
the proposal alone starts nothing. Nothing here touches 3DAPI or Etsy.

## Failure
- Missing `DAPILOT_STORE_DIR` or the policy file: script exits 1; `ok:false`.
- `viability.json` with `ok:false` (no comparables): not a failure. Research
  found no competing listings, so `price.candidates.json` has `no_market:
  true`, and `margin` (the lowest price at the minimum margin) stands where
  the market candidates would. With nothing to compare, choose the lowest
  candidate listed, say "no comparables on Etsy; priced on cost" in
  `reason`, and continue; the person approving the price sees the same.
- `viability.json` without `market.thin`: a band written before the flag
  existed. Read it as not thin and say so in the log.
- The check fails on the model's own output: `ok:false` as in step 4. The
  retry brief carries the errors.

## Do not
- Do not write a price that is not in `candidates`. Not rounded, not
  averaged, not "a bit above". The check will refuse it.
- Do not write an `override` block. Only a person does, by hand.
- Do not change `offer.units`. The pack is the market's, decided by
  `market_stats.py`; a person who wants to sell singles of a set product
  writes another `units` with an override block, and the floor follows.
- Do not compute margins, break-even, shares or a spread. Read them; thin
  comes from `market.thin` and nowhere else.
- Do not pick the higher candidate because it earns more. When `median`
  and `p75` are both allowed, `p75` is proposed on (a), (b) and (c) or not
  at all. When `p75` is the only candidate it is proposed with the failed
  tests named, never with them hidden.
- Do not look at more than two comparable images. The band is the
  evidence; photos settle finish, not price.

`$PY` is `${DAPILOT_PYTHON:-python3}`.

Files in this skill

  • SKILL.md8.5 KB
  • eval/cases.md10 KB
  • eval/expected.octagon-fidget.deep.price.candidates.json2.7 KB
  • eval/expected.octagon-fidget.deep.price.json690 B
  • eval/expected.octagon-fidget.price.rejected.json2.4 KB
  • eval/expected.synthetic-viable.price.candidates.json2.7 KB
  • eval/expected.synthetic-viable.price.json604 B
  • eval/expected.synthetic-weighted-oneshop.price.candidates.json2.6 KB
  • eval/expected.synthetic-weighted-oneshop.price.json690 B
  • eval/expected.synthetic-weighted-p75.price.candidates.json2.7 KB
  • eval/expected.synthetic-weighted-p75.price.json869 B
  • eval/expected.synthetic-weighted-skipped.price.candidates.json2.4 KB
  • eval/fixtures/synthetic-viable/comparables.json9.3 KB
  • eval/fixtures/synthetic-viable/quote.json1.4 KB
  • eval/fixtures/synthetic-viable/viability.json2.4 KB
  • eval/fixtures/synthetic-weighted-oneshop/comparables.json4.9 KB
  • eval/fixtures/synthetic-weighted-oneshop/quote.json1.4 KB
  • eval/fixtures/synthetic-weighted-oneshop/viability.json2.6 KB
  • eval/fixtures/synthetic-weighted-p75/comparables.json4.4 KB
  • eval/fixtures/synthetic-weighted-p75/quote.json1.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.