| Phase | Duration | What You Get | Effort |
|---|---|---|---|
| 1: Foundation | 2 weeks | Contract + SDK framework | Medium |
| 2: Consolidation | 2 weeks | Migrate existing teams | Medium |
| 3: Agent MVP | 4 weeks | Prompt → PR automation | High |
| 4: Production Ready | 2 weeks | Drift detection, governance | Low |
| Total | 10 weeks | Full autonomous delivery | ~1000 eng-hours |
Create the contract and SDK framework that all teams will build on.
Allowed: apps/* -> packages/@analytics/*
Forbidden: packages/@analytics/* -> apps/*
apps/ into packages/@analytics/.# New package structure (app-agnostic shared)
packages/@analytics/
├── protocol/
│ ├── src/
│ │ ├── eventEnvelope.ts
│ │ ├── runtimeGuards.ts
│ │ ├── normalizers.ts
│ │ └── index.ts
│ └── package.json
├── sdk/
│ ├── src/
│ │ ├── track.ts
│ │ ├── validate.ts
│ │ ├── policies.ts
│ │ └── index.ts
│ ├── package.json
│ └── tsconfig.json
└── config/
├── dispatch-policy.json
├── market-defaults.json
└── package.json
# App-owned contracts (business/domain specific)
apps/@nvc/search/analytics/contracts/
├── search-filters.ts
└── index.ts
apps/@nvc/cart/analytics/contracts/
├── cart-events.ts
└── index.ts
Tasks:
packages/@analytics/protocol package (app-agnostic only)Owner: Platform Team
Time: 3-4 hours
Deliverable: Empty but functional package
File: packages/@analytics/sdk/src/track.ts
import { z } from "zod";
const CONTRACT_REGISTRY = new Map();
export function registerContract(contract) {
CONTRACT_REGISTRY.set(contract.name, contract);
}
export function track(eventName, payload) {
const contract = CONTRACT_REGISTRY.get(eventName);
if (!contract) {
console.warn(`[Analytics] No contract found for event: ${eventName}`);
return;
}
// Validate
let validated;
try {
validated = contract.schema.parse(payload);
} catch (error) {
console.error(`[Analytics] Validation failed for ${eventName}:`, error);
// Non-blocking — log but continue
return;
}
// Apply market policy
const enriched = applyMarketPolicy(eventName, validated);
// Dispatch
dispatch(eventName, enriched);
}
function applyMarketPolicy(eventName, payload) {
const market = payload.market;
const policy = getDispatchPolicy(eventName, market);
// Add metadata
return {
...payload,
_timestamp: Date.now(),
_dispatchMethod: policy,
};
}
function dispatch(eventName, payload) {
const method = payload._dispatchMethod;
if (method === "satellite") {
// Dispatch to Adobe Launch
if (window._satellite) {
window._satellite.track(eventName, payload);
}
} else if (method === "dataLayer") {
// Dispatch to data layer
if (window.digitaldata) {
window.digitaldata.push({
event: eventName,
data: payload,
});
}
}
// Log for observability
console.log("[Analytics] Event tracked:", {
eventName,
payload,
method,
});
}
function getDispatchPolicy(eventName, market) {
const policyMap = require("../config/dispatch-policy.json");
return policyMap[eventName]?.[market] || policyMap[eventName]?.default || "satellite";
}
Note: Shared SDK is runtime-only. App-specific typing (if used) stays in app folders and is never imported by shared packages.
Tasks:
Owner: Platform Team
Time: 6-8 hours
Deliverable: Working SDK, tested
File: packages/@analytics/config/dispatch-policy.json
{
"SEARCH_FILTER_APPLIED": {
"default": "satellite",
"us-en": "satellite",
"za-en": "satellite",
"ca-en": "dataLayer"
},
"CART_ITEM_ADDED": {
"default": "satellite",
"us-en": "satellite",
"za-en": "satellite"
},
"CHECKOUT_STARTED": {
"default": "dataLayer",
"us-en": "dataLayer"
}
}
Tasks:
Owner: Analytics Team
Time: 2 hours
Deliverable: Policy schema and examples
File: apps/@nvc/search/analytics/contracts/search-filters.ts
import { z } from "zod";
export const SEARCH_FILTER_APPLIED = {
name: "SEARCH_FILTER_APPLIED",
version: "1.0.0",
schema: z.object({
filterId: z.string().min(1),
filterValue: z.string(),
market: z.enum(["us-en", "za-en", "ca-en"]),
timestamp: z.number(),
}),
};
Tasks:
Owner: Platform Team
Time: 1-2 hours
Migrate existing analytics implementations to the new SDK.
Tasks:
Owner: Analytics Governance
Time: 4-6 hours
Deliverable: Audit spreadsheet
For each existing event, create a contract:
// apps/@nvc/cart/analytics/contracts/cart-events.ts
import { z } from "zod";
export const CART_ITEM_ADDED = {
name: "CART_ITEM_ADDED",
version: "1.0.0",
schema: z.object({
sku: z.string(),
price: z.number().min(0),
quantity: z.number().int().min(1),
market: z.enum(["us-en", "za-en", "ca-en"]),
}),
};
export const CART_ITEM_REMOVED = {
name: "CART_ITEM_REMOVED",
version: "1.0.0",
schema: z.object({
sku: z.string(),
market: z.enum(["us-en", "za-en", "ca-en"]),
}),
};
Tasks:
Owner: Analytics Team + Domain Teams
Time: 8-10 hours
Deliverable: Complete contract catalog
Choose smallest app (e.g., search filters):
Before:
// apps/@nvc/search/components/filterPanel.tsx
import { trackEvent } from "@shared/analytics";
export function FilterPanel() {
const handleFilterChange = (filter) => {
// Team A style: direct satellite.track call
window._satellite?.track("FILTER_APPLIED", {
filterId: filter.id,
filterValue: filter.value,
});
// Team B might do: window.digitaldata.push({...})
// Team C might call: customAnalytics.logEvent(...)
};
}
After:
// apps/@nvc/search/components/filterPanel.tsx
import { analytics } from "@analytics/sdk";
import { SEARCH_FILTER_APPLIED } from "../analytics/contracts/search-filters";
export function FilterPanel({ market }) {
const handleFilterChange = (filter) => {
// App-level builder: business mapping and shape stays here
const eventPayload = {
filterId: filter.id,
filterValue: filter.value,
market,
timestamp: Date.now(),
};
analytics.track(SEARCH_FILTER_APPLIED.name, {
...eventPayload,
});
};
}
Tasks:
Owner: NVC Search Team
Time: 6-8 hours
Deliverable: One app migrated, working
Week 4 Plan:
Monday: Migrate cart
Tuesday: Migrate checkout
Wednesday: Migrate financing
Thursday: Migrate homepage
Friday: Buffer + any fixes
Tasks per app:
Owner: Each domain team + Platform team
Time: 10-12 hours per app
Build the agent that automates contract + code + test generation.
User Prompt
↓
Intent Parser (NLP)
↓
Clarification Loop (if needed)
↓
Contract Generator
↓
Code Generator
↓
Test Generator
↓
PR Orchestrator
↓
GitHub PR
What it does: Convert natural language to structured intent.
Example:
Input: "Add analytics for search filters in ZA and CA markets"
Output: {
domain: "nvc/search",
events: ["FILTER_APPLIED", "FILTER_CLEARED", "FILTER_RESET"],
markets: ["za-en", "ca-en"],
baseMarkets: ["us-en"],
payloadFields: {
filterId: { type: "string", required: true },
filterValue: { type: "string", required: true },
resultCount: { type: "number", required: false }
}
}
Tasks:
Owner: Platform Team
Time: 8-10 hours
Deliverable: Intent parser with LLM integration
What it does: Ask focused questions when intent is ambiguous.
If ambiguous, ask max 3 questions:
1. Should events be debounced? (yes/no)
2. Market-specific payload overrides? (list or none)
3. Dispatch method preference? (satellite/dataLayer/both)
Tasks:
Owner: Platform Team
Time: 6-8 hours
Deliverable: Clarification system
What it does: Generate TypeScript contract code from intent + answers.
Input:
{
domain: "search",
events: ["FILTER_APPLIED", "FILTER_CLEARED"],
markets: ["us-en", "za-en", "ca-en"],
payloadFields: { ... }
}
Output: apps/@nvc/search/analytics/contracts/search-filters.ts (generated code)
Tasks:
Owner: Platform Team
Time: 10-12 hours
Deliverable: Contract generator
What it does: Generate SDK integration code in the app feature.
Input: Intent + contract location
Output:
track() calls in correct componentsTasks:
Owner: Platform Team
Time: 12-16 hours
Deliverable: Code generator
What it does: Generate unit + E2E tests automatically.
Generates:
Tasks:
Owner: Platform Team
Time: 10-12 hours
Deliverable: Test generator
What it does: Create a GitHub PR with all generated files.
Tasks:
Owner: Platform Team
Time: 4-6 hours
Deliverable: PR orchestrator
Add governance, monitoring, and confidence for production rollout.
What it does: Monitor production payloads vs contract.
Tasks:
Owner: Platform Team
Time: 8-10 hours
Deliverable: Production monitoring
What it does: Control which changes can auto-merge.
Policy:
Tasks:
Owner: Platform Team
Time: 6-8 hours
Deliverable: Governance automation
What it does: Track system health.
Metrics:
Tasks:
Owner: Platform Team
Time: 6-8 hours
Deliverable: Quality dashboards
What it does: Validate agent with 20+ real use cases.
Test cases:
Tasks:
Owner: Platform Team + Domain Teams
Time: 8-10 hours
Deliverable: Agent ready for production
Total: ~4.5 FTE for 10 weeks = ~1800 eng-hours
| Risk | Mitigation |
|---|---|
| Agent generates invalid code | Keep manual review gate for 6 months; agent accuracy < 95% triggers review |
| Regression in production | Drift detection catches issues within 1 hour; auto-rollback if SLO breaks |
| Team adoption resistance | Show before/after time savings; require all new analytics use agent |
| LLM accuracy issues | Hybrid approach: agent generates, human validates; audit monthly accuracy |
By Week 10: