Products: route, drift, alert, portal, cost, run
Phases: brainstorm, design-thinking, innovation-strategy, party-mode,
product-brief, architecture, epics (incl. Epic 10 TF compliance),
test-architecture (TDD strategy)
Brand strategy and market research included.
27 KiB
dd0c/portal — Test Architecture & TDD Strategy
Product: Lightweight Internal Developer Portal Phase: 6 — Architecture Design Date: 2026-02-28 Status: Draft
1. Testing Philosophy & TDD Workflow
Core Principle
dd0c/portal's most critical logic — ownership inference, discovery reconciliation, and confidence scoring — is pure algorithmic code with well-defined inputs and outputs. This is ideal TDD territory. The test suite is the specification.
The product's >80% discovery accuracy target is not a QA metric — it's a product promise. Tests enforce it continuously.
Red-Green-Refactor Adapted to This Product
RED → Write a failing test that encodes a discovery heuristic or ownership rule
GREEN → Write the minimum code to pass it (no clever abstractions yet)
REFACTOR → Clean up once the rule is proven correct against real-world fixtures
Adapted cycle for discovery heuristics:
- Capture a real-world failure case (e.g., "Lambda functions named
payment-*were not grouped into a service") - Write a unit test encoding the expected grouping behavior using a fixture of that Lambda response
- Fix the heuristic
- Add the fixture to the regression suite permanently
This means every production accuracy bug becomes a permanent test. The test suite grows as a living record of every edge case the discovery engine has encountered.
When to Write Tests First vs. Integration Tests Lead
| Scenario | Approach | Rationale |
|---|---|---|
| Ownership scoring algorithm | Unit-first TDD | Pure function, deterministic, no I/O |
| Discovery heuristics (CFN → service mapping) | Unit-first TDD | Deterministic logic over fixture data |
| GitHub GraphQL query construction | Unit-first TDD | Query builder logic is pure |
| AWS API pagination handling | Integration-first | Behavior depends on real API shape |
| Meilisearch index sync | Integration-first | Depends on Meilisearch document model |
| DynamoDB schema migrations | Integration-first | Requires real DynamoDB Local behavior |
| WebSocket progress events | E2E-first | Requires full pipeline to be meaningful |
| Stripe webhook handling | Integration-first | Depends on Stripe event payload shape |
Test Naming Conventions
All tests follow the pattern: [unit under test]_[scenario]_[expected outcome]
TypeScript/Node.js (Jest):
describe('OwnershipInferenceEngine', () => {
describe('scoreOwnership', () => {
it('returns_primary_owner_when_codeowners_present_with_high_confidence', () => {})
it('marks_service_unowned_when_top_score_below_threshold', () => {})
it('marks_service_ambiguous_when_top_two_scores_within_tolerance', () => {})
})
})
Python (pytest):
class TestOwnershipScorer:
def test_codeowners_signal_weighted_highest_among_all_signals(self): ...
def test_git_blame_frequency_used_when_codeowners_absent(self): ...
def test_confidence_below_threshold_flags_service_as_unowned(self): ...
File naming:
- Unit tests:
*.test.ts/test_*.pyco-located with source - Integration tests:
*.integration.test.ts/test_*_integration.pyintests/integration/ - E2E tests:
tests/e2e/*.spec.ts(Playwright)
2. Test Pyramid
Recommended Ratio: 70 / 20 / 10
┌─────────────┐
│ E2E / Smoke│ 10% (~30 tests)
│ (Playwright)│ Critical user journeys only
├─────────────┤
│ Integration │ 20% (~80 tests)
│ (real deps) │ Service boundaries, API contracts
├─────────────┤
│ Unit │ 70% (~280 tests)
│ (pure logic)│ All heuristics, scoring, parsing
└─────────────┘
Unit Test Targets (per component)
| Component | Language | Test Framework | Target Coverage |
|---|---|---|---|
| AWS Scanner (heuristics) | Python | pytest | 90% |
| GitHub Scanner (parsers) | Node.js | Jest | 90% |
| Reconciliation Engine | Node.js | Jest | 85% |
| Ownership Inference | Python | pytest | 95% |
| Portal API (route handlers) | Node.js | Jest + Supertest | 80% |
| Search proxy + cache logic | Node.js | Jest | 85% |
| Slack Bot command handlers | Node.js | Jest | 80% |
| Feature flag evaluation | Node.js/Python | Jest/pytest | 95% |
| Governance policy engine | Node.js | Jest | 95% |
| Schema migration validators | Node.js | Jest | 100% |
Integration Test Boundaries
| Boundary | What to Test | Tool |
|---|---|---|
| Discovery → GitHub API | GraphQL query shape, pagination, rate limit handling | MSW (mock service worker) or nock |
| Discovery → AWS APIs | boto3 call sequences, pagination, error handling | moto (AWS mock library) |
| Reconciler → PostgreSQL | Upsert logic, conflict resolution, RLS enforcement | Testcontainers (PostgreSQL) |
| Inference → PostgreSQL | Ownership write, confidence update, correction propagation | Testcontainers (PostgreSQL) |
| API → Meilisearch | Index sync, search query construction, tenant filter injection | Meilisearch test instance (Docker) |
| API → Redis | Cache set/get/invalidation, TTL behavior | ioredis-mock or Testcontainers (Redis) |
| Slack Bot → Portal API | Command → search → format response | Supertest against local API |
| Stripe webhook → API | Subscription activation, plan change, cancellation | Stripe CLI webhook forwarding |
E2E / Smoke Test Scenarios
- Full onboarding: GitHub OAuth → AWS connection → discovery trigger → catalog populated
- Cmd+K search returns results in <200ms after discovery
- Ownership correction propagates to similar services
- Slack
/dd0c who ownsreturns correct owner - Discovery accuracy: synthetic org with known ground truth scores >80%
- Governance strict mode: discovery populates pending queue, not catalog directly
- Panic mode: all catalog writes return 503
3. Unit Test Strategy (Per Component)
3.1 AWS Scanner (Python / pytest)
What to test:
- Resource-to-service grouping heuristics (the core logic)
- Confidence score assignment per signal type
- Pagination handling for each AWS API
- Cross-region scan aggregation
- Error handling for throttling, missing permissions, empty accounts
Key test cases:
# tests/unit/test_cfn_scanner.py
class TestCloudFormationScanner:
def test_stack_name_becomes_service_name_with_high_confidence(self):
# Given a CFN stack named "payment-api"
# Expect service entity with name="payment-api", confidence=0.95
def test_stack_tags_extracted_as_service_metadata(self):
# Given stack with tags {"service": "payment", "team": "payments"}
# Expect service.metadata includes both tags
def test_stacks_in_multiple_regions_deduplicated_by_name(self):
# Given same stack name in us-east-1 and us-west-2
# Expect single service entity with both regions in infrastructure
def test_deleted_stacks_excluded_from_results(self):
# Given stack with status DELETE_COMPLETE
# Expect it is not included in discovered services
def test_pagination_fetches_all_stacks_beyond_first_page(self):
# Given mock returning 2 pages of stacks
# Expect all stacks from both pages are processed
class TestLambdaScanner:
def test_lambdas_with_shared_prefix_grouped_into_single_service(self):
# Given ["payment-webhook", "payment-processor", "payment-refund"]
# Expect single service "payment" with confidence=0.60
def test_lambda_with_apigw_trigger_gets_higher_confidence(self):
# Given Lambda with API Gateway event source mapping
# Expect confidence=0.85 (not 0.60)
def test_standalone_lambda_without_prefix_pattern_kept_as_individual(self):
# Given Lambda named "data-export-job" with no siblings
# Expect individual service entity, not grouped
class TestServiceGroupingHeuristics:
def test_cfn_stack_takes_priority_over_ecs_service_for_same_name(self):
# Given CFN stack "payment-api" AND ECS service "payment-api"
# Expect single service entity (not duplicate), source=cloudformation
def test_explicit_github_repo_tag_overrides_name_matching(self):
# Given AWS resource with tag github_repo="acme/payments-v2"
# Expect repo_link="acme/payments-v2" with confidence=0.95
# (not fuzzy name match result)
Mocking strategy:
- Use
mototo mock all boto3 calls — no real AWS calls in unit tests - Fixture files in
tests/fixtures/aws/contain realistic API response payloads - Each fixture named after the scenario:
cfn_stacks_multi_region.json,lambda_functions_with_apigw.json
@pytest.fixture
def mock_aws(aws_credentials):
with mock_cloudformation(), mock_ecs(), mock_lambda_():
yield
def test_full_scan_produces_expected_service_count(mock_aws, cfn_fixture):
setup_mock_cfn_stacks(cfn_fixture)
result = AWSScanner(tenant_id="test", role_arn="arn:aws:iam::123:role/test").scan()
assert len(result.services) == cfn_fixture["expected_service_count"]
3.2 GitHub Scanner (Node.js / Jest)
What to test:
- GraphQL query construction and batching
- CODEOWNERS file parsing (all valid formats)
- README first-paragraph extraction
- Deploy workflow target extraction
- Rate limit detection and backoff
Key test cases:
// tests/unit/github-scanner/codeowners-parser.test.ts
describe('CODEOWNERSParser', () => {
it('parses_simple_wildcard_ownership_to_team', () => {
const input = '* @acme/platform-team'
expect(parse(input)).toEqual([{ pattern: '*', owners: ['@acme/platform-team'] }])
})
it('parses_path_specific_ownership', () => {
const input = '/src/payments/ @acme/payments-team'
expect(parse(input)).toEqual([{ pattern: '/src/payments/', owners: ['@acme/payments-team'] }])
})
it('handles_multiple_owners_per_pattern', () => {
const input = '*.ts @acme/frontend @acme/platform'
expect(parse(input).owners).toHaveLength(2)
})
it('ignores_comment_lines', () => {
const input = '# This is a comment\n* @acme/team'
expect(parse(input)).toHaveLength(1)
})
it('returns_empty_array_for_missing_codeowners_file', () => {
expect(parse(null)).toEqual([])
})
it('handles_individual_user_ownership_not_just_teams', () => {
const input = '* @sarah-chen'
expect(parse(input)[0].owners[0]).toBe('@sarah-chen')
})
})
describe('READMEExtractor', () => {
it('extracts_first_non_heading_non_badge_paragraph', () => {
const readme = `# Payment Gateway\n\n\n\nHandles Stripe checkout flows.`
expect(extractDescription(readme)).toBe('Handles Stripe checkout flows.')
})
it('returns_null_when_readme_has_only_headings_and_badges', () => {
const readme = `# Title\n\n`
expect(extractDescription(readme)).toBeNull()
})
})
describe('WorkflowTargetExtractor', () => {
it('extracts_ecs_service_name_from_deploy_workflow', () => {
const yaml = loadFixture('deploy-workflow-ecs.yml')
expect(extractDeployTarget(yaml)).toEqual({
type: 'ecs_service',
name: 'payment-api',
cluster: 'production'
})
})
it('extracts_lambda_function_name_from_serverless_deploy', () => {
const yaml = loadFixture('deploy-workflow-lambda.yml')
expect(extractDeployTarget(yaml)).toEqual({
type: 'lambda_function',
name: 'payment-webhook-handler'
})
})
})
Mocking strategy:
- Use
nockormswto intercept GitHub GraphQL API calls - Fixture files in
tests/fixtures/github/for realistic API responses - Test the GraphQL query builder separately from the HTTP client
3.3 Reconciliation Engine (Node.js / Jest)
What to test:
- Cross-referencing AWS resources with GitHub repos (all 5 matching rules)
- Deduplication when multiple signals point to the same service
- Conflict resolution when signals disagree
- Batch processing of SQS messages
Key test cases:
describe('ReconciliationEngine', () => {
describe('matchAWSToGitHub', () => {
it('explicit_tag_match_takes_highest_priority', () => {
const awsService = buildAWSService({ tags: { github_repo: 'acme/payment-gateway' } })
const ghRepo = buildGHRepo({ name: 'payment-gateway', org: 'acme' })
const result = reconcile([awsService], [ghRepo])
expect(result[0].repoLinkSource).toBe('explicit_tag')
expect(result[0].repoLinkConfidence).toBe(0.95)
})
it('deploy_workflow_match_used_when_no_explicit_tag', () => {
const awsService = buildAWSService({ name: 'payment-api' })
const ghRepo = buildGHRepo({ deployTarget: 'payment-api' })
const result = reconcile([awsService], [ghRepo])
expect(result[0].repoLinkSource).toBe('deploy_workflow')
})
it('fuzzy_name_match_used_as_fallback', () => {
const awsService = buildAWSService({ name: 'payment-service' })
const ghRepo = buildGHRepo({ name: 'payment-svc' })
const result = reconcile([awsService], [ghRepo])
expect(result[0].repoLinkSource).toBe('name_match')
expect(result[0].repoLinkConfidence).toBe(0.75)
})
it('no_match_produces_aws_only_service_entity', () => {
const awsService = buildAWSService({ name: 'legacy-monolith' })
const result = reconcile([awsService], [])
expect(result[0].repoUrl).toBeNull()
expect(result[0].discoverySources).toContain('cloudformation')
expect(result[0].discoverySources).not.toContain('github_repo')
})
it('deduplicates_cfn_stack_and_ecs_service_with_same_name', () => {
const cfnService = buildAWSService({ source: 'cloudformation', name: 'payment-api' })
const ecsService = buildAWSService({ source: 'ecs_service', name: 'payment-api' })
const result = reconcile([cfnService, ecsService], [])
expect(result).toHaveLength(1)
expect(result[0].discoverySources).toContain('cloudformation')
expect(result[0].discoverySources).toContain('ecs_service')
})
})
})
3.4 Ownership Inference Engine (Python / pytest)
This is the highest-value unit test target. Ownership inference is the most complex logic and the most likely source of accuracy failures.
Key test cases:
class TestOwnershipScorer:
def test_codeowners_weighted_highest_at_0_40(self):
signals = [Signal(type='codeowners', team='payments', raw_score=1.0)]
result = score_ownership(signals)
assert result['payments'].weighted_score == pytest.approx(0.40)
def test_multiple_signals_summed_correctly(self):
signals = [
Signal(type='codeowners', team='payments', raw_score=1.0), # 0.40
Signal(type='cfn_tag', team='payments', raw_score=1.0), # 0.20
Signal(type='git_blame_frequency', team='payments', raw_score=1.0), # 0.25
]
result = score_ownership(signals)
assert result['payments'].total_score == pytest.approx(0.85)
def test_primary_owner_is_highest_scoring_team(self):
signals = [
Signal(type='codeowners', team='payments', raw_score=1.0),
Signal(type='git_blame_frequency', team='platform', raw_score=1.0),
]
result = score_ownership(signals)
assert result.primary_owner == 'payments'
def test_service_marked_unowned_when_top_score_below_0_50(self):
signals = [Signal(type='git_blame_frequency', team='unknown', raw_score=0.3)]
result = score_ownership(signals)
assert result.status == 'unowned'
def test_service_marked_ambiguous_when_top_two_within_0_10(self):
signals = [
Signal(type='codeowners', team='payments', raw_score=0.8),
Signal(type='codeowners', team='platform', raw_score=0.75),
]
result = score_ownership(signals)
assert result.status == 'ambiguous'
def test_user_correction_overrides_all_inference_with_score_1_00(self):
signals = [
Signal(type='codeowners', team='payments', raw_score=1.0),
Signal(type='user_correction', team='platform', raw_score=1.0),
]
result = score_ownership(signals)
assert result.primary_owner == 'platform'
assert result.primary_confidence == 1.00
assert result.primary_source == 'user_correction'
def test_correction_propagation_applies_to_matching_repo_prefix(self):
correction = Correction(repo='payment-gateway', team='payments')
candidates = ['payment-processor', 'payment-webhook', 'auth-service']
propagated = propagate_correction(correction, candidates)
assert 'payment-processor' in propagated
assert 'payment-webhook' in propagated
assert 'auth-service' not in propagated
3.5 Portal API — Route Handlers (Node.js / Jest + Supertest)
What to test:
- Tenant isolation enforcement (tenant_id injected into every query)
- Search endpoint proxies to Meilisearch with mandatory tenant filter
- PATCH /services enforces correction logging
- Auth middleware rejects unauthenticated requests
describe('GET /api/v1/services/search', () => {
it('injects_tenant_id_filter_into_meilisearch_query', async () => {
const spy = jest.spyOn(meilisearchClient, 'search')
await request(app).get('/api/v1/services/search?q=payment').set('Authorization', `Bearer ${tenantAToken}`)
expect(spy).toHaveBeenCalledWith(expect.objectContaining({
filter: expect.stringContaining(`tenant_id = '${TENANT_A_ID}'`)
}))
})
it('returns_401_when_no_auth_token_provided', async () => {
const res = await request(app).get('/api/v1/services/search?q=payment')
expect(res.status).toBe(401)
})
it('tenant_a_cannot_see_tenant_b_services', async () => {
// Seed Meilisearch with services for both tenants
// Query as tenant A, assert no tenant B results
})
})
describe('PATCH /api/v1/services/:id', () => {
it('stores_correction_in_corrections_table', async () => {
await request(app)
.patch(`/api/v1/services/${SERVICE_ID}`)
.send({ team_id: NEW_TEAM_ID })
.set('Authorization', `Bearer ${adminToken}`)
const correction = await db.corrections.findFirst({ where: { service_id: SERVICE_ID } })
expect(correction).toBeDefined()
expect(correction.new_value).toMatchObject({ team_id: NEW_TEAM_ID })
})
it('sets_confidence_to_1_00_on_user_correction', async () => {
await request(app).patch(`/api/v1/services/${SERVICE_ID}`).send({ team_id: NEW_TEAM_ID })
const ownership = await db.service_ownership.findFirst({ where: { service_id: SERVICE_ID } })
expect(ownership.confidence).toBe(1.00)
expect(ownership.source).toBe('user_correction')
})
})
3.6 Slack Bot Command Handlers (Node.js / Jest)
What to test:
- Command parsing (
/dd0c who owns <service>) - Typo tolerance matching logic (delegated to search, but bot needs to handle 0 results)
- Block kit message formatting
- Error handling (unauthorized workspace, missing service)
3.7 Feature Flags & Governance Policy (Node.js / Jest)
What to test:
- Flag evaluation (
openfeatureprovider) - Governance strict vs. audit mode
- Panic mode blocking writes
4. Integration Test Strategy
Integration tests verify that our code correctly interacts with external boundaries: databases, caches, search indices, and third-party APIs.
4.1 Service Boundary Tests
- Discovery ↔ GitHub/GitLab: Use
nockorMSWto mock the GitHub GraphQL endpoint. Assert that the Node.js scanner constructs the correct query and handles rate limits (HTTP 403/429) via retries. - Catalog ↔ PostgreSQL: Use Testcontainers for PostgreSQL to verify complex
upsertqueries, foreign key constraints, and RLS (Row-Level Security) tenant isolation. - API ↔ Meilisearch: Use a Meilisearch Docker container. Assert that document syncing (PostgreSQL -> SQS -> Meilisearch) completes and search queries with
tenant_idfilters return the expected subset of data.
4.2 Git Provider API Contract Tests
- Write scheduled "contract tests" that run against the live GitHub API daily using a dedicated test org.
- These detect if GitHub changes their GraphQL schema or rate limit behavior.
- Assert that
HEAD:CODEOWNERSblob extraction still works.
4.3 Testcontainers for Local Infrastructure
- Database:
testcontainers-nodespinning uppostgres:15-alpine. - Search:
getmeili/meilisearch:latest. - Cache:
redis:7-alpine. - Run these in GitHub Actions via Docker-in-Docker.
5. E2E & Smoke Tests
E2E tests treat the system as a black box, interacting only through the API and the React UI. We keep these fast and focused on the "5-Minute Miracle" critical path.
5.1 Critical User Journeys (Playwright)
- The Onboarding Flow: Mock GitHub OAuth login -> Connect AWS (mock CFN role ARN validation) -> Trigger Discovery -> Wait for WebSocket completion -> Verify 147 services appear in catalog.
- Cmd+K Search: Open modal (
Cmd+K) -> type "pay" -> assert "payment-gateway" is highlighted in < 200ms -> press Enter -> assert service detail card opens. - Correcting Ownership: Open service detail -> Click "Correct Owner" -> select new team -> assert badge changes to 100% confidence -> assert Meilisearch is updated.
5.2 The >80% Auto-Discovery Accuracy Validation
- The "Party Mode" Org: Maintain a real GitHub org and a mock AWS environment with exactly 100 known services, 10 known teams, and specific chaotic naming conventions.
- The Assertion: Run discovery. Assert that > 80 of the services are correctly inferred with the right primary owner and repo link.
- This is the most important test in the suite. If a PR drops this below 80%, it cannot be merged.
5.3 Synthetic Topology Generation
- Script to generate
Nmock CFN stacks,MECS services, andKGitHub repos to feed the E2E environment without hitting AWS/GitHub limits.
6. Performance & Load Testing
Load tests ensure the serverless architecture scales correctly and the Cmd+K search remains instantaneous.
6.1 Discovery Scan Benchmarks
- Target: 500 AWS resources + 500 GitHub repos scanned and reconciled in < 120 seconds.
- Tooling: K6 or Artillery. Push 5,000 synthetic SQS messages into the Reconciler queue and measure Lambda batch processing throughput.
6.2 Catalog Query Latency
- Target: API search endpoint returns in < 100ms at the 99th percentile.
- Test: Load Meilisearch with 10,000 service documents. Fire 50 concurrent Cmd+K search requests per second. Assert p99 latency.
6.3 Concurrent Scorecard Evaluation
- Ensure the Python inference Lambda can evaluate 1,000 services concurrently without database connection exhaustion (using Aurora Serverless v2 connection pooling).
7. CI/CD Pipeline Integration
The test pyramid is enforced through GitHub Actions.
7.1 Test Stages
- Pre-commit: Husky runs ESLint, Prettier, and fast unit tests (Jest/pytest) for changed files only.
- PR Gate: Runs the full Unit and Integration test suites. Blocks merge if coverage drops or tests fail.
- Merge (Main): Deploys to Staging. Runs E2E Critical User Journeys and the 80% Accuracy Validation suite against the Party Mode org.
- Post-Deploy: Smoke tests verify health endpoints and ALB routing in production.
7.2 Coverage Thresholds
- Global Unit Test Coverage: 80%
- Ownership Inference & Reconciliation Logic: 95%
- Feature Flag & Governance Evaluators: 100%
7.3 Test Parallelization
- Jest tests run with
--maxWorkers=50%locally,100%in CI. - Integration tests using Testcontainers run serially per file to avoid database port conflicts, or use dynamic port binding and separate schemas for parallel execution.
8. Transparent Factory Tenet Testing
Testing the governance and compliance features of the IDP itself.
8.1 Feature Flag Circuit Breakers
- Test: Enable a flagged discovery heuristic that generates 10 phantom services.
- Assert: The system detects the threshold (>5 unconfirmed), auto-disables the flag, and marks the 10 services as
status: quarantined.
8.2 Schema Migration Validation
- Test: Attempt to apply a PR that drops a column from the
servicestable. - Assert: CI migration validator script fails the build (additive-only rule).
8.3 Decision Log Enforcement
- Test: Run a discovery scan where service ownership is inferred from
git blame. - Assert: A
decision_logentry is written to PostgreSQL with the prompt/reasoning, alternatives, and confidence.
8.4 OTEL Span Assertions
- Test: Run the Reconciler Lambda.
- Assert: The
catalog_scanparent span contains child spans forownership_inferencewith attributes forcatalog.service_id,catalog.ownership_signals, andcatalog.confidence_score. Use an in-memory OTEL exporter for testing.
8.5 Governance Policy Enforcement
- Test: Set tenant policy to
strictmode. Simulate auto-discovery finding a new service. - Assert: Service is placed in the "pending review" queue and NOT visible in the main catalog.
- Test: Set
panic_mode: true. Attempt aPATCH /api/v1/services/123. - Assert: HTTP 503 Service Unavailable.
9. Test Data & Fixtures
High-quality fixtures are the lifeblood of this TDD strategy.
9.1 GitHub/GitLab API Response Factories
- JSON files containing real obfuscated GraphQL responses for Repositories,
CODEOWNERSblobs, and Team memberships. - Use factories (e.g.,
fisheryor custom functions) to easily override fields:buildGHRepo({ name: 'auth-service', languages: ['Go'] }).
9.2 Synthetic Topology Generators
- Scripts that generate interconnected AWS resources (e.g., a CFN stack containing an API Gateway routing to 3 Lambdas interacting with 1 RDS instance).
9.3 CODEOWNERS and Git Blame Mocks
- Diverse
CODEOWNERSfiles covering edge cases: wildcard matching, deep path matching, invalid syntax, user-vs-team owners.
10. TDD Implementation Order
To bootstrap the platform efficiently, testing and development should follow this sequence based on Epic dependencies:
- Epic 2 (GitHub Parsers): Write pure unit tests for
CODEOWNERSparser andREADMEextractor. Value: High ROI, zero dependencies. - Epic 1 (AWS Heuristics): Write unit tests for mapping CFN stacks and Tags to Service entities. Value: Core product logic.
- Epic 2 (Ownership Inference): TDD the scoring algorithm. Build the weighting math. Value: The brain of the platform.
- Epic 3 (Service Catalog Schema): Integration tests for PostgreSQL RLS and upserting services. Value: Data durability.
- Epic 2 (Reconciliation): Unit tests merging AWS and GitHub mock entities. Value: Pipeline glue.
- Epic 4 (Search Sync): Integration tests for pushing DB updates to Meilisearch.
- Epic 5 (API & UI): E2E test for the Cmd+K search flow.
- Epic 10 (Governance & Flags): Unit tests for feature flag circuit breakers and strict mode.
- Epic 9 (Onboarding): Playwright E2E for the 5-Minute Miracle flow.
This sequence ensures the most complex algorithmic logic is proven before it is wired to databases and APIs.