architecture — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited architecture (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill is the specialized, authoritative description of how Lagune is structured: repository layout, the three owners, how the published skills and their templates are organized, the catalog of specializations, the engine command, and the tracking-map model. Consult it before changing source and whenever a decision depends on the shape of the codebase.
The product mission and workflow philosophy live in CLAUDE.md. The toolchain, code conventions, build path, and how the tracking hooks are implemented live in the engineering skill. The language conventions and the boundaries on what prose carries and who may author it live in the writer skill. This skill covers the _shape_, not the _build_.
| Owner | What it holds | Where it lives |
|---|---|---|
| The project | the charter, the phase artifacts, the tracking map, its categories and its own catalog | .lagune/, committed with the project |
| Lagune's instructions | the nine commands, each beside the template it fills | the skill folders the skills CLI installs from skills/ |
| Lagune's engine | the hooks, validation, the built-in specializations and their categories | the lagune package on npm, reached through the engine command |
Everything about installing commands, updating them, and supporting agents belongs to the skills CLI. Lagune keeps no agent list and no per-agent format, and never inspects how its skills were installed: it applies wherever its artifacts exist.
| Directory | Purpose |
|---|---|
skills/ | The nine published skills, authored here and installed as they stand. |
catalog/ | The built-in specializations, published with the engine. |
src/bin/ | The engine's entry: the hook routing, catalog, the categories, migrate, the dashboard, and the fallback. |
src/cli/ | Argument parsing, messages, pickers, and the hook runner. |
src/catalog/ | The built-in index, the name grammar, listing and merging, and the project's own index. |
src/categories/ | The categories, their changes, and the project's record of them. |
src/core/ | Tracking, the project's configs, markdown, CVSS, scanning, and the migration. |
src/hooks/ | The 14 hooks. |
src/dashboard/ | The live view of .lagune/. |
src/types/ | The single home for every type declaration. No type is declared elsewhere. |
lib/ | Build output, ignored by git, published together with catalog/. |
test/ | Poku test suites, run against Node, Bun, and Deno. |
website/ | The documentation site, an npm workspace of the root package. |
src/ into one self-contained JavaScript file in lib/, so the published package carries no runtime node_modules for the end user. Vite builds the dashboard client into lib/dashboard.package.json bin field maps the lagune command to the engine entry in lib/, so the engine command runs the bundle directly.lib/ and catalog/. The skills are not part of the package: the skills CLI installs them from the repository, so the default branch is what users get.Each command is a folder under skills/ holding its SKILL.md, plus template.md when the command fills one (charter, detect, plan, harden, specialize, prove). The skills CLI copies the whole folder, so nothing else belongs in it.
name (equal to its folder), the description, argument-hint, and user-invocable, plus disable-model-invocation on prove and specialize, and never metadata.internal, which would hide it from discovery.template.md next to the skill. The skill carries the reasoning, the template carries the shape of the result.Every deterministic step a skill or a built-in specialization writes uses the engine command with long flags only.
A specialization is an agent-agnostic knowledge module that loads only on demand. The built-ins live in catalog/, each indexed in src/catalog/builtin.ts with its tags and its categories. A project's catalog is the built-ins of the categories it records plus its own specializations, its own winning a name collision. .lagune/configs.json records the categories and indexes the project's own specializations, whose files live in .lagune/catalog/<name>.md. While no category is recorded the project uses OWASP alone.
The engine computes the catalog on demand: catalog prints it as name: tags, flagging a required built-in, and catalog <name> prints the one specialization the project resolves under that name, its own first and then the built-in, whatever the categories. Adding a built-in is one file in catalog/ plus one row in the index, never a new command. /lagune.specialize grows a project's own catalog the same way.
Every skill calls the engine as npx --yes lagune <subcommand> from the project root, never at a pinned version, so nothing moves with a release. Everything a person reads, from the docs to the engine's own messages, shows it as npx lagune <subcommand>. A hook subcommand is routed ahead of the argument parser, so its arguments reach the hook untouched, and a subcommand the engine does not know fails instead of reading as clean. The engine's own subcommands are catalog, the category subcommands (add, remove, and list with --categories), list --findings, migrate, and the dashboard.
A project the Lagune CLI installed carries .lagune/manifest.json, and that manifest alone decides who answers there: migrate runs this design, everything else the old CLI knows is handed to the frozen [email protected], and what only this design has fails with a notice pointing to the migrate page. The hand-off runs the npm installed with the running Node.js, with no shell and never one a search path or the project could plant, and a runtime without one fails with the command to run instead. Without a manifest, init, pull, and update fail, pointing to the skills CLI.
.lagune/ holds only the project's state, created by the first command or hook that writes: the memory artifacts under memory/, the tracking map, configs.json with the recorded categories and the index of the project's own specializations, their files under catalog/, and the proofs, which keep themselves out of version control with their own .gitignore. There is no manifest, no generated file, and no .gitignore block.
Each detect finding is one tracked item. The later phases carry that same item forward: it is a single entry that plan and harden re-report by name as they act on it, never a separate entry per phase. Its identity is its name (the finding's name, written identically as the section title in detect.md, plan.md, and harden.md), and the map keys on that name and nothing else. The prose artifacts carry only that shared title and never a path. The file paths are the one volatile thing, and they live only in the tracking map. A rename or a moved path is corrected in one place, and every memory stays valid. The tracking map is tracking only: no prose, no note, no separate id, no phase.
.lagune/tracking.json, a sibling of memory/. It is internal state, committed but never hand-edited and never shown to the user.{ entries: [{ name, paths }] }, where name is the identity and paths is string[] (charter has no items). There is no cross-entry link: the conveyor lives in the prose (which artifact carries the item's section), not in the map. Types live in the types module under src/types/./lagune.repair, never as part of a phase. Track records each finding a phase reports and re-reports an existing item by name as a later phase writes, following a renamed path and never removing. By convention detect is where a finding is born and plan and harden re-report it, but the hook keys on the name alone and does not enforce the order. Untrack stands a proven-closed finding down: verify hands it the name, and the hook removes that finding's section from the prose artifacts and drops its entry from the map in one pass. Repair realigns the whole map against the artifacts and the code, and is the only act that surfaces what is gone. Without tracking there is nothing to repair, so track is what populates the map along the normal flow./lagune.verify calls the untrack hook, handing it a proven-closed finding's name, which is why verify alone writes across the other phases' artifacts. Only the /lagune.repair command calls the repair hook, to realign the whole map when something diverged. The phases never repair the tracking themselves: when one notices the tracking is inconsistent (a tracked path that no longer exists), it runs /lagune.repair and continues. There is no user-facing track or untrack command, both are internal plumbing./lagune.repair command reads the user's source only to learn a renamed file's new path, never to author or judge security. Only untrack edits the .lagune/memory/*.md prose, and only to stand a proven-closed finding down: it injects no IDs or anchors and authors no security content. The one call no hook makes alone (fixed vs renamed) is returned for the agent or user to decide, never guessed.The tracking-map model (item identity, where the map lives, its shape, who calls each hook, and the non-goals) lives in the section above. This section covers how that machinery is implemented.
src/core/ holds only what the hooks share: the map's I/O (load, serialize, write), the matcher and fold that drive registration, the removal that drives stand-down, and the payload guards (an { entries: [...] } guard that requires name plus a paths string array, and a { names: [...] } guard for untrack), each failing closed. The matcher finds the single map entry whose name equals the observed name, then overwrites paths in place when they changed (moved) or leaves it (unchanged), and a name with no match is new. Every hook consumes the same flat entry shape the file has. A separate core module holds the pure section-removal surgery, apart from the tracking module so repair can reuse it later.unresolved (orphan, or renamed-candidate when its paths match a reported entry under a new name), never removing it itself, and rejects an empty payload rather than accepting it. Each exposes a pure engine the tests exercise directly, plus the payload-driven function the CLI entry runs.src/hooks/, each with a cli.ts that turns its process arguments into a result. The engine entry routes a hook subcommand to that handler through the hook runner. Passing the input as a process argument keeps it inert, so a value with quotes or backticks can never inject into the command.The validate hook judges one thing: that a written memory artifact kept its template's shape, never its security content. A plan's CVSS line is part of that shape: its score and band follow from the vector, so the hook rescores it and rejects a line that drifted. Detect's "Applied specializations" section must name a specialization as the catalog lists it, and the legacy heading and path rows still validate. Ownership stays with the writer: each phase checks the artifact it wrote, verify checks the record it wrote its verdicts into, and the no-argument sweep over the whole memory belongs to /lagune.repair, which reports drift for the owning phase to fix. It writes nothing, and like track and untrack it is internal plumbing the commands run, with no user-facing command.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.