Agent skill

Graphos Factory

by apollographql in apollographql/skills

Build and iterate on an Apollo Connectors subgraph for a GraphOS supergraph from a REST API, with or without an OpenAPI or Swagger spec, in a dedicated git workspace that records what the API…

MITAuto-check passedBackend & APIs

Install Graphos Factory

skills CLI
$ npx skills add apollographql/skills --skill graphos-factory -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install apollographql/skills graphos-factory --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/apollographql/skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/graphos-factory .claude/skills/graphos-factory && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
graphos-factory
GitHub stars
117
Token cost
~15k tokens
SKILL.md length
5,240 words
Files
31 (incl. scripts, references)
Skills in repo
12
Repo updated
First seen
Licence
MIT

At a glance

Build and iterate on an Apollo Connectors subgraph for a GraphOS supergraph from a REST API, with or without an OpenAPI or Swagger spec, in a dedicated git workspace that records what the API…

  • Works in 4 steps: If a workspace exists, read the decision… → Read the target's contract (named above… → For new intake or before select / apply,… → …
  • The user wants to wrap a REST API as a GraphQL subgraph for their supergraph
  • SKILL.md covers Before anything else, Verbs, Instruments and Policies you apply every time…, plus 4 more sections
  • Runs Shell scripts from its folder; calls bash, git and cargo

What it does

Graphos Factory is an agent skill from apollographql/skills. Build and iterate on an Apollo Connectors subgraph for a GraphOS supergraph from a REST API, with or without an OpenAPI or Swagger spec, in a dedicated git workspace that records what the API offers, the user's operation and field selection, every design decision, and the evidence each verification layer produced. Use whenever the user wants to wrap a REST API as a GraphQL subgraph for their supergraph, add or remove operations or fields from an existing connector subgraph, refresh one against a new spec, or…

Its SKILL.md is about 15k tokens, which your agent loads only when the skill is triggered. The skill folder holds 33 other files, including scripts and reference files (for example `graphos-factory-core/references/api-discovery.md`, `graphos-factory-core/references/connectors-language.md` and `graphos-factory-core/references/customer-context.md`). Compatibility notes: macOS or Linux (WSL on Windows) with bash, curl, git and jq; Java 17+ for the end-to-end and live layers. Any Agent Skills host (Claude Code, Codex, Cursor…

It sits in Backend & APIs, covering GraphQL, OpenAPI specifications and Architecture decision records. It works with OpenAPI, GraphQL and Git. The repository describes itself as: Apollo GraphQL Agent Skills. The licence is MIT.

When your agent uses it

  • The user wants to wrap a REST API as a GraphQL subgraph for their supergraph
  • Remove operations
  • Fields from an existing connector subgraph
  • Refresh one against a new spec

Example prompts

  • “connector”
  • “subgraph”
  • “Apollo”
  • “/graphos-factory”

Requirements

  • Node.js
  • A Bash shell
  • Compatibility (from SKILL.md): macOS or Linux (WSL on Windows) with bash, curl, git and jq; Java 17+ for the end-to-end and live layers. Any Agent Skills host (Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI and others) that can run shell commands.

Workflow steps

4 steps, taken from the first numbered list in SKILL.md.

  1. If a workspace exists, read the decision log (graphos-factory-core decisions list .,
  2. Read the target's contract (named above and under References) and
  3. For new intake or before select / apply, make the cheap call first: does the
  4. Pick the verb the user needs (the target's verbs, init among them, follow

What it can do on your machine

Read from SKILL.md and the folder at commit 5f02fcf. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Ships 1 file in scripts/ (Shell, from the files we listed), which the agent can run.

    Shell commands in SKILL.md call:

    • bash
    • git
    • cargo
    • npx
    • gh

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • github.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

  • Compatibility

    macOS or Linux (WSL on Windows) with bash, curl, git and jq; Java 17+ for the end-to-end and live layers. Any Agent Skills host (Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI and others) that can run shell commands.

    From compatibility in the SKILL.md frontmatter.

Context cost

Graphos Factory loads about 15k tokens when it runs, and up to ~20k if it reads all its reference files. Until then it costs about 158 tokens; SKILL.md has 5,240 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~158
When it runs · the whole SKILL.md, loaded when a task matches
~15k
With references · SKILL.md plus every file in references/, read only if the agent opens them
~20k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.

SKILL.md

The full file from apollographql/skills at commit 5f02fcf, republished under its MIT licence (© apollographql). 5,240 words, ~14,693 tokens.

Download SKILL.mdSave it as .claude/skills/graphos-factory/SKILL.md (or your agent's skills folder). This skill also uses 30 other files; get the full folder from GitHub.
name
graphos-factory
description
Build and iterate on an Apollo Connectors subgraph for a GraphOS supergraph from a REST API, with or without an OpenAPI or Swagger spec, in a dedicated git workspace that records what the API offers, the user's operation and field selection, every design decision, and the evidence each verification layer produced. Use whenever the user wants to wrap a REST API as a GraphQL subgraph for their supergraph, add or remove operations or fields from an existing connector subgraph, refresh one against a new spec, or record live API traffic as test fixtures, even if they do not say "connector", "subgraph" or "Apollo".
compatibility
macOS or Linux (WSL on Windows) with bash, curl, git and jq; Java 17+ for the end-to-end and live layers. Any Agent Skills host (Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI and others) that can run shell commands.
license
MIT
metadata.version
0.11.0

graphos-factory

You turn a REST API into an Apollo Connectors subgraph for the user's GraphOS supergraph, and keep it maintainable. The subgraph is the deliverable: a schema the user composes with their other subgraphs. You are the generator: no tool writes the schema for you. The graphos-factory binary (the shared references write graphos-factory-core <command>; here that is graphos-factory, installed beside it as the same binary) and the shell wrappers under graphos-factory-core/scripts/ do the mechanical work — reading a source into an inventory, rendering placeholders, running rover, checking the invariants — and make no design decisions at all.

Source kinds. workspace.yaml's source_kind names the source. Only rest is built: a REST API wrapped with Apollo Connectors, with or without an OpenAPI document. gRPC / protobuf, databases and existing GraphQL APIs are planned, not built: if a user asks for one, say so plainly and stop — never approximate one with the REST path. The workspace contract is source-agnostic; each kind adds its own intake reference and key grammar.

Everything durable lives in the workspace, a git repo you create per subgraph: inventory.json is what the source offers, selection.yaml is what the user chose, <name>.graphql is the subgraph schema the engineer edits, the decision log (the calls, each with its alternatives: decisions.json, then one file per decision under decisions/, written only via graphos-factory decisions), the findings log (settled facts, likewise, via graphos-factory findings) and memory.md are what you and your predecessors learned. Read workspace-contract.md once for the full layout. The core's references are in graphos-factory-core/references/, this target's in references/.

Never regenerate the schema wholesale after the first commit. Compute the delta and edit only what changed. Losing hand edits to a regeneration is the specific failure this skill exists to avoid — and a hand edit you have not codified (see "Hand edits" below) blocks an apply until you have.

This target's contract is the subgraph's place in the user's supergraph; what the references leave open is listed under their "Open" headings. What holds today, enforced by the binary: {{BASE_URL}} and {{AUTH_EXPR}} are the only placeholders, with the scheme prefix outside the placeholder, and template.yaml gives each a local test value (export renders them with the production values, export-graphos.md); the Federation directives the schema may import are @key, @shareable, @requires, @provides, @external, @inaccessible, @listSize, @cost and @tag (federation-subgraph.md); one @source is the shape the layers model, and a second is a lint warning (unmodelled, not forbidden). @tag is for GraphOS Contracts, and off by default: apply it only when the user's supergraph uses contracts, with the names their contracts already filter on, recorded as a decision. Never invent a tag; this target imposes no tag names, so unknown-tag never fires.

Setup, before the first command. <skill> below is the directory that holds this file. Under the Claude Code plugin, its SessionStart hook (scripts/session-start.sh) has already run bootstrap.sh and put the binary on PATH: its output says so. Anywhere else (npx skills add, gh skill install, a copied directory; Codex, Cursor, GitHub Copilot, Gemini CLI or any other agent), check with bash <skill>/scripts/bootstrap.sh --check, and on exit 127 run bash <skill>/scripts/bootstrap.sh once (about 3 MB into ~/.cache/graphos-factory-core/). Then run . <skill>/scripts/env.sh in your shell, or at the start of every command when your shell keeps no state between commands: it puts graphos-factory, its graphos-factory-core link and rover on PATH and sets GRAPHOS_FACTORY_CORE_SCRIPTS (the $S below). The toolchain (toolchain.sh) is installed only once the user agrees, whatever the host.

<!-- core:begin -->

Before anything else

  1. If a workspace exists, read the decision log (graphos-factory-core decisions list ., including --open for questions still awaiting the user), the findings (graphos-factory-core findings list .) and .factory/memory.md in full. Do not re-open a resolved decision unless the user asks. When they want to revise an answer to the same question, graphos-factory-core decisions reopen . --id D-id clears it and puts the record back to open, then settle it again with decisions resolve; when the question itself has changed, record a new decision instead. Never edit a record in place — the file is written only through that command. If the workspace still carries a legacy .factory/decisions.md and no decisions.json, run graphos-factory-core decisions migrate . once to import it (it preserves the D-nnnn ids and removes the .md), then continue. A decisions.json in the older format (provenance records titled Hand edit codified: or Source patch:, prose with no alternative) is split once with graphos-factory-core decisions migrate . --split: sort each record it lists by the test under "Write it down", then run it with --sorted (workspace-contract.md § decisions.json).
  2. Read the target's contract (named above and under References) and naming.md. Violating them breaks the schema a router can run rather than a test: the target's @source and placeholder rules, and {Service}_Type / {service}_field with snake_case for multi-word names.
  3. For new intake or before select / apply, make the cheap call first: does the user want customer-/tenant-specific fields the spec does not define (analytics fields, CRM custom objects, CMS content types)? If not, it is a generic wrapper — record context_mode: generic in workspace.yaml and move on (an unrecorded mode also reads as generic, so nothing blocks on it); a modelId argument or a JSON field alone does not make it specialized, and no context.yaml is needed. Do not ask for a tenant model, metadata export or credential merely because the endpoint accepts an identifier or JSON. Only if it is specialized or you are unsure, read customer-context.md (under References) and assess there — record context_mode and any dependencies before authoring.
  4. Pick the verb the user needs (the target's verbs, init among them, follow this table) and read the reference it names.

Verbs

VerbRead firstWhat you do
discoverspec-intake or api-discoverybuild/refresh inventory.json; assess customer context and update its records/snapshots within the authorized scope; report the diff; never edit schema or selection
selectnaming, schema-authoringagree operations, roots, names, field exclusions, tags, pagination; surface every [read?] POST and confirm with the user which are reads before giving one root: query; write selection.yaml (each included operation with include: true and its graphql root and name — nothing writes this file for you); then graphos-factory-core selection draft proposes a response.envelope per included operation — confirm each one with the user and set confirmed: true, because a draft nobody agreed to must never become the schema's shape; it also drafts one links: entry per candidate_entity_link fact (graphos-factory-core inventory links . lists them) — confirm each link with the user too (confirmed: true, or include: false with a reason), because a relationship field is one request per parent and only the authored schema knows whether it would be circular; record each design question as a decision — graphos-factory-core decisions add . --title T --question Q --choice LABEL … [--multiple] (choices are numbered 1, 2, 3…; ids are kept only when every --choice is id:label) — and settle each with the user via graphos-factory-core decisions resolve . --id D-id --chosen N --note …, never as loose chat messages; record what the references settle for you as a finding (graphos-factory-core findings add . … --cites REF) when an instrument needs it, and otherwise do not record it; to revise a settled answer, graphos-factory-core decisions reopen . --id D-id and resolve it again.
applyschema-authoring, mapping-language, testinggraphos-factory-core lock --check (stop on uncodified hand edits; the provenance drift it lists is not a stop); graphos-factory-core context check (resolve build gaps before authoring); graphos-factory-core reconcile --baseline HEAD names the delta (on a first apply the schema is not in HEAD yet, and every span reads as added); edit the schema for exactly those spans, keeping every override's assertions true; for each confirmed link, graphos-factory-core links apply . --dry-run prints the field-level @connect keyed by $this (no @key; no header and no argument unless the by-id root field carries a per-call credential, which it mirrors — same argument, same header or query parameter; for a nullable fk, the isSuccess and selection null guards, because the router still sends GET …/ for a null parent) to paste into the host type — it refuses circular, self, no-fk-field, field-exists, nullable-fk, no-host, no-root-field and target-refused (a stale entry whose keep-or-drop decision is not resolved keep) (connectors-language § Relationship fields) and exits 1 whenever any entry is refused, even with others printed; graphos-factory-core scaffold drafts the case, stub and unit entry of every operation that has none (the notes say what the unit entry could not carry) — audit every placeholder, then e2e.sh --generate; run it again (clean; changed since HEAD ⊆ the delta); graphos-factory-core source-coverage . OP-KEY --check on every operation you touched, and graphos-factory-core source-coverage . --check (no OP-KEY: every selected operation, each failing one named) before lock — zero unaccounted and zero unresolved in both directions, and no unaccounted behaviour row (an optional argument's doc comment must carry the source's omission sentence, or a behaviour omit says why not — a finding's not-applicable, a decision's editorial), is the bar (--check fails closed); each path you meant to leave off is recorded, not a silence: one you chose not to expose is a decision whose alternative is exposing it, graphos-factory-core decisions add . --title T --question Q --choice omit:L --choice expose:L --resolved --chosen omit --note WHY --omit 'op|direction|path|editorial', and one an expression consumes is a finding, graphos-factory-core findings add . --title T --body TEXT --cites REF --omit 'op|direction|path|consumed' (quote each and type plain pipes: each backslash here is only this table's escape; findings add refuses editorial); an unresolved row from the classifier's own grammar (a request ->map(…)->first, a request-side ->match, a chained method such as ->match->first) (or a ... spread that is not the disc->match(…) abstract-type form) is recorded and reported as not verified by this layer, never cleared by editing inventory.json (schema-authoring.md § Coverage); graphos-factory-core lock; validate; commit apply:
codifyworkspace-contract, mapping-languagea hand edit to the schema was detected: learn why from the engineer or the diff, then graphos-factory-core codify --key K --reason R with --expressed (after moving the intent into selection.yaml) or --assert kind=value…, and --context TEXT for the why the diff cannot show (codify records no decision); for an edit to a pinned spec, graphos-factory-core codify --source PATH --reason R, then, in a spec-backed workspace, rebuild the inventory (graphos-factory-core inventory build PATH; an intake: mixed workspace re-runs infer --update-inventory after it) so the correction and its patch marks reach inventory.json, and re-run reconcile; commit decide:. --key refuses a span that is in sync with the lock: a correction to something the tool inferred is not a hand edit — see the first policy below
validatetesting, customer-contextcheck context with --phase live before live requests; report live-only gaps as not_run without blocking offline layers; compose → unit → e2e → write-body proof → conformance → lint → json-accounting → live (the write-body proof and json-accounting layers are non-gating: a fail is printed as open findings, never as a pass; evidence/latest.json is contract_version: 2); write evidence/latest.json; report skipped layers as skipped; a unit run with zero cases is not_run, never a pass; every suite with zero cases fails unless its header's first sentence cites its decision (D-0019 or D-k7m2qx), and a skip: true case fails; every selected operation is a live case or a live exclusions: entry with its reason; lint's link-untested warns on each relationship field that no unit entry targets (<Type>.<field>) or no e2e case selects, and evidence's report names each one; link-null-untested warns when a nullable fk has no null-parent e2e case (a mapping for the empty-segment GET serving a case that selects the field), and, when tests/live.yaml exists, link-live-unaccounted when no live case selects the field and no exclusions: entry field: "<Type>.<field>" names it; a field with neither unit nor e2e, with no null-parent case, or excluded live is not validated; say so (scaffolding 7as tests scaffold the unit the and route case by hand, connectors-language § Relationship fields); a conformance body the oracle could not judge is unchecked (no documented shape) or unmatched (no such operation, unreadable body — this one fails), never a pass; an accepted gap is graphos-factory-core codify --waive TARGET --status S --reason R; before reporting, graphos-factory-core lock --check --provenance; when it exits 3, relock (graphos-factory-core lock) only when a plain graphos-factory-core lock --check reports no changed or unattributed span, pinned-source problem or edited inventory (it then prints the relock advice, and --json sets relock_advisable) — the drift is provenance alone (a case, a stub, a memory.md line), or a document sources.lock.yaml no longer pins, which a relock stops watching on purpose. Otherwise the schema, a pinned document or inventory.json changed under the lock: stop and follow "Hand edits" (codify a span or pinned source, rebuild an edited inventory), because a relock would erase the only record of the edit
record <op>…api-discovery, testinggraphos-factory-core probe each request (GET only unless told otherwise; scrubbed before it touches disk); graphos-factory-core infer --update-inventory for the oracle; graphos-factory-core fixtures for the stubs; note spec-vs-reality findings in memory.md
<!-- core:end -->

This target's verbs, init first (it records the target, skill.name: graphos-factory; --target is optional):

VerbRead firstWhat you do
init <name>workspace-contractdecide the subgraph name (snake_case or kebab-case, naming.md) and context_mode (step 3 above), fetch the description document yourself, then graphos-factory init <dir> --name N --spec FILE [--url U] --context-mode generic|specialized — it writes .factory/workspace.yaml (skill.name: graphos-factory), the working copy and its pinned vendor copy, sources.lock.yaml and inventory.json exactly as sources pin + inventory build would, plus four skeletons: <directory>.graphql (the spec links and the one @source, no root field, so compose fails until the first apply), template.yaml (BASE_URL = the document's first absolute server, else a local stand-in its comment asks you to complete; AUTH_EXPR = {$env.<SERVICE>_TOKEN} with the scheme named; local test values only, never the production host, which is given at export), supergraph.yaml (rover's compose config for this one subgraph, not the user's supergraph) and tests/router.yaml (the e2e override_url); act on every comment in them, and one that already exists is left alone. It refuses a directory that already holds a workspace; --dry-run --json lists every file's path, content and sha256 without writing. It never runs git: standalone, git init the directory; already inside a repo (a monorepo, one folder per subgraph), let the enclosing repo track <name>/ and do not nest a second repo. Then commit. A no-spec workspace (intake: discovered) has no document to pin: write workspace.yaml (with skill.name: graphos-factory) and sources.lock.yaml by hand, as workspace-contract shows
exportexport-graphosonly after validate: graphos-factory export . --out DIR --base-url URL (DIR outside the workspace; URL the production host, which the user gives you) renders DIR/<directory>.graphql and prints the rover commands, the router's credential variables and minimums, and what is not verified. It refuses an unvalidated workspace (no evidence, evidence not recorded on the committed workspace as it is now, a layer not pass, an operation with no executed evidence, a lint error) and a base URL that is not an absolute http(s) URL, carries userinfo or is local, and says why, in evidence's terms: fix that (commit, then evidence) and re-run, never work around it. Hand the user its output. Never run rover subgraph check or publish yourself, never handle, ask for or print a GraphOS key, and report supergraph_check and anything else it lists as not verified

validate on this target also runs the target's evidence layer, supergraph_check: composing the subgraph against the user's existing supergraph. It is not built and always records not_run with its reason (references/verification.md). The compose layer composes the subgraph alone. Report supergraph_check as not run, never as a pass, and say plainly that the subgraph has not been checked against the user's supergraph.

<!-- core:begin -->

Instruments

One binary, the product's, plus the shell wrappers in graphos-factory-core/scripts/ around rover, Java and curl for the validation layers. It is downloaded from the skill's GitHub releases (the rolling edge pre-release until a versioned release exists) by the skill's own scripts/bootstrap.sh (the product's session-start hook may run it; otherwise run it yourself), which also links it as graphos-factory-core; it is built from crate/ only when developing the skill itself (bootstrap.sh --build). Every wrapper takes the binary from $GRAPHOS_FACTORY_CORE_BIN, then that link in the bootstrap cache, then PATH, prints which one and its version to stderr, and exits 78 (a fail, never a skip) when it is older than the pin (crate/Cargo.toml, an installed skill's release.env, or $GRAPHOS_FACTORY_CORE_VERSION); evidence hands the wrappers its own binary. On 78, re-run bootstrap.sh (--build in a checkout) or set GRAPHOS_FACTORY_CORE_BIN — never lower the pin to get past it. Run everything from the workspace root. graphos-factory-core <command> [verb] --help (or -h) prints that command's usage and reads or writes nothing. A flag the verb does not declare — a typo, or two flags quoted into one argument — is refused with exit 2 and nothing runs; fix the spelling, never drop the flag. rover is needed for compose, unit, e2e and live; Java for e2e's WireMock; nothing is ever installed into a workspace. rover's composition plugin is under the Elastic License v2, which only the user accepts, by setting APOLLO_ELV2_LICENSE=accept: until then those four layers are not_run. Ask once: show the link, and only on an explicit yes set APOLLO_ELV2_LICENSE=accept for the commands you run this session (record the consent in memory.md); never set it unasked, and tell them export in their shell profile makes it permanent.

bash
S=path/to/graphos-factory-core/scripts
export GRAPHOS_FACTORY_CORE_SCRIPTS=$S                    # evidence needs to find the wrappers
# If the host supplies a model identifier, set GRAPHOS_FACTORY_CORE_LLM_MODEL to that exact value.
# Never infer or guess a model identifier. The lock records unknown when this value is absent,
# and `lock` prints a one-line warning for that and for a skill source of binary-build.

graphos-factory-core inventory build openapi.json          # description document (OpenAPI 3.x or Swagger 2.0) -> .factory/inventory.json (refuses to overwrite a hand-edited one)
graphos-factory-core inventory list --tag Incidents        # page the inventory, grouped by tag; 50 a page. --json: total, offset, limit, next_offset (null on the last page) — a loop over every operation follows next_offset
graphos-factory-core inventory list --support needs_review # what still needs a decision
graphos-factory-core inventory describe get:/v2/incidents  # one operation, shapes expanded
graphos-factory-core inventory links . [--json]            # every candidate_entity_link fact flat: shape, property path (album_id / []>album_id / songs[]>album_id), the canonical GET-by-id operation, whether the host is a list item and whether that operation is selected, then every GET-by-id refused as a target because its response does not carry its key — a hint, never a finding; judge each and record the call with `graphos-factory-core decisions add`
graphos-factory-core inventory diff old.json new.json      # what a spec refresh changed

graphos-factory-core context check . [--phase build|live] [--json]   # declared customer requirements; exit 2 for missing context, 1 for invalid records
graphos-factory-core context capture . (--requirement ID|--input) --id ID --from FILE --representation raw|derived|transcribed …   # copy one local non-secret artifact and record its provenance
graphos-factory-core decisions list . [--open] [--causal] [--json]    # the decision log: decisions only — calls with their alternatives, resolved, and open questions awaiting the user. The records of `.factory/decisions.json` (numbered `D-nnnn`, never added to), then one file per record added since under `.factory/decisions/` (random `D-k7m2qx`); --causal orders by after/amends and shared affects, a `via:` line per edge
graphos-factory-core decisions add . --title T [--question Q] [--choice LABEL]… [--multiple] [--phase P] [--affects PATH]… [--resolved --chosen N --note TEXT --decision TEXT --by user|agent] [--omit 'operation|direction|path|reason']… (a resolved request omit also removes an optional source body member from the write-body proof; a member the source requires stays a gap, `required_member_omitted`, and `decisions add` warns when an omit targets one) [--json-reason 'Type.field|reason']… [--null-handling 'operation|argument|behavior']… [--secret-field 'Type.field|disposition|reason']… [--slug S] [--after D-id]… [--amends D-id]…   # record a decision, as its own file with a random id, printing `N  label` per choice (choices numbered 1, 2, 3… in flag order; ids are kept only when every `--choice` is `id:label`, else each value is a whole label; a duplicate id, or an invalid one when every value looks like `id:label`, is refused at the flag) (--after names decisions it presumes, --amends ones whose answer it changes where their affects overlap): an open question, or with --resolved a call already made (`resolution.by` defaults to agent; --note says why you did not ask); refuses, exit 1 `no-alternative`, a record that names its alternative none of three ways — a --question, two or more --choice, or an --omit with reason `editorial` — a settled fact is `findings add`; --omit records a structured scope exclusion `source-coverage` reads, only on a resolved record, and an `editorial` omit lives only here; path `.` omits the whole direction for that operation, which is how to record an EmptyResponse 204's empty root row; --json-reason records why a response field is left typed as the workspace's JSON scalar, from `crate::json_accounting::REASONS`, read only on a resolved record — never a selection.yaml field or a schema doc comment; --null-handling records what the connector sends for an explicit null argument (behavior: send_null or omit), read only on a resolved record; --secret-field records a reviewed credential-shaped response field (disposition expose or exclude) that lint's secret-field-exposed rule reads, only on a resolved record; direction `behaviour` waives an omission sentence an argument's doc comment leaves out, path `query:NAME`/`path:NAME`/`header:NAME`/`body:KEY.PATH`, reason `editorial` or `not-applicable`
graphos-factory-core decisions resolve . --id D-id [--chosen N|LABEL]… [--note TEXT] [--decision TEXT] [--by user|agent] [--force]   # settle an open decision (with --force it replaces the whole resolution: pass the old --decision and the old note plus what you add, to amend a record without losing it); the same command a headless agent and a host UI both use
graphos-factory-core decisions reopen . --id D-id [--json]   # revise a decision: clear a resolved/superseded record's resolution and set it back to open (everything else kept); refuses an unknown id, an open record, or a missing/invalid log
graphos-factory-core decisions supersede . --id D-id [--json]   # mark a resolved decision replaced: status only, its answer kept; it stops counting as resolved everywhere (its omits, json_reasons and null_handling, its affects, a links: decision: naming it), so re-record what still applies; the fix for lint's `stale-omit` (a finding's: `findings supersede`)
graphos-factory-core decisions link . --id D-id (--after D-id | --amends D-id)… [--json]   # edges on a decision recorded as its own file, the fix for lint's `decision-overlap`; an old D-nnnn record never gains a field, so put the edge on the newer record
graphos-factory-core decisions migrate . [--keep-md] [--force] [--dry-run] [--json]   # one-time: import a legacy .factory/decisions.md into decisions.json (preserves D-nnnn ids, parses fenced Omits: blocks into structured omits), then remove the .md
graphos-factory-core decisions migrate . --split [--sorted FILE] [--dry-run] [--json]   # one-time, an older-format log (FILE: YAML/JSON mapping each listed D-nnnn to `{as: decision, question, choices, chosen, by, note}` | `{as: finding, cites, body, …}` | `{as: memory, line}` | `{as: drop}`; without it the unassigned records are listed and nothing is written, exit 1 `unsorted`): codify records move onto the override, waiver or patch they shadow, refresh records become findings, a record with a question, choices or an editorial omit stays a decision; lists every other record for you to sort by hand and refuses to write without --sorted until each is assigned
graphos-factory-core findings list . [--json]             # the findings log (`.factory/findings.json`, then one file per finding added since under `.factory/findings/`): facts a reference or the wire settled, `F-nnnn` or random `F-9k2wde`, current until superseded; every omits/affects reader takes these with the resolved decisions
graphos-factory-core findings add . --title T --body TEXT [--cites REF] [--source agent|codify|sources] [--affects PATH]… [--omit 'operation|direction|path|reason']… [--evidence E]… [--related D-id|F-id]…   # record a fact an instrument needs, or the next session does; omit reason `consumed` or `not-applicable` — refuses `editorial`, which is a decision
graphos-factory-core findings supersede . --id F-id       # a later finding replaces it; it stops counting
graphos-factory-core source-coverage . [OP-KEY] [--json] [--check]   # source coverage: every fact the source offers is covered by the schema, or a resolved decision or current finding says why not (`spans obligations` is the old spelling); every request-body and response path the source offers (parameters are not classified), classified as mapped / consumed / omitted-decided / unaccounted / unresolved, plus unverified-default and transport-expansion-missing at an `x-expansion` boundary, plus a behaviour section when an optional argument's source says what omitting it does (documented / waived / unaccounted); zero of those four in both directions and zero unaccounted behaviour is the completion bar, and --check fails closed on any; with no OP-KEY it checks every operation selection.yaml includes, one counts line each, --json wraps each operation's object in `operations` with `totals` and `failing`, an operation it cannot classify counts failing, and nothing selected exits 1; a resolved omit no row needs any more (the path is now mapped, envelope-read for an `editorial` entry, or no longer offered) is a `stale-omit`, counted and listed with its decision id but not part of the bar, and lint warns on it
graphos-factory-core spans json-accounting . [--json] [--check]   # every response field the current SDL still types as the workspace's JSON scalar, nested and list-wrapped fields included, matched against a `json_reasons` entry on a resolved decisions.json record (never selection.yaml or a schema doc comment — the decisions-only rule) for a reason from a closed vocabulary (free-form-object, recursive, vendor-undocumented, polymorphic-without-discriminator, depth-cap) whose predicate is re-checked against inventory.json every run — accounted / unaccounted / stale (recorded reason no longer holds) / recoverable (inventory is now fully typed there, overrides the rest) / unresolved (reason recorded, but no shape located for the type: by name, root-field operation or parent property); `defaults.fields: all` is never itself a reason, and --check fails closed on anything but accounted
graphos-factory-core serialization . [--json]   # read-only: the standalone CLI for the write_body_proof evidence layer; per body-sending write, each argument's value proven at its own body pointer / query key / path or header placeholder by an executed e2e case (needs the run log), omission at its pointer, explicit null per selection.yaml `defaults.null_handling` (state `omit`) or a per-argument null_handling decision; an argument the connector spells in a way the reader cannot place is placed by an executed case whose stub demands its value at one body pointer (`ambiguous_value` when the value is not distinctive; `--json` lists `placements`, static or executed); unproven_mapping and null_handling_undecided are open findings, never passes; replaces list-arg-unproven / mutation-cases / loose-write-body / unit-no-body
graphos-factory-core reconcile . --baseline HEAD           # schema <-> selection <-> inventory delta; overrides; hand edits; what moved since HEAD; exits 2 with the URLs to compare when not one connector pairs (a base-URL split)
graphos-factory-core sources pin . --path openapi.json [--url URL]   # keep the vendor's bytes as .factory/sources/*.upstream.*; record kind/version/hashes
graphos-factory-core sources status . [--json]             # every pinned document: kind, upstream integrity, hand edits, patches; plus every recorded docs/probe source (url, retrieved_at, sha256, operations, note)
graphos-factory-core sources refresh . --path openapi.json --from FILE [--url URL] --reason R   # a newer vendor document: re-apply patches[] (re-applied / obsolete / conflict), rebuild + diff the inventory; both shas and each patch's fate go to a finding (source: sources)
graphos-factory-core lock . [--check [--provenance]] [--skill-dir DIR] [--model MODEL]   # record applied hashes plus skill, binary, toolchain, input and output provenance; --check also lists provenance drift, --provenance fails on it; a write warns on stderr when the model is unknown or the skill source is binary-build, naming --model / GRAPHOS_FACTORY_CORE_LLM_MODEL and --skill-dir / GRAPHOS_FACTORY_CORE_SCRIPTS
graphos-factory-core codify . --key K --reason R [--context TEXT] --assert contains=…   # turn a hand edit into an override (reason, context, assertions), or --expressed (a finding only when --context is given); no decision record — --decision D-id (numbered or random) only attaches a real one
graphos-factory-core codify . --source openapi.json --reason R [--context TEXT]   # turn a hand edit to a pinned spec into patches[] carrying reason and context; no decision record
graphos-factory-core probe . --key "get:/x/{id}/" --url URL  # one recorded, scrubbed live request -> .factory/samples/
graphos-factory-core infer . --update-inventory            # samples -> shapes (the no-spec conformance oracle)
graphos-factory-core fixtures .                            # recordings -> case-scoped WireMock stubs (.factory/recordings.yaml)
graphos-factory-core scaffold . [--op KEY] [--force] [--dry-run]   # draft a case, a stub and a unit entry per selected operation from the connector + shapes (a sparse-fieldsets GET: an omitted and a `_fields_narrowed` case, no unit entry for a comma default); audit before --generate
graphos-factory-core scaffold . --op KEY --status CODE|all-missing  # error cases: per documented non-2xx status a case with `# expect-upstream-status: CODE` and a stub answering it (the documented body, if any); refuses a stub equal to an existing one, or to another case planned in the same run
graphos-factory-core error-statuses .   # one line per included operation: root field, documented non-2xx statuses, the statuses the last evidence run saw answer its own request, operation key (`-` for none); for e2e.sh, and to see what is still owed
graphos-factory-core selection draft . [--links|--envelopes] [--op KEY] [--force] [--dry-run]   # propose a response.envelope per included operation and a links: entry per candidate_entity_link fact; every block lands `confirmed: false` for you to confirm
graphos-factory-core links apply . --dry-run [--link "<shape> > <path>"] [--json]   # print the field-level @connect for each confirmed links: entry (the by-id selection verbatim, {$this.<fk>}, the one @source, no @key, the root connector's static headers and queryParams, and the by-id root field's per-call credential mirrored or none; a nullable fk gets the isSuccess and selection null guards); refuses circular / self / no-fk-field / field-exists / nullable-fk / no-host / no-root-field / target-refused (a stale entry lint flags link-target-refused, until its keep-or-drop decision is resolved `keep`); never writes the schema
graphos-factory-core selection set . --op KEY… --include true|false [--dry-run] [--json]  # toggle an operation's include flag in place, block or one-line flow entry, comments and anchors preserved (drives a UI toggle)
graphos-factory-core selection review . [--candidate FILE --expect-input TOKEN] [--expect-review TOKEN]  # read-only JSON snapshot/review; never saves or applies
graphos-factory-core batch find . [--json] [--all-types] [--check]   # per keyed type (graphql.entity), the operations returning an array of it and whether one takes a list of its key: batchable / partial-shape / needs-scope / style-conflict / paginated / list-no-key-filter / none (reads only: GET, an inventory read, or a selection root: query); with graphql.batch: true it prints a paste-ready type-level $batch connector (you paste it; nothing writes the schema); --check exits 1 when a batchable keyed type has no type-level $batch connector and no graphql.batch: false (lint batchable-entity-unbatched); 2 when the schema does not parse
graphos-factory-core scaffold . --op batch:<Shape>   # for a type with a $batch connector: a case through a root whose selection maps the key alone, its root stub and an `x-required` lookup stub demanding the exact key list — e2e fails unless the router makes that request

bash $S/compose.sh .                                 # rover supergraph compose            (layer 1)
bash $S/unit.sh . [--only PATTERN]                   # rover connector test                (layer 2)
bash $S/e2e.sh . [--generate] [--only PATTERN]        # WireMock (java) + real Apollo Router; every stub loads once, for the whole suite — a stub's request matcher must be unique workspace-wide (layer 3); a case's `# expect-upstream-status: CODE` fails it unless the operation's own request was answered CODE (a status served to a nested lookup or side call is not the operation's), an error case without one on an operation documenting several statuses is UNPROVEN, never a pass, and a documented status no passing case saw answer its own request is `not_run` in `error_coverage` and leaves the operation `unchecked`
graphos-factory-core validate .                            # fixture/request bodies vs the spec  (layer 4)
graphos-factory-core lint .                                # contract + coverage invariants, incl. fixture-collision (two stubs matching the identical request) and fixture-overlap (a stub matching every request a sibling matches: add the `absent` matchers it names, or a priority), sparse-fieldsets (a GET's `fields` default derived and forwarded; `sparse_fieldsets: {enabled: false}` opts out), and undocumented-root-field / argument-constraints-undocumented (a root field or constrained argument with no doc comment, or an optional argument whose doc comment drops the source's omission sentence, "If not passed, …" or "Defaults to …", traced through headers and nested write bodies too; the finding quotes the text to write), and argument-required-optional-in-source (an argument made `!` over an optional source parameter, error); secret-field-exposed (a response field named like a credential — token/secret/password/passcode/credential(s), or an api/private/client/secret/encryption/signing/access `*Key` compound; exclude it or `decisions add --secret-field 'Type.field|expose|reason'`); decision-without-alternative (a decision record with neither a question nor choices, warning); decision-id-duplicate, decision-link-unresolved and decision-link-cycle (a merge kept one id twice, or an after/amends dangles or loops, errors) and decision-overlap (two resolved decisions, at least one recorded as its own file, decide the same span and neither names the other: `decisions link`, warning); entity rules for every @key type: entity-without-lookup (per resolvable key; `resolvable: false` stubs exempt) and entity-key-not-embedded (read through the selection) (errors), entity-without-consumer and entity-field-unresolved (warnings); and two more warnings: unknown-tag (a `@tag` outside the target's vocabulary) and failure-case-missing (per operation, the documented non-2xx statuses no e2e case exercises, meaning the operation's own request was answered with it, not a nested one) (layer 5)
bash $S/live.sh .                                    # tests/live.yaml against the real API (layer 6)
graphos-factory-core evidence . --scripts $S               # run every layer, write .factory/evidence/latest.json (includes write_body_proof, in-process, same-run against wiremock_e2e's own current results — never a stale latest.json)
graphos-factory-core render . --out /tmp/x                 # the placeholder-free copies the wrappers `eval`: stdout is shell assignments (RENDERED_SCHEMA, COMPOSE_CONFIG; ROUTER_CONFIG and its router.yaml only with a --wiremock-port, --router-port or --health-port flag); not a hand-off: the user-facing render is a target's `export`, where it has one

bash skills/<skill>/scripts/bootstrap.sh [--build]   # install this skill's binary and its graphos-factory-core link (download; --build from crate/)
cargo test --manifest-path path/to/crate/Cargo.toml  # the instruments' own tests

inventory list and describe exist so you never read a large raw spec: page through the inventory and expand one operation at a time. A page holds 50 operations unless --limit says otherwise; operations alone is not the whole inventory — keep calling with --offset <next_offset> until next_offset is null.

<!-- core:end -->

This target's instruments:

bash
graphos-factory init <dir> --name N --spec FILE [--url U] [--retrieved-at T] [--created-at T] [--context-mode generic|specialized|undecided] [--dry-run] [--json]   # a new spec-backed workspace: workspace.yaml (skill.name graphos-factory), working + vendor copy, sources.lock.yaml, inventory.json; refuses an existing workspace; never runs git
graphos-factory export . --out DIR [--base-url URL] [--json]   # the gate over evidence/latest.json, then DIR/<directory>.graphql rendered with the production host and the rover hand-off (link fields lint leaves not validated among what it did not verify); exit 1 refused, 2 usage; never runs rover

export is the target's one command; every other instrument above is the core's. One validated workspace shows the path end to end: gitea (Swagger 2.0, pinned and patched; supergraph_check recorded not_run). Every layer, the write and a live run against a seeded Gitea included, is executed upstream before a snapshot is published; this repository's CI re-runs only the offline checks (lint, validate, reconcile, lock --check --provenance).

<!-- core:begin -->
Show full SKILL.md (2,305 more words)Show less

Policies you apply every time (the why is in the references)

  • Query vs Mutation is semantic, and a POST is a write until the user says otherwise. The inventory records every POST as write and marks the ones whose name suggests a read with a read_hint ([read?] in inventory list). At select, list every hinted POST and ask the user which are reads before writing root: query for any; selection.yaml carries the explicit graphql.root, and a POST selected as a Query needs its reason recorded as a decision (the resolution of the open question you raised at select, or graphos-factory-core decisions add . --resolved … with its query and mutation choices): a write under Query is one callers, caches and retries treat as safe, so the choice has to be reviewable.

  • The inventory is facts; the selection is judgements. inventory.json is a straight reading of the source: every field is checkable against the document, it is regenerable at any time, it is never hand-edited, and it never governs the schema. Every judgement that changes the schema or the tests — the GraphQL root, the field name, the response envelope, what is exposed of the pagination, tags, exclusions — is in selection.yaml, where the tool drafts it as a hint and you confirm it with the user. When an inference is wrong, do not correct the inventory (the lock hashes it; lint reports unacknowledged-inventory-edit and inventory build refuses to overwrite it): fix the document in the pinned working copy and codify --source, or write the judgement you want into selection.yaml and re-run reconcile. codify --key is for neither — it records a hand edit to the schema and refuses a span that is in sync.

  • A relationship link is a judgement in links:. The inventory's candidate_entity_link fact only proposes; selection draft writes the entry confirmed: false, and an unconfirmed draft never governs — no field is applied, reconcile notes it, lint warns. A confirmed link is a field-level @connect keyed by $this, no @key, mirroring its by-id root connector's credential — none under @source auth (connectors-language.md § Relationship fields) — and, for a nullable fk, guarded so a null parent resolves to null instead of failing on GET …/; the entity form is the cross-subgraph and $batch exception and needs a decision. A confirmed link whose target the current rules refuse or whose fact is gone is stale and raises an open decision for the field — choices keep and drop, the refusal as its context; run the decisions add command reconcile and lint print and name the record in the entry's decision:. While it is open the link is reconcile drift, link-target-refused and a target-refused refusal from links apply. Resolved keep exempts it; resolved drop: set include: false citing the decision and remove only that field and its hand-written tests on the next apply — never regenerate.

  • Never an executable SDL default. pageLimit: Int = 5 is a value the router sends when the caller omits the argument and a consuming agent copies as "the value to use". Write pageLimit: Int and put the provider's default — with its minimum, maximum and enum, whenever the inventory carries them — in the argument's doc comment (schema-authoring.md § Argument constraints). No offline layer sees the difference. The one exception is a sparse-fieldsets argument (fields: String = "…"), whose default lint derives and requires (§ Sparse fieldsets). The rest of an argument's doc comment is at most one source sentence, opt-in per service, and never its type or requiredness (§ Argument descriptions).

  • Untyped beats unreachable, but typed beats untyped. Emit a <Prefix>_JSON scalar only when the shape cannot be typed honestly (any oneOf at connect/v0.3; at v0.4 one whose members no wire value tells apart, or that is nested where no connector of its own can re-fetch it; free-form objects; additionalProperties maps) and say why in a doc comment. At v0.4 a discriminated oneOf is a union or interface, mapped as ... kind->match([wire, { __typename: "Member", … }], …, [@, null]) (mapping-language.md § Abstract types). Never a typed guess that drops payload data.

  • An error's message path comes from the inventory's documented shape, never a guessed convention. Read errors[].shape_ref for the operations a source's or connector's errors block covers before writing the expression (schema-authoring.md § Errors); lint's error-path-unresolved checks it.

  • Every included operation gets executed evidence: a case plus a scoped fixture, a unit entry when its arguments are scalars, and a conformance check against the spec or the inferred schema. A skipped layer is reported as skipped, never as a pass.

  • Fixtures are truth. Never edit a recorded fixture to make a test pass — fix the schema, the case, or the expectation. Scrub before commit; a committed file names a credential's environment variable, never its value.

  • A .factory/ file is a regular file, or nothing. Every instrument reads and writes .factory/* through one custody module: a symlink at any component of the path — .factory itself, sources/, the file — is refused, on read and on write alike, and the message names the workspace-relative path, never the link's target. refused — a symlink, not a regular file means the workspace is wrong, not the tool: restore the real file (git checkout -- it, or copy it in) and re-run. Never work around it by reading or writing the target yourself.

  • Account for everything. Every operation the API has is selected, excluded (with a reason), unsupported (with a reason), or unresolved. "What was left on the table" is the question the user is actually asking.

  • Write it down — one test decides where: could a reasonable engineer have gone the other way, and would the service still be valid? (the shapes are in workspace-contract.md)

    • Yes → graphos-factory-core decisions add, with the question and every alternative, resolved by the user or by you with --note saying why you were confident enough not to ask.
    • No, because a reference or the wire settles it → graphos-factory-core findings add --cites …, and only when an instrument needs its omits or affects or the next session needs the fact.
    • A vendor quirk, a dead endpoint, an auth detail → memory.md. Also memory.md, under ## Tried and rejected: a negative result, an accepted residual no instrument can waive, a standing correction the user gave — none is derivable from current state, and the next session reads memory.md, not git log.
    • A measurement, a correction to an earlier record, a stash, "found and fixed" → the commit message, or an edit to the record it corrects.
    • Something the next service would want → graphos-factory-core/references/lessons.md.

    A spec correction is none of these: edit the working copy, then graphos-factory-core codify --source records it as patches[] in sources.lock.yaml with the reason; add the live evidence as verified.

<!-- core:end -->
  • The subgraph is checked alone: supergraph_check is not_run. Compose, unit, e2e, conformance and live prove the subgraph by itself; whether it composes with the user's other subgraphs is unverified until they run rover subgraph check (references/verification.md). Say so in every report, and record any entity or @shareable choice as a decision (references/federation-subgraph.md).
<!-- core:begin -->

Discover, then select, then apply

discover never edits the schema or selection. It rebuilds the inventory, reports added / removed / changed operations, and assesses customer context. It may persist requirements and authorized metadata snapshots; unresolved inputs remain visible in context.yaml. The user changes selection.yaml (by hand, through a host UI, or by asking you); only then does apply touch the schema, and only for the operations the delta names.

Hand edits: detect, codify, keep editable

.factory/applied.lock.yaml is the schema as you last wrote it, one hash per span (header, each type, each root field by operation key), plus one for each pinned document and one for inventory.json. Anything that hashes differently is a hand edit — the engineer's or a host UI's — and graphos-factory-core reconcile, lock --check and lint all name it. An edited inventory.json has nothing to codify: it is built, never edited, so rebuild it and put the correction where it belongs (the pinned document, or selection.yaml). Nor is a root field whose path several operations match equally (unattributed-span): declare its operation in selection.yaml; lock and codify refuse it (schema-authoring.md). Never start an apply while one exists: you could not tell it from your own delta, and "correcting" it back toward the selection is the exact failure this skill exists to prevent.

When you find one, codify it before anything else:

  1. Learn why. Read the git log of the file and the diff of the span; ask the engineer if the reason is not obvious. Never invent one.
  2. Prefer the selection. If the edit is something selection.yaml can say — a tag, a rename, an exclusion, a root, a name — put it there, check graphos-factory-core reconcile shows no drift for the operation, then graphos-factory-core codify --key K --reason R --expressed. The next apply reproduces the edit from the selection; nothing is frozen.
  3. Otherwise assert the intent. graphos-factory-core codify --key K --reason R --assert contains=… --assert tag=… records an override: what must stay true of the span, not its bytes. Write assertions for the point of the edit (the comment that explains it, the ordering, the extra argument), not for incidental text. --until says when to revisit; --expires makes reconcile say so. --pin (no assertions) freezes the span and is a last resort — lint warns on it.
  4. Commit decide:. codify wrote the why onto the override (reason, and context from --context TEXT for what the diff cannot show) and refreshed the lock; it records no decision, and --decision D-id only attaches the entry to a real one. Add a memory.md line when the reason is a vendor fact rather than a local preference.

An override does not lock an operation. When a later selection or inventory change names an overridden span, edit it like any other, keep every assertion true (reconcile checks them), say in the apply: commit which intents you carried forward, and re-run graphos-factory-core lock. If a change would make an assertion impossible, stop and ask — do not pick a side. Read the override's reason and context before touching the span; that is where the why lives.

The same rules govern a pinned spec. graphos-factory-core sources pin --path openapi.json keeps the vendor's bytes untouched as .factory/sources/openapi.upstream.json and acknowledges the working copy in applied.lock.yaml; from then on openapi.json is editable exactly like the schema. When the spec is wrong (a field the API sends as null, a missing response, a wrong type), edit the working copy — never the upstream — and codify it: graphos-factory-core codify --source openapi.json --reason R computes the structural difference from the upstream (formatting never counts) and records it as patches[] on the entry, each operation with the reason (and --context, when given). lock --check, reconcile and lint name an uncodified spec edit (unacknowledged-source-edit) and refuse an apply until it is codified; a modified upstream is an error to restore, never to codify. A newly fetched vendor document is refreshed in with sources refresh --path openapi.json --from FILE --reason R (the patches are replayed and each reported re-applied, obsolete or in conflict; a finding records both shas and each patch's fate; the inventory is rebuilt and diffed; see spec-intake.md), never codified as patches and never copied over the working copy; exit 3 means a conflict whose intent you must re-apply by hand and codify, exit 4 an interrupted write to restore with the git checkout it names. Every discover reads the working copy, so the inventory marks the operations a patch touches. Add verified: (the sample or page that proves the vendor wrong) to a patch by hand; a patch without it is a guess.

Finishing an apply

Report context readiness and any unresolved live requirements separately from connector test results. Then report, in this order:

  1. graphos-factory-core lock --check before you started (no hand edits);
  2. operations added, changed, removed (the graphos-factory-core reconcile report before you edited, and its clean line after, with changed since HEAD naming only spans the delta covers);
  3. every override: its assertions ok, its drift from the selection (the engineer's, reported not fixed), and whether you changed it and why;
  4. the validation table from evidence/latest.json, naming every skipped or not-run layer and its reason;
  5. the graphos-factory-core lock result, including the recorded skill revision, dirty state and commit hash.

The provenance record describes the authored state. The model value is self-reported. It does not prove the model identity or the full session history. It does not claim bit-for-bit replay.

Quote the passing tool output rather than describing it. If any selected operation lacks executed evidence, the workspace is not validated — say so plainly instead of listing the layers that did run.

<!-- core:end -->

When the user asks how the subgraph reaches their graph, answer from references/export-graphos.md: export, then rover, which the user runs.

References

  • customer-context.md — read only when the intake is specialized or unclear (step 3 gates it): the specialized workflow, missing inputs, discovery, questions, and the context.yaml requirements contract
  • workspace-contract.md — files, shapes, git conventions
  • connectors-language.md — @source/@connect, expression variables, relationship fields, entities, errors, v0.3 vs v0.4
  • mapping-language.md — selection/body/queryParams: shapes, the methods the router runs, literals, absence, troubleshooting
  • naming.md — prefixes, snake_case, the semantic root, aliasing
  • schema-authoring.md — envelopes, pagination, argument constraints, scalar choice, enums, nullability, depth, the JSON policy
  • spec-intake.md — reading a description document (OpenAPI 3.x or Swagger 2.0); what securitySchemes and security become in the inventory; spec pathology classes; pinned specs and patches[]
  • api-discovery.md — the no-spec playbook: docs, probes, provenance, confidence; recording an OAuth flow from the docs
  • testing.md — the eight layers, what each is blind to, and the traps
  • selection-review.md — the selection review contract a UI or a headless agent uses to propose a selection change
  • lessons.md — cross-subgraph memory; append when you learn something vendor-independent
  • federation-subgraph.md — versions from the user's graph, the directives a connector subgraph may carry and may not, @tag and contracts, what is still open
  • export-graphos.md — when to export, the command and its gate, what the user runs (rover, the router), and what an export does not verify
  • verification.md — what the layers prove, why supergraph_check is not_run, and the rover subgraph check hand-off

© apollographql, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 30 other files (scripts, references) in skills/graphos-factory of apollographql/skills.

  • SKILL.md
  • graphos-factory-core/references/api-discovery.md
  • graphos-factory-core/references/connectors-language.md
  • graphos-factory-core/references/customer-context.md
  • graphos-factory-core/references/lessons.md
  • graphos-factory-core/references/mapping-language.md
  • graphos-factory-core/references/naming.md
  • graphos-factory-core/references/schema-authoring.md
  • graphos-factory-core/references/selection-review.md
  • graphos-factory-core/references/spec-intake.md
  • graphos-factory-core/references/testing.md
  • graphos-factory-core/references/workspace-contract.md
  • graphos-factory-core/release.env
  • graphos-factory-core/scripts/bootstrap.sh
  • graphos-factory-core/scripts/cache.sh
  • graphos-factory-core/scripts/compose.sh
  • graphos-factory-core/scripts/e2e.sh
  • graphos-factory-core/scripts/elv2.sh
  • … and 13 more

Open the folder on GitHubat commit 5f02fcf

Compare with similar skills

Graphos Factory next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Graphos Factory compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Graphos Factory this skillapollographql/skills117—~15kAutomated safety check: PassMIT
API DesignerJeffallan/claude-skills12k1 repos~2kAutomated safety check: PassMIT
API Contract Designrsmdt/the-startup560—~1.1kAutomated safety check: PassMIT
API Designeraiskillstore/marketplace433—~3.6kAutomated safety check: PassNone
API Architectcuriositech/some_claude_skills244—~1.4kAutomated safety check: PassMIT
Use Yaakmountain-loop/yaak19k—~1.9kAutomated safety check: PassMIT

Similar skills

  • API Designer

    Jeffallan/claude-skills

    Designs REST and GraphQL APIs from resource modeling to an OpenAPI 3.1 contract, with versioning, pagination and RFC 7807 error handling.

    12k GitHub starsUsed in 1 repo~2k tokens
    Backend & APIsAuto-check passed
  • API Contract Design

    rsmdt/the-startup

    REST and GraphQL API design patterns, OpenAPI/Swagger specifications, versioning strategies, and authentication patterns.

    560 GitHub stars~1.1k tokensUpdated 2 mo ago
    Backend & APIsAuto-check passed
  • API Designer

    aiskillstore/marketplace

    Design and document RESTful and GraphQL APIs with OpenAPI/Swagger specifications, authentication patterns, versioning strategies, and best practices.

    433 GitHub stars~3.6k tokensUpdated today
    Backend & APIsAuto-check passed
  • API Architect

    curiositech/some_claude_skills

    Expert API designer for REST, GraphQL, gRPC architectures. An agent skill from curiositech/some_claude_skills.

    244 GitHub stars~1.4k tokensUpdated 1 mo ago
    Backend & APIsAuto-check passed
  • Use Yaak

    mountain-loop/yaak

    A skill your agent uses when the user mentions Yaak, a Yaak workspace, or the yaak command, or asks to call, hit, or smoke test HTTP/REST endpoints, save or organize API requests for reuse or manual…

    19k GitHub stars~1.9k tokensUpdated 3 days ago
    Backend & APIsAuto-check passed
  • Vibex

    Raja0sama/vibex

    Diagrams and checkable docs from a codebase. An agent skill from Raja0sama/vibex.

    388 GitHub stars~7.8k tokensUpdated 4 days ago
    Backend & APIsAuto-check passed

More from apollographql/skills

All 12 skills in this repo
  • Apollo Client

    apollographql/skills

    Guide for building React applications with Apollo Client 4.x.

    117 GitHub stars~1.9k tokensUpdated 2 days ago
    Auto-check passed
  • Apollo Connectors

    apollographql/skills

    DEPRECATED: superseded by the graphos-factory skill (npx skills add apollographql/skills@graphos-factory), which builds and maintains a Connectors subgraph from a REST API with recorded decisions…

    117 GitHub stars~1.8k tokensUpdated 2 days ago
    Auto-check passed
  • Apollo Federation

    apollographql/skills

    Guide for authoring Apollo Federation subgraph schemas. An agent skill from apollographql/skills.

    117 GitHub stars~1k tokensUpdated 2 days ago
    Auto-check passed
  • Apollo iOS

    apollographql/skills

    Guide for building Apple-platform applications with Apollo iOS, the strongly-typed GraphQL client for Swift.

    117 GitHub stars~2k tokensUpdated 2 days ago
    Auto-check passed
  • Apollo Router Plugin Creator

    apollographql/skills

    Guide for writing Apollo Router native Rust plugins. An agent skill from apollographql/skills.

    117 GitHub stars~4.1k tokensUpdated 2 days ago
    Auto-check passed
  • Apollo Server

    apollographql/skills

    Guide for building GraphQL servers with Apollo Server 5.x. An agent skill from apollographql/skills.

    117 GitHub stars~1.8k tokensUpdated 2 days ago
    Auto-check passed

Categories

Questions about Graphos Factory

What does Graphos Factory do?

Build and iterate on an Apollo Connectors subgraph for a GraphOS supergraph from a REST API, with or without an OpenAPI or Swagger spec, in a dedicated git workspace that records what the API…. Graphos Factory is an agent skill from apollographql/skills. Build and iterate on an Apollo Connectors subgraph for a GraphOS supergraph from a REST API, with or without an OpenAPI or Swagger spec, in a dedicated git workspace that records what the API offers, the user's operation and field selection, every design decision, and the evidence each verification layer produced.

When should I use Graphos Factory?

Graphos Factory fits situations like: the user wants to wrap a REST API as a GraphQL subgraph for their supergraph; remove operations; fields from an existing connector subgraph; refresh one against a new spec.

How do I install Graphos Factory in Claude Code?

Run `npx skills add apollographql/skills --skill graphos-factory -a claude-code`. Or copy the skill folder (skills/graphos-factory in apollographql/skills) into .claude/skills/graphos-factory in your project. Claude Code loads it when a task matches its description.

How do I install Graphos Factory in Codex?

Run `npx skills add apollographql/skills --skill graphos-factory -a codex`. Or copy the skill folder (skills/graphos-factory in apollographql/skills) into .agents/skills/graphos-factory in your project. Codex loads it when a task matches its description.

Can I use Graphos Factory in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add apollographql/skills --skill graphos-factory -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/graphos-factory, .gemini/skills/graphos-factory, .github/skills/graphos-factory and .opencode/skills/graphos-factory in your project.

What does Graphos Factory need to run?

Going by SKILL.md and its folder, Graphos Factory needs a shell for the scripts in its folder and the command-line tools its instructions call (bash, git, cargo, npx and gh). Our summary lists: Node.js; A Bash shell. Compatibility (from SKILL.md): macOS or Linux (WSL on Windows) with bash, curl, git and jq; Java 17+ for the end-to-end and live layers. Any Agent Skills host (Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI and others) that can run shell commands..

Does Graphos Factory access the network?

SKILL.md names 1 domain. As links in the text: github.com. This is read from the text; nothing was executed.

Is Graphos Factory safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.

What licence does Graphos Factory use?

Graphos Factory is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Graphos Factory use?

About 15k tokens (SKILL.md is roughly 59k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 5.5k tokens, read only when the agent opens those files.

What are the alternatives to Graphos Factory?

Skills that share tags, products or a category with Graphos Factory: API Designer (Jeffallan/claude-skills, 12k stars), API Contract Design (rsmdt/the-startup, 560 stars), API Designer (aiskillstore/marketplace, 433 stars) and API Architect (curiositech/some_claude_skills, 244 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Graphos Factory?

apollographql (a GitHub organization) maintains it in apollographql/skills, which has 117 GitHub stars. The repository holds 12 skills in this directory. The repository was last updated on October 7, 2026.

Source: apollographql/skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.