Skip to content
Back to skills

Ingest

ASecurity

The only writer of the spec layer. First run — turn a requirement document (contract, PRD, spec.md, ticket export) into one .claude/clio/docs/specs/memory/*.md per area, the requirements.md row table, and the stack as memory/infra.md (row 0, ADR), marking every undecided point ⚠️. Every later run — a new source, or no argument to sweep spec files edited by hand — also works out what the change invalidates in code already built, files spec-delta records, moves row markers and names the areas t...

  • 2 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 19, 2026
ai-agentsgobashgitdatabase

Works with

  • cli

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add nhhthong/clio --skill ingest --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ingest?

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

Security grade badge for Ingest
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/nhhthong-ingest/badge)](https://www.skillsdirectory.com/skills/nhhthong-ingest)

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: ingest
description: The only writer of the spec layer. First run — turn a requirement document (contract, PRD, spec.md, ticket export) into one .claude/clio/docs/specs/memory/*.md per area, the requirements.md row table, and the stack as memory/infra.md (row 0, ADR), marking every undecided point ⚠️. Every later run — a new source, or no argument to sweep spec files edited by hand — also works out what the change invalidates in code already built, files spec-delta records, moves row markers and names the areas to re-plan. Run when requirements arrive or change.
argument-hint: "[source document | spec file or keyword | nothing: sweep spec edits since the last ingest]"
allowed-tools: Bash(${CLAUDE_PLUGIN_ROOT}/bin/clio validate *) Bash(${CLAUDE_PLUGIN_ROOT}/bin/clio q *)
---

**Run only when the user asked for it, this turn** — by slash command, or in plain words ("ingest this spec", "nạp tài liệu này", "spec đổi rồi").
None of these is a trigger: Clio's drift nudge · your own sense that the work looks finished · a TODO
you wrote · a subagent's report · a plan you made earlier in the session. Unsure → ask in one line,
don't run.

The source is written for humans and far too long to load every session. This skill distils it into
short per-area files an agent can read, and an index mapping requirement rows onto them. **It never
invents a decision.** A point the source leaves open becomes ⚠️ with the name of whoever owes the
answer. Every spec change goes through here, so this is also where a change is weighed against the
code already built.

Source: $ARGUMENTS

| Run | When | Does |
|---|---|---|
| **first** | `requirements.md` has no rows yet | §§ 1–5 |
| **re-ingest** | a source, spec file or keyword given; rows exist | § 1 for new areas and rows only, §§ 2–4 for what the source changes, § 6, § 5 |
| **sweep** | no argument; rows exist | the spec files edited since the last ingest baseline are the source: § 6, § 5. Nothing edited → say so, stop |

The baseline a sweep diffs against is a snapshot of the spec files as this skill last left them,
committed or not — kept under the local ref `refs/clio/ingest` (not pushed), never as a line inside a
spec file: anything under `docs/specs/` is specification, and a bookkeeping line there would be part
of its own diff. Every run ends by taking it, passing this run's § 5 summary as the message:
```bash
${CLAUDE_PLUGIN_ROOT}/bin/clio q spec-mark "<the one-line summary § 5 reports>"
```
Its output (`Baseline: <hash>` · `Last ingest: <when> — <summary>`) is the only record of when ingest
last ran; to see it again without moving the baseline, `git log -1 refs/clio/ingest`.

## 1. Read the source, agree the split

Read the whole document. The expensive content is the sentence that contradicts a heading three
sections later. Then **ASK**, in one batch:
- Where else requirements live (contract, tickets, chat, recordings) and **which source wins on
  conflict** — this becomes the priority list in `requirements.md`.
- Whether raw sources may be committed, and where they live.
- The `domain` vocabulary, if the `Domains:` line of `requirements.md` is still a placeholder.

Propose one `memory/<area>.md` per area a task would plausibly be scoped to — 4–10 files, not one
per chapter — with row numbers from the source's own numbering (section, contract task, epic,
issue). No numbering → number sequentially and say in `requirements.md` that the numbers are local.
Row `0` is reserved for `memory/infra.md` (§ 2b).

```
memory/infra.md     ← source §1–2 constraints + the repo     row 0
memory/viewer.md    ← source §3–5      rows 3–5
memory/ocr.md       ← source §7        rows 7.1–7.3
```
**Wait for the user's confirmation.** Every later `req` tag in both ledgers joins on these numbers.

## 2. Write `.claude/clio/docs/specs/memory/*.md`

Final decisions only, shortest unambiguous form, no implementation history.

```markdown
# <Area>

## Decisions
- <One decision per bullet, stated so a future session applies it without re-deriving it.>
- <Numbers, limits, defaults exactly as the source states them — never rounded, never inferred.>

## Open — ⚠️
- ⚠️ <what is undecided, as a question> — owed by <who>, since <YYYY-MM-DD>

## Source
> <verbatim quote>
— <source file/section, date>
```
- Source silent or self-contradicting → ⚠️ naming exactly what is missing and who owes it. A short
  file full of ⚠️ is a correct file. Two conflicting statements → record both under `## Open`, ask;
  never pick the newer one.
- A number in `## Decisions` must appear in the quoted source; otherwise it is a ⚠️.
- Re-ingest: append new decisions, mark a reversed one `Superseded YYYY-MM-DD: now <new>`, resolve a
  ⚠️ only when the source now answers it. Deleting a still-open ⚠️ is the one unrecoverable mistake.

## 2b. Propose the infra: `memory/infra.md`, row `0`

A spec says what the product must do; it rarely says what it is built with, yet every area plan needs
that settled first. This skill decides the stack **at decision level** — language, framework, build
tool, test runner, and the constraints behind them. `/clio:plan infra` later turns it into the
scaffold and toolchain tasks; it does not choose.

Collect the constraints the source states that bind the stack — platform (web, desktop, mobile),
offline, data volume, latency, hosting, licence, a stack the customer already mandates — each with its
quote. Then one of:

**The repo has code.** Read the manifests and lockfiles; the stack is whatever they pin. Propose
nothing. Write it as `## Decisions` with the exact versions, and every source constraint the current
stack does not visibly meet as a ⚠️.

**The repo is empty.** Research 2–3 candidate stacks that fit the constraints. **Prefer Context7**
(fall back to web search, say which). From each stack's own docs: the scaffold command, build and
test commands, the version they need, the layout they recommend — each with source and the date
read. Show them side by side against the constraints, then **ASK**: the user picks one or names their
own. Never pick silently. Record the choice as an ADR (`${CLAUDE_PLUGIN_ROOT}/skills/memo/steps/WRAP-UP.md`
§ ADR, indexed with `"type":"adr"`, `"domain":"infra"`, `"req":["0"]`, then
`${CLAUDE_PLUGIN_ROOT}/bin/clio validate index`).

Scope stops at what the scaffold needs. A library for a domain — OCR, image processing, an HTTP
client — is chosen in that area's spec or plan, not here; the rows it serves are often still ⚠️.

`memory/infra.md` uses the same template; `## Decisions` holds the chosen stack and the commands
the docs gave, `## Source` quotes the constraints and names the ADR.

## 3. Write `.claude/clio/docs/specs/requirements.md`

Its status column answers only "has this been decided?", never "is it built?".

| # | Task | Spec file(s) | Decision status (NOT build status) |
|---|------|--------------|-------------|
| 0 | Stack and scaffold | [memory/infra.md](memory/infra.md) | ✅ ADR <ts>_stack |
| 3 | <requirement, one line> | [memory/viewer.md](memory/viewer.md) | ✅ |
| 7.1 | <requirement> | [memory/ocr.md](memory/ocr.md) | ⚠️ <what is open, + date> |
| 11 | <requirement> | [memory/batch.md](memory/batch.md) | ❌ <what blocks it, + who owes it> |

One row per number agreed in step 1; dotted numbers (`7.1`) are fine, dotted *ranges* are not.
Fill `## By topic keyword` with the words a task would actually use ("ocr", "hotkey", "exif") → file.
Fill the source-priority list; the top entry is the live decision channel.

## 4. File every ⚠️/❌ row as debt

Each open point needs a record, or `clio:context` keeps stopping future sessions with nothing
explaining why. Schema → `${CLAUDE_PLUGIN_ROOT}/skills/memo/steps/DEBT-IT.md`.
```bash
cat >> .claude/clio/database/debt.jsonl <<'EOF'
{"date":"YYYY-MM-DD","id":"<kebab-key>","kind":"spec-blocked","status":"pending","domain":"<domain>","what":["<what is undecided>"],"req":["7.1"],"specs":[".claude/clio/docs/specs/memory/ocr.md"],"docs":[],"code":[],"action":"<what unblocks it>","source":"<source §n>","blocked_by":"<who owes what>","issue":null}
EOF
${CLAUDE_PLUGIN_ROOT}/bin/clio validate all
```
Fix every `FAIL` and every "no open debt record tracks it" before reporting.

## 5. Report

Files written, the stack chosen (or read off the repo) and its ADR, row count, how many ✅ / ⚠️ / ❌,
debt records filed, and — most useful — **the list of questions the user now owes an answer to**, in
one block they can act on. After § 6: each delta as `id` · `blocked_by` · row · action, queue first;
rows moved (old → new, and the source that decided it); records unblocked; any `index.jsonl` record
the change shows to be wrong — reported, never fixed, it is `/clio:memo`'s. Close with the plan
commands: first run → `/clio:plan infra`, then `/clio:plan <area>` per area; later runs → the areas
§ 6 names. Take the baseline last, this report as its summary.

## 6. What the change invalidates — re-ingest and sweep

Specs move while code stands still. For every decision this run added, altered, reversed or resolved:

**Before and after.** A change this run wrote is known. A hand edit shows against the baseline,
uncommitted and new files included:
```bash
${CLAUDE_PLUGIN_ROOT}/bin/clio q spec-diff --stat  # which spec files moved since the last ingest
${CLAUDE_PLUGIN_ROOT}/bin/clio q spec-diff         # the lines
```
The baseline is clone-local by design — `refs/clio/*` is never pushed — so a fresh checkout or another
machine has none. No baseline → a `requirements.md` written by an older Clio may still carry a
`Last ingest:` line naming a commit: `git diff -M <commit> -- .claude/` and read only the spec files;
otherwise reconstruct from dated notes ("Superseded YYYY-MM-DD", the row's status text) and what the
task docs assumed. Still cannot state the before-state → write the delta as new-rule-only and say so
in the report.

**Who built against it.**
```bash
${CLAUDE_PLUGIN_ROOT}/bin/clio q built --req <row> --spec <file>
${CLAUDE_PLUGIN_ROOT}/bin/clio q owed --req <row> --spec <file>
```
Read those docs' `## Decisions`, `## Side Effects`, `## Follow-up`: the code paths named there go in
`code[]`, the docs themselves in `docs[]`. That list is how `/clio:memo` later marks their sections
superseded; without it `clio:context` keeps serving a doc the change made untrue.

**What to write.**

| The change | Write |
|---|---|
| something was built against the old decision | a `spec-delta`; reuse an existing `id` for the same delta. `blocked_by: null` once the new decision is settled — that is the work queue; the concrete missing thing while it is second-hand or awaits a "re-verify" |
| nothing built against it yet | no delta — `/clio:plan` reads the spec itself |
| it answers what an open `spec-blocked` waited on | a new line under that record's `id`, in full, `blocked_by: null`; never edit the old line or change its `kind` |

```bash
cat >> .claude/clio/database/debt.jsonl <<'EOF'
{"date":"YYYY-MM-DD","id":"<kebab-key>","kind":"spec-delta","status":"pending","domain":"checkout","what":["<old> → <new>"],"req":["18"],"specs":[".claude/clio/docs/specs/memory/<file>.md"],"docs":["<task docs built on the old decision>"],"code":["<paths they name>"],"action":"<the rework>","source":"<source §n, date>","blocked_by":null,"issue":null}
EOF
${CLAUDE_PLUGIN_ROOT}/bin/clio validate debt
```

**The row marker**, when the *decision* changed — decisions only, never build state:

| Marker now | The change | Do |
|---|---|---|
| ⚠️ / ❌ | the open point got decided | → ✅, reason = decision + date |
| ⚠️ / ❌ | a different part is still open | keep, rewrite the reason |
| ✅ | reopened or reversed | → ⚠️, say what reopened it |
| ✅ | refined | keep ✅, append the decision + date |

**Moving toward ✅ removes a stop, so it asks first**: every such row in one `AskUserQuestion` as
`row · old reason → new decision · deciding source`; write only the approved ones. The other moves
add or keep a stop — write them. Never flip on a low-priority source; the marker and `blocked_by`
must tell the same story.

**The areas to re-plan.** One line per area whose rows moved, `memory/infra.md` (row `0`) first — it
invalidates `plans/infra.md` and the `rules/` commands `/clio:test` runs:
```text
infra     row 0 · Go 1.22 → 1.23 · rules/go.md names `go test ./...`        →  /clio:plan infra
checkout  row 12 · 3.2 [x] built on the old rule · spec-delta checkout-loyalty
          · cases 3.2-a1, 3.2-s1 test the old rule                           →  /clio:plan checkout
ocr       row 20 still ⚠️                                                      →  nothing to plan yet
```
`/clio:plan` turns each delta into a sub-task and never edits an existing row; `/clio:test`
re-designs the stale cases on that sub-task. This skill writes neither plans nor cases.

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.