resolve-dependency
Use for taking one external dependency from open to resolved — `/resolve-dependency <name> [answer]` names it and may carry the provider the user chose; `/resolve-dependencies` walks every open one in
SKILL.mdskills/resolve-dependency/SKILL.md
GitHubResolve a dependency
One external dependency, taken from whatever state it is in to resolved:
a chosen provider, a contract document beside its definition, and the config
keys every consumer codes against. The instruction names the dependency, and
may carry the user's answer after the name — /resolve-dependency currency-service Open Exchange Rates, or a document URL — which is the
provider they chose on the definition. /resolve-dependencies (plural, no name)
walks every dependency that is still open, one at a time, in the order the
Build gate lists them, and ends with the list empty or with what is left
named plainly.
architecture owns the definition's shape and the research playbook; this
skill is the guided flow over it. grilling owns the question mechanics.
Everything you write goes to specs/design/dependencies/<name>/ — never to a
component's design.json, which only references the dependency by name.
Read the state first
Read specs/design/dependencies/<name>/dependency.json from the snapshot and
say, in one line, where it stands. The definition holds a resource block —
what the thing is — and the platform derives the state from it:
| On disk | State | This flow's job |
|---|---|---|
resource.ref set and the organization has a registered resource of that name |
registered | nothing — the platform copied the record; say so and stop |
resource.ref set but the organization has NO registered resource of that name (the definition says "The organization has no registered resource with this name") |
needs-input | ask which provider; the answer replaces the ref with an inline resource |
no resource.provider (suggestions may be open) |
needs-input | ask which provider, settle it |
resource.provider, no resource.contract — or a contract whose file is not on disk |
needs-contract | get the contract document |
contract on disk with origin: "assumed" and no accepted |
needs-acceptance | ask the user to accept |
| contract on disk | resolved | nothing — say so and stop |
Do the steps below in order, skipping the ones the state has already passed.
Each step is at most one ask_question; a /resolve-dependencies walk asks
them per dependency, never as one batch across dependencies — the user
answers one system at a time.
1. Settle the provider — the user's choice, never yours
All research for an open dependency happens here, and the user chooses.
- No provider yet: ask the "Which provider?" card first. ONE
ask_question, before any research: thesuggestionson file as the options (label = the suggestion's name; description = its one-line distinction for THIS product; mark at most one recommended only when a real signal favours it), then ALWAYS these two, in this order —{ "label": "Another provider", "freeText": true, "description": "Name a different provider, or paste a link to its API document." }and{ "label": "Find one for me", "description": "I research the options and come back with what fits." }. Never write options back into the file — the question lives in this conversation.- A named provider settles
resource.provider: go on to step 2. - A document URL settles both:
resource.providerfrom what the provider is called, and step 2's route 1 with that URL. - Find one for me: research the capability (
web_search) and come back with ONE more card — the providers that genuinely fit, each with its distinction, your recommendation marked, and Another provider as free text. One fit is still a question ("Use Stripe?").
- A named provider settles
- The instruction already carries an answer (a name or a URL after the dependency's name): treat it as the card's answer and skip the card.
- A Registered External resource fits (
list_external_resourcesnames it). Say so and write only the stub{ "name": "<its exact name>", "resource": { "ref": "<its exact name>", "name": "<its exact name>" } }— the platform copies the record (provider, keys, instructions, contract document) into the project at save, and no contract step follows. Never retype the keys or the instructions: their identities are load-bearing.
Write the choice: resource.provider set, suggestions removed. The config
keys come last (step 3), from the provider chosen — never before.
2. Get the contract document
The contract is ONE WHOLE DOCUMENT beside the definition, never a slice: the coding agent cuts what its component calls at coding time. Four routes — a ladder, climbed in this order, and the user is told which rung you took:
Point at the published document.
web_searchfor the provider's published OpenAPI or GraphQL document. Then write, independency.json,"resource": { …, "contract": { "type": "openapi", "path": "openapi.yaml", "origin": "provider" } }and"provenance": { "sourceUrl": "<that URL>" }— and STOP. Do not fetch the document; do notaddFileit. The platform fetches it at save (https only, at most 5 MiB), lands it beside the definition and fillsprovenance.sha256andreadOn. A GraphQL schema is{ "type": "graphql", "path": "schema.graphql", "origin": "provider" }. If the save reports the document could not be fetched, tell the user and fall to route 3.Derive it from the provider's documentation. When no public document exists but the provider's OWN developer reference does — pages that name the operations the design calls, with their parameters and responses — write the interface from those pages: the operations the design needs and nothing more,
addFileasopenapi.yamlin the dependency's directory, with anx-aep-source: <page url>on EVERY operation naming the page it came from. Setcontractto{ "type": "openapi", "path": "openapi.yaml", "origin": "derived" }andprovenance.sourceUrlto the reference's root page. No permission is needed: the dependency reads resolved, flagged derived. The bar is the whole design, not part of it — if one operation the design needs has no page, or the only pages are marketing, a blog, a third-party tutorial or a partial reference, this rung does not apply: go to 3.Ask for it. When neither a document nor documentation exists (most couriers, most private APIs), ask ONE question — "How should I get its interface?" — whose
optionsare EXACTLY these three,actionincluded (the console runs the action when the user answers; an option without it is a dead button):[ { "label": "Give a link", "freeText": true, "description": "Paste a URL to the provider's published OpenAPI document." }, { "label": "Upload one", "action": { "kind": "upload-interface", "dependency": "<name>" }, "description": "Upload the document here; I read it once it lands." }, { "label": "Proceed on your assumption", "recommended": true, "action": { "kind": "accept-assumption", "dependency": "<name>" }, "description": "I write the interface from the provider's documentation, covering only what this design calls. Choosing this authorizes it — nothing further is asked; validation cannot check it against a published document." } ]- Give a link → route 1 with that URL.
- Upload one → the answer arrives once the document is on disk (the
platform records
origin: "provider"); read the definition from the snapshot and go on to step 3. - Proceed on your assumption → the user's authorization is already
recorded on the definition when the answer reaches you; go on to route
- Never describe this option as needing a later acceptance.
Assume it — authorized by that answer, and only then. Write the contract yourself from the provider's documentation pages and what you know — the operations the design needs and nothing more —
addFileasopenapi.yamlin the dependency's directory, with a short note of what you are unsure about (auth scheme, pagination, error shapes) in theinfo.description. Setcontractto{ "type": "openapi", "path": "openapi.yaml", "origin": "assumed" }andprovenance.sourceUrlto the documentation you read. Re-readdependency.jsonfrom the snapshot first: the user'sacceptedrecord is already on the contract — carry it over exactly; you never write or alter it. Nothing further is asked of the user: the dependency reads resolved (flagged assumed) the moment the file lands.
An SDK is a contract of its own type: { "type": "sdk", "path": "sdk.json" },
and sdk.json names a packages entry for every implementation language
the design's components use (the ecosystem-prefixed identifier the provider
publishes), the docs URL, and the calls the design relies on. A provider
whose SDK sits on a published API keeps the API as the contract and names
the package in sdk.json beside it only when the design codes against the
library; the dependency carries one contract.
3. Settle the config keys
Derive resource.config from the contract — a REST API's securitySchemes,
an SDK's constructor arguments — following the architecture skill's
conventions (SCREAMING_SNAKE_CASE, secret only for credentials, a
description saying where the user finds the value). Keys already on the
file stay unless the contract contradicts them.
Close
One line per dependency you touched: its name, the provider chosen, the state it is in now, and the one thing (if any) still needed from the user. Nothing else: the files carry the detail, and the Build gate re-reads them.