map-framework-mapping
Skill0 starsChanged 18 days agoApache-2.0View on GitHub
Map generated controls to framework objectives with per-objective analysis, coverage-level scoring, and strict quality gates. Produces map-framework-mapped.json consumed by map-assemble. The hardest sub-skill — requires careful per-objective reasoning, not batch keyword matching.
We split this skill into the rules it gives an agent
38 rules. About 0 are common, 21 are distinctive, and 17 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈21
Distinctive · estimate
This author's own choices. Worth a look.
17
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 33
CCMv4 (guidance file present): run the per-domain fan-out (the service-approval orchestrator dispatches one mapping subagent per domain — …
- Very specific: line 46
If custom_to_ccmv4_anchor.json is present, use it solely to find optional CCMv4 guidance.
- Distinctive: line 79
Use the existing coverage levels honestly: a technical prerequisite supporting an enterprise process is usually Supportive, not Full …
- Very specific: line 177
If .service-approval/<slug>/04-map/applicability-matrix.json exists, load it and apply it as a hard pre-filter.
- Very specific: line 185
Use the entry's reason (and derived_from) as the objective's N/A reason
- Distinctive: line 186
Do NOT attempt to map any controls to it
- Very specific: line 189
Read service_categories from the matrix and carry it forward: when an objective's guidance carries a by_service_category block for one of …
- Distinctive: line 233
Before mapping any control, screen each objective for applicability to the
- Very specific: line 366
Step 2: Build candidate control list: Use framework_keywords from each control to build an initial candidate list.
- Very specific: line 371
Read the security_rationale — does the reasoning chain connect to the objective's requirement?
- Very specific: line 372
Write a 1-sentence rationale explaining the SPECIFIC connection.
- Distinctive: line 395
Keep it to one short sentence.
- Distinctive: line 427
Do NOT banish a redundant twin to an "uncited" / suggestions list just because a sibling already maps.
- Distinctive: line 437
After mapping controls to an objective, assign the objective's overall coverage using these HARD RULES:
- Very specific: line 449
Never assign Fully Covered when all controls are Supportive
- Very specific: line 453
Every MAPPED control row MUST carry a citation object that shows its work — the specific evidence that this control satisfies this …
- Very specific: line 470
When source == "threat_model", copy mitigation_statement verbatim from the control's threaded mitigation_statement field (resolved …
- Distinctive: line 510
All rules below are HARD RULES — violations are validation failures that MUST be fixed before writing output.
- Distinctive: line 517
If any two consecutive objectives in the same domain have the EXACT same set of control IDs mapped, this is a spray signal requiring review.
- Distinctive: line 527
Read each objective's specification — they ask different things
- Distinctive: line 534
If a control appears in more than 15 framework_mapping entries, review every mapping individually.
- Distinctive: line 541
If you cannot write a specific rationale connecting them, REMOVE the mapping
- Distinctive: line 549
Only map to controls that DIRECTLY implement backup, replication, or DR capabilities — NOT generic infrastructure controls.
- Distinctive: line 550
NOT generic infrastructure controls.
- Distinctive: line 553
GRC domain: Only map to controls implementing governance automation (Config rules for compliance, tag policies for classification) — not …
- Very specific: line 568
Every control-to-objective mapping MUST include a rationale string.
- Very specific: line 572
Look up the mapped control_id in the controls
- Distinctive: line 574
If the rationale describes a different control's mechanism, fix the rationale or the control_id
- Very specific: line 579
If more than 5 controls map to a single objective at Full Coverage, review each individually.
- Distinctive: line 583
After building the initial mapping, check for controls not mapped to any objective.
- Very specific: line 584
Read the control's mechanism and security_rationale
- Distinctive: line 585
Search framework objectives for alignment by mechanism (not just domain keyword)
- Distinctive: line 586
If a genuine match exists, add the mapping with a specific rationale
- Distinctive: line 590
Only leave a control orphaned if it truly has no framework objective match its mechanism class satisfies.
- Distinctive: line 607
Use as a STARTING POINT for candidate controls, then apply per-objective evaluation:
- Very specific: line 658
If Rule 7 fails, you MUST complete the review above, repair genuine mappings in the domain partials, and write the remaining reviews as a …
- Very specific: line 662
Do not proceed to assembly until both pass.
- Distinctive: line 780
Quality cap: No control may map to more than 5 objectives.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Service Approval — Map: Framework Mapping
Map each generated control to framework objectives individually. This is the most reasoning-heavy sub-skill in the map pipeline. It produces quality-gated framework mappings with rationale.
Inputs:
.service-approval/<slug>/04-map/map-framework-parsed.json— normalized framework objectives.service-approval/<slug>/04-map/map-controls-generated.json— controls with rich metadatadata/domain-mapping-guidance/<framework>.json— per-domain mapping guidance (CCMv4 today)
Output: .service-approval/<slug>/04-map/map-framework-mapped.json
Schema: schemas/map-framework-mapped.schema.json — encodes the status enum (MAPPED, N/A, or UNCERTAIN), coverage enum, control_id pattern matching map-controls-generated, and per-mapping required fields. See also the "Schema rules" section below.
This is one of 4 focused map sub-skills. Each writes to a separate intermediate state file.
Execution model: per-domain specialization (CCMv4) vs single-pass (others)
Mapping ~197 objectives in one pass produces keyword-driven mis-maps: a control
gets attached to an objective whose words overlap but whose intent differs.
To cut that, CCMv4 is mapped per domain — each domain is handled in a focused
context loaded with only that domain's objectives plus curated guidance on what
each objective actually means and which mechanisms legitimately satisfy it. The
17 per-domain partials are then merged deterministically into the single
map-framework-mapped.json.
- CCMv4 (guidance file present): run the per-domain fan-out (the
service-approval orchestrator dispatches one mapping subagent per domain — see
Step 3c there) and merge with
scripts/merge-domain-mappings.py. The per-objective reasoning rules in Phase 1 and the guidance in Phase 0 apply per domain. - Non-CCMv4 (no
data/domain-mapping-guidance/<framework>.jsonfor the active framework): fall back to the single-pass behavior — map all objectives in one pass using Phases 0–4 below. NIST 800-53 / CIS / ISO have no guidance file yet, so they take this path; advanced-operator--frameworkflags keep working. Map directly to the supplied objective's full requirement. The pinned AWS Threat Model is the primary evidence for the generated control; the customer's framework defines the requirement being evaluated. Set_metadata.framework_mapping_method: "direct_objective_evidence"in the output. Ifcustom_to_ccmv4_anchor.jsonis present, use it solely to find optional CCMv4 guidance. Its scores measure wording similarity, not confidence in a mapping. Neither a low score nor missing CCM guidance makes a custom objectiveUNCERTAIN; neither a high score nor a suggested CCM ID proves coverage. Borrow guidance only after checking that the original custom requirement has the same intent and scope. Never substitute CCM wording, applicability, or coverage decisions for the customer's requirement.
Direct custom-framework evidence contract
For every custom objective, you MUST make an independent applicability and evidence decision using its full specification:
- MAPPED: identify the exact sub-requirement supported, the control's actual
mechanism, and the limits of that support. For an AWS Threat Model-sourced
control, cite its real mitigation alias and matching UUID from the pinned
model, retain the readable source link when available, and copy quoted statements
verbatim. Missing readable links remain disclosed source advisories.
The citation source MUST remain
threat_model; a CCM match is not evidence. - N/A: explain why the requirement is outside the assessed service's control scope. Organizational governance is not automatically covered by a technical configuration. Missing evidence for an applicable requirement is not N/A.
- UNCERTAIN: describe the specific missing or conflicting evidence for the objective-to-control relationship. A weak CCM score alone is not a reason.
AWS documentation verifies implementation feasibility and service behavior.
Controls absent from AWS Threat Models may supplement the primary mapping only
with explicit assessment_gap or aws_doc attribution and a verified AWS
documentation reference_url in their mapping citation. Keep them visibly
separate from AWS Threat Model-backed recommendations; never invent a model
citation for them. Do not force every model mitigation into this framework:
retain genuinely unmatched controls with the existing Rule 7 review.
Use the existing coverage levels honestly: a technical prerequisite supporting an enterprise process is usually Supportive, not Full Coverage. Preserve the citation-as-gate and source-facts/identity checks below. Run an independent semantic review of MAPPED, N/A and UNCERTAIN decisions before assembly; numerical CCM similarity and CCM-only validator skips are not evidence of accuracy.
Pick the path by checking for the guidance file:
FW=ccmv4 # active framework slug (lowercased); from --framework or auto-detect
test -f data/domain-mapping-guidance/${FW}.json \
&& echo "per-domain fan-out (guidance found)" \
|| echo "single-pass fallback (no guidance for ${FW})"Whichever path runs, the output contract is identical — framework_mapping[],
framework_coverage_summary (incl. by_domain), and quality_gate_results in
map-framework-mapped.json, consumed unchanged by map-assemble.py,
validate_state.py::check_framework_mapped, and the schema.
Prerequisites
test -f .service-approval/<slug>/04-map/map-framework-parsed.json && echo "framework: OK" || echo "ERROR"
test -f .service-approval/<slug>/04-map/map-controls-generated.json && echo "controls: OK" || echo "ERROR"
test -f .service-approval/<slug>/04-map/applicability-matrix.json && echo "applicability: OK" || echo "WARN: no matrix — all objectives assumed applicable"Load all inputs:
python3 -c "
import json
fw = json.load(open('.service-approval/<slug>/04-map/map-framework-parsed.json'))
cg = json.load(open('.service-approval/<slug>/04-map/map-controls-generated.json'))
print(f'Framework: {fw[\"framework\"][\"name\"]} — {len(fw[\"framework_objectives\"])} objectives')
print(f'Controls: {len(cg[\"controls\"])}')
# Load applicability matrix if present
import os
matrix_path = '.service-approval/<slug>/04-map/applicability-matrix.json'
if os.path.exists(matrix_path):
mx = json.load(open(matrix_path))
na_count = sum(1 for v in mx['objective_applicability'].values() if not v['applicable'])
print(f'Applicability matrix: {na_count} objectives pre-screened as N/A')
else:
print('Applicability matrix: not present — all objectives assumed applicable')
"Context Available to This Skill
Scope of the KB-dependent rules below: apply curated allow/deny classes, applicability matrices, and per-objective ceilings only to the framework and objective they actually describe. For a custom objective without applicable curated guidance, use the direct evidence contract above. Missing KB classes do not prohibit a mapping, and CCMv4 N/A decisions do not transfer to custom IDs. The generic requirements for truthful coverage, citations, per-objective rationales, source identity, and review of unmatched controls still apply.
For each control, you have access to:
description— what the control doesmechanism— the AWS enforcement mechanismsecurity_rationale— 2-3 sentence reasoning chain from mitigation goal to enforcementframework_keywords— domain/mechanism keywords for candidate matchingmitigation_intent— canonical intent classification (e.g.,cmk_encryption,vpc_placement)security_domain— security concern category (e.g.,data_protection_at_rest,network_isolation)condition_keys,actions,resource_types— technical detailsmitigation_ids,parameters_controlled— traceability
The objective's allow_mechanisms / deny_mechanisms (from the guidance KB) are the
authoritative eligibility gate — one vocabulary, the mechanism classes. A control is
classified into mechanism classes from its mechanism + keywords (the same
classify_control the accuracy validator uses); it may map to an objective only if one
of its classes is in that objective's allow_mechanisms and none is in deny_mechanisms.
The mitigation_intent and security_domain tags are a supporting hint in the same
spirit (a control tagged cmk_encryption should not land on an IAM or network objective),
but they do not override the mechanism-class gate — where they disagree, the KB
allow/deny wins. Use the tags to build candidates fast, then confirm against the
allow/deny classes.
An empty or incorrect classification is a tooling-vocabulary gap, not evidence
that a control has no framework match. You MUST report that gap for correction
and re-review the control against the objective text. Do not disguise a known
classifier omission as a no_framework_match exception. Likewise, membership
in an allowed class only permits a candidate mapping: the rationale must still
show how this particular control satisfies the objective at the claimed level.
The security_rationale field is your SECONDARY context. It explains why this control exists
and what security goal it achieves. Use it to write the mapping rationale — but do NOT use it
to override the eligibility constraint from mitigation_intent.
Pre-Phase: Applicability Matrix Screen (BEFORE per-objective mapping)
If .service-approval/<slug>/04-map/applicability-matrix.json exists, load it and
apply it as a hard pre-filter. The matrix lists only objectives with a
positive structural decision (an N/A, or a capability-unlocked applicable) plus
the service's service_categories; an objective absent from the matrix
defers to the KB default — never force it N/A.
- For each entry in
objective_applicabilitywhereapplicable: false:- Set
status: "N/A"immediately - Use the entry's
reason(andderived_from) as the objective's N/A reason - Do NOT attempt to map any controls to it
- Set
- For entries where
max_coverageis set (e.g.,"Partial Coverage"):- The objective IS mappable, but no control may exceed that coverage ceiling
- Read
service_categoriesfrom the matrix and carry it forward: when an objective's guidance carries aby_service_categoryblock for one of the service's categories, that block'smappable/allow_mechanisms/deny_mechanisms/coverage_ceilingsoverride the objective-level defaults for this run (e.g. LOG-02/LOG-09 aredefault_naforserverless_compute). - For objectives not in the matrix: apply the KB default (backward compatibility).
This screen runs BEFORE Phase 0 (domain-applicability guidance). Where both the matrix and the Phase 0 guidance agree an objective is N/A, use the matrix reason. Where they disagree, the more restrictive rule wins (i.e., if either says N/A, it's N/A).
Mechanism Coverage Ceilings (HARD RULE — applies to ALL objectives)
Full Coverage is opt-in, not the default. Most controls partially satisfy an
objective; a control may claim Full Coverage only when its mechanism class
fully implements the mechanism the objective names. The ceilings are data in the
CCMv4 guidance file (data/domain-mapping-guidance/ccmv4.json), resolved per
mechanism class:
- Per-objective
coverage_ceilings[class](the tightest — e.g. CEK-03 capsat_rest_encryptionat Partial because it needs classification-driven encryption, not merely CMK existence), else - KB-level
mechanism_class_ceilings[class](e.g.misconfig_detectionandchange_controlcap at Partial;tagging_classificationcaps at Supportive; preventive classes such asaccess_control/network_isolation/at_rest_encryptionmay reach Full), else default_coverage_ceiling(Partial Coverage).
A control that matches several allowed classes is judged by its strongest legitimate class. The category override (Pre-Phase step 3) may tighten a ceiling for the service's category.
Key insight: a detective Config rule cannot be Full Coverage for an
enforcement objective — it detects drift but does not prevent the non-compliant
state. tools/validate/validate_domain_mapping_accuracy.py reads these KB
ceilings and FAILs any MAPPED control that exceeds its effective ceiling, so
assign coverage from the KB, not from optimism.
Phase 0: Domain Applicability Screen (run BEFORE per-objective mapping)
Before mapping any control, screen each objective for applicability to the
target service class. Several objectives describe controls a customer literally
cannot act on for a serverless / managed / infrastructure service — mapping any
control to them over-claims coverage. Mark these N/A by default; an override
requires an explicit applicability_justification plus a reference_url.
0.0 CCMv4: consult the per-objective guidance file (authoritative)
For CCMv4 the screen is driven per objective from
data/domain-mapping-guidance/ccmv4.json, NOT by a blunt domain-wide N/A list.
Each objective carries a mappable field and allow_mechanisms /
deny_mechanisms mechanism classes:
mappable→ the objective is legitimately satisfiable by a platform/infra config control for this service class. Map controls whose mechanism is inallow_mechanisms; never map a control whose mechanism is indeny_mechanisms.default_na→ leave the objective N/A with the guidancena_reason, unless the service genuinely supports it — then override toMAPPEDwith both anapplicability_justificationand areference_url.conditional→ mappable only when a genuine service capability backs it; treat likedefault_na(provide the override fields) and map onlyallow_mechanisms-class controls.
deny_mechanisms is the discriminator that prevents keyword mis-maps — e.g.
IVS-07 (secure migration process) denies transit_encryption, so a TLS
control never maps there; LOG-12 (access-event logging) denies
misconfig_detection; STA-12 (supply-chain agreements) denies
access_control (layer sharing). Because this is per-objective, a domain that was
once blanket-N/A can still have a genuinely-mappable objective — e.g. BCR-04
(business-continuity planning) is mappable to multi-AZ resilience controls even
though the rest of BCR (backup, DR exercises) stays default_na.
tools/validate/validate_domain_mapping_accuracy.py enforces this: a control
whose mechanism class is in an objective's deny_mechanisms (and not in
allow_mechanisms), or a default_na objective mapped without the override
fields, is a finding. The blunt mechanism→domain matrix and N/A-by-default list
in 0.1–0.3 below remain the non-CCMv4 fallback and a human-readable summary of
the same intent.
0.1 Mechanism is not the same as application code
The controls this pipeline generates are platform / infrastructure config mechanisms: SCP, RCP, IAM policy, AWS Config rule, CloudFormation Guard, CloudFormation Hook, OPA / Checkov, tag policy, KMS key policy, resource-based policy. None of them inspect a workload's application source code, dependencies, business logic, or runtime behavior — they evaluate IAM requests, resource configuration, or IaC template properties.
CloudFormation Guard, Config rules, SCPs, RCPs, and tag policies MAY satisfy CCC (change control), IVS (infrastructure security), CEK (encryption), LOG, IAM, and GRC objectives. They may NEVER satisfy an application-code objective.
0.2 Mechanism → domain deny matrix (CCMv4)
This matrix is framework-specific (CCMv4) and lives in this sub-skill — it is not a
generic, service-agnostic rule. Consult it during candidate building: if a control's
mechanism is in the denied set for an objective, do NOT map it.
| Mechanism (platform/infra config) | Denied objectives | Why |
| CloudFormation Guard, AWS Config Rule, SCP, RCP, Tag Policy, OPA Policy, IAM policy | AIS-02 (secure development), AIS-04 (secure deployment/testing), AIS-05 (application security testing), and any AIS objective about application code, logic, dependencies, runtime behavior, or security testing | These mechanisms inspect IAM requests, resource configuration, or IaC template properties — never the function's source code, dependencies, or runtime behavior. |
For an infrastructure / platform service, AIS code/testing objectives MUST be N/A
unless a control directly tests application code (none exist for a managed compute
service today). Use reason: "Application-code security testing is the customer's
responsibility; <service> provides no control that inspects function/application
code." (Note: AIS objectives about interface/API authentication that a request-time
control genuinely governs — e.g. an authorizer-config control — remain mappable; the
deny is specific to the application-code/testing objectives.)
0.3 N/A-by-default domains for serverless / managed / infrastructure services
Classify these domains as N/A by default. An objective in one of them may only be
MAPPED if the entry carries BOTH an applicability_justification (one sentence on
why the service genuinely supports it) AND a reference_url (a public AWS doc backing
the justification):
- DCS (Datacenter Security) — physical/datacenter controls are AWS-managed under the shared-responsibility model; the customer cannot act on them for a managed service.
- UEM (Universal Endpoint Management) — endpoint/device management does not apply to a serverless/managed service.
- BCR (Business Continuity & Operational Resilience) — backup/DR-exercise/continuity processes; only map where the service exposes a genuine backup/replication/DR API (multi-AZ is NOT backup, NOT a DR exercise).
- IPY (Interoperability & Portability) — cross-cloud data-portability objectives (e.g. IPY-03) do not apply unless the service provides a portability capability.
- TVM vulnerability-metric objectives (TVM-10-class: CVE counts, vuln metrics, mean time to remediate) — a managed/serverless service does not itself produce vulnerability metrics. Keep TVM-05-style "works-with-Inspector" objectives mappable — pairing the service with Amazon Inspector is legitimate and is NOT screened out.
Existing Rule 4 guidance for HRS, STA, and the organizational GRC objectives still
applies; this screen makes the DCS/UEM/BCR/IPY/TVM-metric set an enforced list rather
than soft guidance.
0.4 Recording the screen
For every N/A-by-default objective you leave N/A, write the reason explaining
non-applicability. For any you override to MAPPED, add the two extra fields on the
objective entry:
{
"objective_id": "BCR-01",
"status": "MAPPED",
"coverage": "Partially Covered",
"reason": "...",
"applicability_justification": "Lambda exposes versioned function code and alias routing that support a documented recovery procedure.",
"reference_url": "https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html",
"controls": [ ... ]
}tools/validate/validate_domain_applicability.py reads map-framework-mapped.json and
flags (a) any platform-config control mapped to a denied application-code objective, and
(b) any N/A-by-default domain objective that is MAPPED without both fields.
Phase 1: Map Controls to Framework Objectives
Iterate over EVERY framework_objectives[] entry. For each objective:
1.1 Per-objective analysis (REQUIRED)
CRITICAL — Do NOT batch-map controls to entire domains. Each individual objective MUST be evaluated separately, even when objectives share a domain. The rationale for mapping CTRL-X to IAM-01 must be different from IAM-02, because the objectives ask different things.
Step 1: Read the objective specification. For each framework objective, read its full title AND specification text. Understand what the objective actually requires — not just which domain it's in.
Step 2: Build candidate control list. Use framework_keywords from each control to build
an initial candidate list. Then read each candidate's security_rationale to assess fit.
Step 3: Evaluate each candidate individually. For each candidate control:
- Does this control's specific mechanism directly address what the objective is asking for?
- Read the
security_rationale— does the reasoning chain connect to the objective's requirement? - Write a 1-sentence
rationaleexplaining the SPECIFIC connection.
Authoring rule — state only what is verified. The rationale must describe the mechanism of the mapped control and the objective it serves. Do NOT make causal claims ("ensures", "guarantees", "prevents all", "completely blocks", "fully mitigates") — those exceed what a single control can be verified to do. If you cannot honestly write a verified mechanism-to-objective sentence, omit the mapping or hedge to a broader objective-level statement and lower the coverage_level accordingly.
Rationale quality rules:
- GOOD: "SCP denies CreateMemory without KmsKeyArn at request time — supports the encryption-key generation objective."
- BAD: "Addresses Encryption and Key Management Policy and Procedures" (just restates the objective title)
- BAD: "Addresses Key Generation" (too vague, no mechanism)
- BAD: "Guarantees no resource is created without encryption" (causal overreach — a single SCP can be bypassed by exempted principals or by service-internal calls)
- The rationale MUST mention the control's mechanism (SCP deny, Config rule check, KMS key policy, etc.)
- The rationale MUST NOT be the objective title or a rephrasing of it
- Keep it to one short sentence; broader, verified statements beat speculative specifics.
Step 4: Assign coverage level based on how directly the control addresses the objective:
- Full Coverage: The control's mechanism directly implements what the objective requires (e.g., KMS key policy -> CEK-10 "Key Generation")
- Partial Coverage: The control contributes to the objective but doesn't fully implement it (e.g., Config CMK check -> CEK-09 "Encryption Audit" — detects but doesn't enforce)
- Supportive: The control is tangentially related through shared domain only (e.g., tag policy -> IAM-03 "Identity Inventory" — tags support inventory but aren't identity management)
Step 5: If no controls have a specific rationale, mark the objective as:
- N/A with a reason (e.g., "Managed by AWS under shared responsibility", "Organisational HR control", "No backup/DR API for this service")
Step 6: Map defense-in-depth twins — do NOT stop at one control per objective.
Multiple control IDs legitimately mapping to one objective is expected, not spray:
a single mitigation is commonly implemented as a preventive/proactive twin (SCP, RCP,
CloudFormation Guard) plus a detective twin (AWS Config rule) plus a responsive twin (SSM
Automation), and each is a distinct layer of defense. When an AWS Threat Model control shares one of
its mitigation_ids with a control you already mapped to an objective, evaluate that
control for the same objective too:
- Map it as its own row with its own
rationaledescribing its mechanism (the Config rule detects drift; the SSM automation remediates it) and the samecitation— copy the shared mitigation'smitigation_alias/mitigation_uuid/mitigation_statement(verbatim) andmitre_techniques[]exactly as on the twin's citation. - Its
coverage_levelis set from its own mechanism class and the KB ceiling, not the twin's — a detective Config rule or responsive SSM automation isPartial CoverageorSupportive, neverFull Coverage, so a defense-in-depth twin rarely trips Rule 6 (≤5 Full Coverage per objective). It maps only if its mechanism class is in the objective'sallow_mechanismsand none is indeny_mechanisms(same eligibility gate as any other control). - Do NOT banish a redundant twin to an "uncited" / suggestions list just because a sibling
already maps. Every AWS Threat Model-sourced control must appear as a MAPPED row under an
objective its mechanism class legitimately satisfies (or, only if genuinely no objective
fits its mechanism, be left unmapped — never dumped to the non-AWS Threat Model suggestions
section). The assemble step lists ONLY non-AWS Threat Model (
trace_source != "threat_model") controls in "Suggestions for Review"; an AWS Threat Model control that reaches no objective is a mapping gap to fix here, not a suggestion.
1.2 Objective coverage rollup (coverage-level consistency)
After mapping controls to an objective, assign the objective's overall coverage using these HARD RULES:
| Highest coverage_level among mapped controls | Objective coverage |
| Full Coverage | Fully Covered |
| Partial Coverage (no Full Coverage) | Partially Covered |
| Supportive (no Partial or Full) | Supportive Only |
| No controls | N/A |
Fully Coveredrequires at least oneFull CoveragecontrolPartially Coveredrequires at least onePartial CoveragecontrolSupportive Onlyis the maximum for objectives with onlySupportivecontrols- Never assign
Fully Coveredwhen all controls areSupportive
1.3 Citation-as-gate (REQUIRED for every MAPPED row)
Every MAPPED control row MUST carry a citation object that shows its work —
the specific evidence that this control satisfies this objective. This is what
converts "silently wrong" into "explicitly uncertain."
"citation": {
"control_id": "CTRL-RES-PRV-001",
"mechanism_class": "at_rest_encryption",
"objective_requirement": "<the specific sub-requirement of THIS objective the control satisfies>",
"mitigation_alias": "Mitigation.LAMBDA.Function.3",
"mitigation_uuid": "…",
"mitigation_statement": "<the mitigation's AWS Threat Model statement, VERBATIM — copy from the control's threaded mitigation_statement, do not paraphrase>",
"mitre_techniques": ["T1530"],
"source": "threat_model" // threat_model | assessment_gap | aws_doc
}When source == "threat_model", copy mitigation_statement verbatim from the
control's threaded mitigation_statement field (resolved deterministically from
the AWS Threat Model export by map-generate-controls). A deterministic validator confirms
this text appears verbatim in the AWS Threat Model export's mitigations[].statement; a
paraphrased or invented statement FAILs the map gate. If you cannot copy the
statement verbatim, omit the mitigation_statement field rather than paraphrase.
Decide per row (the row's status is the signal — there is no separate
confidence score field):
- You can name the concrete linkage (this control's mechanism ↔ this
objective's named sub-requirement, backed by an AWS Threat Model mitigation or an AWS
doc): emit
status:"MAPPED"with the full citation (objective_requirement+sourceare what make it concrete). - You believe there is a relationship but cannot produce that concrete
linkage: DO NOT assert it as MAPPED and DO NOT silently drop it. Emit the row
with
status:"UNCERTAIN"and areview_reasonnaming what linkage is missing. UNCERTAIN rows are surfaced in a "review needed" section, excluded from coverage %, and never block the phase — they are the honest middle between a confident map and an N/A.
validate_domain_mapping_accuracy.py FAILs a MAPPED row that carries no
concrete citation (objective_requirement + source): fix the citation or
demote the row to UNCERTAIN. It does NOT fail an UNCERTAIN row.
1.4 Traceability (attach the AWS Threat Model source + MITRE)
Each control was threaded with mitigation_alias, mitigation_uuid,
mitigation_statement, and mitre_techniques[] in map-generate-controls (Phase
3c, resolved deterministically from the AWS Threat Model source). Copy those onto the
citation so the rendered framework-mapping.md can cite the exact AWS Threat Model
mitigation and MITRE techniques behind the mapping, and so the citation can be
provenance-checked against the export. A control with trace_source:"assessment_gap"
has no AWS Threat Model alias — set source:"assessment_gap" and leave the
mitigation/MITRE fields empty.
Phase 2: Quality Gates (MANDATORY)
All rules below are HARD RULES — violations are validation failures that MUST be fixed before writing output.
Rule 1 — Coverage-level consistency
Already enforced in Phase 1 Step 5. Verify with validation script.
Rule 2 — No domain-level spray mapping
If any two consecutive objectives in the same domain have the EXACT same set of control IDs mapped, this is a spray signal requiring review. If three or more consecutive objectives in the same domain share identical control sets, this is a hard validation failure.
Each objective within a domain asks a different question — the control set SHOULD differ. When objectives share controls, each mapping MUST have a distinct rationale referencing the specific objective requirement. Iteration 15 had 13 spray clusters (e.g., AIS-01 through AIS-06 all mapping to the same 2 controls, BCR-01 through BCR-11 all mapping to 1 control).
How to fix: For each spray cluster:
- Read each objective's specification — they ask different things
- For objectives where the controls genuinely apply, write unique rationales
- For objectives where controls DON'T apply, remove the mapping and mark N/A if no other controls fit
- For domains like BCR (backup/DR) where the service has no relevant API, mark most objectives N/A rather than mapping a single weak control to every objective in the domain
Rule 3 — No single control mapped to >15 objectives
If a control appears in more than 15 framework_mapping entries, review every mapping individually. Most controls are specific enough that they should map to 3-8 objectives. >15 indicates keyword spray, not genuine alignment.
How to fix: For each mapping where this control appears:
- Re-read the objective specification
- Re-read the control's
security_rationale - If you cannot write a specific rationale connecting them, REMOVE the mapping
- Prioritize: keep the mapping where the control's mechanism most directly addresses the objective
- Target: each control should map to 3-12 objectives
Rule 4 — Organizational process domains
Framework objectives about organizational processes MUST NOT be mapped to purely technical controls:
- BCR domain (BCR-01 to BCR-11): Cover backup plans, DR exercises, communication plans. Only map to controls that DIRECTLY implement backup, replication, or DR capabilities — NOT generic infrastructure controls. Multi-AZ != backup. Multi-AZ != DR exercise.
- HRS, DCS, UEM, STA domains: Almost always N/A for cloud service technical controls. DCS/UEM are enforced as N/A-by-default in the Phase 0 screen.
- GRC domain: Only map to controls implementing governance automation (Config rules for compliance, tag policies for classification) — not operational controls.
- AIS domain (application/interface security): AIS objectives about application
code, logic, dependencies, runtime behavior, or security testing (e.g. AIS-02,
AIS-04, AIS-05) MUST be N/A for an infrastructure/platform service unless a
control directly tests application code. CloudFormation Guard, AWS Config rules,
SCPs, RCPs, and tag policies are platform/infrastructure config mechanisms — they
MAY satisfy CCC (change control), IVS (infra security), CEK (encryption), LOG, IAM
objectives, but they may never satisfy an application-code AIS objective (they do
not inspect function source, dependencies, or runtime behavior). Map AIS code/testing
objectives to N/A with reason "Application-code security testing is the customer's
responsibility;
<service>provides no control that inspects function/application code." See the Phase 0 mechanism→domain deny matrix.
Rule 5 — Rationale required and mechanism-consistent
Every control-to-objective mapping MUST include a rationale string. Additionally:
5a — Rationale-control consistency: The rationale MUST describe the mechanism of the MAPPED control, not a different control. After writing each mapping:
- Look up the mapped
control_idin the controls - Confirm the rationale describes THAT control's specific mechanism
- If the rationale describes a different control's mechanism, fix the rationale or the control_id
- Common error: mapping CTRL-RES-PRV-004 (VPC endpoint policy) but writing a rationale about aws:SecureTransport (which is CTRL-RES-PRV-003)
Rule 6 — Max controls per objective at Full Coverage
If more than 5 controls map to a single objective at Full Coverage, review each individually.
Bulk full-coverage mapping indicates overly broad keyword matching.
Rule 7 — Orphaned control review
After building the initial mapping, check for controls not mapped to any objective. For each:
- Read the control's mechanism and
security_rationale - Search framework objectives for alignment by mechanism (not just domain keyword)
- If a genuine match exists, add the mapping with a specific rationale
- An AWS Threat Model-sourced control (
trace_source == "threat_model") that shares a mitigation with an already-mapped control is a defense-in-depth twin, not an orphan — map it to the same objective per Phase 1 Step 6 rather than leaving it orphaned. - Only leave a control orphaned if it truly has no framework objective match its mechanism
class satisfies. A leftover AWS Threat Model orphan is a signal to re-check Step 6 — it must not be
silently dropped or pushed to the non-AWS Threat Model "Suggestions for Review" section (that
section is
trace_source != "threat_model"controls only). - You MUST record each genuinely unmatched control in
unmapped_control_reviews[]:control_id,status: "no_framework_match", a control-specificrationale, and non-emptyreviewed_objective_idsnaming the closest objectives reviewed. Explain why the mechanism does not satisfy those objectives. A valid threat-model mitigation need not fit the customer's selected framework; retain the control without claiming framework coverage. Do not invent a mapping to clear Rule 7. Unexplained omissions, duplicate reviews, and reviews for unknown or already referenced controls fail validation.
Phase 3: Domain Keyword Guide
Use as a STARTING POINT for candidate controls, then apply per-objective evaluation:
| Keywords in objective | Primary domain | Candidate control types |
| Access/Identity/Role/Principal/Auth | IAM | IAM policies, SCPs, permission boundaries |
| Encryption/KMS/CMK/TLS/Certificate | CEK | KMS key policies, encryption configs |
| Network/VPC/Subnet/Endpoint/Firewall | IVS | VPC endpoints, SGs, network policies |
| Logging/CloudTrail/CloudWatch/Audit | LOG | CloudTrail, CloudWatch, logging configs |
| Tagging/Owner/Classification/Governance | GRC | Tag policies, Config rules |
| Config Rule/Compliance | A&A | Config rules, conformance packs |
| Backup/Recovery/Resilience/Multi-AZ | BCR | Only if service has backup/DR API |
| Change/Configuration/Baseline/Drift | CCC | Config rules, Guard rules, drift detection |
| Vulnerability/Patch/Scan | TVM | Vulnerability scanning, patching automation |
| Incident/Forensics/Response | SEF | CloudTrail, alarms, runbooks |
| Data/Privacy/PII/Classification | DSP | Encryption, data lifecycle (see DSP note below) |
| AI/ML/Model/Guardrail/Prompt | AIS | Only secure deployment pattern controls |
DSP data-classification note (DSP-03, DSP-04). These objectives concern the
sensitivity of data (PII, confidential, public) — classifying data, not resources.
A tag-policy / aws:RequestTag / resource-ownership-tag control (e.g. "tag resources as
security-owned vs platform-owned") classifies the resource, not the data inside it,
and does NOT satisfy DSP-03/04. Mark DSP-03/04 N/A unless the service provides a
genuine data-sensitivity classification capability (e.g. Macie-style content
classification) — a managed compute service such as Lambda does not. Do not map a tag /
SCP / Config control to DSP-03/04 on a keyword match for "classification."
Phase 4: Write Output
Per-domain fan-out (CCMv4): each per-domain mapping subagent writes a
partial for its domain to
.service-approval/<slug>/04-map/_domains/<DOMAIN>.json containing only that
domain's objective entries — same per-objective entry shape as framework_mapping[]
below (a bare list, or an object with a framework_mapping key; both are
accepted). It does NOT write map-framework-mapped.json directly. After all
domains complete, the orchestrator runs the deterministic merge:
python3 scripts/merge-domain-mappings.py --slug <slug>The merge concatenates the partials in canonical CCMv4 domain order, recomputes
framework_coverage_summary (incl. by_domain) and the cross-domain quality
gates (Rule 2 spray / Rule 3 ≤15-per-control / Rule 5 rationale / Rule 6 / Rule 7
orphans), verifies every parsed objective appears exactly once, and writes
map-framework-mapped.json. The cross-domain gates run here, not in the
per-domain subagents (a single subagent cannot see another domain's controls).
The merged file is byte-shape identical to the single-pass output below.
The merge writes diagnostic output and exits nonzero when a quality gate fails.
If Rule 7 fails, you MUST complete the review above, repair genuine mappings in
the domain partials, and write the remaining reviews as a JSON array to
04-map/unmapped-control-reviews.json. Re-run the merge (it reads that file
automatically), then validate_state.py on map-framework-mapped.json.
Do not proceed to assembly until both pass. Single-pass mapping includes the
same array directly as unmapped_control_reviews in its output.
Single-pass (non-CCMv4): write
.service-approval/<slug>/04-map/map-framework-mapped.json directly, in this exact
shape:
{
"schema_version": "1.0",
"framework_mapping": [
{
"objective_id": "IAM-01",
"domain": "Identity & Access Management",
"title": "Identity and Access Management Policy and Procedures",
"status": "MAPPED",
"coverage": "Fully Covered",
"reason": "Multiple preventive and detective controls enforce IAM policies for this service",
"controls": [
{
"control_id": "CTRL-ACC-PRV-001",
"coverage_level": "Full Coverage",
"rationale": "Permission boundary restricts service actions to approved set, directly implementing least-privilege access provisioning",
"citation": {
"mechanism_class": "access_control",
"objective_requirement": "provisioning identities with least privilege",
"mitigation_alias": "Mitigation.LAMBDA.Function.6",
"mitigation_uuid": "…",
"mitre_techniques": ["T1078"],
"source": "threat_model"
}
}
]
},
{
"objective_id": "IAM-13",
"domain": "Identity & Access Management",
"title": "...",
"status": "UNCERTAIN",
"coverage": "N/A",
"reason": "Candidate control relates to authorization but the specific sub-requirement linkage could not be evidenced.",
"review_reason": "No AWS Threat Model mitigation or AWS doc concretely ties this control's mechanism to the objective's uniquely-identified-user requirement.",
"controls": [
{
"control_id": "CTRL-ACC-PRV-002",
"coverage_level": "Supportive",
"rationale": "Resource policy scopes principals — may support user identification but the linkage is not concrete.",
"review_reason": "cannot cite the specific sub-requirement satisfied"
}
]
},
{
"objective_id": "DCS-01",
"domain": "Datacenter Security",
"title": "...",
"status": "N/A",
"coverage": "N/A",
"reason": "Physical datacenter security is managed by AWS under the shared responsibility model",
"controls": []
}
],
"framework_coverage_summary": {
"total_objectives": 197,
"mapped": 135,
"not_applicable": 60,
"uncertain": 2,
"by_domain": {
"IAM": { "total": 16, "mapped": 13, "not_applicable": 2, "uncertain": 1, "coverage_percentage": 81.3 },
"CEK": { "total": 21, "mapped": 19, "not_applicable": 2, "uncertain": 0, "coverage_percentage": 90.5 }
}
},
"quality_gate_results": {
"rule_1_coverage_level_consistency": "PASS",
"rule_2_no_spray": "PASS",
"rule_3_max_15_per_control": "PASS",
"rule_4_org_process_domains": "PASS",
"rule_5_rationale_present": "PASS",
"rule_6_max_5_full_per_objective": "PASS",
"rule_7_orphan_review": "PASS",
"orphaned_controls": []
}
}Schema rules:
framework_mappingMUST include ALL objectives (mapped + N/A + uncertain) — every one from map-framework-parsed.json- Each entry MUST have:
objective_id,domain,title,status,coverage,reason,controls statusMUST be EXACTLY"MAPPED","N/A", or"UNCERTAIN"— case-sensitive, no substitutes. Do NOT use"UNMAPPED","NOT_APPLICABLE","n/a", or any other variant.MAPPED= a confident, cited mapping;N/A= not applicable;UNCERTAIN= a believed-but-uncitable relationship, surfaced for review, excluded from coverage %, never blocking. AnUNCERTAINrow MUST carry areview_reason.coverageMUST be one of"Fully Covered","Partially Covered","Supportive Only","N/A"(use"N/A"for UNCERTAIN rows — they do not count toward coverage)controls[]MUST be array of{control_id, coverage_level, rationale}objects; aMAPPEDrow's controls MUST additionally carry a concretecitation(objective_requirement+source; see Phase 1.3) — otherwise the row must beUNCERTAINcoverage_levelMUST be EXACTLY one of"Full Coverage","Partial Coverage","Supportive"— these are the only valid values. Do NOT emit"High","Medium","Low"or any other variant.rationaleis REQUIRED on every control mapping;citationis REQUIRED on every confident MAPPED control (else the row must be UNCERTAIN)framework_coverage_summaryMUST be populated with per-domain breakdown (incl.uncertain)quality_gate_resultsMUST report PASS/FAIL for each ruleorphaned_controlslists control IDs not mapped to any objective
Framework-Agnostic Design
This skill works with ANY control framework, not just CCMv4. The framework objectives
come from map-framework-parsed.json which is produced by the map-parse-framework
sub-skill. Supported formats:
- CCMv4 (Cloud Controls Matrix v4)
- NIST 800-53 rev5
- CIS Benchmarks
- ISO 27001:2022
- Custom frameworks (any Excel/JSON/Markdown with objective IDs + descriptions)
The mapping logic uses the objective's description and control_specification text
to match against control rationale and description. No framework-specific keywords
are hardcoded — the same coverage-level scoring works across all frameworks.
Quality cap: No control may map to more than 5 objectives. If a control matches >5, keep only the 5 highest-coverage-level matches. This prevents spray-mapping where generic controls (like tagging) map to every objective.
Validate Output
Output validation runs automatically via PostToolUse hook. The JSON Schema (see Schema above) hard-fails on status / coverage / control_id drift per the enum details at "Schema rules". validate_state.py::check_framework_mapped retains the 7 quality-gate rules (Rule 1 coverage-level consistency, Rule 2 domain spray, Rule 3 ≤15 objectives per control, Rule 5 rationale required, Rule 6 ≤5 Full Coverage per objective, Rule 7 orphaned controls) plus cross-file lookups against map-controls-generated and map-framework-parsed.
The Phase 0 domain-applicability screen is checked by two CCMv4-specific
validators, both run in the map review gate (Step 3e of service-approval):
tools/validate/validate_domain_applicability.pyflags platform/infra-config controls mapped to application-code AIS objectives (AIS-02/04/05), N/A-by-default domain objectives (DCS/UEM/BCR/IPY, TVM-10-class) mapped without anapplicability_justification+reference_urloverride, and resource-tag controls mapped to data-sensitivity objectives (DSP-03/04). N/A-by-default within that scope is read per-objective fromdata/domain-mapping-guidance/ccmv4.json(so a guidance-mappableobjective such as BCR-04 is no longer blanket-flagged).tools/validate/validate_domain_mapping_accuracy.pyreads the guidance file and the siblingapplicability-matrix.jsonand flags, for every objective: a control whose mechanism class is in the objective'sdeny_mechanisms(and not inallow_mechanisms); adefault_naobjective mapped without the override fields; a row MAPPED for an objective the matrix marked not-applicable for the service's category; a control whosecoverage_levelexceeds the KB ceiling for its class; and a confidentMAPPEDcontrol that carries no concretecitation(which must beUNCERTAINinstead).UNCERTAINrows are advisory — surfaced, never failed.
Print Summary
Map (Framework Mapping) complete:
Framework: {name} — {total} objectives
Mapped: {mapped} ({mapped_pct}%)
N/A: {na}
Uncertain (review needed): {uncertain}
Quality gates: all PASS
Orphaned controls: {count}
Output: .service-approval/<slug>/04-map/map-framework-mapped.jsonIn this repository24 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .github/copilot-instructions.mdCopilot instructions
- assessSkill
- generate-detectiveSkill
- generate-preventive-iamSkill
- generate-preventive-rcpSkill
- generate-preventive-recommendSkill
- generate-preventive-rootcauseSkill
- generate-preventive-scpSkill
- generate-preventiveSkill
- generate-v3Skill
- intakeSkill
- map-assembleSkill
- map-generate-controlsSkill
- map-parse-frameworkSkill
- mapSkill
- research-api-surfaceSkill
- research-attack-surfaceSkill
- research-capabilitiesSkill
- research-mitigationsSkill
- research-v3Skill
- service-approval-v3Skill
- summarize-v3Skill
- validate-v3Skill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

