Skip to contents

These charts describe specmill v0.1.9. Use the first chart to create, adopt, tailor, or update a client. Use the second to trace a wrapper from schema discovery through generation and runtime behavior. The site article renders the charts; GitHub also renders the Mermaid blocks in this source.

Create and maintain a client

flowchart TD
    accTitle: Create and maintain a specmill client
    accDescr: New, adopted and configured clients converge on tailoring, planning, remediation, application and verification before use and maintenance.
    Start["Pin the reviewed toolkit version<br/>Save local schemas and their provenance"]
    Kind{"Client starting point?"}
    New["New package<br/>configure_client: preview names and policy"]
    Init["initialize_client: create package, YAML,<br/>client-owned helper and optional companions"]
    Existing["Existing package without specmill.yml<br/>Record passing tests and public/runtime contracts"]
    Config["configure_client: preview YAML<br/>Select one operation and map the existing helper"]
    Adopt["propose_mappings and verify_adoption<br/>Review comparisons and fixed contracts;<br/>adopt only verified files or retain manual code"]
    Update["Already configured client<br/>Keep reviewed YAML; inspect_client compares helpers;<br/>manually merge required helper changes"]
    Tailor["Tailor YAML names, selection, inputs, mappings,<br/>media, request controls and response policy;<br/>keep hooks and iteration policy in client R"]
    Plan["generate_client: plan<br/>Review inventory, diagnostics, drift and file actions"]
    Review{"Plan ready to apply?"}
    Remediate["Correct source defects or YAML/helper mismatches;<br/>review a complete mapping or retain implementation;<br/>resolve file ownership without bypassing protection"]
    Apply["generate_client: apply<br/>Write eligible output and ownership manifest"]
    Check["generate_client: check<br/>Then run fixed contract, transport and existing tests"]
    Pass{"Fresh output and tests pass?<br/>Selected limitations reviewed?"}
    Use["Install or reload the client<br/>Use dry run before approved live calls;<br/>inspect verbose status when needed"]
    Save["Commit toolkit pin, schemas, YAML, helper/hooks,<br/>fixtures, generated output and provenance together"]
    Change{"Schema, policy, helper<br/>or toolkit changes?"}

    Start --> Kind
    Kind -->|New| New --> Init --> Tailor
    Kind -->|Existing, not configured| Existing --> Config --> Adopt --> Tailor
    Kind -->|Existing, configured| Update --> Tailor
    Tailor --> Plan --> Review
    Review -->|Needs remediation| Remediate --> Plan
    Review -->|Reviewed| Apply --> Check --> Pass
    Pass -->|No| Remediate
    Pass -->|Yes| Use --> Save --> Change
    Change -->|Yes| Update

configure_client() and propose_mappings() default to read-only proposals. Initialization writes immediately; use it for a new package. Configuration apply creates absent files and refuses conflicts, so merge changes into existing YAML yourself. For multiple APIs, review configure_apis() before configuration or initialization; each included API gets its own YAML with nested tag groups.

Adoption has two paths. Reviewed legacy generated files can use exact adoption hashes. Hand-written wrappers use verify_adoption() comparisons and its returned contracts/files before replacement, as described in the existing-client guide. Keep wrappers that cannot be verified with implementation: existing and an explicit public contract. Do not bulk-delete protected files or hash them merely to remove a warning.

Tailoring belongs in reviewed YAML and client-owned R, rather than generated wrappers. YAML selects names, arguments, mappings, supported encodings and policy functions. R supplies executable hooks, response/retry functions, custom helpers, and explicit batching/pagination policies. New clients can select companions = c('batching', 'pagination') at initialization; existing helpers need manual adoption. Ordinary wrappers still make one helper call, and a page argument alone does not create a loop. Emitted companions run without specmill.

A successful check establishes output freshness, not API compatibility or absence of diagnostics. Contract tests check helper calls; transport tests check serialized requests and response behavior. Dry runs still validate inputs and authentication, and prepared requests may contain credentials. Regeneration does not refresh client-owned transport, hooks, session controls, or companions.

Trace the underlying pipeline

The upper section runs during development. The lower section runs in the generated client without specmill. It describes the standard renderer and current request helper; retained implementations, custom renderers, and custom helpers can have their own behavior.

flowchart TD
    accTitle: Trace specmill generation and client runtime
    accDescr: Schema discovery and policy selection lead to parsing, diagnostics, mappings, rendering and ownership checks. Installed wrappers validate inputs, run hooks and optional guards, then call client transport and response policy.
    subgraph Generation["Development: specmill generation"]
        Load["load_project<br/>Validate project/API/service/group YAML;<br/>resolve helpers, callbacks and schema files"]
        Files["Schema discovery<br/>Explicit files + patterns, minus schemas.exclude"]
        Policy["Resolve policy<br/>Methods intersect; path exclusions accumulate;<br/>defaults merge before operation overrides"]
        Routes["Resolve declared hook routes, when present<br/>Check schema identity, helper, server and body media"]
        Read["read_operations<br/>Read local JSON/YAML; check version and paths;<br/>resolve path items and enumerate METHOD /path"]
        Select{"Included, allowed method,<br/>and no excluded path match?"}
        Excluded["inventory: excluded / policy_exclusion<br/>Skip detailed operation parsing"]
        Parse["Parse selected operation and local references<br/>Drop reviewed exclude_parameters;<br/>resolve parameters, body, security, server and response metadata"]
        Supported{"Fits the supported subset?"}
        Native["Supported operation records<br/>Shared endpoint-table parser + validated metadata"]
        Diagnose["Record unsupported operation / diagnostic<br/>classification, code, source_location and guidance"]
        Recovery{"Reviewed handling?"}
        Mapped["Complete inputs + request mapping<br/>Eligible metadata becomes client-mapped;<br/>keep mapping_diagnostics"]
        Retain["implementation: existing<br/>Keep declared manual source;<br/>keep retained_diagnostics for unsupported metadata"]
        Blocked["Leave unsupported operation visible<br/>Do not generate it or delete previous output"]
        Configure["configure_operation<br/>Apply names, public inputs, mappings, hooks,<br/>request/response policy and route guard settings"]
        Render["Validate public names, definitions and helper arguments<br/>Render wrappers and supplied contract tests;<br/>stage docs and configured formatting"]
        Reconcile["Reconcile candidate output with manifest and local files<br/>Protect unowned/edited/lifecycle files;<br/>review explicit exclusions and renames"]
        Mode{"Generation mode?"}
        Plan["plan: return candidate actions and evidence<br/>No client changes"]
        Check["check: error on stale/protected output<br/>Review diagnostics separately"]
        Apply["apply: lock, revalidate inputs, back up and write<br/>Record hashes and declared route provenance"]

        Load --> Files --> Policy --> Routes --> Read --> Select
        Select -->|No| Excluded
        Select -->|Yes| Parse --> Supported
        Supported -->|Yes| Native --> Configure
        Supported -->|No| Diagnose --> Recovery
        Recovery -->|Eligible complete mapping| Mapped --> Configure
        Recovery -->|Retain manual contract| Retain --> Configure
        Recovery -->|No reviewed handling| Blocked
        Configure --> Render --> Reconcile --> Mode
        Mode -->|plan| Plan
        Mode -->|check| Check
        Mode -->|apply| Apply
    end

    subgraph Runtime["Runtime: generated wrapper and client-owned code"]
        Call["Call installed wrapper<br/>Optional explicit companion orchestrates repeated calls"]
        Inputs["Validate public inputs<br/>Capture params"]
        Pre["Optional pre_request hooks<br/>Update params or hook_state; may skip the request"]
        Skip{"skip_request?"}
        Skipped["Use hook-provided result<br/>post_on_skip selects whether post hooks run"]
        Guard["Build final helper arguments<br/>Optional route_guard checks reviewed METHOD /path"]
        Helper["Client request helper<br/>Resolve runtime options, URL, timeout/retry policy;<br/>serialize params/body and authenticate"]
        Dry{"Dry run?"}
        Prepared["Return prepared request<br/>No HTTP or response policy"]
        HTTP["Send HTTP<br/>Apply configured bounded retries when permitted"]
        Response["Final response<br/>Selected response_policy may replace result<br/>or delegate to status checks and decoding"]
        Post["Optional post_response hooks<br/>Shape the helper result"]
        Result["Return result"]

        Call --> Inputs --> Pre --> Skip
        Skip -->|No| Guard --> Helper --> Dry
        Skip -->|Yes| Skipped
        Skipped -->|post_on_skip is false| Result
        Skipped -->|post_on_skip is true| Post
        Dry -->|Yes| Prepared --> Post
        Dry -->|No| HTTP --> Response --> Post --> Result
    end

    Apply -.->|Install or reload eligible output| Call

The chart shows the main flow, not every error exit. Invalid configuration, unreadable documents, broken document structure, collisions, incompatible definitions, or changing inputs can stop generation before it returns a plan. Operation-level failures normally remain in the inventory. Runtime validation, guard, authentication, transport, policy, and decoding errors propagate; a post-response hook is not an error handler.

Declared route resolution may inspect targets excluded from wrapper generation. All targets must belong to the service’s loaded schemas and agree with the keyed route’s effective helper, resolved schema server, body media and text encoding. Declarations record provenance; they do not select extra wrappers or enforce runtime choices. route_guard is opt-in and checks final mapped method/path after pre-hooks, before the helper. Skipped requests bypass it. It does not check server destinations, redirects, parameters or changes inside a custom helper.

Complete request mappings own every helper argument, including authentication, server and response-policy bindings. They can bridge eligible parser limitations but cannot repair malformed metadata or broken references. Retention preserves source and checks the declared public interface; it does not make the manual implementation schema-compliant.

Find the source of a wrapper’s behavior

Start with a fresh plan using the same callbacks as normal generation:

plan <- specmill::generate_client(root, config = 'specmill.yml', mode = 'plan')
plan$inventory
plan$operations
plan$diagnostics
plan$mapping_diagnostics
plan$retained_diagnostics
plan$server_diagnostics
plan$drift
plan$files

Use service and the original key such as GET /items/{item_id} to find the operation before looking up its public R name. status describes selection or mapping disposition; classification explains the parser finding. A mapped or retained operation can keep its original limitation classification.

Observed behavior Inspect and remediate
Schema or endpoint is missing Check load_project(root)$services, schemas.files, patterns, and filename exclusions first. Then inspect inventory selection reasons.
Endpoint is excluded despite an include entry Methods intersect across project/API/group or service; path exclusions accumulate. An exact include cannot restore a prohibited operation.
Selected endpoint has no generated wrapper Read diagnostics classification, code, source location and guidance. Correct source defects; review complete mappings for capability gaps or explicitly retain manual code. Unsupported status alone never authorizes removal.
Name, argument, default or output file differs Inspect resolved defaults and the operation’s names, parameters, inputs, request and file settings. Later defaults/operation settings override earlier ones. drift reports public-formal differences.
Schema parameter is absent Check per-operation exclude_parameters, inventory excluded_parameters, and public input/parameter overrides. Schema exclusions happen before detailed parameter validation; path parameters cannot use exclude_parameters.
Hook calls another endpoint Inspect inventory routes, manifest route provenance, request bindings and pre-hook state. Declare reviewed targets and opt into route_guard if final method/path must be checked.
Request goes to another server Check runtime <package>.request$base_url, initialization/catalogue override, then operation/path/root schema servers and server_diagnostics. A method/path guard does not control the server.
Timeout or retry behavior differs Runtime request options override YAML defaults per field. Inspect max_retries, retry_writes and the selected client retry_policy; a predicate does not grant write replay permission.
Result type or HTTP-error handling differs Inspect actual response Content-Type, generated response_policy selection, helper option/default, and post hooks. A policy runs after retries, may replace decoding/status handling, and is separate from post-response hooks.
One page or one oversized body is returned/rejected Wrappers do not infer iteration. Body batch limits validate size without splitting. Select explicit client-owned batching/pagination companions and bounded policies for repeated requests.
Plan reports protected or retained files Compare local content and manifest ownership; use reviewed adoption or keep a manual implementation. Protected replacement/removal and retained implementation are separate concepts.
Fresh output still behaves like the old client Install/reload the generated client, then inspect its actual helper/hooks/options. inspect_client(root)$helpers compares helper baselines when known; generation never silently updates those files.

For detailed examples, use configuration, runtime hooks, testing, and troubleshooting.