How a CMMC 2.0 / NIST SP 800-171 readiness engagement runs. The question isn't just where are my gaps — it's am I ready to self-attest in SPRS, or to pass a C3PAO assessment? Finding gaps is one step toward that answer, not the whole of it.
Every piece — the database (NocoDB), the orchestrator (n8n), the script runners — is a Docker container on a dedicated VPS, never a local computer. An action or upload in NocoDB fires a webhook to n8n, which runs a Python script on the host: runner for data work, runner-ai (the one container holding the AI key and internet access) for anything that calls Claude. Because it's all containers, the whole stack is portable — it can be picked up and rehosted elsewhere without re-architecting. The chips under each phase below show that phase's actual path.
The same process as a decision flow — where a control forks toward evidence or toward remediation, and the loop that turns a gap into coverage. Node colors match the legend below.
Stand up the client's own isolated workspace before any data is entered.
Onboarding clones the template base (NocoDB's Duplicate Base) into a per-client base — reference data, views, and the empty Client_Profile / CAGE_Codes tables all come along. Every engagement script resolves its tables by title within whichever base it's told to target (NOCODB_BASE_ID), so the same scripts, runner, and n8n buttons drive any client base with no per-client code. When a reference correction lands (a NIST fix, a reworded control), push_reference_data.py propagates it out to every client base — matching rows by business key, dry-run first, and skipping-and-flagging any base that's drifted rather than overwriting it.
Declare the scope up front — everything downstream keys off it.
Populate objectives → the full control checklist appears in seconds.populate_gap_assessment_objectives.py reads the linked framework(s) and creates a row for every assessment objective (Gap_Assessment_Objectives) and every control (Gap_Assessment_Requirements). It's idempotent — linking another framework later only adds what's missing, so re-running is always safe.
One long conversation, recorded in five passes — each feeds the next.
Needs Review down to zero. This is where the boundary is drawn.Claimed Status: do you actually do this?It's a gap already found. Route to remediation — never request evidence for it.
This is what you'll prove. Open an evidence request for it downstream.
Claimed ≠ assessed. A claim is pre-proof; MET / NOT MET is the post-proof finding — separate fields. And a scope-reducing call (CRMA, Out-of-Scope) stays provisional until its control is proven in Phase 5.
2d · Scoping engine. recompute_scoping_categories.py sweeps the whole inventory and assigns each asset a category per 32 CFR 170.19 — CUI Asset · Security Protection · CRMA · Specialized · Out-of-Scope · Needs Review. It's fail-closed: an asset with a missing input can never compute to Out-of-Scope — it lands in Needs Review with a Rationale naming the gap. Scope-reducing calls (CRMA, Out-of-Scope) stay provisional until their control is proven MET in Phase 5.
2e · Triage. A plain-language Triage_Questions bank drives the "do you do this?" pass. The claim is pre-seeded from the tool stack (infer_claims_from_stack.py) and any SSP (mine_ssp.py), then check_claim_discrepancies.py flags the tell: claimed done, but you run none of the tools this control needs. Every claim carries a provenance rank — Client-confirmed > Interview > SSP > Tech-stack inferred.
Client hands over policies; the machine reads them.
Evidence as verbatim quotes with a sufficiency judgment, marked AI-proposed.Satisfied by documents → the automated review above proposes candidates.
Need a screenshot or config export → personalized evidence request → vision auto-check → reconcile.
Upload fires a webhook → ingest_client_documents.py extracts the text (pdfplumber / python-docx; no OCR, so scanned PDFs come back empty — spot-check). Flip to Queued and review_document.py batches the in-scope Define objectives by control family, asks Claude for verbatim quotes plus a Sufficiency judgment, then mechanically verifies every quote against the source (check_quote_fidelity.py) — a quote not found word-for-word is discarded — and writes Evidence as AI-proposed. The same pass also harvests reusable values from the document (the client's name and the like), so later drafts read with their real details instead of placeholders.
Two independent calls on every candidate artifact — worked from a “Review — start here” console and purpose-built action views that surface only the rows and fields each decision needs, with an action-first Suggested action cue on every row saying what to do next.
Verification Status — have I actually looked at this?Sufficiency — does it satisfy the objective, or just point at where the answer lives?Evidence linked to the personalized Evidence_Request generated back in 2e — generate_evidence_requests.py had resolved {{technology}} to the client's actual tool and led each ask with the plain-language Assessor Intent.check_evidence_vision.py sends each artifact to Claude vision and grades it against the request's What It Must Show — three checks:
Verification (have I looked?) and Sufficiency (does it satisfy the objective, or just point at where the answer lives?). Coverage needs both.Accept / Reject on the request triggers reconcile_evidence.py (webhook → n8n → runner, no button to hunt for): it propagates the decision onto the evidence, rolls the request status up, sends rejected or empty ones back to re-request with your Reviewer Feedback attached for the client to fix, and reports which Perform objectives are now ready to mark MET.Record the findings; the scoring rolls itself up.
MET / NOT MET — and write down why it failed.NOT MET fails the whole requirement; it computes the real DoD SPRS score (points only for what's proven) and opens a POA&M for each new gap — never touching your existing remediation notes.Marking an objective fires recompute_gap_assessment.py automatically (webhook → n8n → runner). It rolls objectives up to requirements — one confirmed NOT MET fails the requirement, even with others unassessed — computes the actual DoD SP 800-171 SPRS score — 110 minus the weight of every requirement not proven MET (not-yet-assessed controls count against it, so it's the defensible floor; N/A is deducted only for genuine incapability, never mere absence) — and writes it, with a full audit basis, onto the assessment and into the SSP. It also auto-creates a POA&M for each newly-failing requirement, linked to the exact objectives that caused it, and never overwrites an existing POA&M, so your remediation notes survive every re-run.
Turn each gap into a specific, adoptable fix — and every gap is one of two kinds.
Documentation Recommendation: proposed language, placement, based on the standard library.POA&M is recorded automatically, and a proposed remediation is drafted for it.Evidence, linked back and marked Implemented.Documentation side. The draft is pulled from the standard library — PPS_Statements, one atomic policy / procedure / standard statement per assessment objective. Pick the statement that satisfies the objective and propose where it goes. The language reads with the client's real values, not placeholders: {{company_name}} and the like resolve automatically, from values mined out of the client's own documents (and flagged for a human to confirm). Based On links the draft back to its source statement, so every recommendation is traceable to library language rather than free-written, and proposed text is never counted as coverage.
Implementation side. When a technical control isn't in place, the recompute opens a POA&M automatically, and a proposer drafts its fix from the data already linked to the failing requirement — target state from Evidence_Requirements, the satisfying tech class from Requirement_Technology_Map, what the client already owns from Client_Technologies, the required value from ODP Values — typed as: apply a setting to a tool you own · acquire & implement one you don't · extend coverage. Both sides are one click on the assessment (Propose remediations / Propose documentation); the drafts land in a review queue, always advisory, never marking anything satisfied on their own.
Assemble the artifact that demonstrates readiness.
SSP, the POA&M, the collected Evidence artifacts, and the SPRS score into one deliverable to self-attest with, or hand a C3PAO.SSP — a first-class implementation-statement layer. Nothing is written from scratch. For every objective, how the control is satisfied is authored as its own implementation statement — seeded from the standard library (PPS_Statements) with the client's real values substituted in, and tied to the governing document and the implementing technology. Each statement carries a Draft → Confirmed status, and that Confirmed lock is the gate: the document renders as a DRAFT (“N of M statements confirmed”) until every assessable objective is Confirmed, then it becomes the Final SSP. It stays fail-closed throughout — unassessed objectives, a MET without evidence, or unsettled scope each hold it in DRAFT — so an incomplete assessment can never be mistaken for a finished one. Exports to Word (.docx) from a one-click Run Action, leading with the real SPRS score and its audit basis.
Package. The client-facing SSP is lean and reads in delivery voice; the evidence and internal workpaper detail split into a companion Objective Evidence Package, with the POA&M as its own spreadsheet. One export bundles them with the SPRS score into a single traceable deliverable — to self-attest with, or hand a C3PAO.
Version control. A finalized SSP freezes into an immutable archive — the exact delivered document, a content hash, and a snapshot of scope, score, and frameworks, kept as a version. Re-freeze next year and the document-to-document diff is the posture-change record: what moved, what newly passed, what regressed.
The loop. New evidence from an adopted recommendation flows back to Phase 4 (re-verify) and Phase 5 (re-score). An unimplemented recommendation stays visibly distinct from a satisfied one — so proposed language can never be mistaken for coverage.
A defensible readiness posture, packaged — a generated SSP, a scored SPRS self-assessment, a POA&M for every remaining gap, and verified evidence behind every MET. Enough to self-attest with confidence, or to hand a C3PAO on day one of a certification assessment.
CMMC 2.0 / NIST SP 800-171 readiness methodology · portable Docker containers on a dedicated VPS · Last updated 2026-08-11 11:13 EDT — Phase 7 matured: the SSP control body is now a first-class implementation-statement layer (Draft→Confirmed gating a DRAFT vs Final document), the client SSP splits into a lean deliverable + Objective Evidence Package + POA&M spreadsheet, and finalized SSPs freeze into an immutable version archive whose year-over-year diff is the posture-change record.