git clone --depth 1 https://github.com/modu-ai/moai-adk /tmp/hns-lsel-curator && cp -r /tmp/hns-lsel-curator/.claude/skills/hns-lsel-curator ~/.claude/skills/hns-lsel-curatorSKILL.md
# hns-lsel-curator — LSEL CLUSTER + drain engine
> **Namespace:** `hns-lsel-*` is user-owned dogfood (CLAUDE.local.md §24). This skill is
> NOT mirrored into `internal/template/templates/` — it lives only in this repo. Graduation
> to `moai-lsel-*` + 16-language distribution is a separate SPEC (out of scope per spec.md §G).
>
> **M1 scope:** drain + cluster + stage candidates. NO APPROVE, NO APPLY (M3).
> **M2 scope:** drain + cluster + **PROPOSE shadow** (no APPROVE, no APPLY). The PROPOSE stage
> emits shadow proposals + self-critiques; APPROVE/APPLY land in M3 via the fresh
> `hns-lsel-applier` path. M2 does NOT write to `memory/` — the first `feedback_*.md` topic
> file is an M3+ deliverable after APPROVE.
## What this skill does
The MoAI-ADK repo accumulates tool-failure stubs in `.moai/lessons-inbox.jsonl` (624 stubs at
M1 start, re-measured — a moving target). The constitution names the orchestrator as the drain
actor, but until this skill there was **zero mechanical drain code** — the drain existed only as
a doctrine paragraph (`moai-constitution.md:147`). This skill closes that gap in user-owned
surfaces, without touching the frozen Go applier (`internal/harness/applier.go:22` —
its write-flag stays `false`; REQ-LSEL-003: bypass, never unfreeze).
The drain is split into a **mechanical core** (`drain.sh`, deterministic, testable) and a
**model-mediated layer** (this SKILL.md + your judgment, invoked for M2+ importance refinement
and proposal drafting).
## The mechanical core — `drain.sh`
`drain.sh` is a portable bash + jq script that lives next to this SKILL.md. It performs the
deterministic half of the drain:
```
drain.sh --inbox <path-to-lessons-inbox.jsonl> --state-dir <path-to-lsel-state>
```
Pipeline (REQ-LSEL-009 + AC-LSEL-009 / AC-LSEL-010):
1. **Companion offset** — read `<state-dir>/drain-offset.json` (seed `{"offset":0}` if absent).
The inbox is append-only and is NEVER mutated; the offset marks consumed stubs
(SPEC-HARNESS-RATCHET-REWIRE-001 D3 companion-offset pattern).
2. **Slice** — read stubs from the offset onwards (`tail -n +<offset+1>`).
3. **Drain-side severity filter** (AC-LSEL-010) — discard noise BEFORE clustering:
- `tool_failure:Bash:UnknownFailure` — the opaque ~65% timeout/sandbox bucket (the dominant
noise share; report §2).
- `tool_failure:Bash:SandboxViolation` — environment constraint, not a code defect.
- any `*:TimeoutError` (Bash + MCP timeouts).
The filter is drain-side because `internal/hook/failure_observer.go` (the inbox writer) is
OUTSIDE the six loop-writable surfaces (plan.md §F.1 [DECISION RESOLVED]), so the loop cannot
edit the writer — it filters on read instead.
4. **Cluster** by `event_key` with frequency count, first/last seen, and up to 3 sample summaries.
5. **Singleton gate** — discard clusters with `frequency < 2` (single-occurrence noise per the
constitution Lessons Protocol drain paragraph).
6. **Importance** — score each survivor with a Generative-Agents-style 1-10 gate:
`importance = min(10, frequency)` (frequency as proxy; the model augments this in M2+ with a
severity hint and retrieval-weighted judgment).
7. **Emit** candidates to `<state-dir>/clusters.json`; advance the companion offset.
### `clusters.json` schema
```json
{
"drained_at": "2026-08-04T08:41:00Z",
"offset_before": 0,
"offset_after": 624,
"total_read": 624,
"noise_discarded": 533,
"singletons_discarded": 4,
"candidates": [
{
"event_key": "tool_failure:Agent:UnknownFailure",
"frequency": 41,
"first_seen": "...",
"last_seen": "...",
"sample_summaries": ["...", "...", "..."],
"source": "tool:Agent",
"importance": 10
}
]
}
```
### Empty-delta no-op
If the inbox has not grown past the offset, `drain.sh` writes an empty-candidate `clusters.json`
and leaves the offset unchanged. Not a failure (acceptance.md §E edge case).
## The model-mediated layer (you, when invoked)
`drain.sh` produces the deterministic candidate set. When this skill is invoked for a real
curation pass (M2+), your job on top of the mechanical output is:
- **Read `clusters.json`** and rank candidates by `importance` then `frequency`.
- **Augment importance** with a severity hint the mechanical core cannot see: a recurring
`Bash:ExitError` cluster points at a real command-shape defect (high signal); a recurring
`Agent:ContextCancelled` cluster may be session-teardown noise (lower signal). Record the
rationale in the candidate's prose when you draft the M2 proposal — do NOT rewrite
`clusters.json` (it is the mechanical artifact; your augmentation lives in the proposal).
- **Do NOT write to `memory/` in M1.** Candidates stage in `clusters.json` only. The first
`feedback_*.md` topic file is produced by the M2 PROPOSE stage after retrieval-before-propose
and self-critique (REQ-LSEL-010).
## What this skill does NOT do (M1 boundaries)
- **No APPROVE / APPLY** — the parallel user-owned applier (`hns-lsel-applier`) is M3.
- **No edits to frozen doctrine** — `.claude/rules/moai/**`, `CLAUDE.md`,
`internal/template/templates/**`, retained agents, `moai-*` skills, and the frozen Go
applier / `curator_dispatch.go` are all byte-for-byte untouched (REQ-LSEL-001 / §B.3).
- **No new `.moai/config/sections/` file** — loop state lives under `.moai/state/lsel/`
(a new section file would be wiped on `moai update`; plan.md §B.4 / AP-LSEL-005).
- **No orchestrator-only synchronous user-question channel** — this is a subagent-owned
mechanism skill; it never invokes the orchestrator's user gate. On a missing input,
return a structured blocker report; the orchestrator runs the user gate (CLAUDE.md §8).
## Durable operations — the session-start trigger (`session_drain.sh`)
The drain's original "schedule" was a session-scoped `/loop` recipe that died with its
owning session (2026-08-04) and executed nothing even while alive — the inbox stalled
for 3 weeks with nobody notified (SPEClaude Code upstream change tracker -> moai-adk update plan + docs sync workflow (dev-only). Tracks new CC release notes, classifies changes by impact tier, cross-references official docs, generates update plan at .moai/research/ or .moai/specs/, and synchronizes docs-site 4-locale + README. NOT distributed to user projects.
GitHub Workflow - Manage issues and review PRs with Agent Teams (dev-only). NOT distributed to user projects.
MoAI-ADK production release via Enhanced GitHub Flow (CLAUDE.local.md §18). Creates release/vX.Y.Z branch, version bump, CHANGELOG (bilingual), PR to main, merge commit (NOT squash), then scripts/release.sh for tag + GoReleaser. Hotfix support via --hotfix flag. All git operations delegated to manager-git. Quality failures escalate to expert-debug. NOT distributed to user projects (dev-only).
Run the 7-phase /moai brain ideation workflow to convert ideas into validated proposals
Identify and safely remove dead code with test verification
Scan codebase and generate architecture documentation in codemaps/
Analyze test coverage, identify gaps, and generate missing tests
Hybrid design workflow — Claude Design import (path A) or code-based brand design (path B)