name: map-controls description: Interactive control-to-rule mapping session. Walk through unmapped requirements, suggest rules using cross-framework analysis, and write selections to control files.
Map Controls
Run an interactive mapping session for a control file. Walks through each unmapped requirement, finds candidate rules via cross-framework search and AI suggestions, lets the author select rules, and writes them back to the control file.
This skill orchestrates /inspect-control for setup and /map-requirement logic for each requirement.
Tool Strategy
This skill uses mcp__content-agent__* tools when available (preferred — deterministic, structured results). When the MCP server is not configured, fall back to filesystem-based alternatives noted as Fallback in each step. See .claude/skills/shared/mcp_fallbacks.md for detailed fallback procedures. The skill must complete successfully either way.
Without MCP server: Cross-framework similarity search is unavailable. Candidate rules will be found by keyword search only, which may miss semantically similar but differently-worded requirements.
Arguments: $ARGUMENTS — format: <control_id> [--product <product_id>] [--policy <path>]
Examples:
/map-controls anssi --product rhel9/map-controls srg_gpos --product rhel10/map-controls hipaa/map-controls anssi --product rhel9 --policy security_policies/anssi_2.md
Phase 1: Setup and Validation
Parse arguments: Extract
control_id, optional--product, and optional--policyfrom$ARGUMENTS.Load control stats: Call
mcp__content-agent__get_control_statswithcontrol_id.- If not found, call
mcp__content-agent__list_controlsand present options viaAskUserQuestion. - Fallback: Read
controls/<control_id>.ymlorproducts/**/controls/<control_id>.yml. Count requirements by status manually. List controls withls controls/*.ymlandls products/*/controls/*.yml.
- If not found, call
If no
--productspecified, discover available products and ask user viaAskUserQuestion:- Run
ls products/(or callmcp__content-agent__list_products) to get the list of available products - "Which product are you mapping rules for?"
- Options: present the most common products from the discovered list + Other
- Run
Check build artifacts: Call
mcp__content-agent__list_built_products.- If the target product is NOT in the built list, ask the user via
AskUserQuestion:- "Product '{product}' has not been built yet. Rule search works best with build artifacts (expanded Jinja templates). Build it now?"
- Options:
- "Yes, build now (Recommended)" — description: "Runs
/build-product {product} --datastream-onlybefore starting the mapping session" - "No, continue without build" — description: "Rule search will use raw source files, which may miss some rules due to Jinja template parsing errors"
- "Yes, build now (Recommended)" — description: "Runs
- If user chooses to build, invoke the
build-productskill:Skill(skill="build-product", args="{product} --datastream-only"). Wait for the build to complete before continuing. - Fallback: Check if
build/{product}/rules/directory exists. If not, offer the build prompt above.
- If the target product is NOT in the built list, ask the user via
Report summary:
## Control File: {title} ({control_id}) | Status | Count | |-----------|-------| | Total | {total} | | Mapped | {mapped} | | Unmapped | {unmapped} |Show the
by_statusbreakdown, only including statuses with non-zero counts.If no
--policyspecified, inform the user:Tip: You can enrich mapping with the original security policy document by adding
--policy <path>(supports PDF, Markdown, HTML). This provides full policy context for each requirement, improving cross-framework matching accuracy.Get work queue: Call
mcp__content-agent__list_unmapped_requirementswithcontrol_id. Fallback: Read the control YAML and filter requirements wherestatusispendingorrules:is empty/missing.Ask user scope via
AskUserQuestion:- "Which requirements do you want to work on?"
- Options:
- "Unmapped only ({N} requirements)" — work through the pending/unmapped queue
- "All requirements ({total})" — review every requirement including already-mapped ones
- "Specific requirement IDs" — let user type specific IDs
Phase 2: Per-Requirement Mapping Loop
Note: Do NOT call get_control_rule_index here — it returns a massive payload (1.6MB+) that wastes context. The per-requirement find_similar_requirements call in Step 2b already provides cross-framework discovery for each requirement individually.
For each requirement in the selected work queue, execute the mapping workflow. This follows the same logic as /map-requirement but in batch.
For each requirement:
Step 2a: Present the Requirement
Display:
---
### [{current}/{total}] Requirement: {requirement_id}
**Title**: {title}
**Description**: {description}
**Current status**: {status}
**Current rules**: {rules or "none"}
---
If --policy was provided:
- Infer document type from the file extension (
.md→markdown,.pdf→pdf,.html→html, otherwise →text) - Call
mcp__content-agent__parse_policy_documentwithsource,document_type, andrequirement_idfor the current requirement. Fallback: For markdown/text, read the file and search for the requirement ID. For PDF, inform user that PDF parsing requires the MCP server. - If sections returned, display the policy context:
### Policy Context (from {policy_path}) **{section_title}** {section_content} - Store the combined section text as
policy_textfor use in Step 2b
Step 2b: Cross-Framework Search
Call mcp__content-agent__find_similar_requirements with:
requirement_text: ifpolicy_textis available, use it; otherwise use the requirement's title + " " + descriptionexclude_control_id: current control_idmax_results: 10
Fallback: Extract 3-5 key terms from the requirement text. Use Grep to search for each term across controls/*.yml and products/*/controls/*.yml. Read matched requirements to extract their rules: lists.
If results found, present grouped by framework:
Similar requirements in other frameworks:
- [{control_id}] {req_id}: "{title}" → rules: {rules}
Extract the union of all rules as cross-framework candidates.
Step 2c: Rule Search in Build Artifacts
Search for candidate rules using rendered build artifacts (Jinja-expanded, product-specific):
Extract 3-5 key terms from the requirement title and description.
For each key term, call
mcp__content-agent__search_rendered_contentwith:query: the key termproduct: the target product from Phase 1limit: 15- Fallback: If the product is not built, use
Grepto search for key terms inrule.ymlfiles underlinux_os/guide/andapplications/. This may miss rules with Jinja-templated descriptions.
Deduplicate results across all term searches. Combine with cross-framework candidates from Step 2b.
For each candidate rule, use
mcp__content-agent__get_rendered_rule(orget_rule_details) to read its title and description. Reason about which rules best match the requirement semantically.Present the top candidates in a table:
### Rule Candidates | Rule ID | Title | Source | Reasoning | |---------|-------|--------|-----------| | {rule_id} | {title} | cross-ref / search | {why it matches} |
Step 2d: Product Availability Check
Since search_rendered_content only returns rules present in the target product's build, all search results are already confirmed available. For cross-framework candidates (from Step 2b) that did NOT appear in the rendered search, call mcp__content-agent__get_rule_product_availability to verify availability.
Fallback: Check each rule's rule.yml for cce@<product> entries and grep for the rule ID in target product profiles/controls.
Flag rules NOT available for the target product. Assess portability:
- Templated rules are likely portable
- Check platform constraints if present
Step 2e: Author Decision
Build unified candidate list: combine cross-framework and AI suggestions, deduplicated, sorted by confidence.
Use
AskUserQuestionwithmultiSelect: true:- "Select rules for '{requirement_id}: {title}'"
- Options: top candidates (up to 4), with confidence and source info
- If more than 4 candidates, present top 3 + "Show more candidates"
Use a separate
AskUserQuestion:- "How should this requirement be marked?"
- Options:
- "Automated" — fully covered
- "Partially automated" — partially covered
- "Not applicable" — doesn't apply
- "Create new rule" — will be collected for gap analysis
- "Skip for now"
Step 2f: Write Selection
- If rules selected: Call
mcp__content-agent__update_requirement_ruleswithcontrol_id,requirement_id,rules, and appropriatestatus. Fallback: Find and edit the requirement's YAML file directly usingEdittool. - If not applicable: Call
mcp__content-agent__update_requirement_ruleswith empty rules andstatus="not_applicable". Fallback: Edit the requirement's YAML file to setstatus: not_applicableandrules: []. - If create new rule: Add to the "new rules needed" list for Phase 3.
- If skipped: Record as skipped, move to next.
Track all changes for the final report.
Phase 3: Gap Analysis
For requirements where the author chose "Create new rule":
For each gap requirement, use LLM analysis to:
- Decompose the requirement into atomic checks (one config change per rule)
- Suggest rule IDs following naming conventions (lowercase, underscores)
- Suggest which template might apply (call
mcp__content-agent__list_templatesand match; Fallback:ls shared/templates/) - Suggest severity based on the requirement
Present the decomposition:
### Gap: {requirement_id} — {title} This requirement could be covered by: 1. {suggested_rule_id} (template: {template_name}) — {description} 2. {suggested_rule_id} (template: {template_name}) — {description}Ask user which rules to create via
AskUserQuestion.For each approved new rule, tell the user to run
/create-rule <rule_id>with the suggested parameters after this session completes.
Phase 4: Final Report
Present a complete summary of the mapping session:
## Mapping Session Complete
**Control file**: {control_id} ({title})
**Target product**: {product}
### Changes Made
- {X} requirements mapped to existing rules
- {Y} requirements marked not applicable
- {Z} requirements skipped
### Rules Added
| Requirement | Rules | Status |
|-------------|-------|--------|
| {req_id} | {rules} | automated |
| ... | ... | ... |
### New Rules Needed
| Requirement | Suggested Rules | Template |
|-------------|----------------|----------|
| {req_id} | {rule_ids} | {template} |
### Updated Stats
Call mcp__content-agent__get_control_stats again to show the updated coverage.
Fallback: Re-read the control YAML and recount requirements by status.
| Status | Before | After |
|----------|--------|-------|
| Mapped | {before} | {after} |
| Unmapped | {before} | {after} |
| Coverage | {before}% | {after}% |
### Next Steps
1. Review changes: `git diff controls/`
2. Build product: `/build-product {product}`
3. Create new rules: `/create-rule <rule_id>` for each gap
4. Test rules: `/test-rule <rule_id>`
5. Draft PR: `/draft-pr`
Important Notes
- The
update_requirement_rulestool replaces existing rules — if a requirement already has rules and the author wants to add more, include the existing rules in the selection. - AI suggestions require API key — if
suggest_rule_mappingsfails, fall back to cross-framework search only. - Large control files (e.g., srg_gpos with 300+ requirements) — consider processing in batches. After every 10 requirements, ask if the user wants to continue or pause.
- Track before/after stats — call
get_control_statsat the start and end to show progress. - Don't overwhelm the user — if cross-framework search and AI both return many candidates, focus on the top 5-10 with highest confidence.