Files
sentryagent-idp/openspec/changes/phase-5-scale-ecosystem/tasks.md
SentryAgent.ai Developer 389a764e8d feat(openspec): propose phase-5-scale-ecosystem change
6 workstreams, 119 tasks — Scale & Ecosystem:
- WS1: Rust SDK
- WS2: Agent-to-Agent (A2A) Authorization
- WS3: Advanced Analytics Dashboard
- WS4: Public API Gateway & Rate Limiting SaaS
- WS5: Developer Experience (DX) improvements
- WS6: AGNTCY Compliance Certification Package

Awaiting CEO approval to begin implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 15:33:08 +00:00

30 KiB
Raw Blame History

1. WS1: Rust SDK — Crate Setup

  • 1.1 Create sdk-rust/ directory and Cargo.toml — name: sentryagent-idp, version: 1.0.0, edition: 2021; add dependencies: tokio (features: full), reqwest (features: json, rustls-tls), serde (features: derive), serde_json, uuid (features: v4), thiserror, async-trait; add dev-dependencies: tokio-test, mockito
  • 1.2 Create sdk-rust/src/lib.rs — crate root with #![deny(warnings)]; re-export AgentIdPClient, TokenManager, AgentIdPError, and all model types from submodules; add crate-level //! doc comment describing the SDK
  • 1.3 Create sdk-rust/src/error.rs — define AgentIdPError enum with variants: HttpError(reqwest::Error), ApiError { status: u16, message: String, code: Option<String> }, AuthError(String), NotFound(String), RateLimited { retry_after_secs: u64 }, ConfigError(String), SerdeError(serde_json::Error), DelegationError(String); derive thiserror::Error and Debug; implement std::error::Error
  • 1.4 Create sdk-rust/src/models.rs — define all request structs (RegisterAgentRequest, UpdateAgentRequest, AuditLogFilters, MarketplaceFilters, DelegateRequest) and all response structs (Agent, AgentList, TokenResponse, Credentials, AuditLogEntry, AuditLogList, MarketplaceAgent, MarketplaceAgentList, DelegationToken, DelegationVerification); all structs derive serde::Serialize, serde::Deserialize, Debug, Clone

2. WS1: Rust SDK — Token Manager

  • 2.1 Create sdk-rust/src/token_manager.rs — define TokenCache struct with access_token: Option<String> and expires_at: Option<std::time::Instant>; define TokenManager struct with fields api_url, client_id, client_secret, cache: Arc<Mutex<TokenCache>>
  • 2.2 Implement TokenManager::new(api_url: &str, client_id: &str, client_secret: &str) -> Self — initializes with empty cache
  • 2.3 Implement TokenManager::get_token(&self) -> Result<String, AgentIdPError> — acquires lock, checks expires_at against Instant::now() + 60s, returns cached token if valid, else calls POST /oauth2/token via reqwest, updates cache, releases lock
  • 2.4 Write unit test token_manager_returns_cached_token — mock POST /oauth2/token using mockito, call get_token() twice, verify mock is hit only once
  • 2.5 Write unit test token_manager_refreshes_expired_token — set expires_at to past, verify get_token() triggers a new POST /oauth2/token call
  • 2.6 Write concurrent safety test token_manager_concurrent_calls_no_race — spawn 50 tokio::spawn tasks all calling get_token() simultaneously, verify mock is hit at most once (no thundering herd), verify all 50 tasks receive valid tokens

3. WS1: Rust SDK — Client Methods

  • 3.1 Create sdk-rust/src/client.rs — define AgentIdPClient struct with fields base_url, client_id, client_secret, http: reqwest::Client, token_manager: Arc<Mutex<TokenManager>>; implement new(base_url, client_id, client_secret) -> Self and from_env() -> Result<Self, AgentIdPError> (reads AGENTIDP_API_URL, AGENTIDP_CLIENT_ID, AGENTIDP_CLIENT_SECRET)
  • 3.2 Create sdk-rust/src/agents.rs — implement all agent methods on AgentIdPClient: register_agent, get_agent, list_agents, update_agent, delete_agent — each acquires a bearer token via token_manager.get_token(), makes the correct HTTP call, deserializes response, maps non-2xx responses to AgentIdPError::ApiError
  • 3.3 Create sdk-rust/src/oauth2.rs — implement issue_token(&self, agent_id: &str, scopes: &[&str]) -> Result<TokenResponse, AgentIdPError> — sends POST /oauth2/token with grant_type=client_credentials
  • 3.4 Create sdk-rust/src/credentials.rs — implement generate_credentials, rotate_credentials, revoke_credentials — map 404 response to AgentIdPError::NotFound, map 401 to AgentIdPError::AuthError
  • 3.5 Create sdk-rust/src/audit.rs — implement list_audit_logs(filters: AuditLogFilters) — serialize filters as query parameters; handle empty result set (return empty Vec, not error)
  • 3.6 Create sdk-rust/src/marketplace.rs — implement list_public_agents(filters) and get_public_agent(agent_id) — no auth header required for these endpoints
  • 3.7 Create sdk-rust/src/delegation.rs — implement delegate(req: DelegateRequest) and verify_delegation(token: &str)
  • 3.8 Implement 429 handling across all client methods — parse Retry-After header, return AgentIdPError::RateLimited { retry_after_secs }; verify zero unwrap() calls in all src/ files (run grep -r 'unwrap()' sdk-rust/src/ — must return empty)

4. WS1: Rust SDK — Tests, Examples, Documentation

  • 4.1 Create sdk-rust/examples/quickstart.rs — working example: create AgentIdPClient::from_env(), call register_agent, call issue_token, print token; example must compile with cargo build --example quickstart
  • 4.2 Create sdk-rust/tests/integration_test.rs — integration tests requiring AGENTIDP_API_URL, AGENTIDP_CLIENT_ID, AGENTIDP_CLIENT_SECRET env vars; test: register agent, issue token, get agent, update agent, rotate credentials, delete agent; each test is #[tokio::test] with #[ignore] attribute (run explicitly with cargo test -- --ignored)
  • 4.3 Write sdk-rust/README.md — installation via Cargo.toml, environment variable configuration, quickstart code example, full method reference table with signatures, error handling guide, link to crates.io
  • 4.4 Run cargo doc --no-deps — verify docs generate without errors or warnings; verify all public items have /// doc comments
  • 4.5 Run cargo clippy -- -D warnings — zero warnings; run cargo test (unit tests only, no --ignored) — all pass

5. WS2: A2A Authorization — Database & Types

  • 5.1 Create src/infrastructure/migrations/008_add_delegation_chains.sql — create delegation_chains table with columns: id (UUID PK), tenant_id (UUID FK), delegator_agent_id (UUID FK), delegatee_agent_id (UUID FK), scopes (TEXT[]), delegation_token (TEXT UNIQUE), signature (TEXT), ttl_seconds (INTEGER CHECK 6086400), issued_at (TIMESTAMPTZ), expires_at (TIMESTAMPTZ), revoked_at (TIMESTAMPTZ nullable), created_at (TIMESTAMPTZ DEFAULT NOW); create all four indexes as specified in spec
  • 5.2 Create src/types/delegation.ts — define interfaces: DelegationChain, CreateDelegationRequest (delegateeAgentId, scopes, ttlSeconds), DelegationVerificationResult (valid, chainId, delegatorAgentId, delegateeAgentId, scopes, issuedAt, expiresAt, revokedAt), DelegationTokenPayload

6. WS2: A2A Authorization — Crypto & Service

  • 6.1 Create src/utils/delegationCrypto.ts — implement signDelegationPayload(payload: DelegationTokenPayload, secret: string): string using HMAC-SHA256 (Node.js crypto.createHmac('sha256', secret)); implement verifyDelegationSignature(payload: DelegationTokenPayload, signature: string, secret: string): boolean; implement generateDelegationToken(): string (UUID v4); export only these three functions — no other exports
  • 6.2 Create src/services/DelegationService.ts — implement IDelegationService interface; createDelegation: validate delegateeAgentId exists in same tenant, validate scopes ⊆ delegator's scopes, reject self-delegation, sign payload, insert delegation_chains row, write audit log entry (delegation.created), return DelegationChain
  • 6.3 Implement DelegationService.verifyDelegation(delegationToken) — fetch chain row by delegation_token, if not found throw NotFoundError, verify HMAC signature, check expires_at > NOW() and revoked_at IS NULL, return DelegationVerificationResult with valid: true/false (never throw on expired/revoked — return valid: false); write audit log entry (delegation.verified)
  • 6.4 Implement DelegationService.revokeDelegation(chainId, requestingAgentId) — fetch chain by ID, verify delegator_agent_id === requestingAgentId (else throw ForbiddenError), check not already revoked (else throw ConflictError), update revoked_at = NOW(), write audit log entry (delegation.revoked)

7. WS2: A2A Authorization — Controller, Routes, Tests

  • 7.1 Create src/controllers/DelegationController.ts — implement createDelegation handler (POST /oauth2/token/delegate): extract authenticated agent ID from request context, call DelegationService.createDelegation, return HTTP 201; implement verifyDelegation handler (POST /oauth2/token/verify-delegation): call DelegationService.verifyDelegation, return HTTP 200; implement revokeDelegation handler (DELETE /oauth2/token/delegate/:chainId): call DelegationService.revokeDelegation, return HTTP 204
  • 7.2 Create src/routes/delegation.ts — Express router registering POST /oauth2/token/delegate, POST /oauth2/token/verify-delegation, DELETE /oauth2/token/delegate/:chainId with authentication middleware on all three routes
  • 7.3 Register delegation router in src/routes/index.ts behind A2A_ENABLED feature flag — return HTTP 404 on all delegation routes when A2A_ENABLED=false
  • 7.4 Add delegation Prometheus metrics: agentidp_delegations_created_total, agentidp_delegations_verified_total (labels: result), agentidp_delegations_revoked_total — increment in DelegationController handlers
  • 7.5 Add delegation endpoints to docs/openapi.yaml — include all request/response schemas, error responses, and authentication requirements as defined in spec
  • 7.6 Write unit tests for delegationCrypto.ts — test sign/verify round-trip, test tampered payload fails verification, test different secrets produce different signatures
  • 7.7 Write unit tests for DelegationService — mock DB and audit service; test: create delegation (valid), create delegation (scope escalation rejected), create delegation (self-delegation rejected), create delegation (delegatee in different tenant rejected), verify delegation (valid), verify delegation (expired — returns valid: false not throw), verify delegation (revoked — returns valid: false), revoke delegation (by delegator — succeeds), revoke delegation (by non-delegator — throws ForbiddenError), revoke delegation (already revoked — throws ConflictError)
  • 7.8 Write integration tests for delegation endpoints — test all happy paths and all error cases defined in spec; verify audit log entries are created for each delegation operation

8. WS3: Analytics — Database, Aggregation Job

  • 8.1 Create src/infrastructure/migrations/009_add_analytics_aggregates.sql — create analytics_daily_aggregates table with columns: id (UUID PK), tenant_id (UUID FK), agent_id (UUID nullable FK), date (DATE), metric_type (VARCHAR 64), count (BIGINT), created_at, updated_at; add unique constraint on (tenant_id, agent_id, date, metric_type); create indexes on (tenant_id, date) and (agent_id, date) WHERE agent_id IS NOT NULL
  • 8.2 Install node-cron npm package — add to package.json
  • 8.3 Create src/jobs/analyticsAggregation.ts — implement runAnalyticsAggregation(targetDate: Date): Promise<void>: execute upsert query aggregating previous day's usage_events rows into analytics_daily_aggregates; query is idempotent (upsert on unique constraint); update agentidp_analytics_aggregation_job_duration_ms gauge and agentidp_analytics_aggregation_job_last_run gauge on completion
  • 8.4 Register cron job in src/app.ts — schedule runAnalyticsAggregation at 00:05 UTC daily using node-cron; log job start, completion, and any errors; do not crash the process on job failure — log error and continue

9. WS3: Analytics — Service, Controller, Routes

  • 9.1 Create src/types/analytics.ts — define interfaces: UsageSummary, AgentActivityResponse, TokenTrendsResponse, DailyAggregate, AnalyticsDateRange
  • 9.2 Create src/services/AnalyticsService.ts — implement IAnalyticsService; getUsageSummary: validate date range (from <= to, max 365 days), check Redis cache (analytics:{tenantId}:summary:{hash}, TTL 60s), on miss query analytics_daily_aggregates, compute totals, write to cache, return UsageSummary
  • 9.3 Implement AnalyticsService.getAgentActivity(tenantId, from, to, agentId?) — validate date range (max 90 days), check Redis cache (TTL 5 min), on miss query analytics_daily_aggregates grouped by agent_id and date, join agent names from agents table, write to cache, return AgentActivityResponse
  • 9.4 Implement AnalyticsService.getTokenTrends(tenantId, from, to, granularity) — support day and week granularity (weekly: date_trunc('week', date)), check Redis cache (TTL 5 min), return TokenTrendsResponse with successfulIssuances, failedIssuances, uniqueAgents per period
  • 9.5 Create src/controllers/AnalyticsController.ts — handlers for getUsageSummary, getAgentActivity, getTokenTrends; parse and validate query parameters; return HTTP 403 for free-tier tenants (check ANALYTICS_FREE_TIER env and tenant subscription); emit agentidp_analytics_query_duration_ms histogram and cache hit/miss counters
  • 9.6 Create src/routes/analytics.ts — Express router for /analytics/usage-summary, /analytics/agent-activity, /analytics/token-trends; all routes require authentication middleware
  • 9.7 Register analytics router in src/routes/index.ts behind ANALYTICS_ENABLED feature flag
  • 9.8 Add analytics endpoints to docs/openapi.yaml — all query parameters, response schemas, and error codes as defined in spec
  • 9.9 Write unit tests for AnalyticsService — test: usage-summary (cache hit), usage-summary (cache miss → DB query), agent-activity (with agentId filter), agent-activity (no filter — all agents), token-trends (daily), token-trends (weekly), date range validation (from > to rejected), date range validation (> max days rejected), free-tier rejection
  • 9.10 Write integration tests for analytics endpoints — test all three endpoints with valid date ranges, verify free-tier rejection, verify invalid date range errors

10. WS3: Analytics — Dashboard UI

  • 10.1 Install recharts and date-fns in dashboard/package.json
  • 10.2 Create dashboard/src/api/analyticsApi.ts — typed fetch functions for all three analytics endpoints: fetchUsageSummary(token, from, to), fetchAgentActivity(token, from, to, agentId?), fetchTokenTrends(token, from, to, granularity); all functions return typed response objects; handle 403 response with a typed AnalyticsNotAvailableError
  • 10.3 Create dashboard/src/components/charts/AgentHeatmap.tsx — renders a grid heatmap (agents × dates) using recharts or a custom CSS grid; color intensity represents apiCalls count; hover tooltip shows agent name, date, apiCalls, tokenIssuances, credentialRotations; accepts agents prop from AgentActivityResponse
  • 10.4 Create dashboard/src/components/charts/TokenTrendsChart.tsx — renders a recharts ComposedChart with a Line for successfulIssuances and a Bar for failedIssuances; X-axis is dates; tooltip shows all three metrics per period; accepts dataPoints prop from TokenTrendsResponse
  • 10.5 Create dashboard/src/components/charts/RotationFrequencyTable.tsx — renders a sortable table of credential rotation counts per agent; columns: Agent Name, Rotations (period), Last Rotation Date; sortable by any column; accepts agents prop derived from AgentActivityResponse filtering credentialRotations
  • 10.6 Create dashboard/src/pages/Analytics.tsx — analytics tab page; renders date range picker (from/to), calls all three analytics APIs, renders AgentHeatmap, TokenTrendsChart, RotationFrequencyTable; shows a UpgradeRequired component when API returns 403
  • 10.7 Add Analytics route to dashboard/src/App.tsx — add /analytics route; add "Analytics" link to dashboard navigation
  • 10.8 Run npm run build in dashboard/ — zero TypeScript errors, zero ESLint errors

11. WS4: API Gateway Tiers — Configuration & Middleware

  • 11.1 Create src/types/tiers.ts — define interfaces: TierName (union: 'free' | 'pro' | 'enterprise'), TierLimits, TierFeatures, TierDefinition (includes id, limits, features, stripeProductId)
  • 11.2 Create src/config/tiers.ts — define TIER_CONFIG: Record<TierName, TierDefinition> with complete limit and feature definitions for free, pro, and enterprise tiers as specified in spec; export getTierConfig(tier: TierName): TierDefinition helper
  • 11.3 Create src/middleware/tierRateLimiter.ts — implement TierRateLimiter middleware: extract tenantId from authenticated request context; check Redis key tier:{tenantId} (TTL 60s) for cached tier; on miss query tenant_subscriptions for tenant's current tier, cache for 60s; look up rate limit config from TIER_CONFIG; apply RateLimiterRedis with key rl:{tier}:{tenantId}; on rejection return HTTP 429 with X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After headers; increment agentidp_rate_limit_hits_total counter with tier and tenant_id labels
  • 11.4 Replace RateLimiterRedis middleware on all authenticated routes in src/routes/index.ts with TierRateLimiter; keep the flat IP-based RateLimiterRedis on unauthenticated routes unchanged; wrap replacement in TIER_RATE_LIMITING_ENABLED feature flag (fall back to old middleware when false)

12. WS4: API Gateway Tiers — Endpoints

  • 12.1 Create src/routes/tiers.ts — Express router for GET /tiers; handler reads TIER_CONFIG, formats response as specified in spec, sets Cache-Control: public, max-age=3600 header; no database query; no authentication required
  • 12.2 Register tiers router in src/routes/index.ts
  • 12.3 Implement BillingService.upgradeTier(tenantId: string, targetTier: 'pro' | 'enterprise'): Promise<{ checkoutUrl: string; sessionId: string; expiresAt: string }> — fetch current tier from tenant_subscriptions, validate no self-upgrade or downgrade, create Stripe Checkout session with STRIPE_PRICE_ID_PRO or STRIPE_PRICE_ID_ENTERPRISE, return checkout URL
  • 12.4 Add upgradeTier handler to src/controllers/BillingController.ts — validate targetTier enum, call BillingService.upgradeTier, return HTTP 200 with checkoutUrl, sessionId, targetTier, expiresAt
  • 12.5 Register POST /billing/upgrade route in src/routes/billing.ts with authentication middleware
  • 12.6 Add STRIPE_PRICE_ID_PRO, STRIPE_PRICE_ID_ENTERPRISE, TIER_RATE_LIMITING_ENABLED to .env.example with documentation comments
  • 12.7 Add GET /tiers and POST /billing/upgrade to docs/openapi.yaml
  • 12.8 Write unit tests for TierRateLimiter — test: free tier limit enforced (60 req/min), pro tier limit enforced (600 req/min), tier looked up from Redis cache (DB not called), tier fetched from DB on cache miss, rollback path (TIER_RATE_LIMITING_ENABLED=false uses old flat limiter)
  • 12.9 Write unit tests for BillingService.upgradeTier — test: upgrade free → pro (creates Stripe session), upgrade free → enterprise (creates Stripe session), already on pro (returns ALREADY_ON_TIER error), downgrade attempt (returns DOWNGRADE_NOT_SUPPORTED error)
  • 12.10 Write integration tests for GET /tiers — verify response structure, verify Cache-Control header, verify no auth required; write integration tests for POST /billing/upgrade — mock Stripe, verify checkout URL returned

13. WS5: Developer Experience — Scaffold Service

  • 13.1 Install archiver and @types/archiver in API package.json
  • 13.2 Create src/types/scaffold.ts — define ScaffoldLanguage union ('typescript' | 'python' | 'go' | 'java' | 'rust'), ScaffoldOptions interface, ScaffoldTemplate interface
  • 13.3 Create scaffold template files for TypeScript in src/templates/scaffold/typescript/: package.json.tmpl, tsconfig.json.tmpl, src/index.ts.tmpl, .env.example.tmpl, .gitignore.tmpl, README.md.tmpl — each file uses {{AGENT_ID}}, {{AGENT_NAME}}, {{CLIENT_ID}}, {{API_URL}} as template variables; .env.example.tmpl MUST include AGENTIDP_CLIENT_SECRET=<your-client-secret> placeholder (never inject real secret)
  • 13.4 Create scaffold template files for Python in src/templates/scaffold/python/: requirements.txt.tmpl, main.py.tmpl, .env.example.tmpl, .gitignore.tmpl, README.md.tmpl — same template variable convention
  • 13.5 Create scaffold template files for Go in src/templates/scaffold/go/: go.mod.tmpl, main.go.tmpl, .env.example.tmpl, .gitignore.tmpl, README.md.tmpl
  • 13.6 Create scaffold template files for Java in src/templates/scaffold/java/: pom.xml.tmpl, src/main/java/Main.java.tmpl, .env.example.tmpl, .gitignore.tmpl, README.md.tmpl
  • 13.7 Create scaffold template files for Rust in src/templates/scaffold/rust/: Cargo.toml.tmpl, src/main.rs.tmpl, .env.example.tmpl, .gitignore.tmpl, README.md.tmpl
  • 13.8 Create src/services/ScaffoldService.ts — implement IScaffoldService; generateScaffold(agentId, language, apiUrl): load template files for language, inject template variables (replace {{AGENT_ID}}, {{AGENT_NAME}}, {{CLIENT_ID}}, {{API_URL}}), build in-memory ZIP using archiver; return { stream: NodeJS.ReadableStream, filename: string }; emit agentidp_scaffold_generated_total counter and agentidp_scaffold_generation_duration_ms histogram

14. WS5: Developer Experience — Scaffold Controller & Route

  • 14.1 Create src/controllers/ScaffoldController.ts — implement getScaffold handler for GET /sdk/scaffold/:agentId: validate language query param against ScaffoldLanguage union (HTTP 400 on invalid); fetch agent, verify agent belongs to authenticated tenant (HTTP 403 if not); call ScaffoldService.generateScaffold; set Content-Type: application/zip, Content-Disposition: attachment; filename="...", pipe stream to response; write audit log entry (scaffold.generated, metadata: { language })
  • 14.2 Create src/routes/scaffold.ts — Express router for GET /sdk/scaffold/:agentId with authentication middleware; apply scaffold-specific rate limiter (10 req/min per tenant, separate from TierRateLimiter)
  • 14.3 Register scaffold router in src/routes/index.ts
  • 14.4 Add GET /sdk/scaffold/:agentId to docs/openapi.yaml — document binary response type, query parameters, all error responses
  • 14.5 Write unit tests for ScaffoldService — test: generate TypeScript scaffold (verify ZIP contains all 6 files), generate Python scaffold (verify all 5 files), verify {{CLIENT_ID}} is replaced in .env.example, verify {{AGENTIDP_CLIENT_SECRET}} is placeholder not real secret, verify invalid language throws ValidationError
  • 14.6 Write integration tests for scaffold endpoint — test: TypeScript scaffold returns ZIP with correct Content-Type and Content-Disposition; Python scaffold returns ZIP; HTTP 400 on invalid language; HTTP 403 when agent belongs to different tenant; HTTP 404 when agent does not exist

15. WS5: Developer Experience — Portal & CLI

  • 15.1 Install @stoplight/elements in portal/package.json — remove swagger-ui-react
  • 15.2 Rewrite portal/app/api-explorer/page.tsx — replace SwaggerUI component with @stoplight/elements <API> component; set apiDescriptionUrl, router="hash", layout="sidebar", hideSchemas={false}, tryItCredentialsPolicy="same-origin"; import Elements CSS; remove all Swagger UI imports and CSS
  • 15.3 Run npm run build in portal/ — verify zero TypeScript errors and zero ESLint errors after Elements integration
  • 15.4 Install unzipper and @types/unzipper in cli/package.json
  • 15.5 Create cli/src/commands/scaffold.ts — implement sentryagent scaffold command with Commander options: --agent-id <id> (required), --language <lang> (default: typescript), --out <directory> (default: .); load config, issue Bearer token, call GET /sdk/scaffold/{agentId}?language={language}, pipe response through unzipper.Extract({ path: outDir }), print success message and next steps; handle errors (404, 403, 400) with human-readable messages
  • 15.6 Register scaffold command in cli/src/index.ts — add .addCommand(scaffoldCommand) to Commander program
  • 15.7 Run npm run build in cli/ — zero TypeScript errors; run node dist/index.js scaffold --help — outputs correct usage

16. WS6: AGNTCY Compliance — Compliance Service

  • 16.1 Create src/types/compliance.ts — define interfaces: ComplianceRequirement (id, description, status, evidence, verifiedAt), ComplianceSection (id, name, status, requirements), ComplianceReport (reportId, generatedAt, agntcySpecVersion, tenantId, overallStatus, sections, summary), AgentCard (agntcyVersion, type, agent, issuedAt, expiresAt)
  • 16.2 Create src/config/agntcyRequirements.ts — define the complete array of AGNTCY requirement objects (minimum 24 requirements), each with: id (e.g., AI-001), description (from AGNTCY spec), section (e.g., agent-identity), and evaluate(tenantId: string, db: Pool): Promise<RequirementEvaluation> function — each evaluator queries the live system and returns { status, evidence }
  • 16.3 Create src/services/ComplianceService.ts — implement IComplianceService; generateComplianceReport(tenantId): run all requirement evaluators from agntcyRequirements.ts in parallel, group results by section, compute overall status (compliant if all pass, partial if any non-compliant, non-compliant if >20% fail), build ComplianceReport, write audit log entry (compliance.report_generated), emit agentidp_compliance_reports_generated_total counter and agentidp_compliance_report_duration_ms histogram
  • 16.4 Implement ComplianceService.generateAgentCard(agentId) — fetch agent from DB, build AgentCard per AGNTCY spec format, set expiresAt = issuedAt + 24 hours, set complianceStandards from system config, emit agentidp_agent_cards_served_total counter with visibility label

17. WS6: AGNTCY Compliance — Controller, Routes

  • 17.1 Create src/controllers/ComplianceController.ts — implement getComplianceReport handler: check tenant tier is pro or enterprise (HTTP 403 TIER_REQUIRED for free tier), call ComplianceService.generateComplianceReport, return HTTP 200; implement getAgentCard handler: check agent visibility (HTTP 401 if private and unauthenticated, HTTP 403 if private and wrong tenant), call ComplianceService.generateAgentCard, return HTTP 200
  • 17.2 Create src/routes/agntcy.ts — Express router for GET /agntcy/compliance-report (requires auth) and GET /agents/:id/agent-card (auth optional); register behind AGNTCY_ENABLED feature flag
  • 17.3 Register agntcy router in src/routes/index.ts
  • 17.4 Add GET /agntcy/compliance-report and GET /agents/:id/agent-card to docs/openapi.yaml
  • 17.5 Write unit tests for ComplianceService — test: generateComplianceReport (all 24 requirements pass → compliant), generateComplianceReport (one evaluator fails → partial), generateAgentCard (public agent), generateAgentCard (private agent — verify agent data is included), generateAgentCard (non-existent agent → throws NotFoundError)
  • 17.6 Write integration tests for compliance endpoints — test: compliance report for pro tenant (HTTP 200, overallStatus), compliance report for free tenant (HTTP 403), agent card for public agent (no auth required), agent card for private agent (auth required, correct tenant succeeds, wrong tenant HTTP 403)

18. WS6: AGNTCY Compliance — Interoperability Tests & Docs

  • 18.1 Create tests/agntcy/interoperability.test.ts — implement all 25+ AGNTCY interoperability test cases as defined in spec: AI-001 (agent UUID uniqueness), AI-002 (W3C DID document), AUTH-001 (OAuth 2.0 token issuance), AUTH-002 (OIDC discovery), AUTHZ-001 (scope enforcement), DEL-001 through DEL-004 (delegation chain), AUDIT-001 through AUDIT-002 (immutable audit log), GOV-001 through GOV-002 (lifecycle governance), INTER-001 (agent card), COMP-001 (compliance report)
  • 18.2 Add "test:agntcy": "jest --testPathPattern=tests/agntcy --forceExit" script to package.json
  • 18.3 Write docs/agntcy/certification-guide.md — complete document with all 6 sections: Overview, Requirement Mapping table, Running the Compliance Report (step-by-step), Agent Card Usage, Self-Certification Checklist, Submitting for Official AGNTCY Certification; no placeholders, no TODOs

19. QA & Release

  • 19.1 Run cargo build and cargo clippy -- -D warnings in sdk-rust/ — zero warnings; run cargo test — all unit tests pass
  • 19.2 Run tsc --noEmit across API, dashboard, portal, and CLI — zero TypeScript errors
  • 19.3 Run full Jest suite (npm test) — all unit tests pass, coverage >= 80% across all new services: DelegationService, AnalyticsService, ScaffoldService, ComplianceService, TierRateLimiter
  • 19.4 Run npm run build in portal/ with Elements integration — zero errors; verify /api-explorer page renders Elements <API> component
  • 19.5 Run npm run build in cli/ — zero errors; run node dist/index.js scaffold --help — shows correct options; run node dist/index.js --help — shows scaffold command listed
  • 19.6 Apply database migrations 008_add_delegation_chains.sql and 009_add_analytics_aggregates.sql against a test database — verify migrations run without errors and tables are created with correct schemas
  • 19.7 Run integration tests for all Phase 5 endpoints — delegation (create, verify, revoke), analytics (usage-summary, agent-activity, token-trends), tiers (GET /tiers, POST /billing/upgrade), scaffold (all 5 languages), AGNTCY (compliance-report, agent-card)
  • 19.8 Run npm run test:agntcy — all 25+ interoperability test cases pass
  • 19.9 Verify feature flags: A2A_ENABLED=false → delegation routes return 404; ANALYTICS_ENABLED=false → analytics routes return 404; TIER_RATE_LIMITING_ENABLED=false → flat rate limiter used; AGNTCY_ENABLED=false → AGNTCY routes return 404
  • 19.10 Verify tier rate limiting: free tenant receives 429 at 61st request/minute; pro tenant allows 600 requests/minute; tier cache refresh within 60s after Stripe webhook updates subscription
  • 19.11 Verify scaffold security: GET /sdk/scaffold/:agentId response ZIP never contains a real client_secret value — .env.example placeholder only
  • 19.12 Commit all Phase 5 work on main — one conventional commit per workstream (e.g., feat(phase-5): WS1 — Rust SDK, feat(phase-5): WS2 — A2A Authorization, etc.)