| name | bluespec.harden |
| description | Apply the defense plan's fixes to the code, safely and one at a time, then record what was applied and what is left. Reads the defense plan and the charter, then hardens every fix, just the fixes, files, or directories you name, or the priorities you choose. |
| argument-hint | Optionally name fixes, files/dirs, or priorities, or leave empty to apply every fix |
| user-invocable | true |
User Input
$ARGUMENTS
The User Input above decides how this command runs. Read it before proceeding.
Outline
You are applying the defense plan to the code and producing a hardening record at .bluespec/memory/harden.md: a record of which plan fixes were applied, what changed in the code, and what is still left. This phase continues from plan. Plan already decided the fix for each finding and its priority, so this phase does not re-decide the fix: it applies it. Every change you make must come from a fix that is really in the defense plan, never a fix you invent here. This is the one phase that changes the user's code, so it is the one that must be the most careful.
The defense plan is the primary input: the changes follow from the fixes it lists. The charter is the governing context: its principles still bind what you do, and a fix must never be applied in a way that breaks one.
Step 1: Decide the scope from the input
This phase is built entirely on the defense plan. It applies fixes the plan already decided. The User Input above selects one of three modes:
- No input (the User Input is empty): apply every fix in the defense plan, highest priority first. This is the full hardening pass across the planned context.
- Fixes, files, or directories given (the input names one or more fix names from the plan, or paths in the project): apply only the matching fixes. Match a name to the plan's fix names, and match a path to the fixes through the tracking map (
.bluespec/tracking.json), which records the paths each fix points at. Leave the rest of the plan untouched.
- One or more priorities chosen (the input names priorities, for example
High, or High and Medium): apply only the fixes the plan gave any of those priorities. The pass covers those priority bands, not the whole plan.
If the input is ambiguous, prefer the most literal reading (an existing path is a path, a known fix name is a fix), and state which mode you chose before continuing.
Step 2: Load context
- Load the defense plan at
.bluespec/memory/plan.md. This is the required primary input.
- If it does not exist, stop and tell the user to run
/bluespec.plan first. There is nothing to apply without it. Do not invent fixes to work around a missing plan.
- If it exists, read its fixes. Each fix's name, priority, the principle it upholds, and the finding it points at is what you apply. Apply only fixes that are really in the plan.
- Check that the scope is covered by the plan before applying:
- If nothing in the scope you chose is in the plan (the named fixes match no fix, the named paths match no fix's finding evidence, or no fix carries any of the named priorities), stop and tell the user to run
/bluespec.plan on that scope first, then run the harden phase again. Do not apply a change the plan did not call for.
- If the scope is partly covered, apply the fixes that are in the plan, and record each uncovered part under Remaining. Do not stop, and do not invent a fix for the uncovered part.
- Load the hardening record at
.bluespec/memory/harden.md.
- If it does not exist, initialize it from the template at
.bluespec/templates/harden-template.md first, and identify every placeholder token of the form [ALL_CAPS_IDENTIFIER].
- If it already exists, read what was applied before. You will reconcile it in Step 3 before applying anything new. Each applied block's identity is the plan fix it points at plus the change it records.
- Load the charter at
.bluespec/memory/charter.md for the governing principles, if it exists. The principles still bind here: never apply a fix in a way that breaks one. If the charter does not exist, apply the fixes from the plan alone.
Step 3: Reconcile the existing record
This record is reconciled, never append-only. If the record was empty or freshly initialized, skip this step. Otherwise, before applying anything new, re-check each existing applied block against the current code:
- Still holds: the change is still in the code and the plan still carries its fix. Keep it, and update its wording or evidence if the code moved on.
- Reverted or dropped: the change is no longer in the code, or the plan no longer carries that fix. Remove the block from the record. Do not keep stale entries for history. The record reflects the code as it is now.
- Only re-check blocks that fall within the current scope. A block outside the scope you are running stays untouched.
A block whose finding verify already proved closed was stood down out of the whole chain, so it is simply absent here. That absence is expected, not a broken chain, and is no reason to run repair.
If reconciling reveals the chain is inconsistent (for example the tracking map points a control at a file that was renamed or moved, so the path on record no longer exists), run /bluespec.repair and then continue. Repair fixes Blue Spec's internal tracking across every phase at once, so the chain stays coherent. Do not try to repair the tracking yourself: this phase reconciles its own record in prose, repair owns the tracking.
Step 4: Apply the fixes, safely and one at a time
This is the one place Blue Spec changes the user's code. Work carefully:
- Confirm before you change code. This phase edits the project. Before applying, show the user the fixes you are about to apply, in plain language, and ask them to confirm. If they decline a fix, skip it and record it under Remaining. Never apply a destructive or irreversible change (deleting data, dropping a table, rewriting history) without calling it out explicitly and getting a clear yes.
- One fix at a time, highest priority first. Apply fixes in priority order (Critical, then High, then Medium, then Low). Apply each fix on its own so each change stays small, reviewable, and easy to undo.
- Apply the smallest change that holds the control. Make the fix the plan described, nothing more. Do not refactor unrelated code, do not add features, do not widen the change beyond the fix.
- Stay safe-by-default. The change must leave the project safe out of the box. Never weaken an existing control to apply a new one, and never break a charter principle to satisfy a fix. If a fix would conflict with a principle, stop and surface the conflict to the user rather than applying it.
- Use real, current dependencies when one is needed. If a fix needs a library, prefer the project's existing tools, and when a new one is genuinely needed, pin a real, maintained version and note it. Confirm the version against current documentation rather than memory.
- When a fix cannot be fully applied, apply what you safely can and record the rest. Mark the block
Partial with what is left, or Blocked with the reason, and leave the plan fix open. Do not force a change you are unsure of.
Step 5: Fill the template
- Replace every placeholder with concrete text. Leave no bracket tokens behind.
- Set
Scope to reflect the mode you ran: all fixes, the named fixes or paths, or the chosen priorities.
- Merge, do not overwrite. The record now holds the reconciled blocks from Step 3 plus what you applied in Step 4. Add a new block only for a fix you actually applied this run, not already represented. Do not duplicate a block that is already there under a different wording.
- Write one block per applied finding, titled with the finding's name (verbatim from the plan and detect, no action prefix). Each block MUST carry these parts:
- Status: one of
Applied, Partial, or Blocked.
- Applied: the control is fully in place in the code.
- Partial: some of the control is in place, the rest goes under Remaining.
- Blocked: the control could not be applied, with the reason, and the plan fix stays open.
- What changed: the change made, in plain language any reader understands.
- Where: a short, plain-language note of where the change landed (the function or area) and any dependency added, so the change can be reviewed and the next phase can verify it. Do not write the file path here. The path goes to the tracking map in Step 7, where reconcile keeps it current through a rename.
- The template ships with three starter blocks. This is a starting point, not a limit. Add or remove blocks so the record matches what was actually applied.
- Record anything left under Remaining: the rest of a Partial fix, a Blocked fix and why, a fix the user declined, or a fix this run did not reach. Name the plan fix each item belongs to. Remove that section if every in-scope fix is fully
Applied.
- Set
Hardened to today's date in ISO format YYYY-MM-DD.
Step 6: Validate before writing
- No bracket tokens remain.
- Every block is titled with a finding name that really exists in the defense plan, and carries Status, What changed, and Where.
- No block for a finding the plan no longer carries, or a change no longer in the code, is still in the record, and no block is duplicated.
- Every
Status is one of Applied, Partial, or Blocked, and every Partial or Blocked fix has a matching entry under Remaining.
- No applied change breaks a charter principle or weakens an existing control.
- The
Scope line matches the mode you actually ran.
- The date is ISO
YYYY-MM-DD.
Step 7: Write and summarize
- Write the reconciled record to
.bluespec/memory/harden.md.
- Register the items you hardened so the tracking map keeps each one current. This is registration, not reconciliation: hand the track hook only the findings this run hardened, as the
entries list of {name, paths}, named by the finding's name (your section title, the same name the plan used). The hook finds that same item by name and updates its paths. It does not create a second entry: one finding is one item. name is the finding's name, and paths holds the file paths the change landed in. The path lives here, in tracking, never in the prose. Run it from the project root, passing the payload as the single argument:
node ./.bluespec/hooks/track.mjs '{"entries":[{"name":"<FINDING NAME>","paths":["<PATH>"]}]}'
Do not edit .bluespec/tracking.json yourself, and do not reconcile here. Track only advances what this phase hardened. Repairing the map across the conveyor stays with /bluespec.repair.
- Output a short summary to the user:
- The scope you ran (all fixes, named fixes or paths, or priorities).
- The fixes applied, each with its status and a one-line note of what changed, highest priority first.
- What changed since the last run: blocks added, blocks removed because the fix is gone or the change was reverted, and blocks updated.
- Anything left under Remaining, including any part of the scope the plan did not cover, any fix the user declined, and any Blocked fix.
- A suggested commit message, for example
fix: apply security hardening from the defense plan.
- Next step: point the user to
/bluespec.verify, the phase that proves each applied control actually holds. Make clear the applied controls are not yet proven until verify confirms them. Frame it as the recommended next step. If anything is left under Remaining (a Blocked fix, a part the plan did not cover, or a fix the user declined), name what is still open so the user can decide whether to revisit /bluespec.plan or rerun /bluespec.harden before verifying.
Keep the record in plain language throughout. A non-developer should understand what was changed and what is still open, while the Where note stays precise enough that, with the path from the tracking map, /bluespec.verify can act on it.