Skip to content

Changelog

What's shipped, when. Newest first. Items move here from roadmap.md as they go live.

Last updated: 2026-05-11

2026-05-11 — In-product guided walkthrough (Phase 29)

Demo-ready interactive tour overlay built with react-joyride.

Behavior: - Auto-launches the first time a user lands in AuditForge after authentication - Restartable any time via a Take tour button in the header - Completion persists in localStorage.auditforge_tour_completed - 21 steps covering the complete audit workflow from orientation through delivery, including all major features (corpus upload, intake, finding review, bulk actions, investigate further, comments, deliverable export, chain-of-custody freeze, cross-engagement search, portfolio patterns) - Tooltips placed next to relevant UI (tabs, primary action buttons) or centered with prose for conceptual explanations - "Skip tour" available at every step; non-blocking overlay

Why it matters: - Onboarding: first-time partner users learn the workflow without needing a Loom video or hand-held demo - Demo aid: when Chris is on a sales call, the tour narrates the product so it's not all "let me show you this — and this — and this" monologue. Prospects see the same overlay as a user would

Implementation: - New frontend/src/components/AuditForgeTour.tsx with useTour() hook + step definitions - data-tour-id attributes added to tab buttons + new-engagement + upload-corpus buttons so tour can target specific elements - Wired into AuditForge.tsx shell

2026-05-11 — Admin-token rotation + per-token rate limits

Closes a real cost-exposure gap surfaced in the security review of the new public AuditForge landing page.

Admin-token rotation: - The legacy Lindy101 shared admin token was published in the public user-manual.md at docs.base2ml.com — anyone reading the docs had full admin access including running expensive audits - Token rotated to a strong randomly-generated value via SSM Parameter Store (version 2); old Lindy101 now returns 401 - User-manual.md scrubbed: admin token no longer published; instead refers readers to chris@base2ml.com to request one

Per-token daily rate limits (defense in depth): - New app/auditforge/rate_limit.py module - Daily caps per token per action_kind: - run — 20 audit runs/day per token - investigate_further — 50/day - recompute (portfolio clusters) — 10/day - intake_extract — 100/day - Bucket id is user:<id> for per-user sessions, admin:<sha256-prefix> for admin token (raw token never stored) - Returns 429 with retry-after hint when exceeded; counters reset daily - In-process state (single ECS task today); promote to Redis when scaling to multi-task

Audit findings (no changes needed): - All 42 mutating endpoints confirmed auth-gated. Anonymous callers cannot reach any cost-bearing endpoint.

2026-05-11 — Hygiene pass

Three small but meaningful cleanups that came out of the customer-pitch gap review.

Signed-URL export lifecycle (30-day expiry). Every delivery=signed_url audit-log export writes an artifact to auditforge/exports/<id>-<ts>.<ext> in S3. The presigned URL itself expires in ≤1 hour, so the underlying object has no operational value beyond a short retention window. - S3 lifecycle rule applied to the shared platform bucket (live now) - Same rule auto-applied to every new per-engagement bucket via app/auditforge/buckets.py provisioning code - Prevents unbounded accumulation; cost-bounded indefinitely

Pre-flag engagement migration. 15 engagements that pre-dated the AUDITFORGE_PROVISION_PER_ENGAGEMENT_BUCKET=true default were still in the shared bucket. Ran scripts/auditforge_migrate_buckets.py --execute --yes: - 24 objects copied across 15 engagements (1.6 MB total) - Each engagement now has a dedicated metis-af-<short_id>-<account> bucket with encryption, versioning, public-access-block, and tagging - Source-bucket objects preserved (rollback path intact) - Engagement records updated with source_bucket field

Real-PDF stress test. Validated the self-serve corpus flow with two real-world public PDFs: - NIST SP 800-171 r2 (1.5 MB, ~250 pages of compliance controls) - NIST Cybersecurity Framework v1.1 (1.0 MB) - Both uploaded cleanly via the + Upload corpus UI - Ingestion (chunking + FAISS + BM25 build) completed in ~50 seconds - client_id auto-bound to engagement id on completion - Confirms the pipeline handles realistic government-domain PDFs at production scale, not just synthetic test text files

2026-05-10 — Multi-reviewer collaboration + budget cap UX (Phase 28)

Two related improvements that close partner-workflow gaps at moderate-to-high engagement volume.

Phase 28a — Default budget cap UX - "Default audit budget" field promoted to a prominent "Engagement defaults" section in firm setup - Explanatory hint: $15 is conservative (12-doc synthetic corpus); $200 is suggested for 50–200 document real-world corpora

Phase 28b — Engagement assignment + threaded comments - POST /auditforge/engagement/{id}/assignment sets the lead reviewer; chip in the engagement header shows current assignee + dropdown picker - Per-finding threaded comments (GET / POST / DELETE /auditforge/finding/{id}/comments) — internal-only context for fellow reviewers, not rendered in deliverables - User-list endpoint auto-scopes to caller's firm so partners can pick a colleague without seeing other firms' rosters - 9 new endpoint tests; 106 endpoint tests pass total

See 39-multi-reviewer.md.

2026-05-10 — Self-serve corpus onboarding (Phase 25–27)

The biggest workflow gap: partners can now drag-and-drop their own documents directly into AuditForge. No more "send Chris your files and wait." End-to-end onboarding from "I have files on my laptop" to "I'm running an audit" in 5–10 minutes.

  • New endpoint POST /auditforge/engagement/{id}/corpus/upload (per-file granularity for network resilience)
  • Per-engagement bucket isolation flipped on by default for new engagements
  • New endpoint POST /auditforge/engagement/{id}/corpus/ingest runs the chunking + indexing pipeline as a background task
  • SSE progress stream at /corpus/stream surfaces stage transitions
  • engagement.client_id auto-binds to the engagement id once ingested — no separate corpus-tenant concept to manage
  • CorpusUploadModal UI: drag-drop, per-file Retry, live ingest progress
  • + Upload corpus button on the Engagements tab

See 38-self-serve-corpus.md.

2026-05-09 — Audit log signed URL (Phase 24)

Large audit logs (50+ MB) can now be downloaded via a presigned S3 URL instead of streaming through the FastAPI request — bypasses ALB timeouts for very large engagements.

  • delivery=signed_url query param on the existing audit-log endpoint
  • 60–3600 second URL expiry, controllable per request
  • Empty audit-log short-circuit returns signed_url: null, empty: true without an S3 round-trip

See 37-audit-log-signed-url.md.

2026-05-09 — Engagement template library (Phase 23)

A firm running the same shape of audit (CMMC pre-assessment, HIPAA review, etc.) for many clients can save an engagement's intake config + archetype as a reusable template.

  • Templates are firm-scoped (admin token sees all)
  • Two creation modes: snapshot from existing engagement, or pass intake fields directly
  • Instantiate → creates a new engagement pre-filled
  • Save-as-template button in engagement header; Start-from-template picker in new-engagement form

See 36-engagement-templates.md.

2026-05-09 — Cluster diff over time (Phase 22)

Patterns view now shows how the firm's accumulated cross-engagement patterns evolve between recompute snapshots. Each cluster is classified as added / grew / shrank / unchanged / removed with member-overlap percentages.

  • Greedy Jaccard matching with 0.30 threshold pairs current vs. previous clusters
  • Single-hop history (only the most-recent prior snapshot retained for diff)
  • Toggle "Show diff vs previous snapshot" in the Patterns tab

See 35-cluster-diff.md.

2026-05-08 — Engagement archive (Phase 21)

After delivery, an admin can archive an engagement to declutter the active list. Reversible, non-destructive — findings, audit logs, deliverables all preserved.

  • POST /engagement/{id}/archive + /unarchive (admin only)
  • include_archived=true query param on list endpoint reveals archived
  • UI: Archive/Unarchive buttons in engagement header; Include-archived checkbox on engagement list

See 34-engagement-archive.md.

2026-05-08 — Engagement freeze on deliver (Phase 20)

Chain-of-custody preservation. Once a partner marks an engagement as delivered to the client, finding mutations are blocked until an admin explicitly unfreezes. Removes the "what if you change findings after we approved them?" procurement question.

  • POST /engagement/{id}/deliver (partner or admin) → freezes mutations
  • POST /engagement/{id}/unfreeze (admin only) → re-allows edits
  • All mutating endpoints check is_frozen and return 423 LOCKED when frozen
  • Audit trail records who delivered and who unfroze

See 33-engagement-freeze.md.

2026-05-08 — Firm logo upload (Phase 19)

Self-serve white-labeling — partners upload their logo via the Firms tab. Stored as a data URL so it embeds cleanly in DOCX without an external CDN.

  • POST /auditforge/firm/{id}/logo accepts multipart upload (PNG, JPEG, SVG, WebP, ≤200 KB)
  • Logo data URL flows into deliverable rendering
  • LogoUploader widget in FirmManagement firm-edit modal

See 32-firm-logo-upload.md.

2026-05-08 — Per-engagement bucket migration script (Phase 18)

CLI tool to move pre-Phase-7 engagements into their own isolated S3 buckets. Dry-run by default; never deletes from source; idempotent.

  • scripts/auditforge_migrate_buckets.py
  • 10 unit tests covering plan, execute, verify-only, NoSuchBucket / AccessDenied non-destructive paths

See 31-bucket-migration.md.

2026-05-08 — Bulk finding actions (Phase 17)

Partner workbench efficiency: select multiple findings via checkboxes and accept/reject/refine all at once. Up to 200 per call; per-id results so partial failures don't abort the batch.

See 30-bulk-finding-actions.md.

2026-05-08 — Admin recovery endpoints (Phase 16)

Closes the SOC 2 readiness gap where a user who lost their TOTP device or forgot their password had no recovery path. Admin-only clear-MFA, clear-lockout, reset-password endpoints. Each writes a structured audit-log line.

See 29-admin-recovery.md.

2026-05-08 — Fine-grained roles (Phase 15)

Three roles inside a firm: admin, partner, associate. Associate is read-only — enforces partner-reviews-before-decision discipline by refusing finding mutations from associate users.

See 28-fine-grained-roles.md.

Earlier phases (Phase 0 – 14)

See the per-phase technical docs linked from README.md. Key milestones:

  • Phase 14 — Audit log export (procurement-review artifact)
  • Phase 13 — Pagination on long-list endpoints
  • Phase 12 — Brute-force lockout (5 failures / 15 min → 15 min lock)
  • Phase 11 — TOTP MFA with backup codes
  • Phase 10 — Cross-engagement portfolio clusters
  • Phase 9 — Per-engagement firm scoping (closes ID enumeration risk)
  • Phase 8 — Per-user authentication (argon2 + opaque session tokens)
  • Phase 7 — Per-engagement S3 bucket isolation (feature flag, default-on as of 2026-05-10)
  • Phase 5 — Cross-engagement findings search
  • Phase 4 — AI-assisted intake extraction
  • Phase 3 — Investigate-further per finding
  • Phase 2.5 — White-label firms + methodology white paper
  • Phase 2 — Partner-driven engagement creation UI
  • Phase 1 — Steering UI (review / accept / reject / refine / export)
  • Phase 0 — Foundation: async multi-provider LLM client, engagement store, intake module skeleton