Skip to content

Product and Engineering Report

Kris Lubiniecki edited this page Jul 7, 2026 · 1 revision

The AI Engineer Tools repo: how Ascenda measures AI workload at the source

Ascenda · Internal briefing · Rev. 3 · Repo: ascendaone-com/ai-engineer-tools (main) · Scope: 4 telemetry producers, 3 shared packages, 2 dev utilities · Status: Phase 1 live, 47 tests, backend-free local testing · Date: 7 July 2026

This repo is the top of Ascenda's data funnel. It contains the instruments that sit inside an AI engineer's working tools — VS Code, Cursor, Claude Code, and OpenAI Codex — and convert the texture of AI-assisted work into a privacy-safe event stream the backend can score. Everything Ascenda's dashboards claim to show about creation, verification, and supervision load originates here. The codebase is built around three shared packages, so the canonical vocabulary and privacy rules exist in exactly one place — and adding the fourth producer took an afternoon, which was the point.

1. The product thesis: friction is measurable before burnout is

Ascenda's core bet, stated in the repo's own README, is that burnout is a lagging indicator. By the time a survey or an exit interview registers it, the damage is months old. What is observable early is workflow friction: context switching, repeated AI prompt loops, verification burden, after-hours pressure. These leave mechanical traces in the tools engineers already use — and for AI engineers specifically, that trace is unusually rich, because so much of the work now flows through prompts, agents, and generated code that must be checked.

The full platform triangulates three data planes. This repo supplies the first; the other two live elsewhere:

  • Objective telemetry (this repo) — discrete, metadata-only events from IDEs and AI agents.
  • Subjective check-ins (the Ascenda mobile app) — NASA-TLX-style self-reports of perceived strain.
  • Personal baselines (the backend) — each person's own rolling norms, so "elevated" means elevated for you, never a fixed population threshold.

From a business perspective, this repo is the differentiator. Survey-only wellbeing products measure how people say they feel, quarterly. Ascenda measures how the work actually behaved, continuously — across the editor and the agents doing the work — and it can do so because it built the collection instruments itself, under its own privacy rules, rather than scraping activity data from Git or Slack. The event stream feeds the same creation / verification / supervision composition that the marketing site and dashboard present to buyers: the taxonomy customers see in a sales demo is literally the category column of this repo's event catalog. Marketing story and telemetry vocabulary are one artifact — and one file.

2. How the system works end to end

Four actors participate: a tool (extension or agent hook CLI), the Ascenda API, the mobile app (the authenticated human), and the backend's aggregation pipeline. The tools in this repo only ever implement the tool side.

Flow: four producers emit discrete events through one shared client → the API enforces consent and identity separation → the backend turns events into weekly aggregates (12-week window) compared against personal baselines (28-day norms) → results surface in the app and dashboards. The mobile app confirms pairing and owns consent and revocation.

The tool side is layered: both IDE extensions are one-line shells over a single shared implementation (ide-extension-core), and both agent hook CLIs are thin mapping layers — everything below them (wire client, token store, classifiers, payload probes, the event sender) lives once in tool-kit, typed by tool-contract. For development and testing, a local dev server implements this same contract faithfully enough that the integration suite drives the real client through it — so the whole system is testable on a laptop with no backend, phone, or credentials handoff.

3. Connecting your tools: a user's guide to auth

From the engineer's side, the whole security model boils down to one idea: you never sign in from your editor or your agent. Your identity lives in exactly one place — the Ascenda app on your phone — and everything else gets an anonymous, revocable permission slip.

Pairing an IDE (once, about a minute)

  1. In VS Code or Cursor, open the command palette and run Ascenda: Connect App. A panel appears with a QR code and a 6-digit code. Both expire after ten minutes — if you miss the window, just run the command again.
  2. On your phone, open the Ascenda app and scan the QR code (or type the 6 digits). You are approving the connection from the app, the only place you are actually signed in. If it's your first tool, the app also asks for your IDE telemetry consent here.
  3. Back in the editor, the panel flips to "Ascenda connected." That's it — no account creation, no email, no password ever entered in the IDE.

What just happened under the hood: your editor received exactly two things — a random installation ID and a scoped write token. Think of the token as a one-way mailbox key: it can post workload events and renew itself, and can do nothing else. It cannot read any data, and it does not know (or ever learn) your name, email, or organisation. The backend privately links that installation to your anonymous user and your phone.

Day to day: nothing

There is no session to keep alive and nothing to re-enter. The token lasts 30 days and quietly renews itself in the background well before expiry, so an installation you pair once keeps working indefinitely — until you say otherwise. Everything it sends is metadata-only (see §5).

Consent is a lease, not a checkbox

The consent you granted in the app is a lease: it has an expiry and must be renewed, deliberately, by you. When it lapses, telemetry does not fail silently and does not retry around it — the tools pause and tell you once. Renew in the app and the queue resumes. Pairing stays intact; only the permission to ingest is gated.

Adding Claude Code and Codex

The agent hook CLIs don't show QR codes — they reuse the credentials of a tool you already paired. Pair the IDE extension first, copy the two values (ASCENDA_TOOL_INSTALLATION_ID and ASCENDA_EVENT_WRITE_TOKEN) into your shell profile, and register the hooks (Claude Code: settings.json; Codex: hooks.json or config.toml — both packages ship ready-to-merge examples). From then on the CLIs manage themselves: renewed tokens are written to ~/.ascenda/tokens/, so rotations survive without you touching your shell profile again.

One behavioral guarantee worth knowing: the hook adapters are built to never block or slow your agent. Codex in particular treats certain hook exit codes as "block the user's action," so the Ascenda hook always exits cleanly, caps every network call at three seconds, and reports problems as a one-line note instead of an error. A dead backend costs you nothing but that note.

Stopping, temporarily or for good

  • Pause from the editor: Ascenda: Disconnect App clears the local token. The pairing still exists server-side; reconnecting is a fresh Connect App.
  • Revoke from the app: this is the real kill switch. The backend invalidates the installation's tokens; the tool's very next send is rejected (401), it stops, and it cannot come back on its own — a revoked installation is only reinstated by you confirming a brand-new pairing in the app. Creating a new anonymous session does not clear revoked state.

If you see this, do that

What you see What it means What to do
"Telemetry paused. Renew IDE telemetry consent…" (Claude hook exit 2 / Codex one-line note) Your consent lease expired Open the Ascenda app and renew consent; nothing else changes
"Event write token invalid or revoked" (Claude hook exit 3 / Codex one-line note) The installation was revoked or the token could not renew Run Ascenda: Connect App and confirm in the app again
QR / code no longer accepted The 10-minute pairing window closed Run Connect App again for a fresh code
App refuses more code attempts Abuse protection: 5 failed confirmations in 15 minutes locks the flow Wait out the lockout (the message says how long), then retry

One design note worth appreciating: because the editor never holds your identity, the worst case for a stolen laptop or a compromised extension is that someone could post anonymous workload metadata until you revoke — they could not read anything, and could not find out whose data it was.

4. A shared vocabulary for workload

The most consequential design decision in the repo is that the backend owns a canonical event catalog — roughly twenty event types — and producers must emit those exact strings. There are no aliases: an unknown type is accepted but lands as unclassified, and the percentage of unclassified events is an operational metric watched for contract drift. This is what lets four very different collectors (two editors, two agent CLIs) feed one comparable dataset.

The catalog is declared exactly once — in @ascenda/tool-contract, which also ships the runtime catalog-to-category map — and a CI guard fails the build if a copy appears anywhere else (it detects renamed types and const-array copies via sentinel strings, not just the type name). Every event type maps to one of five workload categories — the analytical spine of the whole product:

Category What it captures Representative events
Creation Building new work, with or through AI ai_prompt_submitted, ai_file_write, ai_file_edit
Verification Checking that work is correct editor_verification_activity, compile_diagnostic
Supervision Directing and correcting the machine ai_tool_call_started, ai_correction_prompt
Risk Friction and strain signatures context_pressure_high, agent_loop_long, after_hours_ai_session, compile_error
Neutral Ambient activity and recovery editor_activity, recovery_offline_period, context_compression_*

The product read: when the Supervision and Risk share of a week grows while Creation shrinks, an engineer is becoming a manager of machine output — the "supervision creep" pattern the dashboard's AI Workload report visualises and the website sells against. The categories are not marketing gloss added on top of the data; they are columns in the ingest schema.

5. Privacy as architecture, not policy

Because the subject matter is "how hard is this person working," the repo treats privacy as a structural property enforced in independent layers, rather than a promise in a policy document.

  • Identity separation. A tool knows exactly two things: its toolInstallationId and its scoped write token. It never learns an email, name, user ID, push token, or organisation. Only the backend can resolve tool → anonymous user → device — so even a fully compromised extension cannot name its owner.
  • Metadata-only collection. No source code, prompts, AI responses, file names, branch or repository names, or terminal output leave the machine. Workspace names are reduced to a truncated SHA-256 hash; edit sizes to buckets; terminal commands to a class (test, lint, build…) plus a success/failure outcome. The one place raw text is touched — detecting that a prompt is a correction — happens locally, and only the classification is transmitted. A unit test asserts prompt text never leaves the mapper.
  • Server-side stripping. Even if a producer misbehaves, ingest sanitisation removes sensitive keys (prompt, code, filePath, terminalOutput, and friends) before persistence.
  • Identity continuity. A tool's reported telemetry source derives from the identity it originally paired under, not from live host detection — an installation that paired as vscode_extension and later runs inside Cursor keeps reporting consistently instead of silently flipping identity mid-stream.

Commercially this is the posture that gets a telemetry product past a works council, a CISO, and the Australian Privacy Act / GDPR review in the same meeting. It also cleanly separates Ascenda from the "employee monitoring" category: the design makes individual surveillance not just against policy but structurally awkward, which is a sales argument no survey vendor can copy quickly. The repo is equally explicit about what it is not: not a medical device — workload self-awareness, not diagnosis.

6. The packages: three shared, four shipped, two for dev

The repo is an npm workspace with deliberate layering. Three shared packages own everything that must not fork; four producers are what users install; two utilities exist for development and testing. A CI guard (npm run verify: structure checks, full build, 47 tests) fails the build if duplication regrows — and it has been tested against actual bypass attempts.

Shared

  • @ascenda/tool-contract — the DTOs, the 22-event catalog with its runtime category map, source and consent enums, and shared constants — declared once, documented as mirroring the backend API reference. A contract change is a one-file edit reviewed in one place.
  • @ascenda/tool-kit — everything an adapter needs below its mapping layer: the command classifier, duration and line-count buckets, the after-hours calculation (one place, with the known local-vs-UTC backend discrepancy flagged at the definition site), the ~/.ascenda/tokens store, the /v1 HTTP client, the defensive hook-payload probes that tolerate agent version drift, and the token-renewing event sender with an optional per-call timeout for hook-path use.
  • @ascenda/ide-extension-core — everything the IDE extensions do: editor and terminal telemetry, the queue/flush engine (requeues batches on transient failures instead of dropping them), session and after-hours signals, pairing UX, and runtime host detection. Host identity is a runtime question, so the two published extensions are byte-identical bundles.

Shipped

  • ascenda-vscode-extension-telemetry · ascenda-cursor-extension — each a manifest plus a one-line shell re-exporting the core's activation. Packaging is hermetic: prepublish rebuilds the shared packages, typechecks, cleans, and bundles; a .vscodeignore ensures only the bundle ships. The Cursor package additionally carries the Phase 2 MCP adapter scaffold.
  • ascenda-claude-code-hooks — the CLI invoked on Claude Code's lifecycle: prompts become Creation; inferred correction prompts add Supervision events; failed test/build runs become Risk; compaction becomes context-pressure signals; long sessions become agent_loop_long. After the shared-kit consolidation it is almost pure mapping. Bundled self-contained, so it installs and runs standalone.
  • ascenda-codex-hooks — the fourth producer, and the refactor's proof: only Codex-specific logic is new — the lifecycle mapping (including apply_patch as a file edit), config, and turn-state. Codex's Stop hook carries no duration, so the adapter measures turn length itself via a per-session timestamp; 30-minute-plus turns emit agent_loop_long. Because Codex awaits hooks and treats exit code 2 as blocking the user, the CLI always exits 0 and caps HTTP at 3 seconds — telemetry can never stall a turn. Identity rides the canonical cli_agent type with metadata.host: "codex" for later disaggregation.

Dev

  • ascenda-pairing-sim — the console stand-in for the mobile app: confirm codes, list and revoke tools, or run a full e2e without an IDE. Its tool-side calls delegate to the kit's client, so the e2e exercises the exact wire code the real tools use.
  • ascenda-dev-server — a zero-dependency local mock of the /v1 pairing and ingest contract: auto-confirming pairings (no phone), classifying and pretty-printing every received event live, with dev endpoints that simulate consent expiry and revocation. ./scripts/dev-quickstart.sh takes a fresh clone to events-on-screen in about two minutes — no backend, no DevAuth, no credentials handoff. The integration suite drives the real kit client through the full contract flow against it: pair → ingest → renew → rotate → revoke → 401, consent expiry, and unclassified-drift visibility.

7. What the data becomes

Tools deliberately stay dumb: they emit discrete events and never compute scores. The backend pipeline (defined in the shared API contract) does the interpretation. Every 15 minutes a writer rolls events into weekly per-person aggregates over a rolling 12-week window: category shares for creation / verification / supervision, risk counters (context pressure, long agent loops, tool failures, compression), and distinct after-hours sessions. Every 6 hours a baseline service recomputes each person's rolling 28-day norms — prompts per day, task switches, after-hours share, deep-work minutes, compile errors — so that reporting always reads as a delta from that person's normal.

This is where the repo connects to everything downstream: the same aggregates power the app's individual nudges and the organisation-level AI sustainability reports — the radar, work-mode distribution, oversight trend, and DevEx views the website demonstrates with illustrative data. The week a real pilot starts, those illustrative charts have a live data source with the same vocabulary — now spanning both editors and both agents. For a buyer, the pitch compresses to one sentence: the categories on the dashboard are the categories in the ingest schema, measured at the keystroke layer, without reading anyone's code or prompts.

Worked example. A senior engineer's week: prompt counts hold steady across Claude Code and Codex, but ai_correction_prompt and ai_tool_call_failed double, agent_loop_long fires three evenings in a row alongside after_hours_ai_session, and verification commands increasingly end in failure. No single event is alarming; the weekly aggregate against their 28-day baseline is a textbook supervision-creep signature — visible weeks before anything a survey would catch.

8. Engineering assessment

Rev. 1 of this report flagged three watch-items; Rev. 2 closed two. The ledger now:

  • Resolved: fork drift. One extension implementation with two marketplace identities, byte-identical at the bundle level. Classifier from four copies to one, catalog from three declarations to one, wire client and ingest parser to one each — with a tested CI guard against regrowth.
  • Resolved: reliability edges. The adversarial self-review of the refactor surfaced and fixed eight confirmed issues: standalone CLI installs, hermetic VSIX packaging, fresh-clone flows, batch requeue on transient outages, source-identity continuity.
  • Resolved: test coverage. The repo went from zero automated tests to 47, including an integration suite that drives the real client through the full contract flow against the local mock — a laptop-local stand-in for the Azure Dev happy-path verification. npm run verify is the single gate: structure guard, build, all suites.
  • Proven: the shared-core bet. The Codex adapter — a whole new agent platform — landed as roughly three hundred lines of mapping, config, and tests, with zero new copies of anything. The architecture's stated purpose was "a new agent is an afternoon"; the first data point agrees.
  • Still open: two definitions of "after hours." Clients flag 19:00–07:00 local; the backend aggregate standardises on 08:00–18:00 UTC — effectively inverted for Australian users. Decision remains with the backend team; the client fix is a one-file change in tool-kit.
  • Still open: correction detection is heuristic. The pattern-match feeding the supervision metric now has a test harness with labeled positive/negative cases to grow from, but a precision pass is still owed before pilot conclusions lean on it.
  • Still open (small): the backend registry has no codex source value, so Codex reports as cli_agent with a metadata marker — a two-line client change once the registry gains codex_cli. And token filenames contain : (Windows-hostile); fixing it needs a read-fallback migration.

9. Roadmap and what to watch

Phase 1 is live: editor and terminal signals, Claude Code and Codex agent hooks, loose pairing, consent-aware metadata-only ingest, and backend-free local testing. What's next is tracked as two epics on the repo:

  • The one-command installer (#3–#8) — curl | bash detects every agent on the machine, installs the right adapter for each, and pairs with one QR scan. The architecture prerequisites (self-contained bundles, shared credential model, hermetic packaging) already exist; the work is distribution, orchestration, and pairing UX.
  • Open source (#9–#14) — the collectors and vocabulary go public so the privacy claims become auditable rather than promised: licensing unification, contributor experience with an adapter-authoring guide (the Codex adapter is the template), a privacy proof pack with a payload inspector, the open-core boundary with a local no-account ascenda report mode, and an engineer-first launch.
  • Phase 2/3 platform work — the Cursor MCP adapter for native agent events, Copilot OTEL if the surface exists, and backend-delivered personalised baseline deltas surfacing in-place in the IDE.

The through-line remains vocabulary integrity — one small set of event names and five categories staying coherent from a hook process on a laptop to the chart a VP sees. That is no longer a discipline to maintain but a property the build enforces: the vocabulary exists in one file, CI rejects copies, and 47 tests pin the behavior. In product terms the conclusion is unchanged: this repo makes Ascenda's dashboards true. The website tells the story, the backend does the mathematics, the app holds the human's consent and voice — and these packages are where the evidence enters the system.


Prepared from repository documentation and source at main (post PR #1 and PR #2) · Ascenda internal · July 2026 · Rev. 3