// build log LIVE

From Zero to Running System

Phase by phase, one machine, still going

This is the diary of how Fulltrace was built: it started on 28 April 2026 as a folder of notes about how I work, and grew into a system that runs real work and checks it before anything is accepted. Along the way came the context portfolio, a local MCP server, three AI clients, token tracking, a VSCode dashboard, and from late June a task-graph runtime with deterministic verification, gated applies and the Studio operator hub. Nothing here is polished after the fact: the log below reads in the order it happened, including what broke along the way.

building since April 2026 one phase at a time nothing polished after the fact still going
stack [01]

The gateway runs on Node.js behind pm2; the dashboard ships as a VSCode extension with no runtime dependencies of its own.

tech_stackexact versions and packaging
MCP Server
01Node.js v24 (ESM)
02@modelcontextprotocol/sdk v1.29.0
03Express + StreamableHTTP transport
04pm2 + Task Scheduler (Windows auto-start)
VSCode Extension
01TypeScript → ES2020 / CommonJS
02VSCode Extension API
03Zero runtime dependencies
04Packaged as VSIX via @vscode/vsce
the_build [02]

Every Phase, In Order

Each phase produced something independently useful. The portfolio is useful without a server. The server is useful without a dashboard. But stacked, they compound into something that makes every AI session faster by default.

Phases 01 to 17 built the plumbing: the portfolio, the local server, the dashboard, and three connected AI clients. The log below picks up from phase 18, the point where it became a genuine runtime rather than a set of connected tools.

phases_01_17the plumbing: portfolio, server, dashboard, three clients
28 APR
Phase 01: Identity Consolidation
Context Portfolio

Worked through the AIDB AgentOS templates to fill out all ten portfolio files: identity, role, projects, team, tools, communication style, goals, preferences, domain knowledge, and decisions. Each file is a single page of dense, machine-readable markdown describing how I actually work. Merged Claude Code's session-context file with the richer content from the identity file, creating a single authoritative file that loads automatically every session. Cleaned up duplication across all ten files so each is self-contained.

29 APR
Phase 02: Local MCP Server
Portfolio as a Service

Built a Node.js MCP server using the official Model Context Protocol SDK with StreamableHTTP (stateless) transport. Every portfolio file exposed as a typed portfolio:// resource over a loopback endpoint. Files read from disk on every request, so edits are live without a restart. Wrapped in pm2 with Task Scheduler auto-start on Windows login. The server now runs continuously in the background without any manual steps.

30 APR
Phase 03: VSCode Dashboard
Making the Invisible Visible

Built a VSCode extension with two surfaces: a sidebar TreeView (always visible) and a WebviewPanel (on demand). One polling loop drives both. Status endpoint added to the MCP server returning metadata, resource list, request count, and a rolling log of the last 100 requests. pm2 Start, Stop, and Restart buttons inside the editor. Click a portfolio file to open it. Once I could see the request log live, I started using the system differently: watching which resources Claude actually pulls is feedback I never had before.

1 MAY
Phase 04: Skills and Memory Tabs
Dashboard Depth

Added Skills and Memory tabs to the dashboard. Skills tab lists all Claude Code commands from global and project directories with scope badges. Memory tab parses project memory files with YAML frontmatter. Custom slash commands created for the full post-build workflow: update changelog, update docs, sync showcase copy, commit, push, all chained via a single jn-website-commit orchestrator with a confirmation gate at each step.

2 MAY
Phase 05: Full-Height Layout and Skill Logging Fix
Dashboard Polish

Dashboard panel now fills the window and resizes with it. Active tab content scrolls; status card and tab nav stay fixed at the top. Fixed a critical bug: log-skill.js used CommonJS require in an ESM package, so every skill invocation silently threw a ReferenceError and nothing was posted to the server. Fixed with an ES module import. Restarted the pm2 process to pick up the fix.

3 MAY
Phase 06: Portfolio MCP Wiring and Config Portability
Global Portability

Claude global config made portable: skills and memory versioned in the Fulltrace repo and synced to ~/.claude/ via sync.ps1. The global CLAUDE.md now travels with the repo and restores on any machine with a single push. Portfolio MCP section added to the project CLAUDE.md, instructing Claude to use portfolio:// URIs when reading portfolio files rather than searching the filesystem.

4–5 MAY
Phase 07: Codex MCP Bridge and Config-Driven Skills
Multi-Client and Registry

Codex connected to Fulltrace via an HTTP MCP bridge (no separate server). Portfolio-read events from Codex now flow back to the shared dashboard server and appear tagged in the request log. A global config/projects.md registry defined paths, changelogs, and web page targets for all four projects. All skills refactored to read from the registry rather than hardcoding paths. $env:AGENTOSROOT eliminates usernames and machine-specific paths from the repo.

10 MAY
Phase 08: Token Tracking Infrastructure
Visibility Into Cost

End-to-end token tracking built into the server: a normalisation pipeline converts token usage across providers, per-skill and per-tool totalTokens accumulated alongside invocation counters, and cumulative all-time totals persisted in persist.json. Both log-skill.js and log-tool.js now extract token usage from Claude hook payloads and include it in their POST bodies. Session and all-time totals returned in /status.

10 MAY
Phase 09: Haiku Sub-Agent Delegation
Cheaper Portfolio Reads

All ten portfolio-read skills now spawn Haiku sub-agents for resource retrieval. Token overhead shifted from the main session to cheaper Haiku contexts. The same delegation pattern applied to orchestration: jn-website-commit delegates changelog, docs, git, and showcase steps to sub-agents. The --auto flag added to five skills, allowing the orchestrator to skip confirmation gates when chaining. Only the changelog draft step still pauses for review.

12 MAY
Phase 10: Per-Model Token Tracking and Dashboard Redesign
Model-Level Visibility

MCP server extended to aggregate tokens per model (Haiku, Sonnet, Opus). Dashboard status card redesigned as a two-row table with session and all-time columns. Run buttons added to each skill entry for direct invocation from the dashboard, no terminal required. log-skill.js and log-tool.js now extract the model from Claude hook payloads and include it in POST bodies. 60-second auto-save interval added to server.js for regular state persistence between requests.

14–15 MAY
Phase 11: Setup Wizard, Sync Diff UI, and Agents Tab
Operational Tooling

Setup Wizard webview panel added: gather AGENTOSROOT, validate prerequisites, run setup steps with progress feedback, auto-close on success. SyncDiffPanel added: shows file-by-file differences between canonical repo and runtime ~/.claude/ state. Agents tab added alongside Skills and Memory. Server uptime and restart tracking made persistent across restarts. Graceful shutdown handlers flush state to disk before exit.

19 MAY
Phase 12: Create, Run, Remove from Dashboard
The Dashboard Stops Being Read-Only

Skills, memories, and agents can now be created directly from the dashboard via wizard forms: fill in the fields, hit Create, the file lands in the correct directory with proper structure and YAML frontmatter. Remove buttons with confirmation dialogs enable deletion without touching the filesystem manually. Agent Run button wired to fire agents via the Claude Code extension directly. Search and filter controls added to all four registry tabs. New /agent-log server endpoint mirrors the skill and tool log pattern, tracking agent invocations with token usage and per-agent stats.

23 MAY
Phase 13: Cursor Integration and Multi-Client Nomenclature
Three Clients

Cursor connected to the shared Fulltrace MCP server via Streamable HTTP. Command logging via hooks.json posts Cursor slash command invocations to /command-log. Dashboard updated with client pill selectors in Setup and Analytics tabs. All skills renamed to the new Commands nomenclature and moved to clients/claude/commands/. bootstrap.ps1 added for automated new-machine setup. Codex README updated with gateway pattern documentation.

28 MAY
Phase 14: Shared Centralisation
One Source, Three Clients

Global memory files and skills moved from per-client directories into a single clients/shared/ directory. All three sync scripts updated to source from shared. Commands tab deduplication implemented: shared skills appearing in multiple clients collapse to a single row with stacked tool badges. An "All" badge replaces individual badges when all three clients carry the command. Scope terminology standardised from "local" to "project" throughout. Client colour assignments corrected: Claude = blue, Cursor = orange (previously swapped). Codex rate limit adapter fixed for expired rate limit windows.

28 MAY
Phase 15: Showcase Rewrite
Documenting the System

The showcase website Fulltrace section fully rewritten from scratch: seven new pages (overview, architecture, Claude, Codex, Cursor, dashboard, build log) with a persistent two-level navigation so users can discover and move between sections without getting lost. Pages sourced directly from the repo: READMEs, CHANGELOG, and source architecture. Previous pages archived with cross-links preserved for reference.

4 JUN
Phase 16: Templating, Security, and Telemetry Accuracy
Hardening the Shared Layer

Shared skills became client-aware: {{LIGHTWEIGHT_MODEL}} and {{CLIENT_NAME}} placeholders now expand per client at sync time, so one skill file resolves to Haiku on Claude and Cursor and GPT-4o-mini on Codex. Codex skills now self-report to /command-log via a logging block injected at sync, so Codex invocations show in the dashboard alongside Claude and Cursor. The pm2 controls were hardened against shell injection: child_process.exec through cmd.exe replaced with spawn (no shell) plus process-name validation. Stats-cache accuracy fixed: a full ISO lastComputedAt timestamp replaces the date-only cutoff so same-day sessions are re-counted, and the hour-of-day heatmap now bins by local time. jn-ext-deploy made self-contained so it runs without the MCP extension tools loaded.

11 JUN
Phase 17: Multi-Provider Models and Cost Tracking
Beyond One Vendor

Codex gained pay-per-token access to OpenRouter-hosted models with no subscriptions: one API key, four role profiles (planner on DeepSeek V4 Flash, tools on Qwen3 Coder, agentic on Kimi K2.5, cheap on the free Qwen tier), all deployed through the managed config block. The dashboard followed: a shared pricing module estimates cost per model across Anthropic, OpenAI, and OpenRouter, the Codex analytics adapter attributes tokens to real model IDs from rollout logs, and the model tables render columns dynamically from the data instead of a hardcoded Haiku/Sonnet/Opus trio. Two real bugs fell out of the work: Codex token totals were double-counting cached and reasoning tokens (subsets, not additions), and Codex could not reach the MCP server at all since the security hardening because nothing sent the Bearer token. The managed block now authenticates via AGENTOS_TOKEN, kept in sync by sync.ps1.

13 JUN
Phase 18: Agent Loops and the Website Update Pipeline
Agents That Run Themselves

A runtime agent loop, built on top of the multi-provider work: a plan/execute/review pipeline with bounded self-correction, provider-agnostic by construction so the loop holds no model or transport detail of its own. A planner model writes a plan, an executor model runs the agent loop against the MCP tools, and a reviewer model checks the result and sends it back for correction when it falls short. Reliability was treated as first-class from the start: transient provider errors retry with backoff invisibly to the model, invalid tool calls are schema-checked and handed back to the model to fix, and a repeatability harness runs a task many times to measure success rate rather than trusting one green run. The first loop built on it keeps this website current from each project's changelog, readme and shipped version: the model decides what is stale and proposes a change-set, and code applies every edit deterministically, behind a safety gate that confines writes to the website directory and blocks git. The model plans; code writes.

24 JUN
Phase 19: Task-Graph Engine and Run-Control API
From One Loop to a Graph

The linear pipeline became one node type in a task-graph engine: DAG validation before any model spend, bounded concurrency via a hand-rolled promise pool, a per-run token budget as a circuit breaker, and fan-out/gather node factories with deterministic gather order. The first portfolio workflow fanned the code audit out over every enrolled project concurrently. On top of it, a run-control API on the existing gateway: list workflows, start a run, fetch a snapshot, and stream live node lifecycle events over SSE, all behind bearer auth because these endpoints trigger model spend.

25 JUN
Phase 20: Model Failover Routes and Focused Audit
Outages Became Architecture

Free-tier rate limits blocked the first portfolio run, so single models became ordered routes: preferred first, failovers behind. A transient provider error restarts the stage on the next model in the route; a real error is rethrown immediately. Telemetry records the attempted-model chain. In the same push, focused audit mode fixed the worst quality bug: a deterministic selector caps each audit at the highest-signal surfaces and a hard finalisation instruction makes ending a run without findings JSON a failure, not a silent clean pass.

26–27 JUN
Phase 21: Deterministic Verifier and the Apply Engine
Code Checks the Model's Work

A verifier node re-checks every audit result deterministically across four locked failure domains: output, completion, schema, and anchor. Every finding's anchor text must resolve verbatim against the file on disk or the finding is rejected; a failed verdict routes to one bounded re-run at reduced scope. The write path landed alongside it: apply is a replay of a saved, verifier-passed report, dry-run by default, gated to a filesystem allow-list, a clean working tree, and a mandatory build gate, with CRLF-aware matching after a line-ending mismatch blocked the first real candidate. Two real fixes went through the full path and were committed.

28 JUN
Phase 22: Advisory Challenger and Workflow Profiles
A Second Opinion That Cannot Overrule

An opt-in challenger stage runs downstream of the verifier and can raise advisory cautions on a passing result, but never changes a verdict, widens apply eligibility, or triggers a write; the policy is written down, not implied. Workflow profiles then generalised the layer: a registry with a hardened contract puts a stable id and honest metadata over each workflow, validated before any model call. Two no-model detector profiles (documentation drift and changelog coverage) proved the graph carries more than audits.

29 JUN
Phase 23: Platform Foundation
Durable, Local, Honest

A SQLite run ledger and queue (better-sqlite3, WAL) dual-written alongside the canonical JSONL telemetry, behind an async interface portable to a hosted database later. Store reads are opt-in with strict JSONL fallback; orphaned rows are retained and reported, never fabricated into history. Queue payloads are secret-free with credentials injected at drain time, the gateway's non-secret environment is pinned by contract, and an approval spine (grants bound to an exact apply fingerprint, issued by a fail-closed CLI) exists for any future unattended apply, which remains impossible by design.

30 JUN
Phase 24: Differentiation Stack and Website Audit Workflow
The Runtime Reads Its Own Output

A read-only, offline, no-model layer over the audit output: a workflow simulation report with structured evidence anchors, deterministic grouping, manual dispositions persisted in an append-only ledger, historical read-back, and a descriptive learning summary. Everything is a candidate, nothing auto-remediates. The website content audit also joined the graph as a first-class workflow: the proposal-first sibling of the code audit, fanning out over the enrolled projects with no apply path at all.

1–2 JUL
Phase 25: Studio
Operating the Runtime From the Editor

The dashboard grew native Workflows, Runs, and Graph tabs, then consolidated them into Studio: one hub organised around the run lifecycle, from the workflow catalogue through configuration, a live cost estimate priced from per-stage run history, launch behind an explicit spend confirmation, live observation with 4-second change-guarded refresh, and node drill-through to the findings report. Workflow and project catalogues moved into committed JSON registries so enrolment changes get git review. Next on the roadmap: breaking up the 12,000-line webview into modules.

3–6 JUL
Phase 26: Approval-Gated Apply
The First Kept Write

The write path became an operator flow, each step designed first, put through an external adversarial review, then built: a dry-run diff surface in the code-audit panel, frozen approved-diff artefacts whose full hash is the authority key, atomic crash-safe reservation markers, then freeze, approve, and apply from Studio itself. Apply simulation closed the loop: the real apply engine and the target's own build gate run against the real tree, then always revert, so approval is only ever offered on a fix already proven to compile. The invariant is written down: nothing unapproved is kept. A fix-quality pass added a dedicated paid fix-model route and prompts that must stay within the evidence or decline with a reason, and on 4 July the first human-approved, artefact-bound write landed on a real project and was kept, build gate green. Around the milestone: finding-lifecycle badges, a sidebar status hero, a reusable model picker, fail-closed containment of project registry roots, and a one-script new-machine bootstrap. The dashboard shipped fast through this stretch: a new release most days for four days straight.

6–8 JUL
Phase 27: Bigger Fixes, and What Needs Me
Decomposition Without Handing Over the Keys

One fix, one gated write was proven. The next problem was a change too big for one edit. A decomposer now proposes a step plan while holding no write authority of its own: it can suggest, it cannot apply. Each step replays through the same gates and is committed individually with a provenance trailer, eligibility is independently re-checked at the apply seam rather than trusted from the plan, and a cumulative cap walks the real git history so a large change cannot be smuggled through as a run of small ones. The first live decomposition produced a validated three-step plan for about ten cents. Then, in a single day, the decision inbox: a read-only projection answering one question across every store, what actually needs me. Five slices shipped, then it stopped at five of six by explicit call, because the last slice needed a fleet that did not exist yet. Naming it deferred rather than leaving it in progress was itself the point: in-progress is what the cockpit reads, and a paused row sitting there misreports work in flight.

10–12 JUL
Phase 28: The Fence, the Phone, and the Fleet
Assuming the Input Is Hostile

The verifier already assumed the model was unreliable. This phase assumed its input was hostile. Every audit pushes raw file bodies and website HTML into a prompt, so audited material is attacker-controlled the moment a dependency or a copied snippet lands in a repo. The injection fence gives every model call exactly one instruction channel: everything else travels inside a data block delimited by a run-scoped nonce, 72 random bits minted per run, and a block arriving with that nonce already in it refuses rather than degrades, because at 72 bits that is not coincidence. Checked rather than assumed: a before-and-after audit across three targets, about US$0.32, found no quality loss. Alongside it, two capability steps that both led with their limits. Notifications and a read-only mobile page reached the phone, pushing counts and class codes only, never content, on the rule that notification is signal, never authority. And a local Ollama fleet arrived in Studio, with execution-class recording shipped first so no local call could ever have gone unrecorded, and pipeline participation refused outright in v1 rather than allowed because it happened to be free.

13–17 JUL
Phase 29: Consolidation, and a Gate That Was Not There
Finding the Control Had Been Dead All Along

Thirteen top-level tabs became seven: the old standalone Setup, MCP, Commands, Settings and Hooks became pills inside Config, and Development grew into a proper cockpit. The registry closed the injection track by proving its own coverage, enumerating every prompt surface with a source scan that fails if a dispatch site exists without a row, and it immediately made a real catch: a challenger stage shipped two days earlier without one. Then the phase earned its title. Testing whether granting apply authority would prompt, the command executed silently. The carve-out protecting the gate on every write to my code was enforced by none of its three supposed layers, and under bypass mode an ask rule is swallowed entirely. A permission gate that fails open is worse than none, because it is the one you have stopped checking. It was rebuilt fail-closed, returning an explicit allow, ask or deny per command. The uncomfortable part is not that it broke; it is that nothing would have told me.

what_i_learned [03]
01
MCP is simpler than it looks

Connecting an assistant to your own information sounds hard, but the standard tooling, resources, URIs, and the stdio/HTTP transport split, does most of the work. StreamableHTTP's stateless mode means no session management to worry about. The hardest part is understanding the concepts, not building it.

02
Windows extension hosts have a stripped PATH

Tools do not always inherit your normal setup, and half the work is making things run reliably on a real machine. Case in point: the VSCode extension host does not inherit your shell's PATH, so npm globals are not directly callable. Routing commands through cmd.exe fixed it and resolves npm globals correctly.

03
Local-first is enough for now

I considered remote hosting on day one. But every agent I actually use runs on this machine, and binding to loopback only sidesteps the entire auth, TLS, and hosting question. When I need remote access, I will add it; until then it is friction I do not need. Moving to a different PC is the one real cost, so I scripted it: a clone plus bootstrap.ps1 stands the system back up, with setup notes carrying the handful of machine paths the script cannot infer.

04
A dashboard makes the system real

Watching which portfolio resources Claude actually pulls changed how I refine the files. Token counts per model changed which model I use for which task. Seeing invocation logs from Cursor alongside Claude logs confirmed the multi-client wiring was working. Visibility is not optional infrastructure: it is what makes a system worth building.

05
Shared before specialised

Moving from per-client memory and skills directories to one shared clients/shared/ directory was the right call. Any update to a command or memory file now propagates to all three AI tools on the next sync, with no divergence between them. The repo is always the source of truth, and the runtime directories on each machine can always be rebuilt from it.

06
Test the thing that runs, not the thing you wrote

Four bugs shipped without ever crashing, all the same shape: a single backslash before a regex escape inside the webview's giant template literal gets stripped when the template is evaluated, because at that point it is not code, it is string content. \d{4} cooked down to d{4}, so every chart date label quietly fell back to the raw ISO string; \s trims cooked to s and stripped literal letters instead of whitespace. Nothing threw. Reading the source again would never have found them, because the code I was reading was not quite the code that ran. They only turned up once I cooked the real compiled template in Node and ran the functions against known inputs.

07
A safety check nobody tests is a decoration

Testing whether granting apply authority would prompt, it executed silently. The gate protecting my code was enforced by none of its three supposed layers, and under bypass mode an ask rule was swallowed entirely. Nothing was broken, nothing looked wrong, and no error would ever have told me: it failed open, which is the worst way to fail, because a gate that blocks is noisy and gets fixed, while a gate that quietly waves everything through looks exactly like a gate that is working. The lesson generalises past permissions: any control asserted but never exercised should be assumed dead until you have watched it refuse.