Skip to content

Phase 23 — Engagement template library

Last updated: 2026-05-08

A firm running CMMC pre-assessments for many clients fills in nearly the same intake every time — same domain language, same frameworks, same focus areas, same materiality threshold. Phase 23 lets a partner save an engagement's intake config + archetype as a reusable template; subsequent engagements pick the template and the form prefills.

Templates are firm-scoped (a partner only sees their own firm's templates), persisted server-side in auditforge/templates.json, and decoupled from the engagement they were built from (deleting the source engagement does not affect the template).

Endpoints

GET    /auditforge/template[?firm_id=...]
GET    /auditforge/template/{id}
POST   /auditforge/template
PUT    /auditforge/template/{id}
DELETE /auditforge/template/{id}
POST   /auditforge/template/{id}/instantiate

Auth: admin token or session token; partner role required for mutations (associate is read-only per Phase 15). Firm-scoping applies — non-admin callers can only see/mutate templates within their own firm; cross-firm access returns 404 (not 403, to avoid ID enumeration).

Create

Two creation modes:

// Mode 1: snapshot from an existing engagement
{
  "firm_id": "firm-abc",
  "name": "CMMC pre-assessment starter",
  "description": "NIST 800-171 controls, defense corpus",
  "from_engagement_id": "eng-xyz"
}

// Mode 2: pass intake fields directly
{
  "firm_id": "firm-abc",
  "name": "Healthcare HIPAA review",
  "archetype": "premium_defensibility",
  "budget_cents": 250000,
  "intake": {
    "domain": "healthcare provider",
    "audit_purpose": "HIPAA Security Rule readiness",
    "frameworks": ["HIPAA"],
    "focus_areas": ["access control", "audit logging"],
    "materiality": "$25K",
    "doc_hierarchy": {},
    "known_concerns": []
  }
}

Mode 1 (from_engagement_id) is the typical path — partner runs an engagement, refines the intake during the audit, then clicks "Save as template" to capture the final shape. Mode 2 is used by ops or programmatic seeding (e.g., a script that pre-creates standard templates for a new firm).

Instantiate

POST /auditforge/template/{id}/instantiate
{ "client_name": "Acme Defense Corp", "budget_cents": 300000 }

Returns the new engagement (same shape as POST /engagement). Internally: 1. Creates a fresh engagement with the template's firm_id + archetype + budget_cents (overridable per call) 2. Calls update_intake(engagement_id, template.intake) so the new engagement starts with the template's intake fully populated 3. The partner can still edit the intake before kicking off the audit run

The instantiated engagement is independent of the template — editing the template later does not retroactively change engagements already created from it.

UX

EngagementDetail header (Phase 23 addition): when the engagement has an intake and is not frozen, a Save as template button is rendered. Click → prompts for a name, posts from_engagement_id, confirms.

NewEngagementForm (Phase 23 addition): when templates exist, a Start from template dropdown appears at the top of the form. Selecting a template prefills firm, archetype, budget, and all intake fields. The partner reviews and edits before submitting — the template is a starting point, not a lock.

Why a single store, not per-firm files

auditforge/templates.json holds all firms' templates in one file, mirroring firms.json. Tradeoffs:

  • Single file: simpler S3 round-trip, one mutex; firm-scoping enforced in the endpoint layer
  • Per-firm files: isolates blast radius if one firm has many templates (~thousands)

Templates are typically <50 per firm, so the consolidated approach is fine. If template counts ever explode, splitting is a one-line refactor (firm_id → key prefix).

Files

  • app/auditforge/template.pyEngagementTemplate dataclass + TemplateStore (S3-backed lazy-load + best-effort upload pattern)
  • app/auditforge_endpoints.py — 6 template endpoints
  • frontend/src/api/auditforge.tsEngagementTemplate interface; listTemplates, createTemplate, instantiateTemplate, etc.
  • frontend/src/components/NewEngagementForm.tsx — template-picker dropdown + applyTemplate prefill
  • frontend/src/components/EngagementDetail.tsxSave as template button
  • tests/test_auditforge_endpoints.py — 10 endpoint tests covering create-from-body / create-from-engagement / list filter / update / delete / instantiate / 404 paths