Skip to content
Back to skills

Knowledge Flush

ASecurity

Drain queued ★ Insight candidates (harvested from your sessions) into the wiki as a reviewed PR. For each candidate it researches and verifies the best-practice against real sources, checks existing wiki layers for duplicates and links, decides the target layer/category (or justifies a new one), runs wiki-ingest, then opens ONE PR per flush for you to review and merge/reject. Never auto-merges. Use when asked to "flush knowledge", "process the insight queue", "ingest what I learned", or "/dev...

  • 5 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 2, 2026
ai-agentsgoshellnoderailsgitapi

Works with

  • api

Security analysis

A100/100

Scanned September 20, 2026

npx -y skills add choiyounggi/dev-loop --skill knowledge-flush --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Knowledge Flush?

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

Security grade badge for Knowledge Flush
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/choiyounggi-knowledge-flush/badge)](https://www.skillsdirectory.com/skills/choiyounggi-knowledge-flush)

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: knowledge-flush
effort: high
argument-hint: "[optional: filters]"
description: Drain queued ★ Insight candidates (harvested from your sessions) into the wiki as a reviewed PR. For each candidate it researches and verifies the best-practice against real sources, checks existing wiki layers for duplicates and links, decides the target layer/category (or justifies a new one), runs wiki-ingest, then opens ONE PR per flush for you to review and merge/reject. Never auto-merges. Use when asked to "flush knowledge", "process the insight queue", "ingest what I learned", or "/dev-loop:knowledge-flush".
---

# knowledge-flush — queued insights → verified wiki PR

Turn the `★ Insight` candidates harvested from your sessions into a wiki
contribution, **PR-only** — you (the repo owner) review each PR and merge or
reject it. This skill NEVER auto-merges and NEVER pushes to `main`.

The queue lives at `~/.dev-loop/queue/*.jsonl` (written by the Stop hook). Each
row is a candidate: `trigger, directive, why, evidence, domain, tags, content`.

## Non-negotiable order (a PreToolUse gate enforces it)

`hooks/pre-flush-pr-gate.sh` blocks `gh pr create` on a knowledge branch unless
an `INGEST_REPORT.md` with three filled sections exists. So do the work first:

0. **Acquire the shared flush lock — before anything else, including the
   checkout reset in step 1.** This skill and the `hooks/auto-flush.sh` Stop
   hook are the two entry points that drain the same queue; both serialize
   through the same lock (`scripts/flush-lock.sh`, issue #77).

   Generate this run's id **once, here**, and record it — **every** later
   `flush-lock.sh` call in this flush (the abort-path release below, and
   step 5's release) must be prefixed with it. This is mandatory, not a
   style choice: each step in this skill runs in its own fresh shell, so
   nothing carries between them on its own — an unprefixed call computes a
   brand-new run id that no longer matches the one that acquired the lock,
   so `release` is refused as a foreign caller and the lock leaks for the
   full TTL.
   ```sh
   RUNID="flush-$(date +%Y%m%d-%H%M%S)-$$"
   echo "$RUNID"          # record this — every later flush-lock.sh call needs it
   DEV_LOOP_FLUSH_RUN_ID="$RUNID" sh "${CLAUDE_PLUGIN_ROOT}/scripts/flush-lock.sh" acquire
   ```
   - Exit 0 → proceed to step 1.
   - Non-zero exit → **do not touch `~/.dev-loop/repo`.** Read the failure
     line (`held <holder-runid> <age>s` on stderr) and tell the user: "a
     flush is already running (holder `<holder-runid>`, started `<age>s`
     ago) — stopping." Then stop. Do not retry, poll, or wait for the lock.
   The lock is released once, at the very end of step 5 on a successful
   flush. If you abort or hit an unrecoverable error at any point after
   acquiring it, release it before stopping — **prefixed with the same
   `$RUNID`** recorded above — so the next run does not wait out the TTL:
   ```sh
   DEV_LOOP_FLUSH_RUN_ID="<the run id you recorded above>" \
     sh "${CLAUDE_PLUGIN_ROOT}/scripts/flush-lock.sh" release
   ```

1. **Prepare a writable checkout** of the dev-loop repo (never edit the installed
   plugin dir — it is read-only and untracked):
   ```sh
   REPO="$HOME/.dev-loop/repo"
   if [ -d "$REPO/.git" ]; then
     git -C "$REPO" fetch origin && git -C "$REPO" checkout main && git -C "$REPO" reset --hard origin/main
   else
     mkdir -p "$HOME/.dev-loop"
     git clone https://github.com/choiyounggi/dev-loop.git "$REPO"
   fi
   # Commit under THIS user's own identity — each contributor's PR carries their
   # own account; the owner reviews and approves/rejects. Do NOT hardcode an
   # identity, and NEVER commit as an assistant or add a Co-Authored-By trailer.
   # Inherit the user's global git identity (fall back to their gh login only if
   # git has none configured):
   if [ -z "$(git -C "$REPO" config user.email)" ]; then
     GH_USER="$(gh api user -q .login 2>/dev/null)"
     [ -n "$GH_USER" ] && git -C "$REPO" config user.name "$GH_USER" \
       && git -C "$REPO" config user.email "${GH_USER}@users.noreply.github.com"
   fi
   # Branch names carry the contributor so PRs are attributable at a glance:
   WHO="$(git -C "$REPO" config user.name | tr ' ' '-' | tr -cd 'A-Za-z0-9-')"
   BR="knowledge/${WHO:-anon}-$(date +%Y%m%d-%H%M%S)"
   git -C "$REPO" checkout -b "$BR"
   ```
   Read/write the wiki inside `$REPO` (its `INDEX.md`, `wiki/`, `templates/`,
   `AGENTS.md`), NOT `${CLAUDE_PLUGIN_ROOT}`. The push + PR use the ambient `gh`
   auth, so the PR is opened by whichever account this user is logged in as.

2. **Claim your candidates, then for each one run the pre-PR pipeline** (this is
   the whole point — a raw harvested block is a *candidate*, not vetted
   knowledge):

   **Claim first, before reading or ingesting anything:**
   ```sh
   node "${CLAUDE_PLUGIN_ROOT}/hooks/queue-claim.js" claim
   ```
   Work only the ids this command prints — those are the rows this run now
   owns. A row already claimed by another (live or not-yet-TTL-expired) run,
   or claimed by a sibling run that raced past the lock, is not printed;
   skip it — do not read or ingest a row this command did not print.

   a. **Research & verify the best-practice.** Do a real search — official docs,
      primary sources, reputable references (use WebSearch / context7 / the
      relevant framework docs). Confirm the directive is actually correct, not
      just plausibly asserted in the session. Capture checkable citations.
      - Verified against official docs or a reproducible check → `confidence: verified`.
      - Only production experience, no external source → `confidence: field-tested`.
      - Cannot substantiate → either drop the candidate or keep it
        `confidence: unverified` and say so loudly in the report. **Never
        fabricate or approximate a URL.**

   b. **Existing-layer check (dedup + links).** Route to the domain via
      `INDEX.md`, then read that domain's `index.md` and every page whose
      "load when" overlaps. Determine: is this already covered (→ merge/append,
      don't duplicate)? Does it conflict with an existing directive (→ flag,
      don't overwrite)? Which existing pages should it `related:`-link to?

   b′. **Open-PR dedup check.** The merged wiki is not the whole layer — sibling
      flushes may have PRs in flight. List them and diff each candidate against
      their content before ingesting:
      ```sh
      gh pr list --repo choiyounggi/dev-loop --state open \
        --json number,headRefName --search "head:knowledge/"
      ```
      For every open head that touches an overlapping trigger
      (`git fetch origin <head>` then `git diff origin/main origin/<head> -- wiki/`),
      give the candidate one of three verdicts, recorded in the report's
      `## Open-PR check` section:
      - **fold** — the open PR already carries this insight in better/equal form
        → push your unique additions to THAT branch (or note them on its PR) and
        do not re-ingest here;
      - **drop** — pending duplicate with nothing new → retire the candidate;
      - **new** — no overlap → ingest normally.
      Two real pile-ups (#17–#40, then #42/#43 — see #39) came from skipping
      exactly this step.

   c. **Routing decision.** State the target `domain/category` and page. If no
      category fits, decide whether to add one (and justify why the existing
      categories genuinely don't cover it) or place it under the closest fit.
      Apply the layer test from `wiki-ingest` step 3 first: a project-specific
      candidate (its directive names one repository's own files, modules,
      services, conventions or infrastructure, or would be wrong in another
      codebase) is excluded from this PR and listed under
      `## Local-layer candidates` with its
      `wiki-local/<domain>/<category>/<slug>.md` target and the note "run
      wiki-ingest inside that project".

   d. **Ingest.** Run the **`wiki-ingest`** skill with the decisions from a–c:
      merge-before-create, positive-guidance form, sourced frontmatter, ≤120
      body lines, and update the domain `index.md` + `log.md`.

3. **Write `INGEST_REPORT.md`** at `$REPO/.dev-loop/INGEST_REPORT.md` (a separate
   step BEFORE the PR command — the gate evaluates the file before the command
   runs, so it cannot be a heredoc inside the `gh pr create` line). Required
   sections, each with real content:

   ```markdown
   # Knowledge flush — <N> insight(s)

   ## Verified best-practice
   For each insight: the claim, the sources you checked (real URLs/docs), how you
   verified it, and the resulting confidence (verified / field-tested / unverified).

   ## Existing-layer check
   Pages you read, overlaps found, what you merged vs. created new, conflicts
   flagged, and related-links added. MUST include a line
   `Pages read: <page-id>, <page-id>, …` naming the ids you actually opened —
   the gate resolves each id against the checkout's `wiki/` and denies the PR
   on any id that does not exist (fabricated evidence fails closed).

   ## Open-PR check
   The open `knowledge/*` heads you listed, which (if any) overlap each
   candidate, and the per-candidate verdict: fold / drop / new (step 2b′).

   ## Routing decision
   Target domain/category/page for each insight; any new category + why existing
   ones didn't fit.

   ## Local-layer candidates
   Project-specific candidates excluded from this PR, each with its
   wiki-local/<domain>/<category>/<slug>.md target and the project it belongs
   to; write `none` when every candidate was general.
   ```

4. **Commit + PR (no auto-merge).**
   ```sh
   git -C "$REPO" add wiki/ INDEX.md log.md .dev-loop/INGEST_REPORT.md
   git -C "$REPO" commit -m "knowledge: ingest <N> verified insight(s)"
   git -C "$REPO" push -u origin "$BR"
   gh pr create --repo choiyounggi/dev-loop --base main --head "$BR" \
     --title "knowledge: <short summary>" \
     --body-file "$HOME/.dev-loop/repo/.dev-loop/INGEST_REPORT.md" \
     --label dev-loop:knowledge
   ```
   Write the `--body-file` path so the gate can resolve it as text: the
   PreToolUse gate inspects the command string before execution and cannot
   expand skill-local variables like `$REPO` (see
   wiki/platforms/shells/command-text-inspected-before-execution.md) — use a
   literal absolute path or `$HOME`/`~` (which the gate expands).
   Do NOT `gh pr merge`. The owner reviews open `dev-loop:knowledge` PRs and
   merges or rejects each one.

5. **Retire processed candidates — every claimed row you handled, not only the
   ingested.** Move each handled row out of the active queue (append it to
   `~/.dev-loop/queue/.processed.jsonl` and rewrite the session file without it):
   rows you ingested, rows you merged into existing pages, AND rows you dropped
   as unverifiable or duplicate. Retire only rows step 2 claimed for **this**
   run — never a row you did not claim. A dropped row left `pending` re-crosses
   the auto-flush threshold forever — the headless flush would re-run hourly on
   candidates that can never be promoted. When the rewrite leaves a session file
   empty, delete the file — empty leftovers otherwise accumulate in the queue
   directory (the harvester also removes its own empty file on later Stops).

   If step 2 claimed a row you did not end up handling (the run is aborting,
   or the candidate is being left for a retry), release it instead of retiring
   it, so a later run does not wait out the claim TTL to see it again:
   ```sh
   node "${CLAUDE_PLUGIN_ROOT}/hooks/queue-claim.js" release <id>...
   ```

   Once every claimed row is retired or released, release the flush lock —
   this is the normal end of a successful flush. Prefix with the **same**
   `$RUNID` recorded in step 0: this step runs in its own fresh shell, so an
   unprefixed call computes a new run id and `release` is refused as a
   foreign caller, leaking the lock for the full TTL:
   ```sh
   DEV_LOOP_FLUSH_RUN_ID="<the run id you recorded in step 0>" \
     sh "${CLAUDE_PLUGIN_ROOT}/scripts/flush-lock.sh" release
   ```

## Guardrails
- Never run two flushes at once — step 0's lock is the only serialization
  point, and it is shared with `hooks/auto-flush.sh`.
- PR-only. Never auto-merge, never push to `main`, never force-push `main`.
- Commit under the **user's own ambient git/gh identity** — never hardcode an
  account, never commit as an assistant, never add a `Co-Authored-By` trailer.
- A candidate you cannot verify does not get quietly upgraded to `verified`.
- If the queue is empty, say so and stop — do not open an empty PR.
- One PR per flush (batched), so review stays a single pass.

## Triggering — manual and automatic
- **Manual:** invoke this skill (`/dev-loop:knowledge-flush`) any time; it drains
  the shared queue (`~/.dev-loop/queue/`, keyed off `$HOME` so it spans sessions).
- **Automatic:** the Stop hook `hooks/auto-flush.sh` fires this same pipeline in a
  detached headless `claude` run when the queue crosses a threshold and the
  rate-limit window has elapsed — so PRs appear without you running anything. It
  is guarded (rate-limited, batched, recursion-safe) and opens the same reviewed,
  gated PR. Disable with `DEV_LOOP_AUTOFLUSH=0`. See that hook for the knobs.

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.