---
name: knowledge-base-page
description: Write something up as a page in this knowledge base, or edit a page already here. Use when the user wants to record, save, document, write up or keep what they just worked out - a PowerWorld behaviour, a procedure that worked, a failure mode, a gotcha - so it is not lost when the session ends. Also use when asked to add a page, fix a page, or check whether the pages still hang together (missing index rows, links pointing nowhere), or to share a page with everyone else who uses this kit ("send it in", "share it", "get it to everyone").
topic: agents
---
# Writing a page into this knowledge base
This repo is a knowledge base you are expected to **grow**, not just read. A
session that worked something out and left it in the chat has thrown it away.
The schema below was derived by measuring the pages this repo already ships,
and nearly every one of them follows it. **The pages are authoritative.** If this file and the pages ever disagree, read a
few pages and follow those; then fix this file.
## Before you write: does a page already cover it?
`grep` the four content folders on both the plain-English phrasing and the
likely identifier, several terms at once:
```bash
grep -rn -i "shunt\|Shunt\|GenMVRMax" concepts/ methods/ references/ demos/
```
Read a candidate's `## Abstract` first. It is written to tell you whether the
page is the right one. If it covers the ground, **edit that page instead of
adding a second one.** Two pages on one topic is the failure this base exists
to avoid.
## Where the page goes
| The thing you are recording | Folder | `type:` |
|---|---|---|
| What a thing *is* — a model, a concept, a failure mode | `concepts/` | `concept` |
| How to *do* a thing — a procedure that worked | `methods/` | `method` |
| A code/API surface reconstructed in detail | `references/` | `reference` |
| A worked run with real output, failures left in | `demos/` | `method` (aux-file-cookbook is a `reference`) |
| A package or tool's interface | `concepts/` | `tool` |
| A dataset's shape and quirks | `concepts/` | `dataset` |
## The page shape
Copy this. Every field is required.
```markdown
---
type: concept
domain: tooling
topic: contingency
aliases: [the-slug, a-synonym, what-someone-would-search]
tags: [esapp, powerworld, contingency]
---
# Title In Sentence Case
## Abstract
One paragraph, written so a reader can decide from it alone whether to open the
rest. State the answer here, not a promise of the answer.
## Connections
- **Up:** [Home](../index.md)
- **Used in:** [some-method](../methods/some-method.md)
- **Across:** [a-concept](../concepts/a-concept.md)
## Content
### The actual material
```
**`type:`** is one of `concept`, `method`, `reference`, `tool`, `dataset`.
**`domain:`** is one of `tooling`, `cross-cutting`, `weather`, `synthetic-grid`.
Use one of these. If your page genuinely needs a new value, **say so explicitly**
rather than bending the page to fit or quietly inventing one: adding a value is a
decision about the whole base, not about your page.
**`topic:`** is exactly one of these seven slugs. It puts the page under its heading in
`index.md`'s *Browse by topic* section (add the page's line there too) and becomes the second
label on a shared page's issue:
| slug | covers |
|---|---|
| `power-flow` | solving, convergence, DC/AC, dispatch, OPF |
| `contingency` | building sets, solving, reading violations, ranking, remediation of N-1 results |
| `timestep` | PWW, PFW models, hourly runs and results |
| `design` | adding devices, reviewing and comparing designs, sizing, base-case remediation |
| `case-audit` | case sanity, lint, multipliers, islanded load, impedance completeness |
| `scripting` | esapp, SimAuto, aux files, SCRIPT commands, saving, glossary, version requirements, preflight |
| `agents` | `agents/*.md` and `skills/*/SKILL.md` |
Pick the one a reader would be doing when they need the page. Folders stay as they are; the
topic is a label, not a location.
**`aliases:`** and **`tags:`** are `[bracketed, lists]`. They may wrap across
lines. These are what `grep` finds later, so write the phrasings a future
reader would actually type, not a restatement of the title.
## Links are the point
A page with no outbound links is **refused**: it is unreachable and will never
be found again. Links are relative markdown, not wikilinks:
```markdown
[copper-plate](../concepts/copper-plate.md) <- correct
[[copper-plate]] <- not used in this repo
```
Every link target must exist on disk; a typo or a renamed page is a refusal.
Two or more links passes; one gets a warning.
## Bookkeeping: add the index.md row
`index.md` is the catalog. A new page adds **exactly one** row, in the table
for its folder, in the existing style:
```markdown
| [your-page](concepts/your-page.md) | One line on what it covers and when to open it. |
```
Then add one line under the page's topic heading in *Browse by topic*, at the
end of `index.md`:
```markdown
- [your-page](concepts/your-page.md): the one-line hook.
```
A page with no row warns rather than refusing, because two pages already ship
without one. Add it anyway.
## Check the corpus when you have finished
There is no checker to run; the shape above is the whole schema. Three
questions can only be answered across all the pages at once. Run these from
the repo root when you have added or renamed pages:
```bash
# pages with no row in index.md
python -c "import pathlib;R=pathlib.Path('.');i=(R/'index.md').read_text(encoding='utf-8-sig');print([f'{d}/{p.name}' for d in ('concepts','methods','demos','references') for p in (R/d).glob('*.md') if f'{d}/{p.name}' not in i and p.stem not in i] or 'all pages indexed')"
# links that point at a file that does not exist
python -c "import pathlib,re;R=pathlib.Path('.');print([f'{p}: {t}' for d in ('concepts','methods','demos','references') for p in (R/d).glob('*.md') for t in set(re.findall(r'\]\(([^)#]+\.md)',p.read_text(encoding='utf-8-sig'))) if not (p.parent/t).exists()] or 'no dangling links')"
# page names in ## Connections that were never written as links
python -c "import pathlib,re;R=pathlib.Path('.');L=re.compile(r'\[[^]]*\]\([^)\s]*\)');S=re.compile(r'^[a-z][a-z0-9]*(?:-[a-z0-9]+)+$');B=lambda t:[s for s in [re.split(r'\s+[\u2014\u2013-]\s+|\s*\(',x.replace('\x00',' ').strip(),maxsplit=1)[0].strip('*_[] ') for x in re.split(r'[\u00b7\n,;]',re.sub(r'\*\*[^*]+?:\*\*',' \u00b7 ',L.sub(' \x00 ',t.split('## Connections')[1].split(chr(10)+'## ')[0])))] if S.match(s)] if '## Connections' in t else [];print([f'{p}: {n}' for d in ('concepts','methods','demos','references') for p in (R/d).glob('*.md') for n in B(p.read_text(encoding='utf-8-sig'))] or 'no bare page names')"
```
Paste what they print. The first two must come back clean before you say you
are done.
The third is a signal to read, not a gate. It looks for a page name written as
bare text where a link belongs, which the dangling-link check cannot see: with
no `[](...)` around it there is nothing to resolve. The port that seeded the
manual pages lost 19 cross-references exactly this way, and every check passed.
Ordinary hyphenated English standing alone in a
segment takes the same shape, so `no-op` and `per-solve` are expected hits.
Read each one and decide: a page name gets written as a link or deleted, a word
stays.
## Share it — only when the user says yes
The page now exists on this one machine. Everyone else's kit gets it only when
the maintainer merges it, and most users cannot open a pull request. So the
route is a pre-filled GitHub issue: you scrub the page, build the link, the
user reads it and clicks submit, the maintainer merges, and the next kit update
carries the page to everyone.
This is the same channel as [Filing a defect against this knowledge
base](../../AGENTS.md) in `AGENTS.md`, with its own title and label. The rules
there hold here too: **offer, never file on your own; show the exact text
first; say the tracker is public.**
**1. Ask.** Once the checks above are clean, ask one plain question: *"Want to
share this page so everyone using PoWerHiveMind gets it? I'd strip the case details
first and show you exactly what would go out."* No answer, or no, ends it.
**2. Scrub a copy, not the page.** The user's page may keep their case details;
the shared text may not. The cases this kit drives are often CEII or otherwise
restricted. Write the scrubbed copy to the system temp folder (never inside
`concepts/`, `methods/`, `demos/` or `references/`, where it would count as a
page). Remove or generalize:
- case file names and paths, and any private folder path
- bus numbers and bus names, substation, utility, area, zone and owner names:
write `a 345 kV bus`, `an area`, ``
- device identifiers (`GenID`, circuit IDs, contingency labels from the case)
- parameter values read out of the real case
- counts that fingerprint a case (an exact branch or contingency count): give a
ratio or an order of magnitude instead
- names of people
Keep the mechanism, the rule, the failure mode, the code pattern and the field
names (`GenMVRMax` is the API, not the case). Cite a measurement by date and
Simulator build date, never by case: *"measured 2026-10-12, Simulator build
2026-08-01, on a regional planning model."* Public synthetic grids such as Texas2k
(ACTIVSg2000), Hawaii40 and the IEEE test cases may stay named with their
numbers; they are the preferred worked example. If a point can only be shown on
a real case, keep the point and drop the demonstration.
**3. Build the link.** Write a short note file: one line on what the page
covers, why it matters (what went wrong without it), and the `index.md` row you
added. Then:
```bash
python "${CLAUDE_PLUGIN_ROOT}/skills/knowledge-base-page/engine/share_link.py" --page "" --title "New page: methods/.md" --note-file ""
```
Use `Page update: /.md` for an edit to a page that already
exists. Installed as a Claude Code plugin, `${CLAUDE_PLUGIN_ROOT}` is replaced
with the kit's absolute path; anywhere else, type the absolute path of the
directory holding `AGENTS.md` in its place. The issue carries **two labels**:
the kind (`new-page` by default; `page-defect` or `missing-page` with `--label`) and the
page's topic slug, which the script reads from the copy's `topic:` line
(`--topic ` overrides it; a copy with no valid topic is refused). The
script percent-encodes the title, body and both labels, writes the full issue body to
`.issue.md`, and prints the link. If the page would push the
link past about 8,000 characters (where it stops opening), the link carries
the note only and the user pastes the `.issue.md` text into the issue body
themselves; tell them which file. It also lists anything still shaped like case
data: case file names, personal folder paths, email addresses. Read every hit.
A placeholder such as `out.pwb` is fine; a real case name means the scrub
missed it, so fix the copy and run it again. It cannot see bus numbers or
utility names, so that part of the scrub is still yours.
**4. Show, then hand over.** Give the user, in this order:
1. one line: *"The issue tracker is public. Anyone can read what you submit."*
2. what you removed or changed, as a short list (`bus 12345 → a 345 kV bus`)
3. the final text (the `.issue.md` file)
4. the link, to open, review and submit themselves
Let them edit the text or decline. A shared page is never worth leaking a case.
If `gh` is installed and signed in (`gh auth status`), offer it instead of the
link, still only after they approve the exact text:
```bash
gh issue create --repo ChunSikPark/PoWerHiveMind --title "New page: methods/.md" --label new-page --label --body-file ".issue.md"
```
`--body-file` has no 8,000-character limit. If `gh` refuses because a label
does not exist on the repo, run it again without that `--label`.