[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71RS8BZ5CM98P14XAMVJ3KM":3,"skill-same:01M71RS8BZ5CM98P14XAMVJ3KM":48},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":14,"categories":15,"keywords":16,"publishedAt":21,"linkOnly":22,"markdownContent":23,"markdownBody":24,"frontMatter":25,"refName":26,"commitSha":27,"skillDirectory":28,"forksCount":29,"repositoryHtmlUrl":30,"repositoryHomepageUrl":31,"ownerHtmlUrl":32,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":33,"install":34,"resources":47},"01M71RS8BZ5CM98P14XAMVJ3KM","garrison-project-viewer",null,"gongiskhan\u002Fagent-garrison","gongiskhan","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F1997392?v=4","01M71RKMFR53TKXE308C22QTNZ","01M71RKMJA57KZHNDCBNKTWZRR",1,"MIT","https:\u002F\u002Fgithub.com\u002Fgongiskhan\u002Fagent-garrison\u002Fblob\u002Fmain\u002Ffittings\u002Fseed\u002Fproject-viewer\u002F.apm\u002Fskills\u002Fgarrison-project-viewer\u002FSKILL.md",[],[17,18,19,20],"project","viewer","garrison","garrison project viewer","2026-11-10 13:02:02",false,"---\nname: garrison-project-viewer\ndescription: Analyse a codebase into end-to-end flow manifests and serve them in the Project Viewer — a navigator organised by flows rather than by file structure, with no diagrams and no screenshots. Modes: full-run (first analysis, intake-gated), update (re-anchor after commits, re-narrate only what moved), fix-findings, compare (static reading vs what actually executed), generate-tests, walkthrough (narrate one commit), cleanup (consolidate docs then remove originals — always asked). Use for \"analyse this project\", \"explain this codebase\", \"update the project view\", \"walk me through this commit\", or when a Project Viewer button dispatches a card naming a mode.\n---\n\n# Project Viewer\n\nYou turn a repository into a set of **end-to-end flows** a human or an agent can\nread quickly, and you keep them honest as the code moves.\n\nA flow is a narrative: the start of an operation through to its end. Not a graph,\nnot a file tree, not a diagram. Graphs show everything at once and are unreadable;\na flow shows only what matters, in order.\n\n## The four rules\n\nThese are not style preferences. Breaking any of them makes the output worse than\nnothing, because a reader who cannot trust one sample cannot trust any.\n\n**1. Never type code. Ever.**\nEvery code sample is extracted mechanically by `lib\u002Fsamples.mjs`, which reads\n`git show \u003Csha>:\u003Cpath>`, slices the lines, and records the sha256 of exactly what\nit extracted. You choose the file, the line range, and which lines to highlight.\nYou write the prose. You do not produce code text, not even to \"fix up\" an\nindentation. The renderer re-computes that hash against the repository before it\ndisplays anything, and shows an integrity error instead of code when it does not\nmatch — so a hand-written sample does not become documentation, it becomes a\nvisible failure with your name on it.\n\n**2. The spine comes from execution, not from your reading.**\nRun the thing, then narrate what ran. Reading code and guessing the call order is\nhow you get a confident, wrong document. See \"Getting a spine\" below.\n\n**2b. Highlight the climax, not the setup.**\nThe band is your one editorial act on a sample, so spend it on the line where the\nthing actually happens — the call that can throw, the branch that decides, the\nassignment the rest depends on. Lines that merely set up the interesting line are\ncontext: leave them visible in the window, unbanded. A band spread over the whole\nwindow says \"all of this matters equally\", which is the same as saying nothing.\nRule of thumb: if you cannot say in one clause why a banded line is banded, unband\nit.\n\n**3. Collapse, never omit.**\nTrivial plumbing gets `kind: \"glue\"` and `collapsed: true`, which renders as a\none-line row that expands on click. It stays in the document. An agent following\na flow must never reach a dead end because you decided a step was boring.\n\n**4. No diagrams, no screenshots.**\nNot anywhere. Instead of a screenshot of a page, show the view code with a\ndescription. Anyone who wants a diagram can generate one separately.\n\n## Where things live\n\n```\n\u003Crepo>\u002Fviewer\u002F\n  viewer.json            nav order + the anchor of the last refresh\n  flows\u002F\u003CflowId>.json    one manifest per flow\n  specs\u002F\u003CflowId>.json    un-narrated skeleton from a capture, with the spine frozen\n  findings.json          ONE findings collection — status survives re-analysis\n  intake.json            intake answers (never credentials, only a reference)\n  docs-manifest.json     consolidated docs\n  cleanup-allowlist.json explicit deletion list, never a glob\n\n~\u002F.garrison\u002Fproject-viewer\u002F\u003CprojectKey>\u002F\n  captures\u002F\u003CrunId>\u002F      traces + ordered runtime events\n  cache\u002F                 derived, disposable\n```\n\nManifests live **in the repo** for the same reason the drillbook does: a manifest\nand the commit it narrates must travel together through clone, branch and revert.\nRun-scoped captures stay out of the repo.\n\nRead and write them through `lib\u002Fstore.mjs` only — it validates before writing and\nreads back after, so a crashed write cannot poison the next run. The schema is\n`schema\u002Fflow-manifest.schema.json`; the executable copy is `lib\u002Fmanifest.mjs`.\n\n## Modes\n\nParse `mode: \u003Cm>` from the prompt. No mode named means `full-run`.\nEvery mode except `full-run` and `cleanup` skips intake when `viewer\u002Fintake.json`\nalready exists.\n\n### full-run\n\n1. Run intake (below).\n2. Discover flows, in this source order — **the drillbook wins** wherever it and a\n   test describe the same flow:\n   - `drills\u002Fdrillbook.yml` + `drills\u002Fpages\u002F*.yml` — hand-authored steps, so these\n     are the flows the team actually cares about. Start here.\n   - `tests\u002Fe2e\u002F*.spec.ts` — real execution, real assertions.\n   - the live UI, by vision, for anything neither covers. **A flow with no test is\n     itself a finding** — record it with `category: \"missing-test\"`.\n   - recent commits, for walkthroughs.\n3. Get a spine per flow (below), then narrate.\n4. Fill gaps: where the drillbook and the tests together miss something, decide\n   per gap whether a drillbook step or an e2e spec is the right instrument, and say\n   why in one line.\n5. Validate every manifest, verify every sample by hash, then report the Views link.\n\nRespect the detail level. At the maximum level it means *everything worth knowing\nfor a developer or an agent, minus the trivial* — it never means every file top to\nbottom. Trivial end-to-end actions (saving a form field through to the database)\nget **one representative example per page and no more**.\n\n### update\n\nThe mode that makes this affordable. Do not re-analyse the project.\n\nRun the script first — it does the whole mechanical half and tells you exactly what\nis left:\n\n```\nnode scripts\u002Fupdate.mjs --repo \u003Cpath> [--to \u003Csha>] [--flow \u003Cid>]\n```\n\nEach flow is diffed from **its own** anchor, not from one global one, because flows\ndrift apart. Untouched spans are rebased by the hunk offsets above them, re-extracted\nand hash-verified, then re-stamped `fresh` **with no model call**. The run prints how\nmany steps were carried forward for free and which ones need you.\n\nThen: re-narrate only the steps it listed as `stale` or `invalidated`, re-extract\ntheir samples, save, and `POST \u002Fapi\u002Frender`.\n\nTwo things not to route around:\n\n- A flow's anchor **does not advance** while anything in it is stale, so a flow never\n  claims to describe a commit it only half describes. Do not set the anchor by hand.\n- `the span moved in a way the rebase could not follow` is the hash safety net\n  working. Re-extract that step properly; do not widen the span until it matches.\n\nIf a rebase produced a hash mismatch, the step is already marked `stale` — that is\nthe safety net working, not a bug to route around. Re-extract it properly.\n\n### fix-findings\n\nInput is finding ids from the card, or every finding with status `accepted`.\nRead the flow before fixing: the flow tells you how the code is actually used,\nwhich is usually what the finding is about. Run the repo's own gates\n(`npm run typecheck`, the relevant tests). Set each fixed finding to `fixed` via\n`store.setFindingStatus`. Then run `update` for the affected flows.\n\n### compare\n\nStatic reading versus what actually executed. **The whole mode is mechanical** —\nrun the script, then read its output; do not hand-assemble the report.\n\n```\nnode scripts\u002Fcompare.mjs --repo \u003Cpath> --all-runs [--scope src,packages]\n```\n\nIt writes `compare-report.json` into the store's cache dir, with the markdown block\nthe viewer's copy button serves. Then `POST \u002Fapi\u002Frender`.\n\nWhat you must understand before quoting any of it:\n\n- **Candidates, never conclusions.** References are counted by matching text, so\n  comments and strings count too. That inflates counts, which means the scan errs\n  towards calling code live. It misses dead code; it does not confidently condemn\n  live code. Do not delete on its word alone.\n- **The buckets answer different questions.** A value export nothing references is\n  deletable. A symbol used inside its own file has a surplus `export`, not dead\n  code — those are different repairs and the report says which. An unreferenced\n  TypeScript type changes nothing that ships.\n- **Read `blindSpots` and `byArea` before summarising.** `byArea` will usually show\n  most candidates sitting in vendored packages or an archive; saying \"800\n  dead-code candidates\" without that split is a scary number that means nothing.\n- **Never read the unexercised bucket as a coverage verdict.** It lists pages no\n  capture landed on, and if you captured one spec out of twenty, that is a\n  statement about your captures, not about the test suite. The report says so in\n  `blindSpots`; repeat it whenever you quote the number.\n\nFile what is actionable as findings, with `evidence` set to `static`, `runtime` or\n`both`.\n\n### generate-tests\n\nWrite the test the flow deserves, not a test for the coverage number. Assert\nbehaviour a user would notice. If the new test fails because the code is wrong,\nthat is a finding — do not bend the test to match a bug.\n\n### walkthrough\n\nNarrate one commit as a flow whose spine is its diff hunks, via\n`samples.commitDiffSamples`. Group hunks into states by theme rather than by file\nso it reads as a narrative. Fold mechanical hunks into collapsed steps; every hunk\nof the commit must be present somewhere.\n\n**No sha in the card means the uncommitted working tree.** Same job, different\nspine source: `samples.workingTreeDiffSamples`. Anchor at HEAD with\n`anchoredAt.dirty: true`, `source: \"commit\"`, and no `provenance.commitSha` —\nthere is no commit yet. Say in the summary that this narrates work in progress;\nrefresh skips dirty flows by design, and the real commit walkthrough supersedes\nthis one when the changes land. If the tree turns out clean, say so and stop.\n\n### cleanup\n\n**Destructive. Always asked, never a default.** See \"Cleanup\" below.\n\n## Getting a spine\n\nThe brief asks for per-test coverage. Be honest about what is reliable here.\n\n**What does not work:** V8 line coverage through a Next dev server. Coverage\nflushes once per process on exit, so per-test attribution needs inspector calls\nbetween every test, and mapping byte offsets back through dev bundles and their\nsource maps is brittle in exactly the way that produces a confident wrong answer.\nDo not build the spine on it.\n\n**What also does not work: parsing the trace zip.** That was the obvious plan and\nit is a trap. The zip's internals are an implementation detail with no\ncompatibility promise, so a parser for it breaks on a version bump — and it breaks\nin the worst way, still producing *a* spine, just a wrong one.\n\n**What to do instead.** One command does the whole mechanical part:\n\n```\nnode fittings\u002Fseed\u002Fproject-viewer\u002Fscripts\u002Fcapture-runtime.mjs \\\n  --repo \u003Cpath> --spec tests\u002Fe2e\u002F\u003Cfile>.spec.ts \\\n  [--grep \"\u003Ctest title>\"] [--project desktop-chromium]\n```\n\nPass `--project` when the repo defines several viewport projects, or you capture the\nsame flow once per viewport for nothing.\n\nThat runs the test under `runtime\u002Fpv-reporter.mjs` — a Playwright *reporter*, which\nis public API, so a breaking change is a load error you can see rather than a\nsilent wrong answer. Then it:\n\n1. records every meaningful action in order, with its selector and timing;\n2. stitches each action to the URL it happened on (a `goto` sets it, later actions\n   inherit it);\n3. resolves each URL to the file that served it with `lib\u002Froute-resolve.mjs` —\n   Next's app router is a pure function of the filesystem, so `\u002Fapi\u002Fquarters\u002Fhooks`\n   is `src\u002Fapp\u002Fapi\u002Fquarters\u002F[type]\u002Froute.ts` by derivation, not by guessing. An\n   unresolvable URL is recorded as unmapped; it is never guessed;\n4. walks that file's imports two levels deep with `lib\u002Fimport-graph.mjs` and ranks\n   them, giving you a short ordered candidate list;\n5. writes one capture per test to\n   `~\u002F.garrison\u002Fproject-viewer\u002F\u003CprojectKey>\u002Fcaptures\u002F\u003CrunId>\u002F`.\n\n**The drillbook, when the project has one.** It is the source to prefer, because it\nis the one place a human already wrote down what each page is FOR — a ranked import\nlist tells you which files are nearby, a drillbook step tells you what matters.\n\n```\nnode scripts\u002Fcapture-drillbook.mjs --repo \u003Cpath> [--page \u003Cid>]\n```\n\nNothing is executed: it reads `drills\u002Fdrillbook.yml` and the page files, resolves each\ndeclared path to the file that serves it, and records the author's own words as the\nstep's `intent`. The capture comes out `status: \"not-executed\"`, which is the truth —\ndo not narrate it as though a run had passed. Each declared page state becomes its own\nstate in the spec even though they share a path, because a page empty and the same\npage full are two things worth reading about.\n\nThe run prints the book's `globalRules`. Read them before you write about any page.\n\n**Then turn the capture into a spec, and narrate the spec.** Do not hand-write a\nspec from a capture you read — one command does it, and doing it by hand is how a\nstep goes missing:\n\n```\nnode scripts\u002Fbuild-flow.mjs --repo \u003Cpath> --from-run \u003CrunId> [--test \u003Ckey>]\n```\n\nThat writes `viewer\u002Fspecs\u002F\u003CflowId>.json`: one state per page in first-visit order,\none step per action in the order it happened, every description empty, and a frozen\n`spine`. Each step carries a `hints` block — the route file, its layouts, the ranked\ncandidates, the spec line that caused the action, and any admissions the capture made\nabout itself.\n\nYour job starts there: pick, from the candidates, the file and span that explains\neach step, and write the prose. You are choosing between real imports that really\nexist — not reconstructing a call graph from imagination.\n\nFill in `description`, and `file` \u002F `startLine` \u002F `endLine` \u002F `highlights` per step.\nDelete nothing from the spine.\n\n**Also write `logic` on every state** — the functional narration for the logic view\n(the code-free flowchart a reader can toggle to). One or two sentences on what this\nstage ACHIEVES in the domain's terms and why it exists: no file names, no function\nnames, no code talk. It is not a summary of the step descriptions — those say how;\nthis says what and why, for the reader who has not opened the code view yet. Written\nin the same language as the descriptions. A state without it renders mechanically\nand the page says the narration is missing, so skipping it is visible, not silent.\n\nThen build the manifest:\n\n```\nnode scripts\u002Fbuild-flow.mjs --repo \u003Cpath> --spec viewer\u002Fspecs\u002F\u003CflowId>.json\n```\n\nThe build **checks the manifest against the frozen spine and refuses it** if a\nrecorded action is missing or reordered. Adding steps is fine and expected — folding\ntrivial glue into a one-line step is the instruction. Losing one is not. If it\nrefuses, the fix is to put the step back, collapsed; it is never to edit the spine.\n\nA step whose URL resolved to no file arrives as `kind: \"glue\"` with an admission\nsaying so. Narrate it as glue or file a finding — do not promote it to `code` by\ninventing a file for it.\n\n**Read a capture's own admissions before you narrate from it.** Three fields are\nthere specifically to stop you writing something confident and wrong:\n\n- `via` on a route event — the reader passed through a redirect stub to get here.\n  Narrate the destination, and mention the hop only if it matters to the story.\n- `redirects: \"dynamic\"` — the page redirects somewhere the tool could not derive,\n  so the resolved file is the *stub*, not the destination. Do not narrate it as the\n  destination. Say the target is computed, or open the file and find out.\n- `requestedUrl` differing from `url` — the test asked for one path and landed on\n  another. `url` is where the reader actually was. It appears only on the navigation\n  that was actually redirected, never on the actions that followed it.\n- `intent` in a step's hints — a person's words about what this page is for. It\n  outranks every candidate ranking underneath it.\n- `runStatus` — `not-executed` means nothing ran (a drillbook), and a failing run\n  still yields a real spine. Neither may be narrated as a passing one.\n\nIf a step's route looks wrong to you, file a finding. Narrating around a wrong route\nis how a viewer loses the trust it exists to hold.\n\n**Known limit, state it rather than hide it.** The reporter sees actions, not\nnetwork requests, so a route is resolved from the page URL rather than from the\nrequests an action fired. That is accurate for navigations and good enough for\nin-page actions; sharpening it needs a Playwright fixture, which would mean editing\nthe target repo's tests. If a step's route looks wrong, say so in a finding instead\nof narrating around it.\n\n**Vision fallback**, for flows with no test: drive the live pages, record the same\nordered actions, and emit a capture with `source: \"ui\"`. The shape is identical on\npurpose, so everything downstream is indifferent to how the spine was obtained.\nAlso file the missing-test finding — `captures.mjs` has `noCoverageFinding` for it.\nA `source: \"ui\"` capture goes through `--from-run` and the spine check exactly like an\ne2e one; nothing downstream cares how the spine was obtained.\n\n## External systems\n\nRepresent them as samples, so both a human and an agent grasp them at a glance:\n\n- **Database** — `kind: \"db\"`, `asciiSample` holding an ASCII table with real rows\n  where you can read them, plausible ones where the table is empty. Say which.\n- **File writes** — `kind: \"filewrite\"`, `asciiSample` showing the shape (the JSON\n  structure, say) with the important parts present.\n- **Dependencies** — `kind: \"dep\"` with a short `note`. Do not explore a dependency\n  in depth; explain only why it matters to the code at hand.\n\n## Intake\n\nAsk these at the start of a `full-run`, and at the start of `cleanup`. Keep it\nshort — this is five questions, not an interview.\n\n1. **Level of detail.** Offer `overview`, `standard`, `deep`. **Never offer \"cover\n   every file from top to bottom\"** — that option does not exist. Explain that the\n   user picks the level and you decide what it means for this codebase.\n2. **Flows or corner cases they especially care about.** Free text. Empty is fine.\n3. **Whether to spend tokens on real runs when the app's own functionality calls an\n   LLM**, and if so a ballpark budget per run. This cap applies **only** to\n   exercising the app's LLM features. The analysis itself is not capped by it.\n4. **Test credentials** for the system and its dependencies. **Inspect the project\n   first** — `.env.example`, seed scripts, the README, the drillbook — and present\n   what you found, so the user confirms rather than remembering. Credentials go to\n   the vault or wherever the user says. **Never into `intake.json`, never into a\n   manifest, never into a prompt.** Store a reference only.\n5. **Whether to do the documentation consolidation and cleanup.** Always asked.\n6. **Which language to write the descriptions in** — English or Portuguese.\n   Ask because it is not inferable: the interface is bilingual and flips with a\n   toggle, but the prose you write is stored in the manifest and does not.\n   Default to the language the repository's own docs are written in, and say which\n   you picked. Write in ONE language: a `{en, pt}` map is valid in the schema, but\n   filling both means writing every description twice, which doubles the single\n   most expensive part of this whole job. Only do that if the user asks outright.\n\nPersist to `viewer\u002Fintake.json`:\n`{answeredAt, detailLevel, flowsOfInterest[], llmRunBudget: {perRun, used}, credentialsRef, cleanupArmed, proseLanguage}`.\nAlso write the chosen language as `proseLang` into `viewer\u002Fviewer.json` — that copy\nis the one the SERVER reads, so the viewer can tell a reader whose interface\nlanguage differs that the prose will not follow the toggle. **Preserve it when you\nrewrite the index**: `viewer.json` may carry fields you did not set this run, and\nrebuilding it from scratch silently deletes them. Read, modify, write back.\nOn a later run, ask **one** question instead of five: summarise what is on file and\noffer to reuse it, re-asking only what the user names. Increment\n`llmRunBudget.used` so the budget survives across runs.\n\n## Findings\n\nRecord problems as you go, each tied to the flow and the span it concerns, in\n`viewer\u002Ffindings.json`. A finding's `status` is the reason there is one collection\nrather than a copy per flow: **a dismissed finding must not come back on the next\nanalysis.** Never resurrect a `dismissed` finding; if you believe it was wrongly\ndismissed, say so in the report instead of silently reopening it.\n\n## Cleanup\n\nPart of the product, not a side feature — and the destructive half is enforced in\ncode, like every other guarantee here: **the only way a doc leaves the repo is\n`scripts\u002Fcleanup.mjs`, and it refuses to delete anything whose content the machine\ncannot prove already lives in the viewer.** You never `rm` a doc. Not once.\n\n1. **Refuse** unless intake recorded `cleanupArmed: true` (cleanup.mjs checks this\n   too, but you refuse first — do not make the tool do your saying-no for you).\n2. **Propose** `viewer\u002Fcleanup-allowlist.json`: `{entries: [{path, reason}]}` — an\n   explicit list of literal paths. **Never a glob. Never an extension pattern.**\n   (cleanup.mjs rejects glob characters outright.) In this repo, roughly half of\n   all `.md` files are `SKILL.md` and `README.md` inside `fittings\u002Fseed\u002F` and\n   `.codex\u002Fskills\u002F` — executable payload, not documentation; a `*.md` sweep would\n   delete dozens of working fittings.\n3. **Hard exclusions**, enforced by cleanup.mjs, no exceptions: anything under\n   `fittings\u002Fseed\u002F**`, `.codex\u002Fskills\u002F**`, `site\u002F**`, `public\u002Ficons\u002F**`; and\n   `README.md`, `CLAUDE.md`, `AGENTS.md`, `SKILL.md` are slimmed, never deleted.\n4. **Consolidate before destroying — through `store.consolidateDoc` only.** It\n   copies the doc into `viewer\u002Fdocs\u002F`, registers it in `docs-manifest.json` with\n   the sha256 of the exact bytes it copied, and that hash is the deletion gate:\n   cleanup.mjs deletes a file only when the recorded hash matches the file's\n   *current* bytes and the copy still renders. A doc edited after consolidation is\n   undeletable until you consolidate it again — by design. Verify each doc renders\n   at `\u002Fdocs` before going on.\n5. **Ask.** Present the list with counts and reasons. On approval, record\n   `approvedAt` on the allowlist. No `approvedAt`, no deletion — cleanup.mjs\n   refuses an unapproved list.\n6. **Delete**: `node scripts\u002Fcleanup.mjs --repo \u003Cpath>` first (dry run — show the\n   user its output), then with `--apply`. The run is all-or-nothing: one entry\n   failing any gate refuses the whole run, so the removal set always equals the\n   approved list exactly.\n7. **Slimming rules.** `CLAUDE.md` and `AGENTS.md` keep the most important rules,\n   not the decisions, plus a pointer to the viewer. The README keeps setup and the\n   ordinary basics, then links to the viewer for everything else. Remove only what\n   now lives in the viewer; never restructure surviving guidance. Slimming is an\n   edit, not a deletion — it never goes through cleanup.mjs, and it still keeps\n   the original readable in the viewer first via `store.consolidateDoc`.\n8. **Keep deletions out of any feature commit.** And note the standing repo rule:\n   **do not create a git branch unless the user explicitly tells you to.** If the\n   separation needs a branch, ask for it.\n\n## Prompt buttons\n\nThe viewer is the control surface: its buttons POST to the fitting's own server,\nwhich composes a prompt from `lib\u002Fprompts.mjs` and dispatches it. Long work goes\nto a kanban card, because a chat turn caps out around five minutes and an analysis\nrun takes longer than that. Only a short question goes to a gateway turn.\n\nWhen you are picked up from such a card, the prompt names the mode and points at\n`viewer\u002F` rather than inlining content. Read the store; do not expect the prompt to\ncarry the data.\n\n## Before you say you are done\n\n- Every manifest passes `validateFlow`.\n- Every sample verifies by hash — check, do not assume.\n- No step describes code the reader cannot see, and no sample is shown without a\n  description.\n- Collapsed steps are collapsed, not missing.\n- The findings you filed point at spans that exist.\n- If you could not do part of the job, say which part and why. A gap you name is\n  useful; a gap you paper over destroys the trust the whole tool runs on.\n","# Project Viewer\n\nYou turn a repository into a set of **end-to-end flows** a human or an agent can\nread quickly, and you keep them honest as the code moves.\n\nA flow is a narrative: the start of an operation through to its end. Not a graph,\nnot a file tree, not a diagram. Graphs show everything at once and are unreadable;\na flow shows only what matters, in order.\n\n## The four rules\n\nThese are not style preferences. Breaking any of them makes the output worse than\nnothing, because a reader who cannot trust one sample cannot trust any.\n\n**1. Never type code. Ever.**\nEvery code sample is extracted mechanically by `lib\u002Fsamples.mjs`, which reads\n`git show \u003Csha>:\u003Cpath>`, slices the lines, and records the sha256 of exactly what\nit extracted. You choose the file, the line range, and which lines to highlight.\nYou write the prose. You do not produce code text, not even to \"fix up\" an\nindentation. The renderer re-computes that hash against the repository before it\ndisplays anything, and shows an integrity error instead of code when it does not\nmatch — so a hand-written sample does not become documentation, it becomes a\nvisible failure with your name on it.\n\n**2. The spine comes from execution, not from your reading.**\nRun the thing, then narrate what ran. Reading code and guessing the call order is\nhow you get a confident, wrong document. See \"Getting a spine\" below.\n\n**2b. Highlight the climax, not the setup.**\nThe band is your one editorial act on a sample, so spend it on the line where the\nthing actually happens — the call that can throw, the branch that decides, the\nassignment the rest depends on. Lines that merely set up the interesting line are\ncontext: leave them visible in the window, unbanded. A band spread over the whole\nwindow says \"all of this matters equally\", which is the same as saying nothing.\nRule of thumb: if you cannot say in one clause why a banded line is banded, unband\nit.\n\n**3. Collapse, never omit.**\nTrivial plumbing gets `kind: \"glue\"` and `collapsed: true`, which renders as a\none-line row that expands on click. It stays in the document. An agent following\na flow must never reach a dead end because you decided a step was boring.\n\n**4. No diagrams, no screenshots.**\nNot anywhere. Instead of a screenshot of a page, show the view code with a\ndescription. Anyone who wants a diagram can generate one separately.\n\n## Where things live\n\n```\n\u003Crepo>\u002Fviewer\u002F\n  viewer.json            nav order + the anchor of the last refresh\n  flows\u002F\u003CflowId>.json    one manifest per flow\n  specs\u002F\u003CflowId>.json    un-narrated skeleton from a capture, with the spine frozen\n  findings.json          ONE findings collection — status survives re-analysis\n  intake.json            intake answers (never credentials, only a reference)\n  docs-manifest.json     consolidated docs\n  cleanup-allowlist.json explicit deletion list, never a glob\n\n~\u002F.garrison\u002Fproject-viewer\u002F\u003CprojectKey>\u002F\n  captures\u002F\u003CrunId>\u002F      traces + ordered runtime events\n  cache\u002F                 derived, disposable\n```\n\nManifests live **in the repo** for the same reason the drillbook does: a manifest\nand the commit it narrates must travel together through clone, branch and revert.\nRun-scoped captures stay out of the repo.\n\nRead and write them through `lib\u002Fstore.mjs` only — it validates before writing and\nreads back after, so a crashed write cannot poison the next run. The schema is\n`schema\u002Fflow-manifest.schema.json`; the executable copy is `lib\u002Fmanifest.mjs`.\n\n## Modes\n\nParse `mode: \u003Cm>` from the prompt. No mode named means `full-run`.\nEvery mode except `full-run` and `cleanup` skips intake when `viewer\u002Fintake.json`\nalready exists.\n\n### full-run\n\n1. Run intake (below).\n2. Discover flows, in this source order — **the drillbook wins** wherever it and a\n   test describe the same flow:\n   - `drills\u002Fdrillbook.yml` + `drills\u002Fpages\u002F*.yml` — hand-authored steps, so these\n     are the flows the team actually cares about. Start here.\n   - `tests\u002Fe2e\u002F*.spec.ts` — real execution, real assertions.\n   - the live UI, by vision, for anything neither covers. **A flow with no test is\n     itself a finding** — record it with `category: \"missing-test\"`.\n   - recent commits, for walkthroughs.\n3. Get a spine per flow (below), then narrate.\n4. Fill gaps: where the drillbook and the tests together miss something, decide\n   per gap whether a drillbook step or an e2e spec is the right instrument, and say\n   why in one line.\n5. Validate every manifest, verify every sample by hash, then report the Views link.\n\nRespect the detail level. At the maximum level it means *everything worth knowing\nfor a developer or an agent, minus the trivial* — it never means every file top to\nbottom. Trivial end-to-end actions (saving a form field through to the database)\nget **one representative example per page and no more**.\n\n### update\n\nThe mode that makes this affordable. Do not re-analyse the project.\n\nRun the script first — it does the whole mechanical half and tells you exactly what\nis left:\n\n```\nnode scripts\u002Fupdate.mjs --repo \u003Cpath> [--to \u003Csha>] [--flow \u003Cid>]\n```\n\nEach flow is diffed from **its own** anchor, not from one global one, because flows\ndrift apart. Untouched spans are rebased by the hunk offsets above them, re-extracted\nand hash-verified, then re-stamped `fresh` **with no model call**. The run prints how\nmany steps were carried forward for free and which ones need you.\n\nThen: re-narrate only the steps it listed as `stale` or `invalidated`, re-extract\ntheir samples, save, and `POST \u002Fapi\u002Frender`.\n\nTwo things not to route around:\n\n- A flow's anchor **does not advance** while anything in it is stale, so a flow never\n  claims to describe a commit it only half describes. Do not set the anchor by hand.\n- `the span moved in a way the rebase could not follow` is the hash safety net\n  working. Re-extract that step properly; do not widen the span until it matches.\n\nIf a rebase produced a hash mismatch, the step is already marked `stale` — that is\nthe safety net working, not a bug to route around. Re-extract it properly.\n\n### fix-findings\n\nInput is finding ids from the card, or every finding with status `accepted`.\nRead the flow before fixing: the flow tells you how the code is actually used,\nwhich is usually what the finding is about. Run the repo's own gates\n(`npm run typecheck`, the relevant tests). Set each fixed finding to `fixed` via\n`store.setFindingStatus`. Then run `update` for the affected flows.\n\n### compare\n\nStatic reading versus what actually executed. **The whole mode is mechanical** —\nrun the script, then read its output; do not hand-assemble the report.\n\n```\nnode scripts\u002Fcompare.mjs --repo \u003Cpath> --all-runs [--scope src,packages]\n```\n\nIt writes `compare-report.json` into the store's cache dir, with the markdown block\nthe viewer's copy button serves. Then `POST \u002Fapi\u002Frender`.\n\nWhat you must understand before quoting any of it:\n\n- **Candidates, never conclusions.** References are counted by matching text, so\n  comments and strings count too. That inflates counts, which means the scan errs\n  towards calling code live. It misses dead code; it does not confidently condemn\n  live code. Do not delete on its word alone.\n- **The buckets answer different questions.** A value export nothing references is\n  deletable. A symbol used inside its own file has a surplus `export`, not dead\n  code — those are different repairs and the report says which. An unreferenced\n  TypeScript type changes nothing that ships.\n- **Read `blindSpots` and `byArea` before summarising.** `byArea` will usually show\n  most candidates sitting in vendored packages or an archive; saying \"800\n  dead-code candidates\" without that split is a scary number that means nothing.\n- **Never read the unexercised bucket as a coverage verdict.** It lists pages no\n  capture landed on, and if you captured one spec out of twenty, that is a\n  statement about your captures, not about the test suite. The report says so in\n  `blindSpots`; repeat it whenever you quote the number.\n\nFile what is actionable as findings, with `evidence` set to `static`, `runtime` or\n`both`.\n\n### generate-tests\n\nWrite the test the flow deserves, not a test for the coverage number. Assert\nbehaviour a user would notice. If the new test fails because the code is wrong,\nthat is a finding — do not bend the test to match a bug.\n\n### walkthrough\n\nNarrate one commit as a flow whose spine is its diff hunks, via\n`samples.commitDiffSamples`. Group hunks into states by theme rather than by file\nso it reads as a narrative. Fold mechanical hunks into collapsed steps; every hunk\nof the commit must be present somewhere.\n\n**No sha in the card means the uncommitted working tree.** Same job, different\nspine source: `samples.workingTreeDiffSamples`. Anchor at HEAD with\n`anchoredAt.dirty: true`, `source: \"commit\"`, and no `provenance.commitSha` —\nthere is no commit yet. Say in the summary that this narrates work in progress;\nrefresh skips dirty flows by design, and the real commit walkthrough supersedes\nthis one when the changes land. If the tree turns out clean, say so and stop.\n\n### cleanup\n\n**Destructive. Always asked, never a default.** See \"Cleanup\" below.\n\n## Getting a spine\n\nThe brief asks for per-test coverage. Be honest about what is reliable here.\n\n**What does not work:** V8 line coverage through a Next dev server. Coverage\nflushes once per process on exit, so per-test attribution needs inspector calls\nbetween every test, and mapping byte offsets back through dev bundles and their\nsource maps is brittle in exactly the way that produces a confident wrong answer.\nDo not build the spine on it.\n\n**What also does not work: parsing the trace zip.** That was the obvious plan and\nit is a trap. The zip's internals are an implementation detail with no\ncompatibility promise, so a parser for it breaks on a version bump — and it breaks\nin the worst way, still producing *a* spine, just a wrong one.\n\n**What to do instead.** One command does the whole mechanical part:\n\n```\nnode fittings\u002Fseed\u002Fproject-viewer\u002Fscripts\u002Fcapture-runtime.mjs \\\n  --repo \u003Cpath> --spec tests\u002Fe2e\u002F\u003Cfile>.spec.ts \\\n  [--grep \"\u003Ctest title>\"] [--project desktop-chromium]\n```\n\nPass `--project` when the repo defines several viewport projects, or you capture the\nsame flow once per viewport for nothing.\n\nThat runs the test under `runtime\u002Fpv-reporter.mjs` — a Playwright *reporter*, which\nis public API, so a breaking change is a load error you can see rather than a\nsilent wrong answer. Then it:\n\n1. records every meaningful action in order, with its selector and timing;\n2. stitches each action to the URL it happened on (a `goto` sets it, later actions\n   inherit it);\n3. resolves each URL to the file that served it with `lib\u002Froute-resolve.mjs` —\n   Next's app router is a pure function of the filesystem, so `\u002Fapi\u002Fquarters\u002Fhooks`\n   is `src\u002Fapp\u002Fapi\u002Fquarters\u002F[type]\u002Froute.ts` by derivation, not by guessing. An\n   unresolvable URL is recorded as unmapped; it is never guessed;\n4. walks that file's imports two levels deep with `lib\u002Fimport-graph.mjs` and ranks\n   them, giving you a short ordered candidate list;\n5. writes one capture per test to\n   `~\u002F.garrison\u002Fproject-viewer\u002F\u003CprojectKey>\u002Fcaptures\u002F\u003CrunId>\u002F`.\n\n**The drillbook, when the project has one.** It is the source to prefer, because it\nis the one place a human already wrote down what each page is FOR — a ranked import\nlist tells you which files are nearby, a drillbook step tells you what matters.\n\n```\nnode scripts\u002Fcapture-drillbook.mjs --repo \u003Cpath> [--page \u003Cid>]\n```\n\nNothing is executed: it reads `drills\u002Fdrillbook.yml` and the page files, resolves each\ndeclared path to the file that serves it, and records the author's own words as the\nstep's `intent`. The capture comes out `status: \"not-executed\"`, which is the truth —\ndo not narrate it as though a run had passed. Each declared page state becomes its own\nstate in the spec even though they share a path, because a page empty and the same\npage full are two things worth reading about.\n\nThe run prints the book's `globalRules`. Read them before you write about any page.\n\n**Then turn the capture into a spec, and narrate the spec.** Do not hand-write a\nspec from a capture you read — one command does it, and doing it by hand is how a\nstep goes missing:\n\n```\nnode scripts\u002Fbuild-flow.mjs --repo \u003Cpath> --from-run \u003CrunId> [--test \u003Ckey>]\n```\n\nThat writes `viewer\u002Fspecs\u002F\u003CflowId>.json`: one state per page in first-visit order,\none step per action in the order it happened, every description empty, and a frozen\n`spine`. Each step carries a `hints` block — the route file, its layouts, the ranked\ncandidates, the spec line that caused the action, and any admissions the capture made\nabout itself.\n\nYour job starts there: pick, from the candidates, the file and span that explains\neach step, and write the prose. You are choosing between real imports that really\nexist — not reconstructing a call graph from imagination.\n\nFill in `description`, and `file` \u002F `startLine` \u002F `endLine` \u002F `highlights` per step.\nDelete nothing from the spine.\n\n**Also write `logic` on every state** — the functional narration for the logic view\n(the code-free flowchart a reader can toggle to). One or two sentences on what this\nstage ACHIEVES in the domain's terms and why it exists: no file names, no function\nnames, no code talk. It is not a summary of the step descriptions — those say how;\nthis says what and why, for the reader who has not opened the code view yet. Written\nin the same language as the descriptions. A state without it renders mechanically\nand the page says the narration is missing, so skipping it is visible, not silent.\n\nThen build the manifest:\n\n```\nnode scripts\u002Fbuild-flow.mjs --repo \u003Cpath> --spec viewer\u002Fspecs\u002F\u003CflowId>.json\n```\n\nThe build **checks the manifest against the frozen spine and refuses it** if a\nrecorded action is missing or reordered. Adding steps is fine and expected — folding\ntrivial glue into a one-line step is the instruction. Losing one is not. If it\nrefuses, the fix is to put the step back, collapsed; it is never to edit the spine.\n\nA step whose URL resolved to no file arrives as `kind: \"glue\"` with an admission\nsaying so. Narrate it as glue or file a finding — do not promote it to `code` by\ninventing a file for it.\n\n**Read a capture's own admissions before you narrate from it.** Three fields are\nthere specifically to stop you writing something confident and wrong:\n\n- `via` on a route event — the reader passed through a redirect stub to get here.\n  Narrate the destination, and mention the hop only if it matters to the story.\n- `redirects: \"dynamic\"` — the page redirects somewhere the tool could not derive,\n  so the resolved file is the *stub*, not the destination. Do not narrate it as the\n  destination. Say the target is computed, or open the file and find out.\n- `requestedUrl` differing from `url` — the test asked for one path and landed on\n  another. `url` is where the reader actually was. It appears only on the navigation\n  that was actually redirected, never on the actions that followed it.\n- `intent` in a step's hints — a person's words about what this page is for. It\n  outranks every candidate ranking underneath it.\n- `runStatus` — `not-executed` means nothing ran (a drillbook), and a failing run\n  still yields a real spine. Neither may be narrated as a passing one.\n\nIf a step's route looks wrong to you, file a finding. Narrating around a wrong route\nis how a viewer loses the trust it exists to hold.\n\n**Known limit, state it rather than hide it.** The reporter sees actions, not\nnetwork requests, so a route is resolved from the page URL rather than from the\nrequests an action fired. That is accurate for navigations and good enough for\nin-page actions; sharpening it needs a Playwright fixture, which would mean editing\nthe target repo's tests. If a step's route looks wrong, say so in a finding instead\nof narrating around it.\n\n**Vision fallback**, for flows with no test: drive the live pages, record the same\nordered actions, and emit a capture with `source: \"ui\"`. The shape is identical on\npurpose, so everything downstream is indifferent to how the spine was obtained.\nAlso file the missing-test finding — `captures.mjs` has `noCoverageFinding` for it.\nA `source: \"ui\"` capture goes through `--from-run` and the spine check exactly like an\ne2e one; nothing downstream cares how the spine was obtained.\n\n## External systems\n\nRepresent them as samples, so both a human and an agent grasp them at a glance:\n\n- **Database** — `kind: \"db\"`, `asciiSample` holding an ASCII table with real rows\n  where you can read them, plausible ones where the table is empty. Say which.\n- **File writes** — `kind: \"filewrite\"`, `asciiSample` showing the shape (the JSON\n  structure, say) with the important parts present.\n- **Dependencies** — `kind: \"dep\"` with a short `note`. Do not explore a dependency\n  in depth; explain only why it matters to the code at hand.\n\n## Intake\n\nAsk these at the start of a `full-run`, and at the start of `cleanup`. Keep it\nshort — this is five questions, not an interview.\n\n1. **Level of detail.** Offer `overview`, `standard`, `deep`. **Never offer \"cover\n   every file from top to bottom\"** — that option does not exist. Explain that the\n   user picks the level and you decide what it means for this codebase.\n2. **Flows or corner cases they especially care about.** Free text. Empty is fine.\n3. **Whether to spend tokens on real runs when the app's own functionality calls an\n   LLM**, and if so a ballpark budget per run. This cap applies **only** to\n   exercising the app's LLM features. The analysis itself is not capped by it.\n4. **Test credentials** for the system and its dependencies. **Inspect the project\n   first** — `.env.example`, seed scripts, the README, the drillbook — and present\n   what you found, so the user confirms rather than remembering. Credentials go to\n   the vault or wherever the user says. **Never into `intake.json`, never into a\n   manifest, never into a prompt.** Store a reference only.\n5. **Whether to do the documentation consolidation and cleanup.** Always asked.\n6. **Which language to write the descriptions in** — English or Portuguese.\n   Ask because it is not inferable: the interface is bilingual and flips with a\n   toggle, but the prose you write is stored in the manifest and does not.\n   Default to the language the repository's own docs are written in, and say which\n   you picked. Write in ONE language: a `{en, pt}` map is valid in the schema, but\n   filling both means writing every description twice, which doubles the single\n   most expensive part of this whole job. Only do that if the user asks outright.\n\nPersist to `viewer\u002Fintake.json`:\n`{answeredAt, detailLevel, flowsOfInterest[], llmRunBudget: {perRun, used}, credentialsRef, cleanupArmed, proseLanguage}`.\nAlso write the chosen language as `proseLang` into `viewer\u002Fviewer.json` — that copy\nis the one the SERVER reads, so the viewer can tell a reader whose interface\nlanguage differs that the prose will not follow the toggle. **Preserve it when you\nrewrite the index**: `viewer.json` may carry fields you did not set this run, and\nrebuilding it from scratch silently deletes them. Read, modify, write back.\nOn a later run, ask **one** question instead of five: summarise what is on file and\noffer to reuse it, re-asking only what the user names. Increment\n`llmRunBudget.used` so the budget survives across runs.\n\n## Findings\n\nRecord problems as you go, each tied to the flow and the span it concerns, in\n`viewer\u002Ffindings.json`. A finding's `status` is the reason there is one collection\nrather than a copy per flow: **a dismissed finding must not come back on the next\nanalysis.** Never resurrect a `dismissed` finding; if you believe it was wrongly\ndismissed, say so in the report instead of silently reopening it.\n\n## Cleanup\n\nPart of the product, not a side feature — and the destructive half is enforced in\ncode, like every other guarantee here: **the only way a doc leaves the repo is\n`scripts\u002Fcleanup.mjs`, and it refuses to delete anything whose content the machine\ncannot prove already lives in the viewer.** You never `rm` a doc. Not once.\n\n1. **Refuse** unless intake recorded `cleanupArmed: true` (cleanup.mjs checks this\n   too, but you refuse first — do not make the tool do your saying-no for you).\n2. **Propose** `viewer\u002Fcleanup-allowlist.json`: `{entries: [{path, reason}]}` — an\n   explicit list of literal paths. **Never a glob. Never an extension pattern.**\n   (cleanup.mjs rejects glob characters outright.) In this repo, roughly half of\n   all `.md` files are `SKILL.md` and `README.md` inside `fittings\u002Fseed\u002F` and\n   `.codex\u002Fskills\u002F` — executable payload, not documentation; a `*.md` sweep would\n   delete dozens of working fittings.\n3. **Hard exclusions**, enforced by cleanup.mjs, no exceptions: anything under\n   `fittings\u002Fseed\u002F**`, `.codex\u002Fskills\u002F**`, `site\u002F**`, `public\u002Ficons\u002F**`; and\n   `README.md`, `CLAUDE.md`, `AGENTS.md`, `SKILL.md` are slimmed, never deleted.\n4. **Consolidate before destroying — through `store.consolidateDoc` only.** It\n   copies the doc into `viewer\u002Fdocs\u002F`, registers it in `docs-manifest.json` with\n   the sha256 of the exact bytes it copied, and that hash is the deletion gate:\n   cleanup.mjs deletes a file only when the recorded hash matches the file's\n   *current* bytes and the copy still renders. A doc edited after consolidation is\n   undeletable until you consolidate it again — by design. Verify each doc renders\n   at `\u002Fdocs` before going on.\n5. **Ask.** Present the list with counts and reasons. On approval, record\n   `approvedAt` on the allowlist. No `approvedAt`, no deletion — cleanup.mjs\n   refuses an unapproved list.\n6. **Delete**: `node scripts\u002Fcleanup.mjs --repo \u003Cpath>` first (dry run — show the\n   user its output), then with `--apply`. The run is all-or-nothing: one entry\n   failing any gate refuses the whole run, so the removal set always equals the\n   approved list exactly.\n7. **Slimming rules.** `CLAUDE.md` and `AGENTS.md` keep the most important rules,\n   not the decisions, plus a pointer to the viewer. The README keeps setup and the\n   ordinary basics, then links to the viewer for everything else. Remove only what\n   now lives in the viewer; never restructure surviving guidance. Slimming is an\n   edit, not a deletion — it never goes through cleanup.mjs, and it still keeps\n   the original readable in the viewer first via `store.consolidateDoc`.\n8. **Keep deletions out of any feature commit.** And note the standing repo rule:\n   **do not create a git branch unless the user explicitly tells you to.** If the\n   separation needs a branch, ask for it.\n\n## Prompt buttons\n\nThe viewer is the control surface: its buttons POST to the fitting's own server,\nwhich composes a prompt from `lib\u002Fprompts.mjs` and dispatches it. Long work goes\nto a kanban card, because a chat turn caps out around five minutes and an analysis\nrun takes longer than that. Only a short question goes to a gateway turn.\n\nWhen you are picked up from such a card, the prompt names the mode and points at\n`viewer\u002F` rather than inlining content. Read the store; do not expect the prompt to\ncarry the data.\n\n## Before you say you are done\n\n- Every manifest passes `validateFlow`.\n- Every sample verifies by hash — check, do not assume.\n- No step describes code the reader cannot see, and no sample is shown without a\n  description.\n- Collapsed steps are collapsed, not missing.\n- The findings you filed point at spans that exist.\n- If you could not do part of the job, say which part and why. A gap you name is\n  useful; a gap you paper over destroys the trust the whole tool runs on.\n",{},"main","31458d7e0e08a7b3494c27bf2b912ed41c87a138","fittings\u002Fseed\u002Fproject-viewer\u002F.apm\u002Fskills\u002Fgarrison-project-viewer",0,"https:\u002F\u002Fgithub.com\u002Fgongiskhan\u002Fagent-garrison","https:\u002F\u002Fgongiskhan.github.io\u002Fagent-garrison\u002F","https:\u002F\u002Fgithub.com\u002Fgongiskhan","2026-11-07 05:27:23",{"source":7,"skillName":5,"commands":35,"prompt":45,"zipAvailable":46},[36,39,42],{"tool":37,"command":38},"npx","npx skills add gongiskhan\u002Fagent-garrison --skill garrison-project-viewer",{"tool":40,"command":41},"bunx","bunx skills add gongiskhan\u002Fagent-garrison --skill garrison-project-viewer",{"tool":43,"command":44},"pnpm","pnpm dlx skills add gongiskhan\u002Fagent-garrison --skill garrison-project-viewer","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgongiskhan\u002Fagent-garrison\u002Fblob\u002Fmain\u002Ffittings\u002Fseed\u002Fproject-viewer\u002F.apm\u002Fskills\u002Fgarrison-project-viewer\u002FSKILL.md",true,[],[49,61,71,82,93,123],{"skillNo":50,"name":51,"nameZh":6,"description":52,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":53,"categories":54,"keywords":55,"publishedAt":21},"01M71RTFA0HD83YBE1TE7DCE6B","pinned-helper","An owned skill the human has pinned so the Improver must not touch it.","https:\u002F\u002Fgithub.com\u002Fgongiskhan\u002Fagent-garrison\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fimprover\u002Fskills\u002Fpinned-helper\u002FSKILL.md",[],[56,57,58,59,60],"pinned helper","helper","human","touch","it",{"skillNo":62,"name":63,"nameZh":6,"description":64,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":65,"categories":66,"keywords":67,"publishedAt":21},"01M71RTDKBG0WQHPHKFZKFWDHF","legacy-helper","A rarely used helper kept around from an earlier workflow.","https:\u002F\u002Fgithub.com\u002Fgongiskhan\u002Fagent-garrison\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fimprover\u002Fskills\u002Flegacy-helper\u002FSKILL.md",[],[68,57,69,70],"Legacy","legacy helper","workflow",{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":77,"publishedAt":21},"01M71RTCC6ZVVJ9MHHESXPHWA6","human-notes","A human-authored skill not deployed by APM (loose, not in the lock).","https:\u002F\u002Fgithub.com\u002Fgongiskhan\u002Fagent-garrison\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fimprover\u002Fskills\u002Fhuman-notes\u002FSKILL.md",[],[78,58,79,80,81],"notes","human notes","lock","apm",{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":88,"publishedAt":21},"01M71RTB83ZP6VK6XH19Z93FAC","garrison-helper","Helps compose and station Garrison Fittings into Faculty role slots.","https:\u002F\u002Fgithub.com\u002Fgongiskhan\u002Fagent-garrison\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fimprover\u002Fskills\u002Fgarrison-helper\u002FSKILL.md",[],[57,19,89,90,91,92],"garrison helper","slots","role","compose",{"skillNo":94,"name":95,"nameZh":6,"description":96,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":104,"publishedAt":21},"01M71RT6MBGJC4QQZBGHF4H1M8","walkthrough","Record a short, narrated, captioned video walkthrough that proves a finished coding task and shows how it behaves, then self-verify the video via vision on extracted frames and publish it as one scrubbable Tailscale link. Use after completing a change\u002Ffix\u002Ffeature on a web project (Next.js\u002FReact etc.) when the user wants visual proof of work, a demo of a user flow, or to avoid manually retesting and watching a long raw screen recording. Triggers on requests like \"record a walkthrough\", \"show me it working\", \"make a demo video of this\", \"prove the feature\", or finishing a UI\u002Froute\u002Fform\u002FAPI change worth demonstrating. Not for walking through a full from-scratch project build.","https:\u002F\u002Fgithub.com\u002Fgongiskhan\u002Fagent-garrison\u002Fblob\u002Fmain\u002Ffittings\u002Fseed\u002Fwalkthrough\u002F.apm\u002Fskills\u002Fwalkthrough\u002FSKILL.md",[99],{"categoryNo":100,"categoryName":101,"taxonomyType":102,"taxonomyTypeDesc":103},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",[95,105,17,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122],"when","project build","api","react","change","demo video","publish","recording","Video","show me","work","short","fix","show","route","me","next","self",{"skillNo":124,"name":125,"nameZh":6,"description":126,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":127,"categories":128,"keywords":129,"publishedAt":21},"01M71RT54T0SQS42XEP5FVQ28K","Trello","Manage tasks via Trello REST API.","https:\u002F\u002Fgithub.com\u002Fgongiskhan\u002Fagent-garrison\u002Fblob\u002Fmain\u002Ffittings\u002Fseed\u002Ftrello\u002F.apm\u002Fskills\u002Ftrello\u002FSKILL.md",[],[130,131,132,107,133,134,135],"trello","tasks","REST","manage","rest api","via"]