Frontend Contract GuardDocs
v0.1.0 · Local-first

Core Concepts

Frontend Contract Guard records real API traffic, compares a known-good baseline with a candidate build, and traces each changed field to the TypeScript, Angular or Vue code that reads it. Detection is deterministic: the same inputs always produce the same findings.

Detection
DeterministicThe same inputs always give the same findings
Redaction
Before diskHeaders, query params and JSON paths
Evidence
4 tiersNever auto-confirmed by a missing field

Stages 01–05The pipeline, baseline to report

Capture to reportSame engine: desktop, web and CLI
  1. 01
    Capture & sanitizecapture/build.ts

    Drop non-JSON noise and known analytics hosts, normalize endpoint paths (/hotels/htl_01 → /hotels/:id), and redact headers, query params and configured JSON paths — before anything is written to disk.

    Redacted snapshot
  2. 02
    Infer shapeschema-diff/infer.ts

    Per endpoint: types, occurrence counts and a sample, for the baseline and for the candidate separately.

    Shape per endpoint
  3. 03
    Compare structurallycompare.ts

    Deterministic diff of the two shapes: removed, added, type-changed, nullability, optionality and status changes. Value differences are ignored unless you turn them on.

    Structural changes
  4. 04
    Map to sourcesource-mapper

    One TypeScript Program over your project finds call sites, type-linked response access, and Angular/Vue template bindings that read the changed field.

    File and line references
  5. 05
    Score & reportanalyzer + report-generator

    Severity, confidence and an evidence tier are attached to each finding; the report ships as JSON, Markdown and HTML, with a draft Playwright test per finding.

    JSON · Markdown · HTML

StateBaselines vs. candidates

A baseline is a known-good capture for one environment, created by a person from a capture — not auto-generated from a git merge. Candidates are compared against it, and baselines are never overwritten: replacing one archives the old version.

Nothing is pinned automatically

There’s no CI step that mints a new baseline on merge. You choose the capture that becomes the baseline, and you choose when to replace it.

PropertyBaselineCandidate
Created byA person, from a captureAny capture you compare
ScopeOne environment of one projectThe same environment; another environment must be confirmed
When it changesOnly when you replace it; the old one is archivedWith every new capture
RoleThe reference the build is checked againstThe build being checked

ClassificationBreaking vs. informational changes

Severity and confidence are two separate axes. Severity (critical / high / medium / low / info) is how much a change could break things; confidence (high / medium / low / unresolved) is how strong the evidence is that the frontend actually reads the field.

Changes that raise severityRaises severity
  • Field removed from the response
  • A value’s type changed (e.g. a number became a string)
  • A non-nullable field started returning null
Changes treated as informationalInformational
  • A new property added with no mapped consumer yet
  • A timestamp or other value difference (off unless you turn value-diffing on)
  • Array order, when only value differences changed

TriageEvidence tiers

Each finding is labelled with how strong the evidence is — never auto-confirmed by a missing field alone.

  1. 01Structural

    The response shape changed; no evidence yet that the frontend depends on it.

  2. 02Potentially breaking

    A removal, type or nullability change the frontend calls, without a matched usage.

  3. 03Affected usage

    Source code that reads the field was found — type-linked or a name match.

  4. 04Confirmed by test

    A person recorded a failing test for this finding. This is the only way a finding becomes confirmed.

SourceHow source mapping works

One TypeScript Program is built over your .ts files (tests and node_modules excluded). It records HTTP call sites (fetch, axios, Angular HttpClient), the response type at each site, every property access the type checker resolves, and Angular/Vue template bindings — then walks from a changed response field to the exact file and line that reads it.

Example from the demo project

Confidence levels

high

The access resolves via the TypeScript checker to the declared response type, and the endpoint was observed on a browser route.

medium

Type-linked without a route, or a name/template match on at least two trailing path segments.

low

The endpoint is called from source, but no usage of the field was found — not proof the field is unused.

unresolved

No source analyzed, or no call site matched the endpoint.

Noise controlHandling volatile fields

High-traffic APIs return non-deterministic values — transaction IDs, timestamps, counters. Configure ignore rules and redaction paths in fcg.config.json (the desktop app can export a project’s config under CI & Integrations) so those fields don’t generate noise.

fcg.config.jsonExample ignore rules
{
  "rules": {
    "ignore": [
      {
        "id": "trace-ids",
        "path": "**.traceId",
        "reason": "A new id on every request; the UI never reads it"
      },
      {
        "id": "search-request-id",
        "endpoint": "GET /api/hotels/search",
        "path": "meta.requestId",
        "kinds": [
          "type-changed",
          "nullability-changed"
        ],
        "reason": "Generated per request by the gateway"
      }
    ],
    "reportValueChanges": false
  }
}

Each rule needs a reason and at least an endpoint, a path or change kinds. Paths use ** for any depth and * within one segment.

Don’t over-mask

A wildcard ignore rule on a whole object can hide a real backend change along with the noise. Scope ignore rules to the specific volatile field.

Was this page helpful?