KeiSeiKit-1.0/skills/api-design/phase-3-contract.md
Parfii-bot a4e667de10 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

4.8 KiB
Raw Blame History

Phase 3 — Contract skeleton (OpenAPI 3.1 OR GraphQL SDL)

Generate the machine-readable SSoT from RESOURCES + SHAPE. This is the ONLY phase that writes a file outside the skill — the user picks WHERE (phase 3a) and the skill writes a skeleton there (phase 3c).

Reference: _blocks/api-openapi-first.md, _blocks/api-graphql.md.

3a — Contract path click (AskUserQuestion, single-select)

{
  "questions": [
    {
      "question": "Where should the contract skeleton be written?",
      "header": "Contract path",
      "multiSelect": false,
      "options": [
        {"label": "api/openapi.yaml (repo root / api/)",
         "description": "Default for REST. Picks up OpenAPI 3.1 tooling by convention."},
        {"label": "schema.graphql (repo root)",
         "description": "Default for GraphQL. Picks up graphql-codegen / async-graphql by convention."},
        {"label": "docs/api/<name>.yaml (docs folder)",
         "description": "Keep contract alongside human docs; publish site from same folder."},
        {"label": "Skip — I'll commit the skeleton manually from chat",
         "description": "Skill prints the full skeleton inline; user copies into repo."}
      ]
    }
  ]
}

Store as CONTRACT_PATH. Validate: the path must be consistent with STYLE (REST → .yaml/.json; GraphQL → .graphql/.graphqls). If inconsistent → re-ask per NO DOWNGRADE, listing the two correct options.

3b — Contract skeleton rules

The skeleton is NOT the final spec — it is a scaffold the user will flesh out. Rules:

  • OpenAPI 3.1 skeleton must include:

    • openapi: 3.1.0 — never 3.0.x, never 2.0 (per api-openapi-first.md).
    • info with title, version: 0.1.0, description (first 2 lines of INTAKE).
    • servers list with at least production + staging placeholder URLs.
    • components.schemas.Problem ($ref'd by every 4xx/5xx — RFC 9457 shape).
    • components.securitySchemes placeholder (bearerAuth by default; oauth2 if Phase 5 chooses OAuth; filled in Phase 5).
    • components.parameters for cursor, limit, page (depending on PAGINATION — filled in Phase 5).
    • One paths entry PER entity × action cell from Phase 2c: GET /foos, POST /foos, GET /foos/{id}, PATCH /foos/{id}, DELETE /foos/{id}. Each $refs to components/schemas/Foo (stub with id, created_at, updated_at, plus placeholder fields).
    • ETag + idempotency hints as comments in the skeleton where relevant (api-rest-conventions.md).
  • GraphQL SDL skeleton must include:

    • scalar DateTime + scalar UUID declared once.
    • interface Node { id: ID! } (Relay convention).
    • One type Foo implements Node { id: ID! createdAt: DateTime! ... } per entity.
    • type FooConnection { edges: [FooEdge!]! pageInfo: PageInfo! totalCount: Int }
      • type FooEdge { node: Foo! cursor: String! } for every listable entity (Relay spec).
    • Root type Query { foo(id: ID!): Foo foos(first: Int, after: String): FooConnection! } per entity per action cell.
    • Root type Mutation { createFoo(input: CreateFooInput!): Foo! ... } with input types per action cell.
    • Root type Subscription stub — empty if Phase 5 says no realtime.
    • enum ErrorCode { NOT_FOUND FORBIDDEN BAD_USER_INPUT RATE_LIMITED INTERNAL_SERVER_ERROR } — referenced by resolver error extensions.

3c — Emit / write the skeleton

If CONTRACT_PATH != "Skip":

  • Compute absolute path in the current repo.
  • If file exists → STOP, re-ask: "File exists at . Overwrite, merge, or pick a new name?" (three-option AskUserQuestion, fail-closed default = "pick a new name").
  • Write the skeleton. Record CONTRACT = <absolute path> and CONTRACT_LINES = <LOC> for the final report.

If CONTRACT_PATH == "Skip":

  • Emit the full skeleton as a fenced code block in chat.
  • Record CONTRACT = <inline> and CONTRACT_LINES = <LOC>.

3d — Lint + drift-gate hint (inline)

Remind the user ONCE:

Add these to CI next:

  1. spectral lint <contract> — OpenAPI/Spectral ruleset OR graphql-schema-linter schema.graphql — style + breaking-change catch.
  2. oasdiff breaking (REST) OR graphql-inspector diff (GraphQL) on every PR — blocks breaking changes unless breaking: approved label is set.
  3. Contract tests (Schemathesis / Dredd / Pact) run against the deployed server in staging. Drift = test fail.

No AskUserQuestion here — this is guidance.

Verify-criterion

  • CONTRACT_PATH picked; file written or skeleton printed inline.
  • Every entity from RESOURCES surfaces in the skeleton as a schema/type.
  • Every action cell from Phase 2c has a matching path/operation OR a matching query/mutation field.
  • No field values invented — placeholder-only schemas, marked with a # TODO: define fields comment; RULE 0.4 no fabricated sample data.