@ctxr/skill-llm-wiki 1.0.1
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.
- package/CHANGELOG.md +134 -0
- package/LICENSE +21 -0
- package/README.md +484 -0
- package/SKILL.md +252 -0
- package/guide/basics/concepts.md +74 -0
- package/guide/basics/index.md +45 -0
- package/guide/basics/schema.md +140 -0
- package/guide/cli.md +256 -0
- package/guide/correctness/index.md +45 -0
- package/guide/correctness/invariants.md +89 -0
- package/guide/correctness/safety.md +96 -0
- package/guide/history/diff.md +110 -0
- package/guide/history/hidden-git.md +130 -0
- package/guide/history/index.md +52 -0
- package/guide/history/remote-sync.md +113 -0
- package/guide/index.md +134 -0
- package/guide/isolation/coexistence.md +134 -0
- package/guide/isolation/index.md +44 -0
- package/guide/isolation/scale.md +251 -0
- package/guide/layout/in-place-mode.md +97 -0
- package/guide/layout/index.md +53 -0
- package/guide/layout/layout-contract.md +131 -0
- package/guide/layout/layout-modes.md +115 -0
- package/guide/operations/index.md +76 -0
- package/guide/operations/ingest/build.md +75 -0
- package/guide/operations/ingest/extend.md +61 -0
- package/guide/operations/ingest/index.md +54 -0
- package/guide/operations/ingest/join.md +65 -0
- package/guide/operations/maintain/fix.md +66 -0
- package/guide/operations/maintain/index.md +47 -0
- package/guide/operations/maintain/rebuild.md +86 -0
- package/guide/operations/validate.md +48 -0
- package/guide/substrate/index.md +47 -0
- package/guide/substrate/operators.md +96 -0
- package/guide/substrate/tiered-ai.md +363 -0
- package/guide/ux/index.md +44 -0
- package/guide/ux/preflight.md +150 -0
- package/guide/ux/user-intent.md +135 -0
- package/package.json +55 -0
- package/scripts/cli.mjs +893 -0
- package/scripts/commands/remote.mjs +93 -0
- package/scripts/commands/review.mjs +253 -0
- package/scripts/commands/sync.mjs +84 -0
- package/scripts/lib/chunk.mjs +421 -0
- package/scripts/lib/cluster-detect.mjs +516 -0
- package/scripts/lib/decision-log.mjs +343 -0
- package/scripts/lib/draft.mjs +158 -0
- package/scripts/lib/embeddings.mjs +366 -0
- package/scripts/lib/frontmatter.mjs +497 -0
- package/scripts/lib/git-commands.mjs +155 -0
- package/scripts/lib/git.mjs +486 -0
- package/scripts/lib/gitignore.mjs +62 -0
- package/scripts/lib/history.mjs +331 -0
- package/scripts/lib/indices.mjs +510 -0
- package/scripts/lib/ingest.mjs +258 -0
- package/scripts/lib/intent.mjs +713 -0
- package/scripts/lib/interactive.mjs +99 -0
- package/scripts/lib/migrate.mjs +126 -0
- package/scripts/lib/nest-applier.mjs +260 -0
- package/scripts/lib/operators.mjs +1365 -0
- package/scripts/lib/orchestrator.mjs +718 -0
- package/scripts/lib/paths.mjs +197 -0
- package/scripts/lib/preflight.mjs +213 -0
- package/scripts/lib/provenance.mjs +672 -0
- package/scripts/lib/quality-metric.mjs +269 -0
- package/scripts/lib/query-fixture.mjs +71 -0
- package/scripts/lib/rollback.mjs +95 -0
- package/scripts/lib/shape-check.mjs +172 -0
- package/scripts/lib/similarity-cache.mjs +126 -0
- package/scripts/lib/similarity.mjs +230 -0
- package/scripts/lib/snapshot.mjs +54 -0
- package/scripts/lib/source-frontmatter.mjs +85 -0
- package/scripts/lib/tier2-protocol.mjs +470 -0
- package/scripts/lib/tiered.mjs +453 -0
- package/scripts/lib/validate.mjs +362 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: scale
|
|
3
|
+
type: primary
|
|
4
|
+
depth_role: leaf
|
|
5
|
+
focus: chunked iteration, bounded memory, context-window hygiene, and how the skill handles multi-megabyte corpora
|
|
6
|
+
parents:
|
|
7
|
+
- index.md
|
|
8
|
+
covers:
|
|
9
|
+
- "iterEntries yields one entry at a time with a lazy loadBody() thunk"
|
|
10
|
+
- "frontmatter reads are bounded (max 256 KB per entry) via a streaming fs reader"
|
|
11
|
+
- operator-convergence reads frontmatter only — no body touches memory during detection
|
|
12
|
+
- "streaming consumer pattern: load → process → releaseBody → next — keeps peak at 1"
|
|
13
|
+
- listChildren was rewired to use readFrontmatterStreaming so rebuildAllIndices scales
|
|
14
|
+
- "per-phase commits plus diff --op <id> let you inspect large operations without loading the whole tree"
|
|
15
|
+
- the wiki-runner sub-agent auto-compacts its own context between phases; phase commits are the durable checkpoint
|
|
16
|
+
- "Tier 2 fan-out to per-decision sub-agents keeps the wiki-runner's own window lean"
|
|
17
|
+
tags:
|
|
18
|
+
- scale
|
|
19
|
+
- performance
|
|
20
|
+
activation:
|
|
21
|
+
keyword_matches:
|
|
22
|
+
- large
|
|
23
|
+
- big
|
|
24
|
+
- megabytes
|
|
25
|
+
- thousands
|
|
26
|
+
- out of memory
|
|
27
|
+
- out of context
|
|
28
|
+
- too big
|
|
29
|
+
- heap
|
|
30
|
+
- bounded
|
|
31
|
+
tag_matches:
|
|
32
|
+
- scale
|
|
33
|
+
escalation_from:
|
|
34
|
+
- build
|
|
35
|
+
- rebuild
|
|
36
|
+
- fix
|
|
37
|
+
- operator-convergence
|
|
38
|
+
source:
|
|
39
|
+
origin: file
|
|
40
|
+
path: scale.md
|
|
41
|
+
hash: "sha256:39cb1561005bb6836eb2ef3b5834cd3b4701623c8811750bc0bcada9b7ee75ae"
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# Scale-aware processing
|
|
45
|
+
|
|
46
|
+
The skill's phased operations (Build, Extend, Rebuild, Fix, Join) all
|
|
47
|
+
read wiki entries through a single chokepoint: `iterEntries` in
|
|
48
|
+
`scripts/lib/chunk.mjs`. That chokepoint enforces two bounded-memory
|
|
49
|
+
guarantees:
|
|
50
|
+
|
|
51
|
+
## 1. Frontmatter reads are bounded
|
|
52
|
+
|
|
53
|
+
Every entry's frontmatter is read via `readFrontmatterStreaming`,
|
|
54
|
+
which opens the file, reads in 4 KB chunks, and stops as soon as it
|
|
55
|
+
finds the closing `---\n` fence. It raises a loud error if a fence
|
|
56
|
+
is not found within **256 KB** — that's a pathological frontmatter
|
|
57
|
+
ceiling, not a normal case. The body of the file is never loaded
|
|
58
|
+
during detection phases.
|
|
59
|
+
|
|
60
|
+
Result: for a wiki with 10,000 leaves × 50 KB average body, the
|
|
61
|
+
memory cost of walking every entry's frontmatter is ~40 MB
|
|
62
|
+
(4 KB × 10,000) instead of ~500 MB (50 KB × 10,000).
|
|
63
|
+
|
|
64
|
+
## 2. Body access is lazy and explicit
|
|
65
|
+
|
|
66
|
+
The iterator yields `{ path, relPath, data, type, loadBody }`. The
|
|
67
|
+
`loadBody()` method is a thunk — calling it reads the file fresh
|
|
68
|
+
and returns the body string. The iterator never caches bodies, so
|
|
69
|
+
the caller controls exactly how long a body stays in memory.
|
|
70
|
+
|
|
71
|
+
The **streaming consumer pattern** is the load-bearing discipline:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
for (const entry of iterEntries(wikiRoot)) {
|
|
75
|
+
const body = await entry.loadBody();
|
|
76
|
+
// ...do work with body...
|
|
77
|
+
releaseBody(); // balances the loadBody discipline counter
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`releaseBody()` decrements a process-global **discipline counter**
|
|
82
|
+
that tracks caller hygiene — NOT actual memory residency. V8 has no
|
|
83
|
+
cheap JavaScript-side hook for "is this string still alive?", so
|
|
84
|
+
the counter measures whether consumers follow the load-process-
|
|
85
|
+
release pattern, not whether the body is really reclaimed. A
|
|
86
|
+
caller that calls `releaseBody()` while still holding a reference
|
|
87
|
+
to `body` does not free the memory; V8 does that when GC runs and
|
|
88
|
+
the reference falls out of scope.
|
|
89
|
+
|
|
90
|
+
The counter catches a specific class of bug: consumers that
|
|
91
|
+
accidentally *accumulate* bodies in an array or closure across
|
|
92
|
+
iterations. In that case `peakInFlightBodies` grows to N and any
|
|
93
|
+
regression test can flag it. For real memory-residency questions
|
|
94
|
+
run under `node --expose-gc` and measure `process.memoryUsage()`
|
|
95
|
+
directly.
|
|
96
|
+
|
|
97
|
+
An imbalanced `releaseBody()` (more releases than loads) throws
|
|
98
|
+
loudly, so discipline bugs surface at the offending call site
|
|
99
|
+
instead of silently muddying the counter.
|
|
100
|
+
|
|
101
|
+
## 3. Operator-convergence is frontmatter-only by construction
|
|
102
|
+
|
|
103
|
+
The methodology (sections 3.5, 3.6, 8.5) mandates that operator
|
|
104
|
+
detection runs on frontmatter alone. The chunk API enforces this
|
|
105
|
+
mechanically: `iterEntries` does not return a body field, only a
|
|
106
|
+
thunk. A detection phase that never calls `loadBody()` cannot
|
|
107
|
+
accidentally allocate body bytes. Phase 6's tiered AI ladder
|
|
108
|
+
(TF-IDF → embeddings → Claude) operates on the same frontmatter-only
|
|
109
|
+
surface, so the scale guarantee extends through classify and
|
|
110
|
+
rebuild-plan-review as well.
|
|
111
|
+
|
|
112
|
+
## 4. Index regeneration respects the bound
|
|
113
|
+
|
|
114
|
+
`scripts/lib/indices.mjs`'s `listChildren` was rewired to use
|
|
115
|
+
`readFrontmatterStreaming` directly. A `rebuildAllIndices` call on a
|
|
116
|
+
10 k-entry wiki allocates bounded frontmatter bytes per leaf, not
|
|
117
|
+
full files. The scale e2e test measures this via the `totalBodyLoads`
|
|
118
|
+
metric and asserts it stays at zero during a whole-tree rebuild.
|
|
119
|
+
|
|
120
|
+
## 5. diff --op at scale
|
|
121
|
+
|
|
122
|
+
Large operations produce many commits — the orchestrator makes one
|
|
123
|
+
commit per phase, so a single build creates ~6 commits regardless of
|
|
124
|
+
corpus size. `skill-llm-wiki diff <wiki> --op <id> --stat` reads the
|
|
125
|
+
git object database, not the working tree — so the diff cost is
|
|
126
|
+
proportional to what *changed* in the operation, not to the total
|
|
127
|
+
wiki size. A 10,000-file wiki whose build made three operator
|
|
128
|
+
applications renders its diff in tens of milliseconds.
|
|
129
|
+
|
|
130
|
+
## 6. Context-window management in the wiki-runner
|
|
131
|
+
|
|
132
|
+
Byte-level memory is not the only bounded resource in a large
|
|
133
|
+
build. The wiki-runner sub-agent has its own **context window**, and
|
|
134
|
+
a 10k-entry build that fans out Tier 2 calls and stitches
|
|
135
|
+
progress back into the main transcript will overflow that window
|
|
136
|
+
long before it runs out of heap. The skill handles this with three
|
|
137
|
+
rules that together let the wiki-runner survive wikis of any
|
|
138
|
+
size.
|
|
139
|
+
|
|
140
|
+
### Rule 1 — Phase commits are the durable checkpoint
|
|
141
|
+
|
|
142
|
+
Every phase the orchestrator runs ends with a git commit in
|
|
143
|
+
`<wiki>/.llmwiki/git/`. Once `phase draft-frontmatter` has
|
|
144
|
+
committed its output, the wiki-runner's in-memory knowledge of
|
|
145
|
+
**what each entry's frontmatter looks like right now** is
|
|
146
|
+
redundant: `git show HEAD:<path>` can reconstruct it on demand.
|
|
147
|
+
The same applies to every operator-convergence iteration commit,
|
|
148
|
+
every index-generation commit, and the pre-op snapshot itself.
|
|
149
|
+
|
|
150
|
+
This means the wiki-runner can treat any phase's conversation
|
|
151
|
+
history as discardable once the phase's closing commit lands. A
|
|
152
|
+
thousand entries' worth of draft-frontmatter Tier 2 prompts that
|
|
153
|
+
lived inside draft-frontmatter never need to be re-read from the
|
|
154
|
+
wiki-runner's transcript — they're in the decision log and (for
|
|
155
|
+
the chosen frontmatter) in the commit tree.
|
|
156
|
+
|
|
157
|
+
### Rule 2 — Tier 2 work fans out to per-decision sub-agents
|
|
158
|
+
|
|
159
|
+
Every Tier 2 call is a separate sub-agent (see
|
|
160
|
+
`guide/tiered-ai.md` "Tier 2 execution via dedicated sub-agents").
|
|
161
|
+
The wiki-runner sees only the final decision, not the sub-agent's
|
|
162
|
+
prompt or response body. A 10k-entry wiki with 500 mid-band pairs
|
|
163
|
+
produces 500 Tier 2 sub-agents, but the wiki-runner's own window
|
|
164
|
+
absorbs only 500 one-line decisions (plus whatever the
|
|
165
|
+
similarity-cache served without a Claude call at all).
|
|
166
|
+
|
|
167
|
+
### Rule 3 — Self-monitor and auto-compact
|
|
168
|
+
|
|
169
|
+
The wiki-runner periodically checks its remaining context budget.
|
|
170
|
+
When the budget falls below a safety threshold (typically around
|
|
171
|
+
20–25% remaining), it performs an **auto-compact** before
|
|
172
|
+
starting the next phase:
|
|
173
|
+
|
|
174
|
+
1. **Cut to the last clean checkpoint.** The most recent phase
|
|
175
|
+
commit is the earliest safe point to resume from. Everything
|
|
176
|
+
in the conversation after that commit is summarisable.
|
|
177
|
+
2. **Summarise what remains.** Produce a short paragraph per
|
|
178
|
+
completed phase — what it did, which op-id it committed under,
|
|
179
|
+
any warnings worth carrying forward. This summary lives in
|
|
180
|
+
the compacted transcript instead of the full per-phase chatter.
|
|
181
|
+
3. **Re-anchor the plan.** State the current phase, the next
|
|
182
|
+
phase, the remaining work (entries pending / iterations
|
|
183
|
+
remaining), and the pre-op tag. A resume from this compacted
|
|
184
|
+
state must be able to pick up exactly where the pre-compact
|
|
185
|
+
run left off.
|
|
186
|
+
4. **Drop Tier 2 transcripts.** Per-decision sub-agent transcripts
|
|
187
|
+
are already not in the wiki-runner's window (Rule 2), but any
|
|
188
|
+
lingering summaries can be replaced by a single line: "Tier 2
|
|
189
|
+
decisions landed: N same, M different, K undecidable (see
|
|
190
|
+
decisions.yaml)."
|
|
191
|
+
5. **Verify state against git.** Run `skill-llm-wiki log --op
|
|
192
|
+
<id>` to confirm the expected phase commits are reachable
|
|
193
|
+
from HEAD — this catches the rare case where the auto-compact
|
|
194
|
+
dropped a commit the agent thought had landed.
|
|
195
|
+
|
|
196
|
+
Steps 1–5 are cheap: they're all reads against the private git,
|
|
197
|
+
no mutation happens. Auto-compaction is idempotent — running it
|
|
198
|
+
twice in a row is safe.
|
|
199
|
+
|
|
200
|
+
### What the wiki-runner does NOT do
|
|
201
|
+
|
|
202
|
+
- **Pre-emptive compaction.** Auto-compact fires only when budget
|
|
203
|
+
pressure is real. Compacting prematurely wastes the headroom
|
|
204
|
+
you'd need to correctly summarise the phases in the first place.
|
|
205
|
+
- **Mid-phase compaction.** Auto-compact runs between phase
|
|
206
|
+
boundaries, never in the middle of a phase. A half-committed
|
|
207
|
+
phase is not a resumable state; waiting for the phase commit
|
|
208
|
+
is the right protocol.
|
|
209
|
+
- **Main-session compaction.** The main session is not the
|
|
210
|
+
wiki-runner. If the main session's context is under pressure,
|
|
211
|
+
that's a separate concern the main session handles on its own.
|
|
212
|
+
See SKILL.md's "Agent delegation contract" for why the main
|
|
213
|
+
session should never be holding wiki content in its window in
|
|
214
|
+
the first place.
|
|
215
|
+
|
|
216
|
+
### Budget-driven fallbacks
|
|
217
|
+
|
|
218
|
+
If auto-compaction still leaves the wiki-runner under budget
|
|
219
|
+
pressure — e.g. a pathologically large corpus with thousands of
|
|
220
|
+
simultaneously-pending Tier 2 decisions — the wiki-runner should:
|
|
221
|
+
|
|
222
|
+
1. **Split the remaining operation at a phase boundary.** Roll back
|
|
223
|
+
to the pre-op tag via `skill-llm-wiki rollback`, then launch a
|
|
224
|
+
fresh wiki-runner sub-agent whose prompt picks up from the
|
|
225
|
+
last known-good commit SHA with a clean context.
|
|
226
|
+
2. **Narrow the Tier 2 fan-out.** Raise the Tier 1 escalation
|
|
227
|
+
threshold so fewer pairs reach Tier 2, with a note to the
|
|
228
|
+
user that the run was downgraded. This trades quality for
|
|
229
|
+
completability.
|
|
230
|
+
3. **Stop and report.** If neither of the above fits, surface the
|
|
231
|
+
situation to the main session and ask the user whether to
|
|
232
|
+
continue with a narrower run or wait for a `--quality-mode
|
|
233
|
+
claude-first` pass on a smaller slice.
|
|
234
|
+
|
|
235
|
+
The rule is: **never silently drop quality, never silently
|
|
236
|
+
abort.** Either the operation completes end-to-end at the
|
|
237
|
+
requested quality level, or the wiki-runner returns control to
|
|
238
|
+
the user with a clear explanation of what it couldn't finish.
|
|
239
|
+
|
|
240
|
+
## What this does NOT do
|
|
241
|
+
|
|
242
|
+
- Tune garbage collection. V8 handles its own heap; the chunk
|
|
243
|
+
iterator just ensures it gets small objects to collect.
|
|
244
|
+
- Stream *writes*. The orchestrator still does one `writeFileSync`
|
|
245
|
+
per leaf during build. Phase 6 may revisit this if a prose-heavy
|
|
246
|
+
corpus produces write pressure, but today the read path was the
|
|
247
|
+
dominant cost.
|
|
248
|
+
- Cache bodies across iterations. Each convergence pass re-invokes
|
|
249
|
+
`iterEntries` with fresh reads, so frontmatter edits from the
|
|
250
|
+
previous pass land correctly. A caching layer would break this
|
|
251
|
+
and is deliberately omitted.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: in-place-mode
|
|
3
|
+
type: primary
|
|
4
|
+
depth_role: leaf
|
|
5
|
+
focus: converting an existing folder into a wiki in place, lossless and reversible
|
|
6
|
+
parents:
|
|
7
|
+
- index.md
|
|
8
|
+
covers:
|
|
9
|
+
- "--layout-mode in-place never runs without explicit user opt-in"
|
|
10
|
+
- pre-op snapshot captures every byte into the private git repo before mutation
|
|
11
|
+
- "rollback --to pre-<op-id> restores byte-exact pre-operation state"
|
|
12
|
+
- "the user's own git repository is never touched, even when the wiki is inside it"
|
|
13
|
+
- wiki-local .gitignore hides our private metadata from any ancestor user repo
|
|
14
|
+
- cannot combine with --target — the source IS the target in this mode
|
|
15
|
+
tags:
|
|
16
|
+
- layout
|
|
17
|
+
- in-place
|
|
18
|
+
- conversion
|
|
19
|
+
activation:
|
|
20
|
+
keyword_matches:
|
|
21
|
+
- in place
|
|
22
|
+
- in-place
|
|
23
|
+
- overwrite
|
|
24
|
+
- transform my folder
|
|
25
|
+
- convert this directory
|
|
26
|
+
- make my docs into a wiki
|
|
27
|
+
tag_matches:
|
|
28
|
+
- layout
|
|
29
|
+
- conversion
|
|
30
|
+
escalation_from:
|
|
31
|
+
- build
|
|
32
|
+
- fix
|
|
33
|
+
source:
|
|
34
|
+
origin: file
|
|
35
|
+
path: in-place-mode.md
|
|
36
|
+
hash: "sha256:38dd2158263b6cdb006ff86d53035d88172e27be63867116e74100c85ab149f1"
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
# In-place mode
|
|
40
|
+
|
|
41
|
+
Pass `--layout-mode in-place` when the user explicitly says "transform this
|
|
42
|
+
folder into a wiki" or "convert my docs in place". The source folder becomes
|
|
43
|
+
the wiki. History lives in `<source>/.llmwiki/git/` from the first operation
|
|
44
|
+
onward, and every operation tags a rollback anchor before mutating.
|
|
45
|
+
|
|
46
|
+
## Safety envelope
|
|
47
|
+
|
|
48
|
+
Phase 1 of every operation (including the first build) runs `preOpSnapshot`:
|
|
49
|
+
|
|
50
|
+
1. Lazily initialise `<source>/.llmwiki/git/` if missing, commit a `genesis`
|
|
51
|
+
tag of the empty tree.
|
|
52
|
+
2. Write `<source>/.gitignore` with the three skill-internal entries so the
|
|
53
|
+
user's ancestor git repository (if any) ignores our private metadata.
|
|
54
|
+
3. `git add -A` — stage every file in the working tree.
|
|
55
|
+
4. If anything is staged, commit with message `pre-op <op-id>`.
|
|
56
|
+
5. Tag the commit as `pre-op/<op-id>` (loud on collision).
|
|
57
|
+
|
|
58
|
+
The `pre-op/<op-id>` tag is the rollback anchor and lives in a separate
|
|
59
|
+
ref namespace from the final `op/<op-id>` tag so git's ref hierarchy
|
|
60
|
+
never collides. Even a SIGKILL between steps 2 and 5 leaves the tag from
|
|
61
|
+
the previous operation reachable. Rollback is:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
skill-llm-wiki rollback <source> --to pre-<op-id>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This runs `git reset --hard <tag> && git clean -fd` against the private repo,
|
|
68
|
+
returning the working tree to byte-identical pre-operation state. `.work/` and
|
|
69
|
+
`.shape/history/*/work/` are preserved through rollback (protected by
|
|
70
|
+
`.llmwiki/git/info/exclude`); every other untracked change is wiped.
|
|
71
|
+
|
|
72
|
+
## When to choose in-place vs sibling
|
|
73
|
+
|
|
74
|
+
Ask the user if the request is ambiguous. Do not guess. Typical signals:
|
|
75
|
+
|
|
76
|
+
| User says | Mode |
|
|
77
|
+
|-----------|------|
|
|
78
|
+
| "build a wiki from my docs" | sibling (default `<source>.wiki/`) |
|
|
79
|
+
| "convert my docs to a wiki" | **ask** — could mean either |
|
|
80
|
+
| "transform this folder in place" | in-place |
|
|
81
|
+
| "overwrite ./docs with a wiki structure" | in-place |
|
|
82
|
+
| "I want the wiki files alongside ./docs" | sibling |
|
|
83
|
+
| "put it in my memory folder" | hosted with `--target ./memory` |
|
|
84
|
+
|
|
85
|
+
When ambiguous, Claude should say something like: "I can either build a new
|
|
86
|
+
`./docs.wiki/` sibling (default, reversible, leaves `./docs` untouched) or
|
|
87
|
+
transform `./docs` itself with `--layout-mode in-place` (reversible via git
|
|
88
|
+
rollback). Which do you prefer?"
|
|
89
|
+
|
|
90
|
+
## Coexistence with a user git repository
|
|
91
|
+
|
|
92
|
+
If the user's source folder is already tracked by their own git repo, the
|
|
93
|
+
first in-place operation writes `.gitignore` with `.llmwiki/`, `.work/`,
|
|
94
|
+
`.shape/history/*/work/`. The user's git sees those paths as ignored. Our
|
|
95
|
+
private repo's operations never touch the user's `.git/` — see
|
|
96
|
+
[guide/coexistence.md](coexistence.md) for the full coexistence story and
|
|
97
|
+
proof-of-isolation tests.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: layout
|
|
3
|
+
type: index
|
|
4
|
+
depth_role: subcategory
|
|
5
|
+
depth: 1
|
|
6
|
+
focus: Layout modes, hosted-mode contract, and in-place conversion of source folders.
|
|
7
|
+
parents:
|
|
8
|
+
- "../index.md"
|
|
9
|
+
shared_covers: []
|
|
10
|
+
entries:
|
|
11
|
+
- id: in-place-mode
|
|
12
|
+
file: in-place-mode.md
|
|
13
|
+
type: primary
|
|
14
|
+
focus: converting an existing folder into a wiki in place, lossless and reversible
|
|
15
|
+
tags:
|
|
16
|
+
- layout
|
|
17
|
+
- in-place
|
|
18
|
+
- conversion
|
|
19
|
+
- id: layout-contract
|
|
20
|
+
file: layout-contract.md
|
|
21
|
+
type: primary
|
|
22
|
+
focus: hosted-mode layout contract schema, validation, and conflict-resolution rules
|
|
23
|
+
tags:
|
|
24
|
+
- hosted-mode
|
|
25
|
+
- layout-contract
|
|
26
|
+
- schema
|
|
27
|
+
- id: layout-modes
|
|
28
|
+
file: layout-modes.md
|
|
29
|
+
type: primary
|
|
30
|
+
focus: "choosing between sibling (default), in-place, and hosted layout modes"
|
|
31
|
+
tags:
|
|
32
|
+
- layout
|
|
33
|
+
- operation
|
|
34
|
+
children: []
|
|
35
|
+
---
|
|
36
|
+
<!-- BEGIN AUTO-GENERATED NAVIGATION -->
|
|
37
|
+
|
|
38
|
+
# Layout
|
|
39
|
+
|
|
40
|
+
**Focus:** Layout modes, hosted-mode contract, and in-place conversion of source folders.
|
|
41
|
+
|
|
42
|
+
## Children
|
|
43
|
+
|
|
44
|
+
| File | Type | Focus |
|
|
45
|
+
|------|------|-------|
|
|
46
|
+
| [in-place-mode.md](in-place-mode.md) | 📄 primary | converting an existing folder into a wiki in place, lossless and reversible |
|
|
47
|
+
| [layout-contract.md](layout-contract.md) | 📄 primary | hosted-mode layout contract schema, validation, and conflict-resolution rules |
|
|
48
|
+
| [layout-modes.md](layout-modes.md) | 📄 primary | choosing between sibling (default), in-place, and hosted layout modes |
|
|
49
|
+
|
|
50
|
+
<!-- END AUTO-GENERATED NAVIGATION -->
|
|
51
|
+
|
|
52
|
+
<!-- BEGIN AUTHORED ORIENTATION -->
|
|
53
|
+
<!-- END AUTHORED ORIENTATION -->
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: layout-contract
|
|
3
|
+
type: primary
|
|
4
|
+
depth_role: leaf
|
|
5
|
+
focus: hosted-mode layout contract schema, validation, and conflict-resolution rules
|
|
6
|
+
parents:
|
|
7
|
+
- index.md
|
|
8
|
+
covers:
|
|
9
|
+
- "full contract schema (mode, versioning, purpose, global_invariants, layout with children and dynamic_subdirs)"
|
|
10
|
+
- field semantics for path, purpose, content_rules, allow_entry_types, max_depth, dynamic_subdirs.template placeholders
|
|
11
|
+
- contract validation rules applied before every operation
|
|
12
|
+
- "conflict resolution: the contract always wins over methodology defaults when they disagree"
|
|
13
|
+
- "authoring contracts on behalf of the user (always confirm before writing)"
|
|
14
|
+
tags:
|
|
15
|
+
- hosted-mode
|
|
16
|
+
- layout-contract
|
|
17
|
+
- schema
|
|
18
|
+
activation:
|
|
19
|
+
keyword_matches:
|
|
20
|
+
- hosted
|
|
21
|
+
- contract
|
|
22
|
+
- layout.yaml
|
|
23
|
+
- llmwiki.layout
|
|
24
|
+
- in-place
|
|
25
|
+
tag_matches:
|
|
26
|
+
- hosted-mode
|
|
27
|
+
source:
|
|
28
|
+
origin: file
|
|
29
|
+
path: layout-contract.md
|
|
30
|
+
hash: "sha256:78213fe390d9f5e0dd58f23c14bdeaa8136831a3c0987e53f02814c37640f4f2"
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
# Layout contract (hosted mode)
|
|
34
|
+
|
|
35
|
+
A layout contract is a YAML file at the hosted-mode target's root: `<target>/.llmwiki.layout.yaml`. Presence of this file is the sole signal that enters hosted mode. When you read this file, you are operating on a hosted-mode target and must honor every rule below.
|
|
36
|
+
|
|
37
|
+
## Full schema
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
mode: hosted
|
|
41
|
+
|
|
42
|
+
versioning:
|
|
43
|
+
style: in-place # or: sibling-versioned
|
|
44
|
+
backup_before_mutate: true
|
|
45
|
+
backup_dir: .llmwiki.backups # relative to target root
|
|
46
|
+
|
|
47
|
+
purpose: "Persistent memory for the agent across sessions"
|
|
48
|
+
|
|
49
|
+
# Additional hard invariants enforced by Validate and Fix on top of
|
|
50
|
+
# the methodology's defaults.
|
|
51
|
+
global_invariants:
|
|
52
|
+
- "every leaf must declare a source.origin field"
|
|
53
|
+
- "no leaf exceeds 300 lines"
|
|
54
|
+
|
|
55
|
+
layout:
|
|
56
|
+
- path: knowledge
|
|
57
|
+
purpose: "long-lived factual knowledge and reference entries"
|
|
58
|
+
content_rules:
|
|
59
|
+
- "each leaf is a self-contained fact"
|
|
60
|
+
- "covers[] lists concrete concerns, not vague topics"
|
|
61
|
+
allow_entry_types: [primary]
|
|
62
|
+
max_depth: 3
|
|
63
|
+
|
|
64
|
+
- path: daily
|
|
65
|
+
purpose: "time-series daily journal"
|
|
66
|
+
dynamic_subdirs:
|
|
67
|
+
template: "{yyyy}-{mm}-{dd}"
|
|
68
|
+
purpose: "entries from a single day"
|
|
69
|
+
allow_entry_types: [primary]
|
|
70
|
+
content_rules:
|
|
71
|
+
- "one leaf per event or observation"
|
|
72
|
+
- "past days are read-only except via explicit Fix"
|
|
73
|
+
|
|
74
|
+
- path: policies
|
|
75
|
+
purpose: "rules the agent must follow"
|
|
76
|
+
allow_entry_types: [primary, overlay]
|
|
77
|
+
|
|
78
|
+
- path: projects
|
|
79
|
+
purpose: "active and archived project workspaces"
|
|
80
|
+
children:
|
|
81
|
+
- path: active
|
|
82
|
+
purpose: "in-flight projects"
|
|
83
|
+
- path: archive
|
|
84
|
+
purpose: "completed or abandoned projects"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Field semantics
|
|
88
|
+
|
|
89
|
+
- **`mode`** — must be `hosted`.
|
|
90
|
+
- **`versioning.style`** — `in-place` writes directly into the target; `sibling-versioned` produces `.llmwiki.vN` siblings but respects contract structure inside each version.
|
|
91
|
+
- **`versioning.backup_before_mutate`** (default true) — snapshot target to `backup_dir/<timestamp>/` before any structural mutation.
|
|
92
|
+
- **`versioning.backup_dir`** (default `.llmwiki.backups`) — relative to target.
|
|
93
|
+
- **`purpose`** (optional) — becomes the root index's `focus` if not authored otherwise.
|
|
94
|
+
- **`global_invariants`** (optional) — additional hard invariants enforced on top of the methodology defaults.
|
|
95
|
+
- **`layout`** (required) — array of top-level subdirectory specs.
|
|
96
|
+
- **`layout[].path`** (required) — directory name relative to target root. No `/`, no `..`.
|
|
97
|
+
- **`layout[].purpose`** (required) — becomes the directory index's `focus`.
|
|
98
|
+
- **`layout[].content_rules`** (optional) — per-leaf rules checked as soft signals by Validate.
|
|
99
|
+
- **`layout[].allow_entry_types`** (optional, default all) — which entry types are permitted.
|
|
100
|
+
- **`layout[].max_depth`** (optional) — hard cap on nesting within this subtree.
|
|
101
|
+
- **`layout[].dynamic_subdirs`** (optional) — marks the directory as a container for dynamically-named subdirectories.
|
|
102
|
+
- **`layout[].dynamic_subdirs.template`** (required within dynamic_subdirs) — placeholder template. Supported placeholders: `{yyyy}` `{mm}` `{dd}` `{hh}` `{mi}` `{ss}` `{iso}` `{slug}`. Resolved against the clock (or a user-supplied slug) at write time.
|
|
103
|
+
- **`layout[].dynamic_subdirs.purpose`** / `content_rules` / `allow_entry_types` — inherited by dynamically-created subdirectories.
|
|
104
|
+
- **`layout[].children`** (optional) — nested directory specs for fixed deeper structure.
|
|
105
|
+
|
|
106
|
+
## Contract validation
|
|
107
|
+
|
|
108
|
+
Before any operation in hosted mode, parse the contract and verify:
|
|
109
|
+
|
|
110
|
+
- `mode: hosted` is present.
|
|
111
|
+
- Every `path` is a legal single-segment directory name.
|
|
112
|
+
- No duplicate paths at the same level.
|
|
113
|
+
- `dynamic_subdirs.template` uses only supported placeholders.
|
|
114
|
+
- `children` nesting is well-formed (no cycles).
|
|
115
|
+
- `versioning.style: sibling-versioned` is only used where sibling naming is possible.
|
|
116
|
+
|
|
117
|
+
A contract that fails these checks aborts the operation with a clear error. Tell the user exactly which rule failed and where.
|
|
118
|
+
|
|
119
|
+
## Conflict resolution: contract always wins
|
|
120
|
+
|
|
121
|
+
When methodology defaults and the contract disagree, the contract wins:
|
|
122
|
+
|
|
123
|
+
- Rewrite operators cannot override contract structure.
|
|
124
|
+
- The narrowing chain is still enforced, but root `focus` comes from the contract's `purpose` if provided.
|
|
125
|
+
- Validation adds contract invariants on top of methodology invariants; it never removes methodology invariants.
|
|
126
|
+
|
|
127
|
+
A hosted wiki is strictly at least as constrained as a free wiki — never less. Quality is never compromised because the contract adds structure; the methodology's guarantees remain intact.
|
|
128
|
+
|
|
129
|
+
## Writing layout contracts on behalf of the user
|
|
130
|
+
|
|
131
|
+
If the user describes a desired hosted structure in natural language ("I want a memory folder with knowledge, daily entries per day, and projects with active/archive"), draft a `.llmwiki.layout.yaml` matching the description and **show it to the user for confirmation before writing the file**. Getting the contract wrong at Build time means every subsequent operation is constrained by the mistake, so always confirm.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: layout-modes
|
|
3
|
+
type: primary
|
|
4
|
+
depth_role: leaf
|
|
5
|
+
focus: "choosing between sibling (default), in-place, and hosted layout modes"
|
|
6
|
+
parents:
|
|
7
|
+
- index.md
|
|
8
|
+
covers:
|
|
9
|
+
- "sibling mode writes to <source>.wiki/ by default; no version-numbered folders"
|
|
10
|
+
- "in-place mode transforms the user's source folder itself; requires explicit opt-in"
|
|
11
|
+
- hosted mode writes under a pre-declared .llmwiki.layout.yaml contract at --target
|
|
12
|
+
- "private git repo at <wiki>/.llmwiki/git/ carries history and rollback anchors across all modes"
|
|
13
|
+
- "sibling default collision handling refuses on INT-01 / INT-03 rather than guessing"
|
|
14
|
+
- "legacy <source>.llmwiki.v<N>/ is detected via INT-04 and requires explicit migrate"
|
|
15
|
+
tags:
|
|
16
|
+
- layout
|
|
17
|
+
- operation
|
|
18
|
+
activation:
|
|
19
|
+
keyword_matches:
|
|
20
|
+
- layout
|
|
21
|
+
- mode
|
|
22
|
+
- sibling
|
|
23
|
+
- in-place
|
|
24
|
+
- in place
|
|
25
|
+
- hosted
|
|
26
|
+
- .wiki
|
|
27
|
+
- target folder
|
|
28
|
+
- default
|
|
29
|
+
tag_matches:
|
|
30
|
+
- layout
|
|
31
|
+
- any-op
|
|
32
|
+
escalation_from:
|
|
33
|
+
- build
|
|
34
|
+
- extend
|
|
35
|
+
- rebuild
|
|
36
|
+
- fix
|
|
37
|
+
source:
|
|
38
|
+
origin: file
|
|
39
|
+
path: layout-modes.md
|
|
40
|
+
hash: "sha256:5514b435d94710045b2669167f617ed641bd8697ce3300420078c39f2f27f02e"
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
# Layout modes
|
|
44
|
+
|
|
45
|
+
Every top-level operation (build / extend / rebuild / fix / join) accepts
|
|
46
|
+
`--layout-mode <sibling|in-place|hosted>`. The default is **sibling**. Pick the
|
|
47
|
+
mode that matches the user's stated intent — never guess.
|
|
48
|
+
|
|
49
|
+
## Sibling (default)
|
|
50
|
+
|
|
51
|
+
Writes to a sibling directory named `<source>.wiki/` next to the source.
|
|
52
|
+
|
|
53
|
+
- `build ./docs` → creates `./docs.wiki/`
|
|
54
|
+
- History lives in `./docs.wiki/.llmwiki/git/`, not in separate versioned folders
|
|
55
|
+
- No `<source>.llmwiki.v1/`, `.v2/`, etc. anywhere
|
|
56
|
+
- Safe to re-run: second `build ./docs` exits with **INT-03** and prompts for
|
|
57
|
+
`rebuild` / `extend` / `fix` instead of silently overwriting
|
|
58
|
+
|
|
59
|
+
Use when: the user says "build a wiki from my docs folder" without specifying
|
|
60
|
+
a target. This is the default because it is the cheapest reversible outcome
|
|
61
|
+
— the source is untouched and the wiki is visibly separate.
|
|
62
|
+
|
|
63
|
+
## In-place
|
|
64
|
+
|
|
65
|
+
Transforms the source folder itself into a wiki. The private git repo lives
|
|
66
|
+
at `<source>/.llmwiki/git/`, the pre-op snapshot captures every byte, and
|
|
67
|
+
`rollback --to pre-<op-id>` restores byte-exact state.
|
|
68
|
+
|
|
69
|
+
- Must be opted into with `--layout-mode in-place`
|
|
70
|
+
- Cannot be combined with `--target` (triggers **INT-09a**)
|
|
71
|
+
- First run creates `<source>/.gitignore` so an ancestor user git repo ignores
|
|
72
|
+
the private metadata
|
|
73
|
+
|
|
74
|
+
Use when: the user explicitly says "convert my docs folder in place", "transform
|
|
75
|
+
this directory", or "I want the wiki files where my docs are". If the user
|
|
76
|
+
says merely "convert", **ask** whether they mean sibling or in-place before
|
|
77
|
+
running.
|
|
78
|
+
|
|
79
|
+
## Hosted
|
|
80
|
+
|
|
81
|
+
Writes under a pre-existing `.llmwiki.layout.yaml` contract at `--target <path>`.
|
|
82
|
+
The contract defines where entries live, what naming conventions apply, and
|
|
83
|
+
any site-specific rules. Use this when the wiki has a fixed home (like
|
|
84
|
+
`./memory/knowledge/`) and the user wants the skill to respect that layout.
|
|
85
|
+
|
|
86
|
+
- Requires both `--layout-mode hosted` and `--target <path>`. Missing
|
|
87
|
+
`--target` triggers **INT-09b**; a non-empty target without a layout
|
|
88
|
+
contract triggers **INT-01b** (override with `--accept-foreign-target`
|
|
89
|
+
only after confirming with the user).
|
|
90
|
+
- The target must carry a `.llmwiki.layout.yaml` contract **or** be a fresh
|
|
91
|
+
directory the first operation will initialise
|
|
92
|
+
|
|
93
|
+
## Collision handling
|
|
94
|
+
|
|
95
|
+
The CLI refuses to guess when the layout is ambiguous:
|
|
96
|
+
|
|
97
|
+
| Code | Situation | Resolving flag |
|
|
98
|
+
|------|-----------|----------------|
|
|
99
|
+
| INT-01 | Default sibling target exists as a foreign directory | `--target <other>` or `--layout-mode in-place` |
|
|
100
|
+
| INT-01b | Explicit `--target` is a non-empty foreign directory | `--target <other>` or `--accept-foreign-target` |
|
|
101
|
+
| INT-02 | Source is already a managed wiki | pick `extend` / `rebuild` / `fix`, or `build --layout-mode in-place` |
|
|
102
|
+
| INT-03 | Default sibling target is already a managed wiki | pick `extend` / `rebuild` / `fix` |
|
|
103
|
+
| INT-04 | Source is a legacy `<name>.llmwiki.v<N>` folder | `skill-llm-wiki migrate <legacy>` |
|
|
104
|
+
|
|
105
|
+
For each code the CLI prints numbered options; Claude surfaces those to the
|
|
106
|
+
user verbatim so the human makes the call.
|
|
107
|
+
|
|
108
|
+
## Legacy migration
|
|
109
|
+
|
|
110
|
+
Pre–Phase 2 wikis used the naming convention `<source>.llmwiki.v1/`, `.v2/`, ...
|
|
111
|
+
The skill no longer uses that pattern. When the skill detects a legacy folder
|
|
112
|
+
it refuses every operation (INT-04) and prompts for `skill-llm-wiki migrate`,
|
|
113
|
+
which copies the latest version's content into the new `<source>.wiki/`
|
|
114
|
+
sibling, initialises the private git repo, and records the migration lineage
|
|
115
|
+
in `.llmwiki/op-log.yaml`. The legacy folder is left byte-identical on disk.
|