feat(ultraplan-local): v2.3.1 — qualified slug convention for cc-architect-catalog

Resolves v2.3.0 dogfood collision: skill-factory produced a
specialized hooks-pattern.md draft that would have overwritten the
generic seed. Qualified slugs let one feature host multiple named
patterns at different abstraction levels.

Slug convention: <cc_feature>[-<qualifier>]-<layer>.md. Unqualified =
canonical baseline. Qualified = sub-pattern (e.g., hooks-observability-
pattern.md) that does not displace the baseline.

Changes:
- SKILL.md: slug convention section, coverage-table qualified column,
  matcher logic for N patterns per feature, modification rules cover
  qualified-vs-canonical choice and collision handling.
- feature-matcher: catalog map is cc_feature -> {layer -> [skills]};
  selection rules (baseline by default, qualified when justified,
  multi-skill when non-overlapping); supporting_skill accepts list.
- gap-identifier: adds pattern_count[cc_feature] to coverage audit.
- architecture-critic: supporting-skill verification — every cited
  skill name must exist in the catalog (blocker severity).
- First qualified skill: hooks-observability-pattern.md (promoted from
  .drafts/, source ai-psychosis/README.md, ngram-overlap 0.01).
- Version bump 2.3.0 -> 2.3.1 across plugin.json, badges, table, root
  CLAUDE.md, CHANGELOG.

Non-breaking: existing unqualified slugs keep working, no cc_feature
taxonomy changes, hallucination gate unchanged.
This commit is contained in:
Kjell Tore Guttormsen 2026-04-18 17:53:55 +02:00
commit 4bbd17cbfa
11 changed files with 232 additions and 38 deletions

View file

@ -12,7 +12,7 @@ plugins/
llm-security/ v6.0.0 — Security scanning, auditing, threat modeling llm-security/ v6.0.0 — Security scanning, auditing, threat modeling
ms-ai-architect/ v1.8.0 — Microsoft AI architecture (Cosmo Skyberg persona) ms-ai-architect/ v1.8.0 — Microsoft AI architecture (Cosmo Skyberg persona)
okr/ v1.0.0 — OKR guidance for Norwegian public sector okr/ v1.0.0 — OKR guidance for Norwegian public sector
ultraplan-local/ v2.3.0 — Brief, research, architect, plan, execute (five-command pipeline + skill-factory Fase 1) ultraplan-local/ v2.3.1 — Brief, research, architect, plan, execute (five-command pipeline + skill-factory Fase 1)
``` ```
Hvert plugin er selvstendig med egen CLAUDE.md, README, hooks, agents og commands. Hvert plugin er selvstendig med egen CLAUDE.md, README, hooks, agents og commands.

View file

@ -61,7 +61,7 @@ Key commands: `/config-audit posture`, `/config-audit feature-gap`, `/config-aud
--- ---
### [Ultra {brief | research | architect | plan | execute} - local](plugins/ultraplan-local/) `v2.3.0` ### [Ultra {brief | research | architect | plan | execute} - local](plugins/ultraplan-local/) `v2.3.1`
Deep requirements gathering, research, Claude Code feature matching, implementation planning, self-verifying execution, and skill-factory authoring with specialized agent swarms, adversarial review, IP-hygiene scoring, and failure recovery. Deep requirements gathering, research, Claude Code feature matching, implementation planning, self-verifying execution, and skill-factory authoring with specialized agent swarms, adversarial review, IP-hygiene scoring, and failure recovery.
@ -76,7 +76,7 @@ Five core commands plus an authoring command, one pipeline with clear division o
All artifacts land in one project directory: `.claude/projects/{YYYY-MM-DD}-{slug}/` contains `brief.md`, `research/NN-*.md`, `architecture/` *(v2.2)*, `plan.md`, `sessions/`, and `progress.json`. `--project <dir>` works across `/ultraresearch-local`, `/ultra-cc-architect-local`, `/ultraplan-local`, and `/ultraexecute-local`. All artifacts land in one project directory: `.claude/projects/{YYYY-MM-DD}-{slug}/` contains `brief.md`, `research/NN-*.md`, `architecture/` *(v2.2)*, `plan.md`, `sessions/`, and `progress.json`. `--project <dir>` works across `/ultraresearch-local`, `/ultra-cc-architect-local`, `/ultraplan-local`, and `/ultraexecute-local`.
v2.3 (non-breaking) ships the skill-factory Fase 1 MVP: `/ultra-skill-author-local` plus four supporting agents (1 opus orchestrator + 3 sonnet workers) and `scripts/ngram-overlap.mjs` (pure Node stdlib, word-5-gram containment + longest-run secondary signal, calibrated against three source/draft fixture pairs). Catalog growth is now tractable without touching the architect's hallucination gate. Non-goals stay explicit: no automation, no batch, no decision-layer skills, no remote sources — manual `mv` from `.drafts/` to catalog root is the promotion mechanism. v2.3 (non-breaking) ships the skill-factory Fase 1 MVP: `/ultra-skill-author-local` plus four supporting agents (1 opus orchestrator + 3 sonnet workers) and `scripts/ngram-overlap.mjs` (pure Node stdlib, word-5-gram containment + longest-run secondary signal, calibrated against three source/draft fixture pairs). Catalog growth is now tractable without touching the architect's hallucination gate. Non-goals stay explicit: no automation, no batch, no decision-layer skills, no remote sources — manual `mv` from `.drafts/` to catalog root is the promotion mechanism. v2.3.1 adds a qualified-slug convention (`<cc_feature>[-<qualifier>]-<layer>.md`) so one feature can host multiple named patterns at different abstraction levels without displacing the baseline — resolved a collision surfaced in v2.3.0 dogfood.
v2.2 (non-breaking) adds the optional `/ultra-cc-architect-local` step between research and planning. The architect phase is backed by a versioned catalog skill (`cc-architect-catalog`) with 10 seed entries across three layers (reference, pattern, decision). Gaps are captured as issue-ready drafts so the catalog grows from real usage rather than speculation. `/ultraplan-local` auto-discovers the architecture note — existing pipelines keep working unchanged. v2.2 (non-breaking) adds the optional `/ultra-cc-architect-local` step between research and planning. The architect phase is backed by a versioned catalog skill (`cc-architect-catalog`) with 10 seed entries across three layers (reference, pattern, decision). Gaps are captured as issue-ready drafts so the catalog grows from real usage rather than speculation. `/ultraplan-local` auto-discovers the architecture note — existing pipelines keep working unchanged.

View file

@ -1,7 +1,7 @@
{ {
"name": "ultraplan-local", "name": "ultraplan-local",
"description": "Five-command context-engineering pipeline (brief → research → architect → plan → execute) with project folders, CC-feature matching, specialized agent swarms, external research triangulation, adversarial review, session decomposition, and headless execution.", "description": "Five-command context-engineering pipeline (brief → research → architect → plan → execute) with project folders, CC-feature matching, specialized agent swarms, external research triangulation, adversarial review, session decomposition, and headless execution.",
"version": "2.3.0", "version": "2.3.1",
"author": { "author": {
"name": "Kjell Tore Guttormsen" "name": "Kjell Tore Guttormsen"
}, },

View file

@ -4,6 +4,47 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [2.3.1] - 2026-04-18
### Added — Qualified slug convention for cc-architect-catalog
Catalog files now follow `<cc_feature>[-<qualifier>]-<layer>.md`. The
unqualified slug (e.g., `hooks-pattern.md`) remains the canonical
baseline for a `(feature, layer)` pair. Qualified slugs (e.g.,
`hooks-observability-pattern.md`) cover specific sub-patterns without
displacing the baseline.
**Why.** v2.3.0 dogfood surfaced a design gap: the skill-factory
produced a draft `hooks-pattern.md` from a specialized source (progressive-
alert observability) that collided with the existing generic `hooks-pattern.md`
seed. Promoting would have replaced the general pattern with a narrow
one; discarding would have lost real catalog growth. Qualified slugs
resolve this by letting one feature host multiple named patterns at
different abstraction levels.
**Changes.**
- `skills/cc-architect-catalog/SKILL.md` — slug convention section added;
coverage table gains "qualified patterns" column; matcher logic
documented for N patterns per feature; modification rules cover
qualified-vs-canonical choice and slug-collision handling.
- `agents/feature-matcher.md` — catalog map is now
`cc_feature → {layer → [skills]}`; new "Selecting among multiple
patterns per feature" section (baseline by default, qualified when
justified, multiple when non-overlapping, never purely cosmetic);
`supporting_skill` accepts one-or-more skill names.
- `agents/gap-identifier.md` — adds `pattern_count[cc_feature]` signal
to the catalog coverage audit.
- `agents/architecture-critic.md` — adds supporting-skill verification:
every cited skill name must exist in the catalog; blocker severity.
- First qualified skill: `hooks-observability-pattern.md` (promoted from
`.drafts/`, sourced from `ai-psychosis/README.md`, ngram-overlap 0.01,
review_status approved).
**Non-breaking.** Existing unqualified slugs keep working. No changes to
`cc_feature` taxonomy. Hallucination gate unchanged (still validates
against `cc_feature` values, not slugs).
## [2.3.0] - 2026-04-18 ## [2.3.0] - 2026-04-18
### Added — Skill-factory Fase 1 MVP (`/ultra-skill-author-local`) ### Added — Skill-factory Fase 1 MVP (`/ultra-skill-author-local`)

View file

@ -123,6 +123,16 @@ Architect sits between `/ultraresearch-local` and `/ultraplan-local`. It matches
**CC-feature catalog skill:** The architect phase loads the `cc-architect-catalog` skill, which indexes Claude Code primitives (hooks, subagents, skills, output styles, MCP, plan mode, worktrees, background agents) across three layers: `reference` (how a feature works), `pattern` (when to reach for it), `decision` (adoption heuristics). The `feature-matcher` agent only proposes features covered by the catalog *or* an explicit fallback list — a hallucination gate that `architecture-critic` enforces as BLOCKER severity. The `gap-identifier` agent emits issue-ready drafts for missing catalog entries so the catalog grows with real usage rather than speculation. The catalog lives at `skills/cc-architect-catalog/`. **CC-feature catalog skill:** The architect phase loads the `cc-architect-catalog` skill, which indexes Claude Code primitives (hooks, subagents, skills, output styles, MCP, plan mode, worktrees, background agents) across three layers: `reference` (how a feature works), `pattern` (when to reach for it), `decision` (adoption heuristics). The `feature-matcher` agent only proposes features covered by the catalog *or* an explicit fallback list — a hallucination gate that `architecture-critic` enforces as BLOCKER severity. The `gap-identifier` agent emits issue-ready drafts for missing catalog entries so the catalog grows with real usage rather than speculation. The catalog lives at `skills/cc-architect-catalog/`.
**Slug convention (v2.3.1):** catalog files follow `<cc_feature>[-<qualifier>]-<layer>.md`.
- **Unqualified slugs** (e.g., `hooks-pattern.md`) are the canonical baseline — one per `(feature, layer)` pair, covering generic shapes and decision-heuristics for the feature.
- **Qualified slugs** (e.g., `hooks-observability-pattern.md`) cover specific named sub-patterns. Zero-or-more per `(feature, layer)` pair. The qualifier MUST be kebab-case and descriptive (`observability`, `migration`, `multi-tenant`).
- **Matcher logic:** `feature-matcher` builds `cc_feature → {layer → [skills]}` and prefers the unqualified baseline when the brief does not specifically justify a variant. Multiple skills can be proposed together when they cover non-overlapping aspects of the same feature.
- **Critic enforcement:** `architecture-critic` verifies every cited `supporting_skill` name exists as a real file in the catalog (blocker severity). The `cc_feature` hallucination gate is unchanged — still validates against the taxonomy, not slugs.
- **Collision handling:** skill-factory drafts that would overwrite an approved slug are a hard error. Resolution is either to qualify the new slug or revise the existing baseline.
Seeds v2.3.1: 11 skills across 8 features — one qualified pattern (`hooks-observability-pattern.md`, promoted from `ai-psychosis/README.md`, ngram-overlap 0.01, approved). Decision-layer intentionally empty pending skill-factory Fase 2.
## State ## State
All artifacts in one project directory (default): All artifacts in one project directory (default):

View file

@ -1,6 +1,6 @@
# ultraplan-local — Brief, Research, Architect, Plan, Execute # ultraplan-local — Brief, Research, Architect, Plan, Execute
![Version](https://img.shields.io/badge/version-2.3.0-blue) ![Version](https://img.shields.io/badge/version-2.3.1-blue)
![License](https://img.shields.io/badge/license-MIT-green) ![License](https://img.shields.io/badge/license-MIT-green)
![Platform](https://img.shields.io/badge/platform-Claude%20Code-purple) ![Platform](https://img.shields.io/badge/platform-Claude%20Code-purple)
@ -224,7 +224,9 @@ Downstream: `/ultraplan-local` auto-discovers `architecture/overview.md` in proj
### CC-feature catalog ### CC-feature catalog
The architect command reads from `skills/cc-architect-catalog/`, a plugin-internal catalog of hand-written skills covering each CC feature at one or more layers (`reference`, `pattern`, `decision`). v2.2 ships 10 seeds covering all 8 features at the reference layer (plus pattern layer for hooks and subagents). The catalog is designed for later expansion via a separate skill-factory process — gaps surfaced by the architect command are the backlog for that work. The architect command reads from `skills/cc-architect-catalog/`, a plugin-internal catalog of hand-written skills covering each CC feature at one or more layers (`reference`, `pattern`, `decision`). v2.2 shipped 10 seeds covering all 8 features at the reference layer (plus pattern layer for hooks and subagents). The catalog is designed for expansion via the skill-factory process (v2.3) — gaps surfaced by the architect command are the backlog for that work.
**Slug convention (v2.3):** files follow `<cc_feature>[-<qualifier>]-<layer>.md`. Unqualified slugs (e.g., `hooks-pattern.md`) are the baseline/canonical entry for a `(feature, layer)` pair. Qualified slugs (e.g., `hooks-observability-pattern.md`) cover specific sub-patterns without displacing the baseline. `feature-matcher` prefers the baseline when the brief does not specifically justify a qualified variant; it may propose multiple supporting skills when they cover non-overlapping aspects. Slug collisions with approved skills are a hard error — skill-factory drafts must qualify or revise instead.
### Hallucination gate ### Hallucination gate

View file

@ -66,6 +66,14 @@ the catalog or fallback list.
catalog, this is a **major** finding (REVISE — the feature is real but catalog, this is a **major** finding (REVISE — the feature is real but
the catalog has a coverage gap worth surfacing), not a blocker. the catalog has a coverage gap worth surfacing), not a blocker.
**Supporting-skill verification:** `supporting_skill` entries (one or
more skill names per feature, following the
`<feature>[-<qualifier>]-<layer>.md` convention) must match real files
in the catalog. A cited skill that does not exist is a **blocker**.
Multiple supporting skills for one feature are allowed when they cover
non-overlapping aspects — but the `integration_note` must justify
having more than one.
### 3. Contradiction detection ### 3. Contradiction detection
Scan for internal contradictions: Scan for internal contradictions:

View file

@ -47,14 +47,16 @@ enough.
### 2. Consult the catalog ### 2. Consult the catalog
Read `{catalog_root}/SKILL.md` to learn the `cc_feature` taxonomy and Read `{catalog_root}/SKILL.md` to learn the `cc_feature` taxonomy, the
layer model. layer model, and the slug convention (`<feature>[-<qualifier>]-<layer>.md`).
Glob `{catalog_root}/*.md` excluding `SKILL.md`. Parse each skill's Glob `{catalog_root}/*.md` excluding `SKILL.md`. Parse each skill's
frontmatter: frontmatter:
- `name`, `description`, `layer`, `cc_feature`, `source`, `concept`. - `name`, `description`, `layer`, `cc_feature`, `source`, `concept`.
Build an in-memory map: `cc_feature → [skill_names]`. Build an in-memory map: `cc_feature → {layer → [skills]}`. One feature
can have multiple pattern-layer skills (one baseline plus zero-or-more
qualified variants).
**Fallback when the catalog is empty or unreadable:** use this **Fallback when the catalog is empty or unreadable:** use this
hardcoded minimum list. Mark `fallback_used: true` in your output. hardcoded minimum list. Mark `fallback_used: true` in your output.
@ -79,14 +81,37 @@ For each feature you propose, produce:
Goal / Constraint / NFR / Success Criterion) that motivates this Goal / Constraint / NFR / Success Criterion) that motivates this
feature. Prefer verbatim quotes; paraphrase only when length forces feature. Prefer verbatim quotes; paraphrase only when length forces
it. it.
- **supporting_skill** — a skill name from the catalog that supports - **supporting_skill** — one or more skill names from the catalog that
this choice, or `null` if only the fallback hint was used. support this choice, or `null` if only the fallback hint was used.
When multiple pattern skills exist for the feature, apply the
selection rules below.
- **confidence**`high` (direct brief anchor + skill), `medium` - **confidence**`high` (direct brief anchor + skill), `medium`
(brief anchor without strong skill support, or skill match without a (brief anchor without strong skill support, or skill match without a
strong anchor), `low` (inferred need with no explicit anchor). strong anchor), `low` (inferred need with no explicit anchor).
- **integration_note** — one sentence on how this feature integrates - **integration_note** — one sentence on how this feature integrates
with the task at hand. with the task at hand.
#### Selecting among multiple patterns per feature
A feature can have a baseline pattern (`<feature>-pattern.md`) plus
zero-or-more qualified patterns (`<feature>-<qualifier>-pattern.md`).
When the feature is relevant to the brief:
1. **Baseline by default.** If the brief's anchor is generic
("need hooks for policy"), pick the unqualified `<feature>-pattern`.
2. **Qualified when justified.** If the brief explicitly calls for the
qualified variant's concept (e.g., observability, migration,
multi-tenant), pick the qualified pattern and name it in
`supporting_skill`. The anchor must reference the specific aspect,
not just the feature.
3. **Propose both when they cover non-overlapping aspects.** Example:
the brief needs both generic hook shapes *and* observability-style
cadence tracking — list `supporting_skill: [hooks-pattern, hooks-observability-pattern]`
and explain the split in `integration_note`.
4. **Never pick a qualified pattern just because it looks fancier.**
If the brief does not justify the qualifier, the baseline is the
honest answer.
### 4. Propose feature composition ### 4. Propose feature composition
After the per-feature list, write a short (35 bullet) note on how the After the per-feature list, write a short (35 bullet) note on how the
@ -112,7 +137,7 @@ Return your response as markdown, with this structure:
1. **<feature_id>** (confidence: <high|med|low>) 1. **<feature_id>** (confidence: <high|med|low>)
- Brief anchor: "<verbatim quote from brief section X>" - Brief anchor: "<verbatim quote from brief section X>"
- Supporting skill: <skill_name or "none — fallback hint"> - Supporting skill: <skill_name, or [skill_a, skill_b] for multi, or "none — fallback hint">
- Integration: <one sentence> - Integration: <one sentence>
2. ... 2. ...
@ -140,7 +165,8 @@ Return your response as markdown, with this structure:
fallback list.** That is a hallucination; `architecture-critic` will fallback list.** That is a hallucination; `architecture-critic` will
block it. block it.
- **Never invent skill names.** If you don't see a skill for a - **Never invent skill names.** If you don't see a skill for a
feature, say "none — fallback hint". feature, say "none — fallback hint". Every skill name in
`supporting_skill` must match a real file in the catalog.
- **Quote the brief; don't paraphrase silently.** Reviewers need to - **Quote the brief; don't paraphrase silently.** Reviewers need to
verify the anchor matches. verify the anchor matches.
- **Rationale must trace to the brief.** "We should have hooks because - **Rationale must trace to the brief.** "We should have hooks because

View file

@ -38,12 +38,16 @@ enough" notes. You do not propose architecture — only gaps.
### 1. Catalog audit ### 1. Catalog audit
Read `{catalog_root}/SKILL.md` to learn the taxonomy + coverage table. Read `{catalog_root}/SKILL.md` to learn the taxonomy, slug convention
Glob `{catalog_root}/*.md` (excluding `SKILL.md`) and parse (`<feature>[-<qualifier>]-<layer>.md`), and coverage table. Glob
frontmatter. Build: `{catalog_root}/*.md` (excluding `SKILL.md`) and parse frontmatter.
Build:
- `have[(cc_feature, layer)]` — set of (feature, layer) pairs with at - `have[(cc_feature, layer)]` — set of (feature, layer) pairs with at
least one skill. least one skill.
- `pattern_count[cc_feature]` — number of pattern-layer skills per
feature (useful signal for the audit; one baseline plus zero-or-more
qualified variants).
### 2. Read the brief + research ### 2. Read the brief + research
@ -111,7 +115,7 @@ They are not issues; they are informational.
### Catalog coverage audit ### Catalog coverage audit
- Skills in catalog: N - Skills in catalog: N
- Features with reference: [list] - Features with reference: [list]
- Features with pattern: [list] - Features with pattern: [list with (feature, pattern_count) when >1]
- Features with decision: [list] - Features with decision: [list]
- Features with no coverage: [list] - Features with no coverage: [list]

View file

@ -83,32 +83,70 @@ skill-factory output will start as `pending` (channel 2) or
If a future skill covers a feature not in this list, add the row above If a future skill covers a feature not in this list, add the row above
and surface the extension in the relevant CHANGELOG entry. and surface the extension in the relevant CHANGELOG entry.
## Slug convention
Skill filenames follow this pattern:
```
<cc_feature>[-<qualifier>]-<layer>.md
```
- **Unqualified slug** (`<feature>-<layer>.md`) — the baseline/canonical
entry for that (feature, layer) pair. One per pair. Example:
`hooks-pattern.md` = generic hook shapes and pitfalls.
- **Qualified slug** (`<feature>-<qualifier>-<layer>.md`) — a
specialized variant covering one specific sub-pattern or use-case.
Zero-or-more per pair. Example: `hooks-observability-pattern.md` =
progressive-alert observability pattern specifically.
Qualifiers exist because one CC feature can support multiple
non-overlapping patterns at different abstraction levels. Forcing
everything into a single `<feature>-<layer>.md` either bloats the
canonical entry or loses useful specialization. Qualified slugs let the
catalog grow with concrete, named patterns without displacing the
baseline.
`feature-matcher` treats all skills with the same `cc_feature` + `layer`
as candidates and picks the most relevant to the brief (or proposes
several if they cover different aspects). See "How `feature-matcher`
uses this file" below.
## Current seed coverage (v2.3.0 — see .drafts/ for skill-factory output) ## Current seed coverage (v2.3.0 — see .drafts/ for skill-factory output)
| Feature | reference | pattern | decision | | Feature | reference | pattern | qualified patterns | decision |
|---------|-----------|---------|----------| |---------|-----------|---------|--------------------|----------|
| hooks | hooks-reference | hooks-pattern | — | | hooks | hooks-reference | hooks-pattern | hooks-observability-pattern | — |
| subagents | subagents-reference | subagents-pattern | — | | subagents | subagents-reference | subagents-pattern | — | — |
| skills | skills-reference | — | — | | skills | skills-reference | — | — | — |
| output-styles | output-styles-reference | — | — | | output-styles | output-styles-reference | — | — | — |
| mcp | mcp-reference | — | — | | mcp | mcp-reference | — | — | — |
| plan-mode | plan-mode-reference | — | — | | plan-mode | plan-mode-reference | — | — | — |
| worktrees | worktrees-reference | — | — | | worktrees | worktrees-reference | — | — | — |
| background-agents | background-agents-reference | — | — | | background-agents | background-agents-reference | — | — | — |
Total: 10 seed skills, 8 features, 2 layers. Decision-layer is Total: 11 seed skills, 8 features, 2 layers. Decision-layer is
intentionally empty — decisions cross features and require broader intentionally empty — decisions cross features and require broader
synthesis than a single seed pass can provide. Skill-factory populates synthesis than a single seed pass can provide. Skill-factory populates
decision-layer later. decision-layer later.
## How `feature-matcher` uses this file ## How `feature-matcher` uses this file
1. Read this file to learn the `cc_feature` taxonomy. 1. Read this file to learn the `cc_feature` taxonomy and slug convention.
2. Glob the directory for `*.md` files (excluding SKILL.md). 2. Glob the directory for `*.md` files (excluding SKILL.md).
3. Parse each skill's frontmatter. 3. Parse each skill's frontmatter.
4. For each feature mentioned in the brief or research, match against 4. For each feature mentioned in the brief or research, match against
`cc_feature` field. Prefer `pattern` over `reference` when both exist `cc_feature` field. Build the candidate set per feature, grouped by
(pattern is richer). layer. Selection rules:
- **Layer preference:** prefer `pattern` over `reference` when both
exist (pattern is richer).
- **Multiple patterns per feature:** when two or more pattern-layer
skills share the same `cc_feature`, read each `description` and
pick the one(s) most relevant to the brief. If two cover
non-overlapping aspects that both apply, propose both with clear
rationale. Prefer the unqualified baseline (`<feature>-pattern.md`)
when the brief does not specifically justify a qualified variant.
- **Be explicit:** name the chosen skill in `supporting_skill` so
`architecture-critic` can verify the match.
5. When no skill exists for a mentioned feature, fall back to the 5. When no skill exists for a mentioned feature, fall back to the
hardcoded minimum-list inside the `feature-matcher` prompt and mark hardcoded minimum-list inside the `feature-matcher` prompt and mark
the gap in stats (`fallback_used: true`). the gap in stats (`fallback_used: true`).
@ -132,9 +170,24 @@ decision-layer later.
## Modification rules ## Modification rules
- Adding a new skill: create `<feature>-<layer>.md` with the frontmatter - **Adding a canonical skill:** create `<feature>-<layer>.md` with the
above and bump the coverage table in this file. frontmatter contract above. Bump the coverage table in this file.
- Renaming `cc_feature` values: update both this file AND every skill - **Adding a qualified pattern skill:** create
using the old value in the same commit. `<feature>-<qualifier>-<layer>.md` when the new pattern covers a
- Removing a skill: document in CHANGELOG under the version that drops distinct sub-case that does not belong in the unqualified baseline.
it. The qualifier MUST be kebab-case and descriptive (e.g.,
`observability`, `migration`, `multi-tenant`). Add it to the
"qualified patterns" column in the coverage table.
- **Choosing qualified vs. canonical:** if no unqualified skill exists
yet for a `(feature, layer)` pair, the new skill SHOULD be the
unqualified baseline — don't ship a qualified skill without a
canonical one, because `feature-matcher` prefers baseline when the
brief has no specific justification for a variant.
- **Renaming `cc_feature` values:** update both this file AND every
skill using the old value in the same commit.
- **Removing a skill:** document in CHANGELOG under the version that
drops it.
- **Slug collision:** two skills with the same slug are a hard error.
Skill-factory (`/ultra-skill-author-local`) must refuse to promote a
draft that would overwrite an approved skill. Collision is resolved
either by qualifying the new slug or by revising the baseline.

View file

@ -0,0 +1,50 @@
---
name: hooks-observability-pattern
description: Observe user-interaction patterns across session lifecycle hooks and emit cooldown-gated nudges
layer: pattern
cc_feature: hooks
source: ../../../../ai-psychosis/README.md
concept: progressive alerts via lifecycle hooks
last_verified: 2026-04-18
ngram_overlap_score: 0.01
review_status: approved
---
# Hooks — Progressive-Alert Observability Pattern
## Use this when
- Measure behaviour the model itself cannot see: cadence, duration, repetition, time-of-day.
- Surface soft nudges rather than hard blocks — the operator keeps the final call.
- Separate "what happened" (metrics) from "what was said" (prompt text) so no conversation content touches disk.
## Shape
- Wire four lifecycle events: a start handler for baseline counters, a prompt handler for language-category flags, a tool handler for cadence and burst detection, and an end handler for totals and state cleanup.
- Keep per-session counters in a tiny JSON file under the plugin data dir; keep aggregate events in an append-only JSONL log for later reporting.
- Gate every nudge behind two things: a threshold (hard or soft) and a cooldown window, so repeat alerts do not spam the transcript.
- Deliver alerts as `additionalContext` injection, never as a tool block — the goal is awareness, not control.
## Forces
- **Privacy vs. signal.** Richer signal wants more content logged; the user wants none. Resolve by computing boolean flags in-memory and discarding the raw text before the handler returns.
- **Latency budget.** Handlers fire on every prompt and every tool call. Stay well under 100 ms per invocation; append-only JSONL is sub-millisecond and safe.
- **Portability.** Hooks that assume a shell, `jq`, or npm dependencies break on half the operator fleet. Stick to Node stdlib so the same script runs on macOS, Linux, and Windows.
- **Instruction layer alone is not enough.** Behavioural rules in a skill file shape tone but cannot measure duration or frequency. Layer the hook observability on top of the skill — each compensates for the other.
## Gotchas
- A handler that crashes blocks the turn. Catch everything, log, and exit zero by default.
- Cooldowns must be per-category, not global, or the most-triggered alert silences the rarer, more informative ones.
- Late-night and rapid-fire thresholds are legitimate signals but also easy to over-tune; start with generous bands and tighten only with data.
- `additionalContext` from an end-of-session handler is discarded — inject alerts on start, prompt, or tool events where the model will actually see them.
## Anti-patterns
- Storing prompt text or tool arguments "just for debugging" — once it is on disk, the privacy guarantee is gone.
- Treating every elevated metric as an intervention. If the hook starts blocking, the operator works around it and loses the awareness benefit.
- Hardcoding thresholds into the handler. Pull them from a single config so future tuning does not require a rewrite of four scripts.
## Decision quick-check
Reach for this pattern when you need visibility into *how* the user is interacting, not *what* they are saying, and when the response should be a gentle nudge rather than a gate. Otherwise use a PreToolUse denylist (hard limit) or a skill-only instruction layer (style, not cadence).