Skip to content
Back to skills

Goal Mode Tag Discipline

ASecurity

Use when you need exact regex/format details for goal-mode tag emission (parse-tags.mjs semantics, code-fence stripping, attribute quoting, escape-hatch regex, edge cases). Use BEFORE emitting a complex/long verdict or evidence block to verify the tag is parseable.

  • 3 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-airustgo

Works with

  • claude code
  • cursor
  • terminal
  • cli

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add lokafinnsw/claude-code-goal-mode --skill goal-mode-tag-discipline --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Goal Mode Tag Discipline?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Goal Mode Tag Discipline
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lokafinnsw-goal-mode-tag-discipline/badge)](https://www.skillsdirectory.com/skills/lokafinnsw-goal-mode-tag-discipline)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: goal-mode-tag-discipline
description: Use when you need exact regex/format details for goal-mode tag emission (parse-tags.mjs semantics, code-fence stripping, attribute quoting, escape-hatch regex, edge cases). Use BEFORE emitting a complex/long verdict or evidence block to verify the tag is parseable.
---

## v3.0 Status: Optional / Legacy Path

In v3.0+, **tag emission is OPTIONAL**. The preferred path is explicit CLI verbs:

- `<evidence/>` tag → `/goal-mode:goal-evidence-add --criterion N --file path[:line] --note "..."`
- `<task-status>achieved</task-status>` → `/goal-mode:goal-achieve`
- `<audit-verdict agent="X" status="GO">text</audit-verdict>` → `/goal-mode:goal-submit-verdict --agent X --status GO --text "text"`
- `<review-request agents="X"/>` → `/goal-mode:goal-review-request` then dispatch via Agent tool

The tag-emission path described below remains authoritative for:
- Codex `/goal` and non-Claude-Code agents that can't invoke slash commands.
- Users who opt in to legacy v2 driver behaviour via `stopHookDriver: true` config.

For all other Claude Code users in v3.0+, prefer the explicit CLI workflow — it removes the entire class of bugs around tag parsing, fenced-code stripping, attribute quoting, and silent-loop traps that this document spends most of its space describing.

---

# Goal-mode tag emission — exact semantics

This skill is the precise reference for the parser in `engine/parse-tags.mjs`. Use it when you need to know:

- Which exact regex matches a tag
- How code regions are stripped (`engine/stop-hook.mjs::stripCodeRegions`)
- What attribute quoting rules apply
- What the parser silently drops
- How escape-hatch is detected

For high-level behavior + when-to-emit guidance, use the `using-goal-mode` skill instead.

## Pre-parse: code-region stripping

Before parsing tags, `engine/stop-hook.mjs` strips:

```js
text.replace(/```[\s\S]*?```/g, '')   // fenced blocks (multiline)
    .replace(/`[^`\n]+`/g, '');       // inline spans (single line)
```

Implication:
- Tags inside ``` fenced blocks ARE removed before parsing.
- Tags inside `inline backticks` ARE removed before parsing.
- Tags inside `<details>...</details>` are NOT stripped — these are HTML, not Markdown code.

**Rule for emission:** put your machine-parsed tags in prose or inside `<details>` blocks. Never inside code fences.

## Tag inventory + exact regexes

### `<evidence>`

```
Self-closed:  <evidence file="..." line="N" criterion="i" note="..." command="..." exit_code="N"/>
Paired:       <evidence ...>note-body</evidence>
```

Regex: `<evidence\b(${ATTRS_REGION})(?:\/>|>([\s\S]*?)<\/evidence>)` (global)

Where `ATTRS_REGION = (?:"[^"]*"|'[^']*'|[^>"'])*?` — matches attribute name=value pairs in any order with double OR single quoting.

**Validation:**
- `criterion="N"` — integer required. Missing or non-integer → tag silently dropped.
- `line`, `exit_code` — optional integers. `intOrNull(v)` returns null on missing/non-int.
- `file`, `command`, `note` — optional strings, default null/empty.
- Body wins over `note` attribute: `note = body.trim() || attrs.note || ''`.

**Out-of-range criterion:** if `criterion >= acceptance_criteria.length`, the tag is recorded in cursor's evidence list but doesn't count toward coverage. The criterion at index N must exist in the task.

### `<task-status>`

```
<task-status>pursuing|achieved|blocked</task-status>
```

Regex: `<task-status>([\s\S]*?)<\/task-status>` (global)

**Validation:**
- Value is **case-insensitive (v2.0.3+)**: trimmed + lowercased → must be `pursuing`, `achieved`, or `blocked`.
- Pre-v2.0.3: case-sensitive (silently dropped `ACHIEVED`).
- Unknown values: silently dropped.

**Multi-tag:** if multiple `<task-status>` tags are emitted in one turn, `applyMutations` uses `tags.find(t => t.kind === 'task-status')` — first one wins.

### `<blocker>`

```
<blocker>reason text</blocker>
```

Regex: `<blocker>([\s\S]*?)<\/blocker>` (global)

**Validation:**
- Empty / whitespace-only reason → tag silently dropped.
- Only consumed when paired with `<task-status>blocked</task-status>`.

### `<review-request>`

```
<review-request agents="reviewer-1,reviewer-2"/>
```

Regex: `<review-request\b(${ATTRS_REGION})\/>` (self-closed only)

**Validation:**
- `agents` attribute required; comma-separated list, each trimmed; empty strings filtered out.
- Empty agents list → tag silently dropped.
- **Paired form NOT supported** — must be self-closed.
- Triggers `pursuing → review-pending` transition ONLY when all criteria covered AND task is currently `pursuing`.

### `<audit-verdict>`

```
<audit-verdict agent="reviewer-x" status="GO|NOGO|REVISE">
  <verdict body text>
</audit-verdict>
```

Regex: `<audit-verdict\b(${ATTRS_REGION})>([\s\S]*?)<\/audit-verdict>` (global)

**Validation:**
- `agent` attribute required (non-empty string).
- `status` attribute required; **case-normalized to UPPERCASE** before enum check; must be `GO`, `NOGO`, or `REVISE`.
- Missing/unknown agent or status → silently dropped.

**Reviewer-independence check (v2.0.0+):** the engine reads the same turn's transcript and collects `Agent(subagent_type=X)` invocations. If `agent` in the verdict doesn't match any actually-dispatched subagent_type, the verdict is rejected (`payload.rejected: true`, `payload.reason: 'no Agent dispatch detected — reviewer-independence violation'`) and does NOT advance the cursor.

## Escape-hatch detection (v2.0.1 + v2.0.4)

Specific to `<audit-verdict>` when the reviewer's subagent_type is unavailable:

```
Pattern: status="REVISE" AND text matches /^\s*unavailable\b/i
```

Code: `engine/apply-mutations.mjs`:
```js
const ESCAPE_HATCH_RE = /^\s*unavailable\b/i;
const isEscapeHatch = (v) => v.status === 'REVISE' && ESCAPE_HATCH_RE.test(v.text || '');
```

**Examples that match:**
- `<audit-verdict status="REVISE">unavailable; user must run /goal-approve</audit-verdict>` ✓
- `<audit-verdict status="REVISE">UNAVAILABLE in environment</audit-verdict>` ✓
- `<audit-verdict status="REVISE">  unavailable, please approve</audit-verdict>` ✓ (leading whitespace OK)

**Examples that DON'T match:**
- `<audit-verdict status="NOGO">unavailable evidence</audit-verdict>` ✗ (wrong status)
- `<audit-verdict status="REVISE">timing data is unavailable</audit-verdict>` ✗ (substring, not prefix)
- `<audit-verdict status="REVISE">Couldn't dispatch the agent</audit-verdict>` ✗ (doesn't start with "unavailable")

**What escape-hatch does:**
- Cursor → `blocked` immediately
- State.lifecycle → `awaiting-manual-approval` (v2.0.4)
- `cursor.blocker_reason` filled with the unavailable agent names + recovery hint
- Stop hook renders `continuation-blocked.md` ONCE, then suppresses

## Attribute parsing nuances

### Quoting

```
file="path/with spaces.ts"    ✓  (double quotes)
file='path/with spaces.ts'    ✓  (single quotes)
file=path-no-quotes           ✗  (silently parses as empty)
note="contains a > char"      ✓  (attr-region matcher is quote-aware)
note="contains "quoted" text" ✗  (embedded `"` breaks parsing; use single quotes around the value)
note="contains 'quoted' text" ✓  (mix)
```

### Duplicate attributes

```
<evidence file="a" file="b" criterion="0"/>
```
Last-wins semantics: `file="b"`.

### Numeric attributes

`criterion`, `line`, `exit_code` — parsed via `intOrNull(v)`:
- `""` (empty) → null
- Non-numeric → null
- Negative integers ACCEPTED at parse time (range-checking is the consumer's job; out-of-range `criterion` is silently filtered from coverage)

### HTML escapes

NOT decoded. `&lt;tag&gt;` passes through as-is. If your verdict body contains `<` or `>`, just write them — the attr-region matcher and body regex are quote-aware.

### Nested tags

The parser is flat. Don't nest `<evidence>` inside `<evidence>` or `<audit-verdict>` inside `<audit-verdict>`. The outer parse will consume up to the first matching close tag, leaving the inner tag orphaned.

## Tag visit order (within one turn's parse output)

`parseTags()` emits tags in this order:

1. All `<evidence>` (in source order)
2. All `<task-status>` (in source order)
3. All `<blocker>` (in source order)
4. All `<review-request>` (in source order)
5. All `<audit-verdict>` (in source order)

`applyMutations` then processes them in this order with kind-specific rules:

1. **Evidence loop** — all `<evidence>` tags push onto cursor's evidence array.
2. **Task-status** (first only via `find()`) — `achieved` checks coverage, transitions to `review-pending` or `achieved`. `blocked` increments `review_attempts`. `pursuing` resets to pursuing.
3. **Review-request** — if cursor is pursuing AND all criteria covered, transitions to `review-pending`.
4. **Audit-verdicts** — only consumed when cursor is `review-pending`. Filter by reviewer-independence (v2.0.0+) and escape-hatch (v2.0.1+).
5. **Terminal lifecycle checks** — `achieved` / `unmet` / `budget-limited` transitions.

## What happens to malformed tags

| Defect | Result |
|---|---|
| `<task-status>frobnicate</task-status>` | Silently dropped (not in `STATUS_VALUES`) |
| `<evidence note="no criterion"/>` | Silently dropped (criterion required) |
| `<evidence criterion="abc"/>` | Silently dropped (non-integer) |
| `<evidence criterion="0" file="x">body</evidence>` with empty body and no note attr | Recorded with `note=""` |
| `<audit-verdict agent="x">no status</audit-verdict>` | Silently dropped (status required) |
| `<audit-verdict status="GO">no agent</audit-verdict>` | Silently dropped (agent required) |
| `<review-request/>` (no agents) | Silently dropped |
| `<blocker></blocker>` (empty) | Silently dropped |

Silent drops are intentional — the parser is fail-permissive so a single malformed tag doesn't break a multi-tag turn. The cost: no error message tells you what was dropped. **Always preview your emission by re-reading the prose you generated, looking specifically at each tag.**

## Common emission mistakes

### Mistake: tags inside fenced code

```markdown
Here's my evidence:
\`\`\`
<evidence file="a" criterion="0"/>
<task-status>achieved</task-status>
\`\`\`
```

→ `stripCodeRegions` removes the entire fenced block before parsing. Tags lost. Engine fires same prompt next turn.

### Mistake: paired review-request

```html
<review-request agents="x">narrative text</review-request>
```

→ Parser only accepts self-closed form. Tag dropped.

### Mistake: status string case

Pre-v2.0.3:
```html
<task-status>Achieved</task-status>
```
→ Silently dropped.

v2.0.3+: normalized to `achieved`, accepted.

### Mistake: fabricated audit-verdict

```html
<audit-verdict agent="my-reviewer" status="GO">trust me, the code is correct</audit-verdict>
```

Without an `Agent({subagent_type: "my-reviewer", ...})` call in the same turn's transcript:
- v1.x: would have been accepted (bug).
- v2.0.0+: rejected with `payload.rejected: true`. Cursor doesn't advance. Engine surfaces "rejected verdicts" in the next Stop-hook prompt.

### Mistake: wrong escape-hatch wording

```html
<audit-verdict agent="x" status="REVISE">cannot dispatch this reviewer</audit-verdict>
```

→ Text doesn't start with "unavailable". Treated as a regular REVISE → fabricated (no Agent dispatch) → rejected. Does NOT trigger the v2.0.4 escape-hatch path.

Correct:
```html
<audit-verdict agent="x" status="REVISE">unavailable; user must run /goal-approve</audit-verdict>
```

## When to use this skill

Invoke `goal-mode-tag-discipline` skill:

- Before emitting a complex verdict (especially when relaying long subagent output through `<audit-verdict>` body).
- When you've emitted tags but the engine fired the same prompt again — your tags may have been silently dropped; check the rules here.
- When constructing the escape-hatch verdict.
- When designing prompt templates for reviewer subagents — to know what format their output should take so it can be relayed cleanly.

Companion skill `using-goal-mode` covers the high-level behavior (when to do what, lifecycle states, recovery paths).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.