Portfolio case study

I build the systems that make unclear requirements impossible.

NoPM is a live, hosted proof-piece for that thesis. It is a lite, API-first project management tool where governance — immutable audit, scoped tokens, four-eyes approval — is engineered into the data and API layer, not bolted onto the UI. Shipped solo in eight waves using an AI multi-agent workflow.

Sign in →stillnotbald.com

The thesis

Governance belongs in the data layer.

Most PM tools treat audit trails and change control as UI features — export logs, activity feeds, manual history. They can be bypassed by anyone with a database connection or a sufficiently permissive service key.

NoPM inverts that. Every mutation flows through the route() API wrapper and calls writeAudit() exactly once — fail-closed. Postgres triggers enforce append-only semantics. A service key cannot rewrite history. The UI is just a consumer of the same governed API your agent calls.

“The same surface a human uses — 82 documented endpoints, OpenAPI, an llms.txt index, and scoped Bearer tokens — is the surface an AI agent uses. No special mode, no privileged bypass.”


The wedge

Three properties most PM tools can't claim simultaneously.

agent-native
Every action is a stable REST endpoint — scoped resource:action tokens, OpenAPI, an llms.txt index, a nopm CLI. Any agent drives it over plain HTTP, identical to the console.
governed
Every mutation writes one append-only audit row. Deletes need four-eyes — a different checker approves, enforced by a Postgres CHECK constraint. Even a service key can't rewrite history.
governed triangle
Timeline, resource and budget changes pass one audit and sign-off gate. Over-allocation surfaces as a clash, spend rolls into EVM variance, a locked timeline forces a rebaseline sign-off. NoPM records who changed what and who approved it; it is not a scheduler, a timesheet system or an ERP.

What was built

A governance spine and the minimum registers it needs.

NoPM is the governed record beside Jira, Slack and Teams, not a replacement for them.

governance is invisible to the operator — provable in audit
$ nopm tasks rm task_8f3a --reason "superseded by revised cutover plan"
⏸  delete requested — pending a checker's approval (four-eyes)

$ nopm approve req_2c91          # a different token: task:delete:approve
✓  approved & committed           # one immutable audit row written

$ nopm audit --limit 1
created_at            actor_label     action        target
2026-06-08T…          checker (agent) task.delete   task_8f3a
  • Governed Kanban (drag = audited mutation) + read-mostly Gantt with rebaseline gate
  • Trust Center — immutable audit stream, provenance, PAT management
  • Change-control pipeline, notifications, deck export (PDF / PPTX / HTML)
  • Link invites — role-capped, revocable, audited (no email dependency)
  • Workspace → Project → Milestone → Task (typed statuses, maker-checker delete)
  • Risk & Issue registers with severity/likelihood scoring
  • Clash engine — schedule, resource, and dependency conflicts (cross-project)
  • Resource, allocation, timesheets (four-eyes submit/approve/reopen), cost-rate, and capacity
  • Budget: planned / committed / actual / EVM (CPI, SPI, EAC, VAC)
  • Sprints — commitment snapshots, burndown, and velocity flow metrics
  • BYOK governed assistant (user's own API key, AES-256-GCM at rest, ephemeral scoped token — NoPM ships no model key)
  • Documents & charter management with versioning and status workflow

Proof metrics

Numbers that show the governance isn't hand-waving.

77traced requirements (R-IDs) in RTM
1920governance-focused tests (vitest)
82documented REST endpoints + OpenAPI
1immutable audit row per mutation — no exceptions
0bundled LLM — governance ships without any model key

These numbers are not typed into this page — they are generated from the RTM, the test run, and the API route tree, and CI fails if they drift from reality. Every requirement has an R-ID, a status, and a traceability row. Tests target the governance rules first: can a maker self-approve? Does a missing scope return 403? Does a delete without a four-eyes approval fail?


Reviewed, then closed

Ten defects an adversarial review confirmed. Two sprints later.

On 2026-09-06 a six-lens review tested the thesis “NoPM is the governed record beside Jira, Slack and Teams” against the code, and a separate verifier confirmed ten defects. The same day, two sprints closed or narrowed them, and the verifier re-ran. The record below is the re-run, not the plan.

Finding (morning)State (evening)
Eleven mutating routes never wrote an audit row, including the BYOK key set and delete.Closed. A coverage test fails the build for any unaudited write; the key lifecycle is audited without the key.
Ordinary writes persist before the audit row; only two flows are transactional.Open by decision. The Trust Center now says so in plain words.
One scope both raised and decided change requests; a token requester left the DB check vacuous.Closed. change:decide is a separate scope agents never hold; requester and decider are typed identities checked in the API and the database.
Unlocking a frozen baseline took no reason.Partly closed. A reason is required in the API, the dialog and the CLI; the same person can still unlock.
Audit rows carried no client origin or request id.Partly closed. Every row records the declared client and a request id echoed on the response, including rows written inside database functions. No hash chain yet.
No record could point at a Jira key or a Slack thread.Closed. Tasks, projects, risks, issues and decisions carry source, external_ref and external_url, in the API, the MCP tools and the CLI.
No public page said the product complements Jira, Slack or Teams.Closed. README, landing, this page and the product doc say it.
The lock and rebaseline flow had no navigation home; a demo page sat in the Governance section.Closed. Baselines lives under Governance; the demo page moved to My Work.
The audit immutability triggers had no regression test.Closed. A migration-text test fails if any migration drops, disables or bypasses them.
Tool and metric counts drifted between surfaces.Closed. Counts are generated and CI-checked; the pitch notes were realigned.

The review, the sprint receipts and the re-verification are in the repo under docs/research and sprint-log.


Stack & method

Production stack, shipped with AI multi-agent assist.

Technology

  • Next.js 16 App Router + TypeScript + Tailwind v4
  • Supabase — Postgres + RLS + Auth + Storage
  • Vercel deploy + Cloudflare DNS → stillnotbald.com
  • jsPDF / pptxgenjs — governed deck exports
  • vitest 1920 tests, governance-first coverage

Discipline

  • Requirement traceability matrix (RTM) — R-ID per feature, verified in CI
  • Sprint log per wave — plan, decisions, and a signed-off summary
  • Architecture decision records (ADRs) for irreversible choices
  • AI multi-agent workflow — sub-agents per domain, orchestrated by a wave planner
  • Solo build — no team, no PM, no sprint ceremonies

See the governance live.

Every endpoint is documented. Every mutation is audited. The console is open.

Sign in →OpenAPI specAgent API (llms.txt)