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.
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…
$ npx skills add apollographql/skills --skill graphos-factory -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install apollographql/skills graphos-factory --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ 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-srcUse ~/.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/
Install the "graphos-factory" agent skill from https://github.com/apollographql/skills/tree/main/skills/graphos-factory into .claude/skills/graphos-factory/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "graphos-factory", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/apollographql/skills/tree/main/skills/graphos-factoryType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add apollographql/skills --skill graphos-factory -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install apollographql/skills graphos-factory --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/apollographql/skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/graphos-factory .agents/skills/graphos-factory && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "graphos-factory" agent skill from https://github.com/apollographql/skills/tree/main/skills/graphos-factory into .agents/skills/graphos-factory/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "graphos-factory", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add apollographql/skills --skill graphos-factory -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install apollographql/skills graphos-factory --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/apollographql/skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/graphos-factory .cursor/skills/graphos-factory && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "graphos-factory" agent skill from https://github.com/apollographql/skills/tree/main/skills/graphos-factory into .cursor/skills/graphos-factory/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "graphos-factory", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/apollographql/skills.git --path skills/graphos-factory--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add apollographql/skills --skill graphos-factory -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install apollographql/skills graphos-factory --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/apollographql/skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/graphos-factory .gemini/skills/graphos-factory && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "graphos-factory" agent skill from https://github.com/apollographql/skills/tree/main/skills/graphos-factory into .gemini/skills/graphos-factory/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "graphos-factory", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install apollographql/skills graphos-factoryInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add apollographql/skills --skill graphos-factory -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/apollographql/skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/graphos-factory .github/skills/graphos-factory && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "graphos-factory" agent skill from https://github.com/apollographql/skills/tree/main/skills/graphos-factory into .github/skills/graphos-factory/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "graphos-factory", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add apollographql/skills --skill graphos-factory -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install apollographql/skills graphos-factory --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/apollographql/skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/graphos-factory .opencode/skills/graphos-factory && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "graphos-factory" agent skill from https://github.com/apollographql/skills/tree/main/skills/graphos-factory into .opencode/skills/graphos-factory/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "graphos-factory", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
graphos-factoryBuild 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. 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.
4 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 5f02fcf. It shows what the files ask for, not the result of running them.
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.
Ships 1 file in scripts/ (Shell, from the files we listed), which the agent can run.
Shell commands in SKILL.md call:
bashgitcargonpxghFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in 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.
From compatibility in the SKILL.md frontmatter.
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.
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.
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.
The full file from apollographql/skills at commit 5f02fcf, republished under its MIT licence (© apollographql). 5,240 words, ~14,693 tokens.
.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.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 -->
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).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.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.init among them, follow
this table) and read the reference it names.| Verb | Read first | What you do |
|---|---|---|
discover | spec-intake or api-discovery | build/refresh inventory.json; assess customer context and update its records/snapshots within the authorized scope; report the diff; never edit schema or selection |
select | naming, schema-authoring | agree 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. |
apply | schema-authoring, mapping-language, testing | graphos-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: |
codify | workspace-contract, mapping-language | a 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 |
validate | testing, customer-context | check 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, testing | graphos-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):
| Verb | Read first | What you do |
|---|---|---|
init <name> | workspace-contract | decide 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 |
export | export-graphos | only 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 -->
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.
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 testsinventory 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:
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 roverexport 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 -->
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)
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.graphos-factory-core findings add --cites …, and only when an instrument
needs its omits or affects or the next session needs the fact.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.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 -->
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 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.
.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:
git log of the file and the diff of the span;
ask the engineer if the reason is not obvious. Never invent one.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.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.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.
applyReport context readiness and any unresolved live requirements separately from connector test results. Then report, in this order:
graphos-factory-core lock --check before you started (no hand edits);graphos-factory-core reconcile report
before you edited, and its clean line after, with changed since HEAD
naming only spans the delta covers);ok, its drift from the selection (the
engineer's, reported not fixed), and whether you changed it and why;evidence/latest.json, naming every skipped
or not-run layer and its reason;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.
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 contractworkspace-contract.md — files, shapes, git conventionsconnectors-language.md — @source/@connect, expression variables, relationship fields, entities, errors, v0.3 vs v0.4mapping-language.md — selection/body/queryParams: shapes, the methods the router runs, literals, absence, troubleshootingnaming.md — prefixes, snake_case, the semantic root, aliasingschema-authoring.md — envelopes, pagination, argument constraints, scalar choice, enums, nullability, depth, the JSON policyspec-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 docstesting.md — the eight layers, what each is blind to, and the trapsselection-review.md — the selection review contract a UI or a headless agent uses to propose a selection changelessons.md — cross-subgraph memory; append when you learn something vendor-independentfederation-subgraph.md — versions from the user's graph, the directives a connector subgraph may carry and may not, @tag and contracts, what is still openexport-graphos.md — when to export, the command and its gate, what the user runs (rover, the router), and what an export does not verifyverification.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
SKILL.md and 30 other files (scripts, references) in skills/graphos-factory of apollographql/skills.
Open the folder on GitHubat commit 5f02fcf
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.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Graphos Factory this skillapollographql/skills | 117 | — | ~15k | Automated safety check: Pass | MIT | |
| API DesignerJeffallan/claude-skills | 12k | 1 repos | ~2k | Automated safety check: Pass | MIT | |
| API Contract Designrsmdt/the-startup | 560 | — | ~1.1k | Automated safety check: Pass | MIT | |
| API Designeraiskillstore/marketplace | 433 | — | ~3.6k | Automated safety check: Pass | None | |
| API Architectcuriositech/some_claude_skills | 244 | — | ~1.4k | Automated safety check: Pass | MIT | |
| Use Yaakmountain-loop/yaak | 19k | — | ~1.9k | Automated safety check: Pass | MIT |
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.
rsmdt/the-startup
REST and GraphQL API design patterns, OpenAPI/Swagger specifications, versioning strategies, and authentication patterns.
aiskillstore/marketplace
Design and document RESTful and GraphQL APIs with OpenAPI/Swagger specifications, authentication patterns, versioning strategies, and best practices.
curiositech/some_claude_skills
Expert API designer for REST, GraphQL, gRPC architectures. An agent skill from curiositech/some_claude_skills.
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…
Raja0sama/vibex
Diagrams and checkable docs from a codebase. An agent skill from Raja0sama/vibex.
apollographql/skills
Guide for building React applications with Apollo Client 4.x.
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…
apollographql/skills
Guide for authoring Apollo Federation subgraph schemas. An agent skill from apollographql/skills.
apollographql/skills
Guide for building Apple-platform applications with Apollo iOS, the strongly-typed GraphQL client for Swift.
apollographql/skills
Guide for writing Apollo Router native Rust plugins. An agent skill from apollographql/skills.
apollographql/skills
Guide for building GraphQL servers with Apollo Server 5.x. An agent skill from apollographql/skills.
Categories
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.
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.
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.
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.
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.
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..
SKILL.md names 1 domain. As links in the text: github.com. This is read from the text; nothing was executed.
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.
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.
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.
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.
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.