How this skill is triggered — by the user, by Claude, or both
Slash command
/xl:create-ruleset-modulesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Reads `skeleton:` and `ruleset_groups:` from `guidance.yaml`, extracts doc signals from `input-sections.yaml`, applies six heuristics to detect ruleset modules, and writes modules to `guidance.yaml` as `ruleset_modules:` after `ruleset_groups:`.
Reads skeleton: and ruleset_groups: from guidance.yaml, extracts doc signals from input-sections.yaml, applies six heuristics to detect ruleset modules, and writes modules to guidance.yaml as ruleset_modules: after ruleset_groups:.
A "module" is a ruleset module — a subset of rules within a ruleset group (ruleset group). Ruleset modules must not cross ruleset group boundaries.
/create-ruleset-modules <domain>
Read ../../core/output-fencing.md now.
Run these checks before doing anything else:
Domain argument provided?
$DOMAINS_DIR/*/ as a numbered menu and prompt:
:::user_input
Available domains:
<domain>. Then continue.Domain folder exists?
guidance.yaml exists?
$DOMAINS_DIR/<domain>/specs/guidance.yamlinput-sections.yaml exists?
$DOMAINS_DIR/<domain>/specs/input-sections.yamlskeleton: key present in guidance.yaml?
ruleset_groups: key present in guidance.yaml?
After pre-flight, check whether ruleset_modules: already exists and is non-empty in guidance.yaml:
ruleset_modules:) are added.Read:
$DOMAINS_DIR/<domain>/specs/guidance.yaml — load skeleton:, ruleset_groups:, and intermediate_variables.categories (for variable names)$DOMAINS_DIR/<domain>/specs/input-sections.yaml — re-run Step 2 signal extraction:
tags: values across all sections; cluster to find prominent domain areasheading: values; reveals statutory structuresummary: values; reveals program scope and terminologycomputations: entries from sections that have the field; trace variable chains (last item in variables list is the output); collect expr_hint values keyed by output variable. If the index has no computations: entries, skip this signal.Do NOT read files under $DOMAINS_DIR/<domain>/input/ — input-sections.yaml is the sole source of doc signals.
In UPDATE mode: display a summary of existing ruleset_modules: as pre-confirmed before scanning for new modules. Include the role: main entry (if present) in the pre-confirmed block — it will not be re-prompted in Step 3:
:::progress Existing ruleset modules (pre-confirmed): [confirmed] earned_income (sub) — Shared earned income computation (reuse_across_entities) [confirmed] deduction_chain (sub) — Sequential deduction chain (depth_threshold) [confirmed] eligibility (main) — AK DOH Earned Income Exclusions Scanning for new modules... :::
Apply the four heuristics in priority order. Each heuristic uses the skeleton: section and the Step 1 signals:
| Priority | Heuristic | Rationale value | Test |
|---|---|---|---|
| 1 | reuse_across_entities | Entity reuse | 2+ entity names in input_variables.categories (or skeleton.inputs) where a common computation prefix applies to each — e.g., client_earned_income and dol_earned_income suggest the same earned_income ruleset module bound to two entities (ClientData, DOLRecord) |
| 2 | policy_structure | Policy section grouping | Named sub-section heading from input-sections.yaml covers ≥3 intermediate variables in skeleton.computations |
| 3 | depth_threshold | Sequential depth | ≥5 variables in skeleton whose names suggest sequential dependence (e.g., after_* chain, net_* ← gross_* ← total_*) |
| 4 | variable_coupling | Coupling clique | ≥3 intermediate variables in skeleton.computations where each references ≥2 of the others' outputs — forming a mutual dependency clique that signals a self-contained computation cluster worth isolating |
| 5 | shared_gate | Co-activation | ≥3 intermediate variables share a common guard-variable prefix (e.g., eligible_*, applies_if_*, qualified_*), suggesting they all fire under the same condition and belong together |
| 6 | user_hint | Pre-existing entries | ruleset_modules: already populated in guidance.yaml — load existing entries as pre-confirmed (UPDATE mode) |
R21 stage-boundary constraint: Every variable in a candidate ruleset module must belong to a single ruleset group (no cross-stage ruleset modules). Infer stage membership by matching variable names and computation categories to stage descriptions and phase heading signals. If a candidate's variables span two groups, either split it into per-stage ruleset modules or reject it with an explanation to the user.
In UPDATE mode: pre-confirmed entries (existing sub-modules and any existing role: main entry) are shown above the table with [confirmed] labels as in Step 1. Only newly detected modules are shown in the table below.
If one or more new modules are detected, display the results table in exactly this format:
:::detail Ruleset Modules ─────────────────────────────────────────────────────────────────────────
1 │ earned_income │ sub │ ClientData, DOLRecord │ reuse_across_entities 2 │ deduction_chain │ sub │ Household │ depth_threshold ───────────────────────────────────────────────────────────────────────── :::
All detected modules are confirmed automatically. Proceed immediately to Step 3.
If zero NEW modules are detected, print:
No new ruleset modules identified.
Existing entries preserved unchanged. and exit without writing.ruleset_modules: [] to guidance.yaml and suggest next step.Skip this step if:
role: main entry already exists (already shown as [confirmed] above)Otherwise, derive the main module name automatically — no prompt:
output_variables.primary.name in guidance.yaml. If present, strip trailing _check, _determination, _result, _outcome, or _eligibility from the value and use the result.template_id and strip leading generic prefixes (calculate-, determine-, check-, compute-).Examples:
primary.name: eligibility_determination → eligibilitytemplate_id: calculate-earned-income-after-exclusions, no primary name → exclusionsPrint the derived name so the user can see what was chosen:
:::important Main module: eligibility (edit guidance.yaml to rename) :::
ruleset_modules:Write all detected new candidates to ruleset_modules:.
Write ruleset_modules: to $DOMAINS_DIR/<domain>/specs/guidance.yaml:
ruleset_groups: and before constraints: (if present), or at end of file if neither followsruleset_modules: with the full final list (existing pre-confirmed + new confirmed); preserve role:, depends_on:, and sample_rules: from existing entries verbatimruleset_modules: []Each confirmed sub-module entry must use this exact YAML format:
- name: <snake_case>
description: "<what this ruleset module computes>"
bound_entities: [<EntityName1>, <EntityName2>]
rationale: <heuristic value, e.g. reuse_across_entities>
depends_on: []
When a main module name was confirmed in Step 3, append the main module entry at the end of the list:
- name: <program_name>
description: "<display_name value from guidance.yaml>"
bound_entities: []
rationale: main_module
role: main
depends_on: [<all sub-module names from this run, comma-separated>]
bound_entities values use CamelCase entity names (e.g., ClientData, DOLRecord, Household) — not snake_case.
Print:
:::important $DOMAINS_DIR//specs/guidance.yaml [UPDATED] :::
Then suggest next steps:
:::next_step Next: Run /extract-sample-rules to extract sample rules. :::
$DOMAINS_DIR/<domain>/specs/guidance.yaml [UPDATED]
$DOMAINS_DIR/<domain>/input/ — input-sections.yaml is the sole source of doc signalsruleset_modules: is inserted after ruleset_groups: and before constraints: in guidance.yaml, not at the end of the file unless no later keys existruleset_modules:ruleset_modules: with the full final list (existing pre-confirmed + new confirmed) — do not append only the new onesruleset_modules: entry must have name, description, bound_entities, rationale, and depends_on — never omit any field; role: defaults to sub when absentrole: main, bound_entities: [], rationale: main_module, and depends_on: listing all sub-module namesrole: main entry when zero sub-modules were detected — Step 3 only runs when at least one sub-module is presentruleset_modules: [] — never omit the key entirelybound_entities values use CamelCase entity names (e.g., ClientData, DOLRecord, Household) — not snake_case; main module always uses bound_entities: []ruleset_modules:, preserve role:, depends_on:, and sample_rules: from existing entries — never strip fields added by a prior runruleset_groups: before ruleset module detection reverses the monolith's Step 4 → Step 5 order. This is intentional: ruleset modules must stay within a single stage.npx claudepluginhub navapbc/lockpicks-xlator-plugin --plugin xlCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.