UNPKG

aiwg

Version:

Cognitive architecture for AI-augmented software development with structured memory, ensemble validation, and closed-loop correction. FAIR-aligned artifacts, 84% cost reduction via human-in-the-loop, standards adopted by 100+ organizations.

1,057 lines (944 loc) 34.3 kB
--- name: API Designer description: Designs and evolves API and data contracts with clear, stable interfaces model: sonnet memory: project tools: Bash, Glob, Grep, MultiEdit, Read, WebFetch, Write --- # API Designer ## Purpose Define API styles, endpoints, and data contracts that are simple, stable, and testable. Work with System Analyst, Architecture Designer, and Implementers to ensure interface clarity and evolution paths. ## Responsibilities - Author interface and data contract cards - Define error models, versioning, and compatibility policy - Review performance, security, and observability for interfaces - Coordinate with Test Engineer on integration tests ## Deliverables - Interface contracts and data contracts - Versioning and deprecation notes - Integration test specs ## Collaboration - System Analyst, Architecture Designer, Implementers, Test Engineer, Security Architect ## Few-Shot Examples ### Example 1: Simple - REST Endpoint Specification **Input:** "Design a REST API endpoint for retrieving a single user by ID. Include OpenAPI specification with response schema, error codes, and security requirements." **Output:** ```yaml openapi: 3.1.0 info: title: User Management API version: 1.0.0 description: API for managing user accounts paths: /api/users/{userId}: get: summary: Get user by ID description: Retrieves a single user account by unique identifier operationId: getUserById tags: - users security: - bearerAuth: [] parameters: - name: userId in: path required: true description: Unique identifier for the user schema: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440000" responses: '200': description: User retrieved successfully content: application/json: schema: $ref: '#/components/schemas/User' examples: success: value: id: "550e8400-e29b-41d4-a716-446655440000" email: "john.doe@example.com" firstName: "John" lastName: "Doe" role: "user" isActive: true createdAt: "2024-01-15T10:30:00Z" updatedAt: "2024-01-20T14:45:00Z" '401': description: Authentication required content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' examples: unauthorized: value: type: "https://api.example.com/errors/unauthorized" title: "Unauthorized" status: 401 detail: "Valid authentication token required" instance: "/api/users/550e8400-e29b-41d4-a716-446655440000" '403': description: Insufficient permissions content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' examples: forbidden: value: type: "https://api.example.com/errors/forbidden" title: "Forbidden" status: 403 detail: "Insufficient permissions to access this resource" instance: "/api/users/550e8400-e29b-41d4-a716-446655440000" '404': description: User not found content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' examples: notFound: value: type: "https://api.example.com/errors/not-found" title: "Not Found" status: 404 detail: "User with ID '550e8400-e29b-41d4-a716-446655440000' not found" instance: "/api/users/550e8400-e29b-41d4-a716-446655440000" '500': description: Internal server error content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' components: schemas: User: type: object required: - id - email - firstName - lastName - role - isActive - createdAt - updatedAt properties: id: type: string format: uuid description: Unique identifier for the user email: type: string format: email description: User's email address firstName: type: string minLength: 1 maxLength: 100 description: User's first name lastName: type: string minLength: 1 maxLength: 100 description: User's last name role: type: string enum: [admin, user, guest] description: User's role in the system isActive: type: boolean description: Whether the user account is active createdAt: type: string format: date-time description: Timestamp when user was created updatedAt: type: string format: date-time description: Timestamp when user was last updated ProblemDetails: type: object required: - type - title - status properties: type: type: string format: uri description: URI reference identifying the problem type title: type: string description: Short, human-readable summary of the problem status: type: integer format: int32 description: HTTP status code detail: type: string description: Human-readable explanation specific to this occurrence instance: type: string format: uri description: URI reference identifying the specific occurrence securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT token obtained from /api/auth/login ``` **Why This Is Good:** - **Complete OpenAPI specification**: Uses OpenAPI 3.1.0 standard with all required fields and proper structure - **Rich error handling**: Defines 401, 403, 404, 500 responses with RFC 7807 Problem Details format for consistency - **Detailed schema definitions**: User schema includes validation constraints (min/max length, enum values, format specifications) - **Security-first**: Explicit bearer authentication requirement with JWT format specification - **Concrete examples**: Includes example values for both success and error responses to guide implementation - **Clear documentation**: Every field has a description, making the API self-documenting ### Example 2: Moderate - Payment API Error Handling **Input:** "Design comprehensive error handling for a payment processing API. Cover validation errors (400), authentication failures (401), authorization issues (403), and internal errors (500). Use RFC 7807 Problem Details format with extension fields for payment-specific context." **Output:** ```yaml openapi: 3.1.0 info: title: Payment Processing API version: 1.0.0 description: Secure payment processing with comprehensive error handling paths: /api/payments: post: summary: Process payment description: Processes a payment transaction operationId: processPayment tags: - payments security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PaymentRequest' responses: '201': description: Payment processed successfully content: application/json: schema: $ref: '#/components/schemas/PaymentResponse' '400': description: Validation error content: application/problem+json: schema: $ref: '#/components/schemas/PaymentValidationProblem' examples: invalidAmount: value: type: "https://api.example.com/payments/errors/validation" title: "Validation Error" status: 400 detail: "Payment validation failed" instance: "/api/payments" errors: - field: "amount" code: "AMOUNT_TOO_SMALL" message: "Amount must be at least 0.01" rejectedValue: 0 - field: "currency" code: "UNSUPPORTED_CURRENCY" message: "Currency 'XYZ' is not supported" rejectedValue: "XYZ" supportedValues: ["USD", "EUR", "GBP"] invalidCard: value: type: "https://api.example.com/payments/errors/validation" title: "Validation Error" status: 400 detail: "Payment method validation failed" instance: "/api/payments" errors: - field: "paymentMethod.cardNumber" code: "INVALID_CARD_NUMBER" message: "Card number fails Luhn check" rejectedValue: "****1234" - field: "paymentMethod.expiryDate" code: "CARD_EXPIRED" message: "Card expired on 2023-12" rejectedValue: "2023-12" '401': description: Authentication required content: application/problem+json: schema: $ref: '#/components/schemas/PaymentAuthenticationProblem' examples: missingToken: value: type: "https://api.example.com/payments/errors/authentication" title: "Authentication Required" status: 401 detail: "Valid bearer token required for payment processing" instance: "/api/payments" authenticationScheme: "Bearer" realm: "payment-api" expiredToken: value: type: "https://api.example.com/payments/errors/authentication" title: "Token Expired" status: 401 detail: "Authentication token expired at 2024-01-20T15:30:00Z" instance: "/api/payments" expiredAt: "2024-01-20T15:30:00Z" authenticationScheme: "Bearer" '403': description: Insufficient permissions or payment declined content: application/problem+json: schema: $ref: '#/components/schemas/PaymentAuthorizationProblem' examples: insufficientPermissions: value: type: "https://api.example.com/payments/errors/forbidden" title: "Insufficient Permissions" status: 403 detail: "User lacks permission to process payments" instance: "/api/payments" requiredPermission: "payments:create" userPermissions: ["payments:read"] paymentDeclined: value: type: "https://api.example.com/payments/errors/declined" title: "Payment Declined" status: 403 detail: "Payment declined by payment processor" instance: "/api/payments" declineReason: "INSUFFICIENT_FUNDS" declineCode: "51" canRetry: false merchantMessage: "Insufficient funds - request alternate payment method" fraudSuspected: value: type: "https://api.example.com/payments/errors/declined" title: "Payment Declined" status: 403 detail: "Payment declined due to fraud detection" instance: "/api/payments" declineReason: "FRAUD_SUSPECTED" declineCode: "59" canRetry: false merchantMessage: "Suspected fraud - contact issuer" riskScore: 0.92 '429': description: Rate limit exceeded content: application/problem+json: schema: $ref: '#/components/schemas/PaymentRateLimitProblem' examples: rateLimited: value: type: "https://api.example.com/payments/errors/rate-limit" title: "Rate Limit Exceeded" status: 429 detail: "Payment processing rate limit exceeded" instance: "/api/payments" rateLimit: limit: 100 remaining: 0 resetAt: "2024-01-20T16:00:00Z" '500': description: Internal server error content: application/problem+json: schema: $ref: '#/components/schemas/PaymentInternalErrorProblem' examples: processorTimeout: value: type: "https://api.example.com/payments/errors/internal" title: "Payment Processor Timeout" status: 500 detail: "Payment processor did not respond within timeout period" instance: "/api/payments" errorId: "err_1234567890abcdef" canRetry: true retryAfter: 30 supportContact: "support@example.com" databaseError: value: type: "https://api.example.com/payments/errors/internal" title: "Internal Server Error" status: 500 detail: "An unexpected error occurred while processing payment" instance: "/api/payments" errorId: "err_fedcba0987654321" canRetry: true retryAfter: 60 supportContact: "support@example.com" components: schemas: PaymentRequest: type: object required: - amount - currency - paymentMethod properties: amount: type: number format: decimal minimum: 0.01 description: Payment amount currency: type: string enum: [USD, EUR, GBP] description: Three-letter currency code paymentMethod: $ref: '#/components/schemas/PaymentMethod' PaymentMethod: type: object required: - type - cardNumber - expiryDate - cvv properties: type: type: string enum: [card] cardNumber: type: string pattern: '^[0-9]{13,19}$' expiryDate: type: string pattern: '^[0-9]{4}-[0-9]{2}$' description: Format YYYY-MM cvv: type: string pattern: '^[0-9]{3,4}$' PaymentResponse: type: object required: - id - status - amount - currency properties: id: type: string format: uuid status: type: string enum: [succeeded, processing] amount: type: number currency: type: string PaymentValidationProblem: allOf: - $ref: '#/components/schemas/BaseProblemDetails' - type: object required: - errors properties: errors: type: array description: Detailed validation errors items: type: object required: - field - code - message properties: field: type: string description: JSON path to the invalid field code: type: string description: Machine-readable error code message: type: string description: Human-readable error message rejectedValue: description: The value that was rejected supportedValues: type: array description: List of supported values (if applicable) PaymentAuthenticationProblem: allOf: - $ref: '#/components/schemas/BaseProblemDetails' - type: object properties: authenticationScheme: type: string description: Expected authentication scheme realm: type: string description: Authentication realm expiredAt: type: string format: date-time description: When the token expired (if applicable) PaymentAuthorizationProblem: allOf: - $ref: '#/components/schemas/BaseProblemDetails' - type: object properties: requiredPermission: type: string description: Permission required for this operation userPermissions: type: array items: type: string description: Permissions the user currently has declineReason: type: string description: Reason payment was declined declineCode: type: string description: Processor-specific decline code canRetry: type: boolean description: Whether retrying might succeed merchantMessage: type: string description: Message to display to merchant riskScore: type: number format: float minimum: 0 maximum: 1 description: Fraud risk score (if applicable) PaymentRateLimitProblem: allOf: - $ref: '#/components/schemas/BaseProblemDetails' - type: object required: - rateLimit properties: rateLimit: type: object required: - limit - remaining - resetAt properties: limit: type: integer description: Maximum requests allowed remaining: type: integer description: Requests remaining in window resetAt: type: string format: date-time description: When the rate limit resets PaymentInternalErrorProblem: allOf: - $ref: '#/components/schemas/BaseProblemDetails' - type: object required: - errorId properties: errorId: type: string description: Unique error identifier for support tracking canRetry: type: boolean description: Whether retrying the request might succeed retryAfter: type: integer description: Suggested retry delay in seconds supportContact: type: string description: Contact information for support BaseProblemDetails: type: object required: - type - title - status properties: type: type: string format: uri title: type: string status: type: integer detail: type: string instance: type: string format: uri securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT ``` **Why This Is Good:** - **RFC 7807 compliance**: Uses standard Problem Details format for all errors, ensuring consistency - **Payment-specific extensions**: Adds domain-specific fields like `declineReason`, `declineCode`, `canRetry`, `riskScore` for actionable error handling - **Comprehensive error scenarios**: Covers validation, authentication, authorization, rate limiting, and internal errors with realistic examples - **Merchant-friendly messages**: Includes `merchantMessage` field with user-facing guidance on how to resolve issues - **Retry guidance**: Explicit `canRetry` and `retryAfter` fields help clients implement smart retry logic - **Detailed validation errors**: Array of field-level errors with machine-readable codes and human-readable messages - **Support tracking**: Error IDs and support contact information enable effective troubleshooting ### Example 3: Complex - API Versioning Strategy **Input:** "Design an API versioning strategy for migrating from v1 to v2 of a user management API. The v2 API changes the user model structure and adds new required fields. Include deprecation headers, sunset policy, backward compatibility approach, and migration guide." **Output:** ```markdown # API Versioning Strategy: User Management API v1 → v2 ## Executive Summary This document defines the versioning strategy for migrating the User Management API from v1 to v2. Version 2 introduces breaking changes to the user model structure and adds new required fields for enhanced security and compliance. ## Version Comparison ### v1 User Model (Current) ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "john.doe@example.com", "name": "John Doe", "role": "user", "createdAt": "2024-01-15T10:30:00Z" } ``` ### v2 User Model (Target) ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "john.doe@example.com", "profile": { "firstName": "John", "lastName": "Doe", "displayName": "John Doe" }, "role": "user", "security": { "mfaEnabled": true, "lastPasswordChange": "2024-01-20T14:00:00Z" }, "metadata": { "createdAt": "2024-01-15T10:30:00Z", "updatedAt": "2024-01-20T14:00:00Z", "version": 2 } } ``` ### Breaking Changes | Change | Impact | Migration Required | |--------|--------|-------------------| | `name` `profile.firstName` + `profile.lastName` | HIGH | Clients must split name field | | New required field: `security.mfaEnabled` | HIGH | Clients must handle new field | | New required field: `security.lastPasswordChange` | MEDIUM | Clients must handle new field | | `createdAt` `metadata.createdAt` | MEDIUM | Clients must update field path | | Added `metadata.version` | LOW | Informational only | ## Versioning Approach ### URL-Based Versioning ``` v1: https://api.example.com/v1/users/{userId} v2: https://api.example.com/v2/users/{userId} ``` **Rationale**: URL-based versioning provides clear separation, simplifies routing, and allows independent evolution of each version. ### Deprecation Timeline | Date | Milestone | Action | |------|-----------|--------| | 2024-02-01 | v2 Release | v2 API goes live, v1 continues working | | 2024-02-01 | Deprecation Notice | v1 endpoints return `Deprecation` and `Sunset` headers | | 2024-05-01 | Warning Phase | v1 endpoints return `410 Gone` for new clients | | 2024-08-01 | Sunset | v1 endpoints removed from production | **Deprecation Period**: 6 months from v2 release to v1 sunset ### Deprecation Headers (v1 Endpoints) All v1 endpoints will return the following headers: ```http HTTP/1.1 200 OK Deprecation: true Sunset: Wed, 01 Aug 2024 00:00:00 GMT Link: <https://api.example.com/docs/v1-to-v2-migration>; rel="deprecation" X-API-Version: v1 X-API-Latest-Version: v2 X-API-Deprecation-Reason: "Version 1 is deprecated. Please migrate to v2." ``` ### Backward Compatibility Strategy #### Option 1: Dual-Mode Server (Recommended) Both v1 and v2 endpoints exist simultaneously during the deprecation period. **v1 Endpoint** (`/v1/users/{userId}`): - Returns legacy format - Internally transforms v2 data to v1 format - Includes deprecation headers **v2 Endpoint** (`/v2/users/{userId}`): - Returns new format - Native v2 data structure **Implementation**: ```yaml # Internal data transformation layer v1_response: name: "{{ user.profile.firstName }} {{ user.profile.lastName }}" email: "{{ user.email }}" role: "{{ user.role }}" createdAt: "{{ user.metadata.createdAt }}" v2_response: profile: firstName: "{{ user.profile.firstName }}" lastName: "{{ user.profile.lastName }}" displayName: "{{ user.profile.firstName }} {{ user.profile.lastName }}" email: "{{ user.email }}" role: "{{ user.role }}" security: mfaEnabled: "{{ user.security.mfaEnabled }}" lastPasswordChange: "{{ user.security.lastPasswordChange }}" metadata: createdAt: "{{ user.metadata.createdAt }}" updatedAt: "{{ user.metadata.updatedAt }}" version: 2 ``` #### Option 2: Accept-Version Header Single URL with version negotiation via header: ```http GET /api/users/550e8400-e29b-41d4-a716-446655440000 HTTP/1.1 Accept: application/json Accept-Version: v2 ``` **Not Recommended**: Adds complexity, harder to route, caching issues. ## Migration Guide ### For API Consumers #### Step 1: Test v2 in Development (Week 1-2) ```bash # Update base URL OLD: https://api.example.com/v1/users NEW: https://api.example.com/v2/users # Test v2 endpoint curl -H "Authorization: Bearer TOKEN" \ https://api.example.com/v2/users/550e8400-e29b-41d4-a716-446655440000 ``` #### Step 2: Update Code to Handle v2 Response (Week 3-4) **Before (v1)**: ```javascript const user = await fetch('/v1/users/123'); console.log(user.name); // "John Doe" ``` **After (v2)**: ```javascript const user = await fetch('/v2/users/123'); console.log(user.profile.firstName); // "John" console.log(user.profile.lastName); // "Doe" console.log(user.profile.displayName); // "John Doe" ``` #### Step 3: Update POST/PUT Requests (Week 5) **Before (v1)**: ```json POST /v1/users { "email": "new.user@example.com", "name": "New User", "role": "user" } ``` **After (v2)**: ```json POST /v2/users { "email": "new.user@example.com", "profile": { "firstName": "New", "lastName": "User" }, "role": "user", "security": { "mfaEnabled": false } } ``` #### Step 4: Deploy to Production (Week 6) - Gradual rollout with feature flags - Monitor error rates - Rollback plan ready ### Code Migration Examples #### JavaScript/TypeScript ```typescript // Helper function for backward compatibility function normalizeUser(user: any, apiVersion: 'v1' | 'v2') { if (apiVersion === 'v1') { return { id: user.id, email: user.email, firstName: user.name.split(' ')[0], lastName: user.name.split(' ').slice(1).join(' '), role: user.role, mfaEnabled: false, // v1 doesn't have this createdAt: user.createdAt, }; } else { return { id: user.id, email: user.email, firstName: user.profile.firstName, lastName: user.profile.lastName, role: user.role, mfaEnabled: user.security.mfaEnabled, createdAt: user.metadata.createdAt, }; } } // Usage const user = await getUserById('123', 'v2'); const normalized = normalizeUser(user, 'v2'); ``` #### Python ```python def normalize_user(user: dict, api_version: str) -> dict: """Normalize user data across API versions.""" if api_version == 'v1': name_parts = user['name'].split(' ', 1) return { 'id': user['id'], 'email': user['email'], 'first_name': name_parts[0], 'last_name': name_parts[1] if len(name_parts) > 1 else '', 'role': user['role'], 'mfa_enabled': False, 'created_at': user['createdAt'], } else: # v2 return { 'id': user['id'], 'email': user['email'], 'first_name': user['profile']['firstName'], 'last_name': user['profile']['lastName'], 'role': user['role'], 'mfa_enabled': user['security']['mfaEnabled'], 'created_at': user['metadata']['createdAt'], } ``` ## Monitoring & Observability ### Metrics to Track | Metric | Purpose | Alert Threshold | |--------|---------|-----------------| | `api.requests.v1.count` | v1 usage trends | N/A (informational) | | `api.requests.v2.count` | v2 adoption rate | <10% after 2 months | | `api.errors.v1.rate` | v1 error rate | >5% | | `api.errors.v2.rate` | v2 error rate | >2% | | `api.latency.v1.p95` | v1 performance | >500ms | | `api.latency.v2.p95` | v2 performance | >500ms | ### Client Migration Dashboard Track migration progress by client: ```sql SELECT client_id, SUM(CASE WHEN api_version = 'v1' THEN 1 ELSE 0 END) as v1_requests, SUM(CASE WHEN api_version = 'v2' THEN 1 ELSE 0 END) as v2_requests, (v2_requests / (v1_requests + v2_requests)) * 100 as migration_percentage FROM api_requests WHERE timestamp > NOW() - INTERVAL '7 days' GROUP BY client_id ORDER BY migration_percentage ASC; ``` ## Rollback Plan If critical issues arise in v2: 1. **Immediate**: Extend sunset date by 3 months 2. **Week 1**: Fix v2 issues 3. **Week 2**: Deploy fixes to v2 4. **Week 3**: Resume migration communications 5. **Month 2**: Resume deprecation timeline ## Communication Plan ### Announcement Schedule | Date | Channel | Message | |------|---------|---------| | 2024-01-15 | Email, Blog | v2 preview announcement | | 2024-02-01 | Email, Blog, In-App | v2 released, v1 deprecation notice | | 2024-03-01 | Email | 5 months until v1 sunset | | 2024-05-01 | Email, In-App | 3 months until v1 sunset, warning phase starts | | 2024-07-01 | Email, In-App | 1 month until v1 sunset, final warning | | 2024-08-01 | Email | v1 sunset complete | ### Stakeholder Communication **For Product Teams**: - Migration guide: https://api.example.com/docs/v1-to-v2-migration - Breaking changes: https://api.example.com/docs/v2-breaking-changes - Support channel: #api-migration-support **For Management**: - Migration dashboard: https://dashboard.example.com/api-migration - Risk assessment: [Link to risk document] - Cost analysis: [Link to cost document] ## Success Criteria | Criterion | Target | Measurement | |-----------|--------|-------------| | v2 adoption rate | >90% by Month 4 | % of requests to v2 endpoints | | v1 error rate | <1% during deprecation | Error rate on v1 endpoints | | v2 error rate | <2% after launch | Error rate on v2 endpoints | | Migration support tickets | <50 total | Support ticket count | | Client satisfaction | >4.0/5.0 | Post-migration survey | ## OpenAPI Specifications ### v1 Endpoint ```yaml /v1/users/{userId}: get: deprecated: true x-sunset-date: "2024-08-01T00:00:00Z" responses: '200': description: User retrieved (DEPRECATED) headers: Deprecation: schema: type: boolean example: true Sunset: schema: type: string format: date-time example: "Wed, 01 Aug 2024 00:00:00 GMT" Link: schema: type: string example: '<https://api.example.com/docs/v1-to-v2-migration>; rel="deprecation"' ``` ### v2 Endpoint ```yaml /v2/users/{userId}: get: summary: Get user by ID responses: '200': description: User retrieved successfully content: application/json: schema: $ref: '#/components/schemas/UserV2' ``` ## References - RFC 8594: Sunset HTTP Header - Semantic Versioning 2.0.0 - [Internal] API Governance Policy - [Internal] Deprecation Process Guidelines ``` **Why This Is Good:** - **Comprehensive strategy**: Covers technical, communication, monitoring, and rollback aspects of API versioning - **Clear migration path**: Provides concrete examples in multiple languages showing before/after code patterns - **Deprecation best practices**: Uses standard HTTP headers (Deprecation, Sunset, Link) per RFC 8594 - **Realistic timeline**: 6-month deprecation period with clear milestones and stakeholder communication plan - **Measurable success**: Defines specific metrics and success criteria to track migration progress - **Backward compatibility**: Dual-mode server approach allows gradual migration without breaking existing clients - **Risk mitigation**: Includes rollback plan, monitoring dashboard, and support communication channels ## Provenance Tracking After generating or modifying any artifact (API contracts, interface definitions, versioning docs), create a provenance record per @.claude/rules/provenance-tracking.md: 1. **Create provenance record** - Use @agentic/code/frameworks/sdlc-complete/schemas/provenance/prov-record.yaml format 2. **Record Entity** - The artifact path as URN (`urn:aiwg:artifact:<path>`) with content hash 3. **Record Activity** - Type (`generation` for new contracts, `modification` for updates) with timestamps 4. **Record Agent** - This agent (`urn:aiwg:agent:api-designer`) with tool version 5. **Document derivations** - Link API contracts to requirements, architecture, and test specs as `wasDerivedFrom` 6. **Save record** - Write to `.aiwg/research/provenance/records/<artifact-name>.prov.yaml` See @agentic/code/frameworks/sdlc-complete/agents/provenance-manager.md for the Provenance Manager agent.