Skip to content

Repository files navigation

CHAIN — Change Happened. Anyone Informed? Nope.

🔗 CHAIN

Change Happened. Anyone Informed? Nope.

CHAIN detects breaking REST API changes across repositories, walks every downstream dependency—including BFF/proxy hops—and alerts the specific Slack owners before their feature breaks.

When one API contract changes, every linked team should know before production does.

Built for OpenAI Build Week's Developer Tools track with Python, FastAPI, OpenAI GPT-5.6, and three runnable Node.js demo services.

Try the live interactive demo — use the access code included in the Devpost submission. No installation is needed.

Built with Codex and GPT-5.6

Codex was the development partner from the initial PRD to the deployed judge experience. In one continuous build workflow, it helped turn the idea into the graph model, implement the JS/TS static analyzer and incremental store, connect the GitHub webhook and Slack delivery pipeline, create the three-repository fixture, build the visual demo, prepare Vercel deployment, and add and run the 23-test suite. Codex also accelerated the less visible work that normally slows down a prototype: tracing cross-repository evidence loss, fixing the BFF's dual role as both API provider and caller, validating real Slack delivery, and checking the hosted experience end to end.

The key product and architecture decisions remained explicit human choices:

  • Model dependencies as a directed graph and traverse it in reverse so a single upstream change exposes every downstream feature and owner.
  • Preserve caller-owned edges during incremental scans; rescanning the changed provider must not erase dependency evidence discovered in other repos.
  • Keep deterministic static analysis on the common path and invoke GPT-5.6 only at semantic boundaries where rules are brittle.
  • Notify affected owners with endpoint, feature, path, and hop distance instead of broadcasting a generic repository-change alert.
  • Give judges a guarded, browser-based scenario that demonstrates both a real alert and a zero-notification safe change without requiring a local build.

GPT-5.6 is part of the running product, not just the build process. Through the OpenAI Responses API and strict structured output, it performs two focused judgment tasks:

  1. Breaking-change classification: distinguish an external REST contract change from an internal refactor or comment-only edit and return the affected method, path, change type, and rationale.
  2. Ambiguous dependency resolution: match uncertain env-driven HTTP calls to known endpoints with an explicit confidence score.

This hybrid keeps CHAIN fast, inspectable, and cost-conscious while using GPT-5.6 precisely where semantic understanding improves the result. The hosted demo exposes the model provider and rationale so judges can verify that path directly.

The demo scenario

employee-app ("My HR Info")
  └─ GET /api/hr-info
       employee-app-server (BFF)
         └─ GET /api/employee/profile
              hrm-server

hrm-server changes GET /api/employee/profile from the legacy x-session-token contract to required Bearer authentication. It does not know who calls it. CHAIN identifies both the BFF (one hop) and employee app (two hops), then produces a targeted alert for @mina-app naming the "My HR Info" feature.

What is implemented

  • Static JS/TS analysis for Express routes, fetch, and axios calls.
  • Base URL resolution through constants and process.env.* || <fallback> values.
  • Server-handler analysis, so one repo can expose an endpoint and call another.
  • GPT-5.6 structured-output fallback for ambiguous edge matching.
  • JSON graph persistence with repo-scoped incremental replacement.
  • Unbounded reverse graph traversal with shortest-hop deduplication.
  • GPT-5.6 structured diff classification with a deterministic offline fallback.
  • GitHub push webhook handling, HMAC SHA-256 verification, and Compare API diffs.
  • Slack Incoming Webhook alerts with owner, chain, endpoint, and feature.
  • A no-credential demo harness, interactive judge site, graph visualization, and 23 automated tests.

The implementation is one Python package (chain/) split by concern:

GitHub push
  → webhook.py / github.py       signature + diff retrieval
  → analyzer.py                  incremental repo scan
  → classifier.py / llm.py       structured breaking-change judgment
  → graph_store.py / impact.py   arbitrary-depth dependent walk
  → notifier.py                  targeted Slack alert

Interactive judge demo

The FastAPI service includes a visual, single-page demo at /. A judge enters the access code supplied with the submission, optionally pastes their own Slack Incoming Webhook URL, and runs one of two fixed, safe scenarios:

  • Breaking auth change: GPT-5.6 classifies the diff, the graph highlights one-hop and two-hop impact, and two targeted Slack alerts are delivered.
  • Harmless comment: the graph remains safe and zero alerts are generated.

The access code is configured only through CHAIN_DEMO_ACCESS_CODE; it is not hardcoded in this repository. User-supplied Slack webhook URLs are accepted only from https://hooks.slack.com/services/..., used for that request, and never stored or returned. If the field is blank, the server uses its configured demo Slack channel. Delivery is rate-limited while result previews remain available.

The hosted judge experience is available at chain-api-watch.vercel.app. The submission form provides the access code; leaving the Slack field blank sends alerts to the preconfigured demo channel.

Run the site locally:

export CHAIN_DEMO_ACCESS_CODE="choose-a-demo-code"
uvicorn chain.webhook:app --host 127.0.0.1 --port 8000

Then open http://127.0.0.1:8000.

Vercel auto-detects the root app.py FastAPI entry point. Render users can use the included Blueprint instead. On either host, configure OPENAI_API_KEY, SLACK_WEBHOOK_URL, and CHAIN_DEMO_ACCESS_CODE as encrypted environment variables. Do not commit their values. For serverless hosts, also set CHAIN_GRAPH_PATH=/tmp/chain-graph.json.

Quick start: full acceptance demo

Python 3.11+ is required. The demo does not need OpenAI, GitHub, or Slack credentials; missing Slack configuration becomes a clearly labeled dry run.

python3 -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'

./demo/run_demo.sh

The command proves all four acceptance paths:

  1. Initial analysis creates exactly two edges in the expected three-repo chain.
  2. The auth diff is classified as breaking: true.
  3. Impact analysis finds employee-app-server at one hop and employee-app at two hops, including @mina-app and "My HR Info" in the alert.
  4. The next push scans only hrm-server; a comment-only diff sends no alert.

Useful individual commands:

chain init
chain graph
chain analyze hrm-server --sha example-push-sha
chain classify hrm-server demo/fixtures/breaking_auth.diff
chain simulate
chain demo

All commands can also run as python3 -m chain.cli <command>.

Run the sample application

The starting (non-breaking) application is fully runnable. Node.js 18+ is required because the BFF uses the built-in fetch implementation.

npm --prefix sample-repos/hrm-server install
npm --prefix sample-repos/employee-app-server install

# Run these in three terminals:
npm --prefix sample-repos/hrm-server start
npm --prefix sample-repos/employee-app-server start
npm --prefix sample-repos/employee-app start

Open http://localhost:3000 and click Load profile, or run the automated service smoke test:

./demo/smoke_services.sh

The services use ports 3000 (app), 3001 (BFF), and 3002 (HR backend).

Enable GPT-5.6 and Slack

The deterministic fallback makes recorded demos reliable. With OPENAI_API_KEY present, CHAIN_USE_LLM=auto uses the OpenAI Responses API and Pydantic structured output. The default model is gpt-5.6-sol with high reasoning effort; both are configurable.

For a lower-cost smoke test, the included .env.example uses gpt-5.6-luna with low reasoning effort. This keeps the runtime on GPT-5.6 while fitting CHAIN's narrow structured-classification task. Copy and load the file before running the demo; CHAIN deliberately does not load secret files automatically.

cp .env.example .env
# Fill OPENAI_API_KEY and, for real delivery, SLACK_WEBHOOK_URL in .env.
set -a
source .env
set +a

CHAIN_USE_LLM=always chain classify hrm-server demo/fixtures/breaking_auth.diff
CHAIN_USE_LLM=always ./demo/run_demo.sh

Why the offline fallback exists: a stage demo should still be deterministic if Wi-Fi, model access, or a credential fails. Runtime logs say whether OpenAI or the heuristic produced the classification; Slack dry runs are never reported as delivered messages.

The OpenAI integration follows the current GPT-5.6 model guidance: Responses API, an explicit reasoning setting, and a strict structured result consumed by the graph pipeline.

Run the GitHub webhook receiver

export GITHUB_WEBHOOK_SECRET="choose-a-long-random-secret"
export GITHUB_TOKEN="github-personal-access-token"  # needed for private repos
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..."
python main.py

Configure the GitHub webhook URL as:

POST https://<public-host>/webhooks/github
Content type: application/json
Secret: the exact GITHUB_WEBHOOK_SECRET value
Event: push

For a push, CHAIN uses repository.full_name, before, and after to retrieve the GitHub Compare API patches. It then re-analyzes only repository.name and persists that analysis record in data/graph.json.

Unsigned webhooks are rejected. CHAIN_ALLOW_UNSIGNED_WEBHOOKS=1 exists only for local testing and should never be enabled on a public endpoint.

Register real repositories

Edit config/repos.json:

{
  "name": "orders-web",
  "path": "/absolute/path/to/orders-web",
  "base_urls": ["https://orders.internal.example"],
  "slack_user": "<@U01234567>",
  "features": {
    "GET /api/orders": "Order history"
  }
}

base_urls resolve host-to-repository ownership. features maps a caller's method/path to human-readable product context. Use Slack member IDs such as <@U01234567> for real mentions; plain handles are still included as readable text but Incoming Webhooks may not turn them into notifications.

After editing the registry, run chain init once. Later pushes are incremental.

Graph model and incremental behavior

Endpoint handlers are nodes. A standalone frontend call becomes a client_call node. A call inside an Express handler originates directly from that endpoint:

employee-app:CALL:GET:/api/hr-info
  → employee-app-server:ENDPOINT:GET:/api/hr-info
  → hrm-server:ENDPOINT:GET:/api/employee/profile

When an upstream endpoint breaks, impact analysis follows incoming edges. This naturally finds callers of callers at any depth. Each edge records the repo whose analysis produced it. Re-analyzing hrm-server replaces only nodes and edges owned by hrm-server, preserving all incoming dependency evidence from the BFF and frontend.

The store is deliberately JSON for hackathon legibility. data/graph.json is written atomically and keeps the latest 100 scan records so incremental behavior is visible in the demo.

Testing

pytest
python3 -m compileall -q chain tests main.py
./demo/smoke_services.sh

The suite covers static extraction, the exact two-edge fixture, BFF dual roles, incremental replacement, auth/removal/safe diff classification, depth-limited and two-hop traversal, targeted Slack content, GitHub HMAC validation, webhook routing, and the complete core flow.

MVP boundaries

This submission intentionally supports REST/HTTP dependencies, one registry, one JSON store, and Slack only. It does not include a dashboard, graph database, multi-tenant auth, CODEOWNERS/org-chart resolution, email, queues, gRPC, or production job orchestration. See PRD.md for the original scope and acceptance criteria.

About

CHAIN — Change Happened. Anyone Informed? Nope. Detect silent production changes before they surprise your team.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages