# Tasks: enterprise-profiler **Change ID**: enterprise-profiler **Package**: `@suaveplan/profiler` **Target path**: `universal/platform/profiler` **Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/logger` --- ## 0. Pre-flight (non-negotiable) - [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each) - [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest. ## 1. Scaffold - [ ] Create `universal/platform/profiler/` with `src/`, `tests/`, `src/__benchmarks__/`, and all feature subdirectories per the layout in `proposal.md`. - [ ] Write `package.json` at version `0.1.0`: - `"type": "module"`, `"sideEffects": false` - `exports` map: `"."` barrel + `"./core/profiler/profiler"`, `"./core/session/session"`, `"./sinks/console-sink/console-sink"`, `"./sinks/json-sink/json-sink"`, `"./utils/aggregate"`, `"./utils/flamegraph"`, `"./types/common"` - `dependencies`: `@suaveplan/error workspace:^`, `@suaveplan/types workspace:^`, `@suaveplan/events workspace:^`, `@suaveplan/logger workspace:^`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build` - `devDependencies`: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `fast-check catalog:testing`, `mitata catalog:testing`, `@suaveplan/testing workspace:^` - [ ] Write `tsconfig.json` extending `@suaveplan/typescript-config/library.json`. - [ ] Write `vitest.config.ts` using `createTestConfig()`. - [ ] Add `lint`, `typecheck`, `test`, `test:coverage`, `build`, `docs:check`, `bench` scripts. --- ## 2. Types and error codes - [ ] Define all public types in `src/types/common.ts`: `ProfileSample`, `StackFrame`, `ProfileSession`, `FlamegraphNode`, `ProfileAggregate`, `ProfilerOptions`, `SessionOptions`, `ProfilerSink`, `ConsoleSinkOptions`, `JsonSinkOptions`. - [ ] Write `src/types/common.md` (≥200 words, simple + advanced examples). - [ ] Define error codes in `src/error-codes.ts`: `ProfilerErrorCode` union, metadata, `registerProfilerErrorCodes()`. - [ ] Write Zod schemas in `src/types/schemas.ts` + `src/types/schemas.md`. --- ## 3. Aggregate utilities (`src/utils/aggregate.ts`) - [ ] Implement `aggregateSamples(samples)`: compute count, totalCpuMs, avgCpuMs, p50/p95/p99 (insertion-sort on the cpu array), peakHeapMb, avgHeapMb. - [ ] Implement `formatDuration(ms)`: format as `Xms`, `Xs`, `Xm Xs`. - [ ] Write `aggregate.test.ts` (≤500 lines): empty array, single sample, percentile correctness for known data. - [ ] Write `aggregate.md` (≥200 words). --- ## 4. Flamegraph utilities (`src/utils/flamegraph.ts`) - [ ] Implement `buildFlamegraph(session)`: group samples by stack frame path, build tree by folding frames into children. - [ ] Each node's `value` = number of samples that include this frame. - [ ] Produce sorted children (descending by value). - [ ] Write `flamegraph.test.ts` (≤500 lines): empty session, single frame, multi-frame stacks, shared frames merge correctly. - [ ] Write `flamegraph.md` (≥200 words). --- ## 5. Profiler session (`src/core/session/`) - [ ] Implement `ProfilerSession` (internal class): - `sample()` — captures `clock.now()` + heap stats via `process.memoryUsage()` (guarded for non-Node runtimes); returns `ProfileSample`. - `stop()` — sets `endTime`, returns immutable `ProfileSession` snapshot. - `export('json', signal?)` — JSON-serialises session. - `export('flamegraph', signal?)` — calls `buildFlamegraph`, JSON-serialises. - `toFlamegraph()` — synchronous `buildFlamegraph` call. - [ ] Write `session.test.ts` (≤500 lines): sample, stop, export json, export flamegraph, abort on export. - [ ] Write `session.md` (≥200 words). --- ## 6. Profiler (`src/core/profiler/`) - [ ] Implement `createProfiler(options?)`: validates options with Zod; stores sessions in `Map`. - [ ] Implement `profiler.startSession(name, options?)` — generates UUID id, stores session, emits OTel span `profiler.session.start`. - [ ] Implement `profiler.getSession(id)`, `listSessions()`, `clearSessions()`. - [ ] Implement `profiler.use(sink)` — registers sink; on session stop, calls `sink.onSessionEnd(session)` asynchronously. - [ ] On session stop, emit OTel span `profiler.session.stop`, histogram `profiler.session_duration_ms`. - [ ] Write `profiler.test.ts` (≤500 lines): session lifecycle, sink called on stop, multiple sessions. - [ ] Write `profiler.md` (≥200 words). --- ## 7. Sinks (`src/sinks/`) ### 7a. Console sink - [ ] Implement `createConsoleSink(options?)` — on session end, prints a summary table via `console.log` with aggregate stats. - [ ] Write `console-sink.test.ts`: output contains session name and p95 duration. - [ ] Write `console-sink.md`. ### 7b. JSON sink - [ ] Implement `createJsonSink(options: { path: string })` — on session end, appends JSON line to file via `Bun.write` (or `fs.appendFile` fallback). - [ ] Write `json-sink.test.ts`: file written, valid JSON, append-safe. - [ ] Write `json-sink.md`. --- ## 8. Property-based tests (`tests/property.test.ts`) - [ ] `aggregateSamples` invariants: avg ≤ max, p50 ≤ p95 ≤ p99, count equals input length. - [ ] `buildFlamegraph` node value sum: root node value ≤ total samples. - [ ] Session stop idempotency: calling `stop()` twice on a session should be a no-op after the first call (endTime does not change). --- ## 9. Benchmarks (`src/__benchmarks__/profiler.bench.ts`) - [ ] `sample_hot`: 1000 `session.sample()` calls. - [ ] `buildFlamegraph_1k`, `buildFlamegraph_10k`: flamegraph on 1k and 10k sample sessions. - [ ] `aggregateSamples_1k`, `aggregateSamples_10k`: aggregate on 1k and 10k samples. --- ## 10. Docs - [ ] All per-file `.md` files per layout in `proposal.md`. - [ ] `universal/platform/profiler/README.md`: purpose, installation, quick-start, API section, dependency table, benchmarks table. --- ## 11. Verify and publish - [ ] `bunx turbo lint --filter=@suaveplan/profiler` — zero errors. - [ ] `bunx turbo typecheck --filter=@suaveplan/profiler` — zero errors. - [ ] `bunx turbo test --filter=@suaveplan/profiler` — all tests pass. - [ ] `bunx turbo test:coverage --filter=@suaveplan/profiler` — 100% on all touched modules. - [ ] `bun run docs:check --filter=@suaveplan/profiler` — zero errors. - [ ] `bun scripts/publish.ts --package @suaveplan/profiler`. - [ ] Update `CLAUDE.md` package listing. - [ ] Archive: move `openspec/changes/enterprise-profiler/` to `openspec/archive/2026-04-24-enterprise-profiler/`.