@venizia/vulcan 0.1.0-1 → 0.2.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +9 -5
  2. package/base/claude/settings.json +2 -2
  3. package/base/hooks/guard-writes.ts +22 -7
  4. package/base/make/vulcan.mk +30 -21
  5. package/base/rules.md +13 -9
  6. package/base/skills/docs-writing/references/before-after.md +1 -1
  7. package/base/skills/docs-writing/references/page-anatomy.md +2 -2
  8. package/base/skills/knowledge-sync/SKILL.md +70 -136
  9. package/base/skills/knowledge-sync/knowledge-sync.workflow.js +43 -43
  10. package/base/skills/knowledge-sync/references/workflow.md +44 -0
  11. package/package.json +4 -4
  12. package/src/commands/help.command.ts +2 -1
  13. package/src/commands/sync.command.ts +11 -2
  14. package/src/commands/upgrade.command.ts +1 -2
  15. package/src/commands/x.command.ts +42 -1
  16. package/src/common/constants.ts +4 -0
  17. package/src/common/paths.ts +17 -1
  18. package/src/common/types.ts +3 -1
  19. package/src/config/common/constants.ts +10 -0
  20. package/src/config/common/types.ts +1 -1
  21. package/src/config/index.ts +1 -0
  22. package/src/config/packages.ts +33 -0
  23. package/src/config/schema.ts +101 -8
  24. package/src/doctor/checks.ts +99 -29
  25. package/src/doctor/common/constants.ts +6 -0
  26. package/src/doctor/common/types.ts +1 -1
  27. package/src/doctor/index.ts +1 -0
  28. package/src/doctor/runner.ts +8 -0
  29. package/src/doctor/skills.ts +157 -0
  30. package/src/doctor/tags.ts +79 -0
  31. package/src/hooks/common/types.ts +1 -1
  32. package/src/hooks/session-digest.ts +28 -10
  33. package/src/init/common/types.ts +1 -1
  34. package/src/manifest/index.ts +1 -0
  35. package/src/manifest/render.ts +80 -0
  36. package/src/okf.ts +13 -0
  37. package/src/router.ts +60 -0
  38. package/src/rules/base.ts +19 -0
  39. package/src/rules/common/constants.ts +3 -0
  40. package/src/rules/index.ts +1 -0
  41. package/src/rules/parser.ts +35 -0
  42. package/src/sync/base-rules.ts +72 -0
  43. package/src/sync/common/constants.ts +14 -0
  44. package/src/sync/common/types.ts +11 -2
  45. package/src/sync/index.ts +2 -0
  46. package/src/sync/planner.ts +29 -7
  47. package/src/sync/prune.ts +132 -0
package/README.md CHANGED
@@ -17,7 +17,7 @@ flag `bun x` fetches it when `@venizia/vulcan` is not installed yet.
17
17
  | Command | What it does |
18
18
  |---|---|
19
19
  | `init [--config ts\|mts\|mjs\|js]` | Scaffold the project layer: `AGENTS.md`, `.configs/vulcan.config.*`, the three lint configs, `.agents/rules.md`, `.agents/knowledge/index.md`, a Makefile `-include`. Never overwrites; prints what to add to existing files, and the atlas `.mcp.json` entry for projects with a wiki |
20
- | `sync [--dry-run]` | Write the gitignored agent adapters: `CLAUDE.md` (`@AGENTS.md`), `.claude/settings.json` (shipped + slot + project hooks + `claude.settings` over your personal keys; what it applied is recorded in `.claude/vulcan-managed.json`, so a removed choice is removed), `.claude/skills/` links. Idempotent |
20
+ | `sync [--dry-run] [--prune]` | Write the gitignored agent adapters: `CLAUDE.md` (`@AGENTS.md`), `.claude/settings.json` (shipped + slot + project hooks + `claude.settings` over your personal keys; what it applied is recorded in `.claude/vulcan-managed.json`, so a removed choice is removed), `.claude/skills/` links, and the committed `.agents/vulcan.manifest.json`. Prints each base rule added, changed or removed since the manifest last recorded it, with its new text, then records the new hashes. `--prune` also removes what sync proves stale and did not write: an old VULCAN or plugin-era hook command (a shipped hook in an old form, or a `.agents/` hook whose file is gone) and a dead `.claude/skills` link - never a developer's own hook or a real directory. Idempotent |
21
21
  | `doctor` | Check the project follows VULCAN; exit 1 on any violation |
22
22
  | `upgrade [<version\|tag>]` | Install the target `@venizia/vulcan` (default: the `next` dist-tag), then run `sync` from the newly installed CLI |
23
23
  | `x <tool> [args...]` | Run a shipped tool at the pinned version: `okf`, `atlas` |
@@ -39,6 +39,7 @@ export default {
39
39
  knowledge: { bundle: '.agents/knowledge' },
40
40
  hooks: [{ event: 'Stop', command: 'echo done' }],
41
41
  claude: { settings: { permissions: { allow: ['Bash(make test)'] } } }, // team-shared, committed
42
+ digest: { oftenDropped: ['V-B-03', 'W-00'] }, // repeated at every session start; base or own IDs
42
43
  } satisfies VulcanConfig;
43
44
  ```
44
45
 
@@ -46,7 +47,7 @@ An unknown key is an error; every error names the file and the field.
46
47
 
47
48
  ## What ships (`base/`)
48
49
 
49
- - `rules.md` - the base rules: write boundaries, security, process, build, and the IGNIS coding style V-C-01..V-C-18 (37 rules; V-W-05, V-C-03 and V-C-05 are retired).
50
+ - `rules.md` - the base rules: write boundaries, security, process, build, and the IGNIS coding style V-C-01..V-C-18 (37 rules; it lists V-W-05, V-C-03 and V-C-05 as retired).
50
51
  - `slots.json` - rules a project picks a wording for (V-W-01 today).
51
52
  - `skills/` - `knowledge-sync`, `docs-writing` (project-neutral; a project-specific skill stays in its project).
52
53
  - `hooks/` - `session-start.ts` (prints the rules digest) and `guard-writes.ts` (asks before a database write, a migration, a deploy or a mutating HTTP call).
@@ -57,10 +58,13 @@ or shadows a shipped one.
57
58
 
58
59
  ## `doctor` checks
59
60
 
60
- `config` (exactly one valid config) · `layout` (AGENTS.md, rules, knowledge bundle) · `rule-ids` (no
61
- project rule in the base `V-` namespace) · `rule-slots` (every slot chosen, valid option) · `skills` (no shipped skill
61
+ `config` (exactly one valid config) · `layout` (AGENTS.md, rules, knowledge bundle) · `rule-ids` (every
62
+ `V-` ID a project rule points at - an `Extends` list or a first cell - is a base rule, not a retired one) · `rule-slots` (every slot chosen, valid option) · `skills` (no shipped skill
62
63
  shadowed) · `make` (includes `vulcan.mk`, no shipped target redefined) · `hooks` (shipped hooks are
63
64
  not re-registered) · `adapters` (sync is current, no hand-written `CLAUDE.md`, valid settings) ·
64
- `style` (`@venizia/dev-configs` installed, every lint config builds on it).
65
+ `prune` (no stale hook command or dead skill link - `sync --prune` removes them) ·
66
+ `style` (`@venizia/dev-configs` declared by the package holding the lint config or the root, every lint config builds on it).
67
+
68
+ The session-start digest prints the base write boundaries, the slot choices, the project's own rules (bold `| **W-00** |` rows and `| V-W-02, V-W-03 |` rows that elaborate base rules), the project's `<!-- minimap-shape:start -->` ... `<!-- minimap-shape:end -->` block from `.agents/rules.md` verbatim, and the `digest.oftenDropped` rules.
65
69
 
66
70
  MIT licensed - see [LICENSE.md](LICENSE.md).
@@ -7,7 +7,7 @@
7
7
  "hooks": [
8
8
  {
9
9
  "type": "command",
10
- "command": "bun \"${CLAUDE_PROJECT_DIR}\"/node_modules/@venizia/vulcan/base/hooks/session-start.ts"
10
+ "command": "f=\"${CLAUDE_PROJECT_DIR}/node_modules/@venizia/vulcan/base/hooks/session-start.ts\"; if [ -f \"$f\" ] && command -v bun >/dev/null 2>&1; then bun \"$f\"; s=$?; [ \"$s\" -eq 0 ] && exit 0; exit 2; fi; echo \"vulcan: the session-start hook cannot run (node_modules/@venizia/vulcan or bun is missing) - run bun install\" >&2; exit 2"
11
11
  }
12
12
  ]
13
13
  }
@@ -18,7 +18,7 @@
18
18
  "hooks": [
19
19
  {
20
20
  "type": "command",
21
- "command": "bun \"${CLAUDE_PROJECT_DIR}\"/node_modules/@venizia/vulcan/base/hooks/guard-writes.ts"
21
+ "command": "f=\"${CLAUDE_PROJECT_DIR}/node_modules/@venizia/vulcan/base/hooks/guard-writes.ts\"; if [ -f \"$f\" ] && command -v bun >/dev/null 2>&1; then bun \"$f\"; s=$?; [ \"$s\" -eq 0 ] && exit 0; exit 2; fi; if grep -Eq '\"command\" *: *\"bun (install|i)( --?[a-z][a-z0-9-]*(=[A-Za-z0-9._-]+)?)*\"'; then exit 0; fi; echo \"vulcan: the guard-writes hook cannot run (node_modules/@venizia/vulcan or bun is missing) - run bun install (the one command that passes until then)\" >&2; exit 2"
22
22
  }
23
23
  ]
24
24
  }
@@ -1,14 +1,29 @@
1
1
  /**
2
2
  * PreToolUse hook on Bash: catches the writes V-W-02 and V-W-03 reserve for a human, reading the
3
3
  * command and any local script it runs. It asks rather than denies (src/hooks/common/constants.ts).
4
+ *
5
+ * It fails closed: Claude Code blocks a PreToolUse call only on exit 2, so a hook that throws - a
6
+ * partial install, say, where its own modules are missing - would let every command through. The
7
+ * modules load inside the try for that reason.
4
8
  */
5
- import { buildGuardDecision, inspectCommand, parseHookCommand } from '../../src/hooks/index.ts';
6
9
 
7
- const hookCommand = parseHookCommand({ text: await Bun.stdin.text() });
8
- const hits = hookCommand
9
- ? inspectCommand({ command: hookCommand.command, cwd: hookCommand.cwd ?? process.cwd() })
10
- : [];
10
+ // A module, so the awaits below are top-level; it has no static import, by design.
11
+ export {};
11
12
 
12
- if (hits.length > 0) {
13
- console.log(JSON.stringify(buildGuardDecision({ hits })));
13
+ try {
14
+ const { buildGuardDecision, inspectCommand, parseHookCommand } =
15
+ await import('../../src/hooks/index.ts');
16
+ const hookCommand = parseHookCommand({ text: await Bun.stdin.text() });
17
+ const hits = hookCommand
18
+ ? inspectCommand({ command: hookCommand.command, cwd: hookCommand.cwd ?? process.cwd() })
19
+ : [];
20
+
21
+ if (hits.length > 0) {
22
+ console.log(JSON.stringify(buildGuardDecision({ hits })));
23
+ }
24
+ } catch (error) {
25
+ console.error(
26
+ `vulcan: the guard-writes hook failed (${error instanceof Error ? error.message : String(error)}) - blocked; run bun install`,
27
+ );
28
+ process.exit(2);
14
29
  }
@@ -5,48 +5,57 @@
5
5
  VULCAN ?= bun x --no-install vulcan
6
6
 
7
7
  .PHONY: vulcan-sync vulcan-doctor agent-setup okf-check okf-check-strict okf-gen okf-coverage okf-viz \
8
- surface-gen surface-check symbols-gen symbols-check releases-gen releases-check
8
+ surface-gen surface-check atlas-symbols atlas-releases changelogs-migrate \
9
+ knowledge-symbols-check
9
10
 
10
- vulcan-sync:
11
+ vulcan-sync: ## Write the agent adapters, settings, skill links and manifest from the config
11
12
  @$(VULCAN) sync
12
13
 
13
- vulcan-doctor:
14
+ vulcan-doctor: ## Check the project against the VULCAN contract
14
15
  @$(VULCAN) doctor
15
16
 
16
- # The IGNIS name for the same job, kept for muscle memory.
17
- agent-setup: vulcan-sync
17
+ # The older name for the same job, kept for muscle memory.
18
+ agent-setup: vulcan-sync ## The older name for vulcan-sync
18
19
 
19
20
  # OKF v0.2 conformance only; okf-check-strict adds the house rules (links, style, coverage, freshness).
20
- okf-check:
21
+ okf-check: ## Check the knowledge bundle against OKF v0.2
21
22
  @$(VULCAN) x okf check
22
23
 
23
- okf-check-strict:
24
+ okf-check-strict: ## okf-check plus the house rules: links, style, coverage, freshness
24
25
  @$(VULCAN) x okf check --strict
25
26
 
26
- okf-gen:
27
+ okf-gen: ## Regenerate the generated pages and regions of the knowledge bundle
27
28
  @$(VULCAN) x okf gen
28
29
 
29
- okf-coverage:
30
+ okf-coverage: ## Report how much of the code the knowledge bundle covers
30
31
  @$(VULCAN) x okf coverage
31
32
 
32
- okf-viz:
33
+ okf-viz: ## Build the knowledge graph view
33
34
  @$(VULCAN) x okf viz
34
35
 
35
- # The Atlas tables. surface and symbols read the built .d.ts - build first.
36
- surface-gen:
36
+ # The public surface: an optional API gate a project commits and checks. It reads the built .d.ts -
37
+ # build first.
38
+ surface-gen: ## Write the public API surface from the built .d.ts
37
39
  @$(VULCAN) x atlas surface gen
38
40
 
39
- surface-check:
41
+ surface-check: ## Fail when the public API surface differs from the committed one
40
42
  @$(VULCAN) x atlas surface check
41
43
 
42
- symbols-gen:
43
- @$(VULCAN) x atlas symbols gen
44
+ # Symbols are read from the built or installed .d.ts when atlas is asked - nothing to commit.
45
+ # atlas-symbols prints what atlas reads, to see what symbol answers from. Build first.
46
+ atlas-symbols: ## Print the symbols atlas reads from the built or installed .d.ts
47
+ @$(VULCAN) x atlas symbols
44
48
 
45
- symbols-check:
46
- @$(VULCAN) x atlas symbols check
49
+ # Releases are computed from tags when atlas reads the project - nothing to generate or commit.
50
+ # atlas-releases prints the computed table, to see what version and changes answer from.
51
+ atlas-releases: ## Print the release table atlas computes from the tags
52
+ @$(VULCAN) x atlas releases
47
53
 
48
- releases-gen:
49
- @$(VULCAN) x atlas releases gen
54
+ # Writes the packages: list into every changelog entry that declares none - once, then review it.
55
+ changelogs-migrate: ## Write the packages: list into changelog entries that declare none
56
+ @$(VULCAN) x atlas changelogs migrate
50
57
 
51
- releases-check:
52
- @$(VULCAN) x atlas releases check
58
+ # A concept may name code (`symbols:` in its frontmatter); this fails when a named symbol is gone or its
59
+ # signature changed since `vulcan x atlas concepts stamp`. It reads the built .d.ts - run it after build.
60
+ knowledge-symbols-check: ## Fail when a symbol a concept names is gone or its signature changed
61
+ @$(VULCAN) x atlas concepts check
package/base/rules.md CHANGED
@@ -1,16 +1,20 @@
1
1
  # Rules - VULCAN base
2
2
 
3
3
  <!-- Shipped by @venizia/vulcan. Never edit in a project: add extensions in .agents/rules.md.
4
- W, S, P, B are INTERIM until the base-rules work reconciles the rules IGNIS, ARDOR and BANA share.
5
- C is IGNIS's coding style, shipped to every project; the rules that bind IGNIS's own stack live in IGNIS. -->
4
+ W, S, P, B are INTERIM until the base-rules work reconciles the rules the projects share.
5
+ C is the shared coding style, shipped to every project; a rule that binds one project's own stack
6
+ lives in that project. -->
6
7
 
7
8
  Cite by ID. Every rule has "Enforced by". Base IDs carry the `V-` namespace (`V-W-01`, `V-C-04`).
8
9
  A project extends this set in its own `.agents/rules.md` with bare IDs (`W-06`, `C-19`), which can
9
- never collide with a base ID; it never claims the `V-` namespace (`vulcan doctor` enforces it).
10
+ never collide with a base ID. A `V-` ID there points at a base rule (`Extends V-W-01`); `vulcan doctor`
11
+ fails one the base does not carry or has retired.
10
12
 
11
- Retired IDs are never reused: `V-W-05` (task-manager loop), `V-C-03` (`@inject` on container-built
12
- classes) and `V-C-05` (`getError` / `ApplicationError`) - the last two, with the hard stack once in
13
- `V-C-18`, are IGNIS rules now (2026-10-17).
13
+ Retired IDs are never reused (2026-10-17):
14
+
15
+ - `V-W-05` - the task-manager loop
16
+ - `V-C-03` - `@inject` on container-built classes, moved to the project whose stack it binds
17
+ - `V-C-05` - `getError` / `ApplicationError`, moved to the project whose stack it binds, with the hard stack once in `V-C-18`
14
18
 
15
19
  ## W - Write boundaries
16
20
 
@@ -58,8 +62,8 @@ classes) and `V-C-05` (`getError` / `ApplicationError`) - the last two, with the
58
62
  | ID | Rule | Enforced by |
59
63
  |----|------|-------------|
60
64
  | V-C-01 | **English only in code** - identifiers, comments, commit messages, logs, docs. Vietnamese belongs in chat. | SOFT |
61
- | V-C-02 | **Options objects everywhere:** `fn({ key, value })`, never `fn(key, value)`. **One parameter is not an exception** - `isValidName({ name })`, not `isValidName(name)`. A single positional parameter reads fine until the second one arrives, and by then the method is published and every call site pays. The cost is highest on an interface others implement: `IStorageHelper` carried three positional methods for exactly this reason and the migration touched 72 call sites. Mixing the two styles in one interface is itself the defect - a reader cannot tell which shape a method takes without opening it. | SOFT |
62
- | V-C-04 | **Never abbreviate identifiers:** `ProductRepository` not `ProductRepo`, `ProductDocument` not `ProductDoc`; type parameters too (`TDocument`, not `TDoc`). A project lists its own exceptions in its rules (IGNIS: `tz` for a time zone). | SOFT |
65
+ | V-C-02 | **One options object first, at most 5 parameters.** Fold every argument you can into a single options object in the first position: `fn({ key, value })`, not `fn(key, value)`, and one parameter is not an exception - `isValidName({ name })`, not `isValidName(name)`. A second parameter is allowed only where the first object cannot carry it, for example a repository method that takes its data and its query options separately. Never more than 5 parameters. A single positional parameter reads fine until the second one arrives, and by then the method is published and every call site pays. The cost is highest on an interface others implement: `IStorageHelper` carried three positional methods for exactly this reason and the migration touched 72 call sites. Mixing the two styles in one interface is itself the defect - a reader cannot tell which shape a method takes without opening it. | SOFT - lint (`venizia/max-params-own`, 5) once `@venizia/dev-configs` ships `houseConfigs` |
66
+ | V-C-04 | **Never abbreviate identifiers:** `ProductRepository` not `ProductRepo`, `ProductDocument` not `ProductDoc`; type parameters too (`TDocument`, not `TDoc`). A project lists its own exceptions in its rules (for example `tz` for a time zone). | SOFT |
63
67
  | V-C-06 | **Never a silent catch - log it.** Always braces. Early return. `switch` with `default` over a long if-else chain. Strict TypeScript; no `any` where a type exists. | LINT (braces, floating promises) + SOFT |
64
68
  | V-C-07 | **A function name opens with its verb** - `generate` `build` `to` `is` `has` `assert` `extract` `enrich` `get` `resolve`. `is*` returns a boolean and leaves the branch to the caller; `assert*` throws and returns `void`, so the code after it needs none - reach for `assert*` when every caller would throw on `false` anyway. `has*` is the ownership question (`hasBucket`, never `isBucketExists`). That list is for UTILITY functions; service methods lean on a wider set. **Arrow functions, not `function` declarations** (do not churn existing ones). State plus the operations on it live in a class with static members - the `LoggerFactory` shape - never module-level `let` plus exported arrows. | SOFT |
65
69
  | V-C-08 | **Enumerable strings are a const class plus `TConstValue`,** never a raw string-literal union. Every such class carries `static readonly SCHEME_SET = new Set([...])` over its values and `static isValid(value: string): boolean` reading it, so a runtime check exists wherever a value crosses a boundary (`IsolationLevels` in connectors is the model; a subclass redeclares `SCHEME_SET` with `...Parent.SCHEME_SET` first). The `T`/`I` prefix follows the declaration keyword: `type` gets `T`, `interface` gets `I`; an interface turned into a type alias is renamed, and no tool catches it - grep both directions. | SOFT |
@@ -67,7 +71,7 @@ classes) and `V-C-05` (`getError` / `ApplicationError`) - the last two, with the
67
71
  | V-C-10 | **No explicit cast in new code.** `as X`, `as any`, `as unknown as X` and `as never` assert nothing; they silence the compiler where the type is wrong. Fix the type instead: a generic on the setter, a narrower parameter, a typed helper, `satisfies`. A pre-existing cast is removed when the line is touched, never copied. Generic parameters are full words (`ApplicationType`, `TDocument`), never `App` or `TDoc` (V-C-04). | SOFT |
68
72
  | V-C-11 | **SIMPLE FIRST, in code and in prose.** Fewest concepts, plainest words, smallest surface. Complexity must buy correctness, speed or safety the simple version cannot, and the artifact says what it bought. Something a reader cannot follow in one pass is a defect even when it works. Fix the root cause with the smallest change; never layer an abstraction on a misdiagnosed problem. | SOFT |
69
73
  | V-C-12 | **Consistent, never self-invented.** Before writing anything new - a constant, file, binding key, error shape - find the nearest precedent in this repo and copy its shape. Binding keys are namespaced (`controllers.X`, `services.X`, `repositories.X`, `datasources.X`). Every scope is one folder with nested `common/{types,constants}.ts` and an index barrel (`inversion` is the model). A second style for something that already has one is a defect. | SOFT |
70
- | V-C-13 | **Docs style: hyphen `-`, never an em-dash or en-dash. A brand is spelled the way its owner spells it (IGNIS, ARDOR, VULCAN), never "Ignis".** English prose. The human-facing docs site stays native to its generator (IGNIS: plain VitePress, no custom components or theme CSS). The knowledge bundle is agent-facing; never conflate the two. | SOFT - `okf check --strict` (bundle) |
74
+ | V-C-13 | **Docs style: hyphen `-`, never an em-dash or en-dash. A brand is spelled the way its owner spells it (VULCAN, never "Vulcan").** English prose. The human-facing docs site stays native to its generator (for example plain VitePress, no custom components or theme CSS). The knowledge bundle is agent-facing; never conflate the two. | SOFT - `okf check --strict` (bundle) |
71
75
  | V-C-14 | **Our types are camelCase.** A snake_case wire name exists only at the mapper boundary, through a string-literal key - never through an eslint-disable. | LINT (naming convention) + SOFT |
72
76
  | V-C-15 | **One method with a mode option, not several specialized methods.** Payload variants are zod-per-variant with `z.infer`, discriminated. | SOFT |
73
77
  | V-C-16 | **One concept, one implementation.** When the same question is answered in several places, the bug lives at the seam and no tool catches it. Consolidate to one home before building on top. | SOFT |
@@ -1,4 +1,4 @@
1
- # Worked example - a real IGNIS changelog bullet, before and after
1
+ # Worked example - a real changelog bullet, before and after
2
2
 
3
3
  From the 2026-07-18 Repository Read Retry changelog. Every fact survives; the reading effort does not.
4
4
 
@@ -15,7 +15,7 @@ If a page's sections resolve to different quadrants, it is two pages wearing one
15
15
 
16
16
  ## Skeleton 1 - Tutorial (getting started)
17
17
 
18
- 1. **Title** - an achievement: "Build your first IGNIS API", never "Learn IGNIS".
18
+ 1. **Title** - an achievement: "Build your first API", never "Learn the framework".
19
19
  2. **Goal + outcome preview** - 1-3 sentences: "In this tutorial we build a working X." Never "you will learn".
20
20
  3. **Prerequisites** - exact versions and tools, once.
21
21
  4. **Numbered steps** - ONE path, zero choices. Every step ends in a visible result with the expected output shown ("After a few seconds you'll see...").
@@ -52,7 +52,7 @@ Governing rule: describe and only describe. Structure mirrors the code. You cons
52
52
  1. **Title** - accepts an implicit "About...": "Understanding the DI container".
53
53
  2. **The question** - what "why" this page answers. It is also the page's boundary.
54
54
  3. **The picture** - the mental model, a diagram if code and words fail.
55
- 4. **Design decisions** - why IGNIS chose this (prior art: LoopBack, NestJS, Spring), constraints, trade-offs.
55
+ 4. **Design decisions** - why the project chose this (prior art: LoopBack, NestJS, Spring), constraints, trade-offs.
56
56
  5. **Alternatives and objections** - weigh them honestly (Tailwind's "Why not just use inline styles?" move).
57
57
  6. **Boundaries + links** - where the topic ends; link the reference and how-tos.
58
58
 
@@ -1,146 +1,80 @@
1
1
  ---
2
2
  name: knowledge-sync
3
- description: Re-sync the .agents/knowledge OKF bundle against the source code. Use when the user asks to sync knowledge, refresh the knowledge bundle, audit concepts against code, or run a periodic/monthly knowledge audit.
3
+ description: Re-syncs a project's OKF knowledge bundle against its source code - verifies the curated concepts whose code changed since the last sync (delta) or every concept (full), applies the confirmed fixes and logs the sync. Use when the user asks to sync knowledge, refresh the knowledge bundle, audit concepts against code, or run a periodic knowledge audit.
4
4
  user-invocable: true
5
5
  allowed-tools: Read, Write, Edit, Grep, Glob, Bash, Agent, Workflow
6
6
  ---
7
7
 
8
- # Knowledge Sync - re-sync `.agents/knowledge` against source code
9
-
10
- Keep the OKF knowledge bundle true to the code. The generated reference layer self-heals via
11
- `make okf-gen`; this skill covers the **curated** concepts, which only stay honest when someone
12
- re-reads the source.
13
-
14
- This is deliberately **not** a commit gate. A per-commit check cannot tell whether prose is still
15
- true - only re-reading the code can. So the bundle is verified periodically, here.
16
-
17
- ## Core principles
18
-
19
- 1. **Code is ground truth.** Never fix a concept from `docs/wiki`, `AGENTS.md`, a `CLAUDE.md`, or
20
- memory - those drift too. This bundle exists because they did. Every finding needs a `file:line`
21
- witness; every fix is applied faithfully to that witness.
22
- 2. **Curate, do not dump.** Concepts stay tight: fix wrong/stale, fold in only durable high-value
23
- missing facts, skip volatile detail (dependency lists, config keys, version numbers).
24
- 3. **Cheap by default.** Delta mode only verifies concepts whose code areas changed since the last
25
- sync. Full mode (everything + critics) is for monthly/quarterly.
26
- 4. **Nothing is committed.** The agent edits the bundle + `log.md`; the user reviews and commits.
27
- 5. **Subagents never run state-changing git commands.** Read-only `git log`/`diff`/`show` only.
28
-
29
- ## Mode selection
30
-
31
- | What the user asked for | Mode |
32
- |---|---|
33
- | daily / weekly / bi-weekly / "sync knowledge" (no qualifier) | `delta` |
34
- | monthly / quarterly / "full" / "toàn bộ" / last **full** sync > 30 days ago (check `log.md`) | `full` |
35
-
36
- `full` = delta verifiers **plus** 6 directory spot-auditors over every other concept **plus**
37
- completeness + usability critics.
38
-
39
- ## Step 0 - Baseline
40
-
41
- 1. `make okf-gen` then `make okf-check` - refresh the generated layer first so verifiers never chase
42
- facts `gen` already fixes. `check` must be clean before proceeding.
43
- 2. **Anchor the last sync by COMMIT, not by date:**
44
- ```bash
45
- LAST_SYNC=$(git log -1 --format=%H -G '^## [0-9-]+ - knowledge-sync' -- .agents/knowledge/log.md)
46
- ```
47
- The scan range is `$LAST_SYNC..HEAD`. Anchor on the sync's own log heading, never on the last
48
- edit of `log.md`: every ordinary change appends to it (OKF §9), so the last edit is almost never
49
- the last sync and the range would come out empty. The date in the heading is only a display label.
50
-
51
- **If `LAST_SYNC` is empty** no entry carries the `knowledge-sync` heading yet (or `log.md` was renamed). Do not
52
- build a `..HEAD` range from it - that silently means "everything". Either anchor on the commit
53
- that introduced the bundle (`git log --diff-filter=A --format=%H -- .agents/knowledge/index.md`)
54
- or run `full` mode, which does not depend on the range for its spot-auditors.
55
- Never use a bare `--since=YYYY-MM-DD`: git's approxidate reads a date equal to *today* as "since
56
- the current wall-clock time" and silently returns empty, and same-day commits made before the
57
- last sync get double-counted. A hash range has neither problem.
58
- 3. Changed-area scan:
59
- ```bash
60
- git log $LAST_SYNC..HEAD --no-merges --name-only --format= -- packages/ examples/ Makefile make/ .github/ .githooks/ scripts/ \
61
- | sort -u | awk -F/ '{print $1"/"$2}' | sort | uniq -c | sort -rn
62
- git log $LAST_SYNC..HEAD --no-merges --oneline
63
- git diff HEAD --stat -- packages/ examples/ Makefile make/ .github/ .githooks/ scripts/ package.json
64
- ```
65
- The last line is uncommitted code. `okf gen` regenerates from the working tree and the verifiers
66
- read the working tree, so uncommitted changes count as changed areas too - say which are
67
- uncommitted in the target hint. Commit subjects become verifier hints. Ignore commits touching
68
- only `.agents/knowledge/` or `docs/` - that is knowledge maintenance, not code drift.
69
- 4. **Early exit (delta mode):** if no code areas changed, report "bundle already in sync", run the
70
- gates (Step 3), stop. No workflow, no log entry - unless `gen` changed generated files, then a
71
- one-line entry.
72
-
73
- ## Step 1 - Build delta targets
74
-
75
- One target per changed area, from this map. Extend it when a new area appears, and keep it in sync
76
- as concepts are added.
77
-
78
- | Changed area | Concepts to verify (under `.agents/knowledge/`) |
79
- |---|---|
80
- | `packages/core-server/src/base/applications` | architecture/application-lifecycle, packages/core-server |
81
- | `packages/core-server/src/components` | architecture/component-model, architecture/controller-system, architecture/authentication, architecture/authorization-casbin, process/adding-a-component |
82
- | `packages/kernel/src/base/repositories`, `packages/kernel/src/base/datasources` | architecture/repository-hierarchy, architecture/datasource-hierarchy, architecture/transactions, packages/kernel |
83
- | `packages/kernel/src/common/bindings.ts` | conventions/binding-key-namespaces |
84
- | `packages/kernel` (broad/other) | packages/kernel, architecture/di-container, architecture/controller-system, architecture/error-handling-flow |
85
- | `packages/connectors` | packages/connectors, architecture/relational-connector, architecture/sqlite-connector, architecture/search-typesense, architecture/transactions |
86
- | `packages/filter` | packages/filter, architecture/filter-system |
87
- | `packages/core-worker` | packages/core-worker |
88
- | `packages/atlas` | packages/atlas |
89
- | `packages/core-server` (broad/other) | packages/core-server, architecture/error-handling-flow, conventions/gotchas |
90
- | `packages/boot` | packages/boot, architecture/boot-lifecycle |
91
- | `packages/inversion` | packages/inversion, architecture/di-container, conventions/gotchas |
92
- | `packages/helpers` | packages/helpers, conventions/error-handling, process/adding-a-helper |
93
- | `packages/dev-configs` | packages/dev-configs, conventions/coding-style, conventions/testing-conventions |
94
- | `examples/<x>` | examples/&lt;x&gt; |
95
- | `Makefile`, `make/`, `scripts/`, `.githooks/`, root `package.json` | process/build-system, process/testing, process/git-workflow, overview/build-run-test, conventions/gotchas |
96
- | `.github/workflows/` | process/release-publish, process/updating-the-wiki |
97
- | `docs/wiki` | process/updating-the-wiki |
98
-
99
- Per-target fields: `key`, `area` (git pathspec), `concepts`, `hint` (summarize the commit subjects -
100
- the verifier is told to verify, not trust), `model` - `opus` for heavy or architecture-critical areas
101
- (core, inversion, big diffs), `sonnet` for small ones. Ignore noise-only areas (formatting,
102
- lockfiles, generated files). Merge tiny areas (<=3 files each) into one combined target.
103
-
104
- ## Step 2 - Run the workflow
105
-
106
- Invoke the Workflow tool with the saved script. Do not rewrite the orchestration inline:
8
+ # Knowledge sync
9
+
10
+ The generated layer (`vulcan x okf gen`) heals itself; curated concepts stay true only when someone
11
+ re-reads the code. Code is ground truth - never fix a concept from docs, `AGENTS.md` or memory. Every
12
+ finding cites a `file:line` witness. Nothing is committed: the user reviews the diff.
13
+
14
+ ## Requirements
15
+
16
+ - Bun, git, and `@venizia/vulcan` installed in the project (`bun x --no-install vulcan`).
17
+ - The Workflow tool, for the fan-out in Step 3.
18
+
19
+ ## Checklist
20
+
21
+ Copy it into your reply and tick it off:
107
22
 
108
23
  ```
109
- Workflow({
110
- scriptPath: '.claude/skills/knowledge-sync/knowledge-sync.workflow.js',
111
- args: {
112
- repo: '<git rev-parse --show-toplevel>',
113
- mode: '<delta|full>',
114
- since: '<YYYY-MM-DD>', // display label for prompts/log
115
- gitRange: '<LAST_SYNC hash>..HEAD', // what verifiers actually diff
116
- deltaTargets: [...],
117
- },
118
- })
24
+ - [ ] 1. Prerequisites hold
25
+ - [ ] 2. Baseline: gen, check, last sync, changed areas
26
+ - [ ] 3. Targets built, workflow run
27
+ - [ ] 4. Gaps decided
28
+ - [ ] 5. Gates green (loop until they are)
29
+ - [ ] 6. Log entry written; reported
119
30
  ```
120
31
 
121
- It fans out verifiers (+ spot-auditors and critics in full mode), groups findings per concept file,
122
- and runs one Opus applier per file under strict curation rules. Returns
123
- `{ stats, findingsByFile, applySummaries, gaps, notes }`.
124
-
125
- ## Step 3 - Post-workflow
126
-
127
- 1. **Gaps** (usually full mode): for each, decide like a curator - durable, load-bearing, no existing
128
- home? If yes, spawn one Opus agent per gap to create the concept: copy frontmatter/voice/length
129
- from 2-3 sibling concepts, verify every claim against source, cross-link, add a line to `index.md`
130
- in the right section. If a gap is really consolidation (same fact in N files), make ONE file the
131
- canonical home and reduce the others to pointers - never lose unique facts.
132
- 2. **Gates:** `make okf-gen`, then `make okf-check` (must be OK), then
133
- `bun .agents/knowledge-tools/okf.ts coverage --min 100` (structural must stay 100% - every
134
- `packages/*` and `examples/*` needs a concept; the number grows as the repo does).
135
- 3. **Log:** prepend an entry to `.agents/knowledge/log.md` headed `## YYYY-MM-DD - knowledge-sync (<mode>): <summary>` (the `knowledge-sync` word is what Step 0 anchors on), then a
136
- method one-liner (N verifiers / findings / confirmed / files touched), bullets for the notable
137
- corrections (the ones that change how someone works), creations, dedups, gate status. **This entry
138
- is the anchor the next sync reads** - its commit is what `LAST_SYNC` resolves to.
139
- 4. **Do not commit.** Leave the diff for the user.
140
-
141
- ## Report (to the user, Vietnamese)
142
-
143
- Lead with the outcome: gate status + scale (`X findings áp vào Y files, Z claims xác nhận đúng`).
144
- Then the notable corrections as short bullets (what was wrong -> what is true now, with the source
145
- witness), new/merged concepts, and anything an applier skipped, with the reason. If delta mode
146
- early-exited: one line - nothing changed since `<since>`, gates green.
32
+ ## 1. Prerequisites
33
+
34
+ 1. `bun x --no-install vulcan doctor` passes - the manifest is fresh. Fix what it names first.
35
+ 2. Read `.agents/vulcan.manifest.json`: `knowledge.bundle` (the bundle), `packages[].path` (the code
36
+ areas), `knowledge.syncAreas` (the area map, optional), `name` (the brand, upper case).
37
+
38
+ ## 2. Baseline
39
+
40
+ 1. `bun x --no-install vulcan x okf gen`, then `vulcan x okf check --strict` - it must pass before
41
+ anything else, so no verifier chases a fact `gen` fixes.
42
+ 2. Anchor the last sync by commit, never by date:
43
+ `LAST_SYNC=$(git log -1 --format=%H -G '^## [0-9-]+ - knowledge-sync' -- <bundle>/log.md)`.
44
+ Empty: no sync yet - run `full`, or anchor on the commit that added `<bundle>/index.md`. A bare
45
+ `--since=<date>` is wrong: git reads today's date as "now" and returns nothing.
46
+ 3. Changed areas: `git log $LAST_SYNC..HEAD --no-merges --name-only --format= -- <package paths> Makefile make/ scripts/ .github/`,
47
+ plus `git diff HEAD --stat` for uncommitted code. Commits that touch only the bundle or `docs/` are
48
+ knowledge maintenance, not drift.
49
+ 4. Delta mode, nothing changed: report "already in sync", run Step 5's gates, stop.
50
+
51
+ ## 3. Targets and the workflow
52
+
53
+ Mode: `delta` by default; `full` when asked (monthly, quarterly, "full") or when the last full sync
54
+ in `log.md` is over 30 days old. Build the targets and run the workflow as
55
+ [references/workflow.md](references/workflow.md) describes - one target per changed area, from the
56
+ area map when the project has one, else from each concept's `resource`.
57
+
58
+ ## 4. Gaps
59
+
60
+ For each gap the critics propose: durable, load-bearing, and no existing home? Create the concept -
61
+ copy frontmatter, voice and length from 2-3 siblings, verify every claim in source, link it from
62
+ `index.md`. A gap that is really one fact in several files: make one file its home, the rest point
63
+ to it. Never lose a unique fact.
64
+
65
+ ## 5. Gates - validate, fix, repeat
66
+
67
+ 1. `vulcan x okf gen`
68
+ 2. `vulcan x okf check --strict`
69
+ 3. `vulcan x okf coverage --min 100` - structural coverage stays at 100%.
70
+
71
+ A gate that fails: fix what it names, run all three again. Stop only when all three pass.
72
+
73
+ ## 6. Log and report
74
+
75
+ 1. Prepend to `<bundle>/log.md`: `## YYYY-MM-DD - knowledge-sync (<mode>): <summary>` - the
76
+ `knowledge-sync` word is what the next sync anchors on - then one line of method (verifiers,
77
+ findings, confirmed, files touched) and bullets for the corrections that change how someone works.
78
+ 2. Report in the user's language: gate status and scale first (findings applied, files, claims
79
+ confirmed), then each notable correction (was -> is, with its witness), new or merged concepts,
80
+ and anything an applier skipped, with the reason. Do not commit.