A repo-native test intelligence system that turns every pull request into a living behavior contract — automatically generating, managing, and validating test cases with zero external infrastructure.
Most teams have a variation of the same broken loop:
- Developer ships a feature
- QE manually writes test cases from memory or a Confluence doc
- Regression suite grows stale — nobody updates it when behaviors change
- A bug ships that a test case should have caught — but didn't, because nobody wrote it
TIP closes that loop at the source: the pull request.
Pull Request Opens
│
▼
Context Gatherer ──── diff + PR description + existing behavior registry
│
▼
Claude (behavior summary) ──── "Here's what changed in behavioral terms"
│
▼
Developer Approves ──── /approve-behaviors
│
▼
Test Case Generator ──── P0–P3 cases across 5 test patterns
│
▼
Pool Commit ──── YAML committed to /harness/test-cases/ by bot
│
▼
GitHub Issues ──── P0/P1 cases become automation candidates
│
▼
Bug Filed? ──── Agent finds the gap, writes regression TC, logs it
│
▼
Release Branch ──── Auto-generated report: coverage, gaps, deploy gate
The entire system lives in the repo. No external database. No additional infrastructure at M1. Drop in a GitHub Action and it works on any repo.
| # | Name | Status |
|---|---|---|
| M1 | Foundation — iOS Sample App + Behavior Registry | ✅ Complete |
| M2 | PR Agent — Context Gathering + Behavior Summary | 🔧 Scaffolded |
| M3 | Test Case Generation + Pool Management | 🔧 Scaffolded |
| M4 | Bug Loop + Regression Intelligence | 🔧 Scaffolded |
| M5 | Release Readiness Dashboard + Automation Mapping | 🔧 Scaffolded |
The sample app is a real SwiftUI iOS application — not a toy. It has enough surface area to make the behavior registry and test case pool meaningful: authentication, stateful data, navigation, and CRUD flows. It is the subject that TIP is applied to.
Authentication
- Login screen with email + password validation
- Error state for empty fields
- Session state managed via
AuthService(ObservableObject)
Dashboard
- Post-auth greeting with user display name
- Live stats: total todos and completion count
- Updates in real time as todos change — no manual refresh
Todo List
- Add, edit, and delete todos via sheets
- Per-item completion toggle with visual strikethrough
- Persistent across sessions — stored in
UserDefaults
Settings
- Displays current user's email and name
- Logout clears session and returns to login
Navigation
- Tab bar: Dashboard / Todos / Settings
- Sheet-based add and edit flows
App/
├── TIPApp.swift @main entry point
├── Core/
│ ├── Models/User.swift User model (Codable)
│ ├── Navigation/AppTabView.swift Tab bar + shared TodoViewModel injection
│ └── Services/AuthService.swift Auth state (@EnvironmentObject)
├── Features/
│ ├── Auth/
│ │ ├── Views/LoginView.swift
│ │ └── ViewModels/LoginViewModel.swift
│ ├── Dashboard/
│ │ ├── Views/DashboardView.swift Live stats via @EnvironmentObject
│ │ └── ViewModels/DashboardViewModel.swift
│ ├── Todos/
│ │ ├── Models/Todo.swift Codable, Identifiable
│ │ ├── Views/TodoListView.swift
│ │ ├── Views/TodoAddEditView.swift
│ │ └── ViewModels/TodoViewModel.swift UserDefaults persistence
│ └── Settings/
│ └── Views/SettingsView.swift
└── Resources/
└── Assets.xcassets
Patterns:
- MVVM throughout
@EnvironmentObjectfor cross-feature shared state (AuthService,TodoViewModel)@StateObjectonly at the injection point (AppTabView)async/awaitfor auth operations
Requirements: Xcode 16+, iOS 17 simulator
open TIP.xcodeproj
# Select any iOS 17+ simulator → RunLogin with any non-empty email and password — the sample app accepts all credentials.
The harness lives in the repo alongside the code.
harness/
├── behaviors/ One YAML per feature area
│ ├── _schema.yaml Schema reference
│ └── auth.yaml Auth behaviors (AUTH-B001, AUTH-B002, AUTH-B003)
├── test-cases/ One YAML per feature area
│ ├── _schema.yaml Schema reference
│ └── auth_tc.yaml Auth test cases (AUTH-TC001 – AUTH-TC003)
├── audit/ Gap audit log (written by M4 bug agent)
└── reports/ Release readiness reports (generated by M5)
# harness/behaviors/auth.yaml
behaviors:
- id: AUTH-B001
summary: User can log in with a valid email and password and is navigated to the dashboard.
pr_of_origin: "#1"
file_paths:
- App/Features/Auth/Views/LoginView.swift
- App/Core/Services/AuthService.swift
status: active # active | deprecated | pending# harness/test-cases/auth_tc.yaml
test_cases:
- id: AUTH-TC001
behavior_id: AUTH-B001
priority: P0 # P0–P3
title: Successful login with valid credentials
steps:
- Enter a valid email address
- Enter the correct password
- Tap "Log In"
expected_result: User is navigated to the Dashboard tab.
automation_status: automation-candidate
last_result: nullEvery public Swift declaration that implements user-visible behavior carries an inline annotation:
// @test-ids: AUTH-TC001, AUTH-TC002
final class AuthService: ObservableObject {The linter enforces this on every PR:
./scripts/lint_test_ids.sh # checks changed files against mainThe agent pipeline is implemented in agent/ (Python). Each module maps to one concern:
| Module | Milestone | What it does |
|---|---|---|
context_gather.py |
M2 | Collects diff, PR description, comments, existing behaviors |
behavior_summary.py |
M2 | Claude API call → behavior delta or new summary |
pr_comment.py |
M2 | Posts structured PR comment, manages soft-block label |
test_generator.py |
M3 | Claude generates P0–P3 TCs across 5 test patterns |
pool_manager.py |
M3 | Commits approved TCs to YAML pool |
bug_webhook.py |
M4 | Parses bug label event, extracts impacted files from stack trace |
gap_finder.py |
M4 | Cross-references behavior registry + pool to find coverage gaps |
regression_generator.py |
M4 | Generates regression TC, appends to audit log |
release_reporter.py |
M5 | Loads pool → Markdown release report with P0 deploy gate |
result_ingestion.py |
M5 | Ingests JUnit XML or manual pass/fail → updates pool YAMLs |
semantic_mapper.py |
M5 | Matches unannotated test functions to pool entries via Claude |
Three workflows ship with the repo:
.github/workflows/
├── pr_agent.yml Triggers on PR open/push + issue_comment
├── bug_agent.yml Triggers when 'bug' label is applied to an issue
└── release_report.yml Triggers on push to release/** branches
| Secret | Used by |
|---|---|
ANTHROPIC_API_KEY |
All Claude API calls |
BOT_PAT |
bug_agent.yml (needs push access to commit regression TCs) |
vscode-extension/ ships a CodeLens provider that reads the test pool and overlays pass/fail status next to every @test-ids annotation:
// @test-ids: AUTH-TC001 ← ✅ AUTH-TC001: Successful login (2026-06-15)
final class AuthService: ObservableObject {Red for fail, green for pass, grey for not yet run. Click opens the test case detail.
- Copy
harness/and.github/workflows/into your repo - Add
ANTHROPIC_API_KEYto repo secrets - Open a PR — the agent takes it from there
The behavior registry starts empty and fills itself as PRs land. No upfront migration needed.
cp .env.example .env # add ANTHROPIC_API_KEY and GITHUB_TOKEN
pip install -r requirements.txt
# Run the agent locally against a PR
python -c "
from pathlib import Path
from agent.context_gather import gather
from agent.behavior_summary import generate
ctx = gather(PR_NUMBER, 'owner/repo', Path('harness'))
summary = generate(ctx)
print(summary.raw_summary)
"
# Submit a manual test result
./scripts/submit_result.sh AUTH-TC001 passWhy YAML in the repo? The registry versions with the code. It appears in PRs as diffs. It works for any team with zero infrastructure setup.
Why a soft block and not a hard CI gate?
Social friction is faster to adopt than hard gates. The needs-behavior-review label is visible to reviewers without blocking CI for teams that haven't fully committed yet.
Why Claude for behavior summarization and not a rules engine? Behaviors are expressed in natural language — "user can log in" — not in code terms. LLMs are the right tool for translating code diffs into behavioral semantics.
Why no "approve all" button in M3? Anti-rubber-stamp design. Each test case is a 10-second decision. The value of the review is in that decision, not the approval.