codealmanac 0.1.8 → 0.1.10
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/dist/{chunk-73A5TGBC.js → chunk-QLHJP2XK.js} +2 -2
- package/dist/{cli-HIXXCUSQ.js → cli-W3OYVJYH.js} +3 -3
- package/dist/codealmanac.js +1 -1
- package/dist/{doctor-IS6N7V63.js → doctor-ODFNJUKH.js} +2 -2
- package/dist/{register-commands-JAPO3AUB.js → register-commands-JHC2OFKM.js} +18 -10
- package/dist/register-commands-JHC2OFKM.js.map +1 -0
- package/dist/{wiki-EHZ7LG7R.js → wiki-IPSRRGOT.js} +15 -16
- package/dist/wiki-IPSRRGOT.js.map +1 -0
- package/guides/mini.md +2 -2
- package/guides/reference.md +9 -9
- package/hooks/almanac-capture.sh +9 -1
- package/package.json +1 -1
- package/prompts/bootstrap.md +11 -1
- package/dist/register-commands-JAPO3AUB.js.map +0 -1
- package/dist/wiki-EHZ7LG7R.js.map +0 -1
- /package/dist/{chunk-73A5TGBC.js.map → chunk-QLHJP2XK.js.map} +0 -0
- /package/dist/{cli-HIXXCUSQ.js.map → cli-W3OYVJYH.js.map} +0 -0
- /package/dist/{doctor-IS6N7V63.js.map → doctor-ODFNJUKH.js.map} +0 -0
|
@@ -140,23 +140,22 @@ function describeLastCapture(almanacDir, nowFn) {
|
|
|
140
140
|
message: "last capture: never"
|
|
141
141
|
};
|
|
142
142
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
entries
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
).map((e) => {
|
|
143
|
+
const logDirs = [path.join(almanacDir, "logs"), almanacDir];
|
|
144
|
+
const captures = logDirs.flatMap((dir) => {
|
|
145
|
+
let entries;
|
|
146
|
+
try {
|
|
147
|
+
entries = readdirSync(dir);
|
|
148
|
+
} catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
return entries.filter(
|
|
152
|
+
(e) => e.startsWith(".capture-") && (e.endsWith(".log") || e.endsWith(".jsonl"))
|
|
153
|
+
).map((e) => ({ dir, name: e }));
|
|
154
|
+
}).map((e) => {
|
|
156
155
|
try {
|
|
157
156
|
return {
|
|
158
|
-
name: e,
|
|
159
|
-
mtime: statSync(path.join(
|
|
157
|
+
name: e.name,
|
|
158
|
+
mtime: statSync(path.join(e.dir, e.name)).mtimeMs
|
|
160
159
|
};
|
|
161
160
|
} catch {
|
|
162
161
|
return null;
|
|
@@ -235,4 +234,4 @@ function countHealthProblems(jsonStdout) {
|
|
|
235
234
|
export {
|
|
236
235
|
gatherWikiChecks
|
|
237
236
|
};
|
|
238
|
-
//# sourceMappingURL=wiki-
|
|
237
|
+
//# sourceMappingURL=wiki-IPSRRGOT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/doctor-checks/wiki.ts"],"sourcesContent":["import { existsSync, readdirSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type Database from \"better-sqlite3\";\n\nimport { ensureFreshIndex } from \"../../indexer/index.js\";\nimport { openIndex } from \"../../indexer/schema.js\";\nimport { findNearestAlmanacDir } from \"../../paths.js\";\nimport { findEntry } from \"../../registry/index.js\";\nimport { runHealth, type HealthReport } from \"../health.js\";\nimport { formatDuration } from \"./duration.js\";\nimport type { Check, DoctorOptions } from \"./types.js\";\n\nexport async function gatherWikiChecks(options: DoctorOptions): Promise<Check[]> {\n const checks: Check[] = [];\n const repoRoot = findNearestAlmanacDir(options.cwd);\n\n if (repoRoot === null) {\n checks.push({\n status: \"info\",\n key: \"wiki.none\",\n message: \"No wiki in current directory\",\n fix: \"run: almanac bootstrap (to create one in this repo)\",\n });\n return checks;\n }\n\n checks.push({\n status: \"info\",\n key: \"wiki.repo\",\n message: `repo: ${repoRoot}`,\n });\n\n try {\n await ensureFreshIndex({ repoRoot });\n } catch {\n // non-fatal: counts below and the health probe report any real issue.\n }\n\n checks.push(await describeRegistry(repoRoot));\n\n const almanacDir = path.join(repoRoot, \".almanac\");\n const dbPath = path.join(almanacDir, \"index.db\");\n checks.push(...describeCounts(dbPath));\n checks.push(describeIndexFreshness(dbPath));\n checks.push(describeLastCapture(almanacDir, options.now));\n checks.push(await describeHealth(repoRoot, options));\n\n return checks;\n}\n\nasync function describeRegistry(repoRoot: string): Promise<Check> {\n try {\n const entry = await findEntry({ path: repoRoot });\n if (entry !== null) {\n return {\n status: \"ok\",\n key: \"wiki.registered\",\n message: `registered as '${entry.name}'`,\n };\n }\n return {\n status: \"info\",\n key: \"wiki.registered\",\n message: \"not yet registered (will register on first command)\",\n };\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n status: \"problem\",\n key: \"wiki.registered\",\n message: `could not read registry: ${msg}`,\n fix: \"inspect ~/.almanac/registry.json; remove or fix the malformed entry\",\n };\n }\n}\n\nfunction describeCounts(dbPath: string): Check[] {\n const checks: Check[] = [];\n let pageCount: number | null = null;\n let topicCount: number | null = null;\n\n if (existsSync(dbPath)) {\n try {\n const db = openIndex(dbPath);\n try {\n pageCount = countRows(db, \"pages\");\n topicCount = countRows(db, \"topics\");\n } finally {\n db.close();\n }\n } catch {\n pageCount = null;\n }\n }\n\n if (pageCount !== null) {\n checks.push({\n status: \"info\",\n key: \"wiki.pages\",\n message: `pages: ${pageCount}`,\n });\n }\n if (topicCount !== null) {\n checks.push({\n status: \"info\",\n key: \"wiki.topics\",\n message: `topics: ${topicCount}`,\n });\n }\n\n return checks;\n}\n\nfunction countRows(db: Database.Database, table: string): number {\n const row = db\n .prepare<[], { n: number }>(`SELECT COUNT(*) AS n FROM ${table}`)\n .get();\n return row?.n ?? 0;\n}\n\nfunction describeIndexFreshness(dbPath: string): Check {\n if (!existsSync(dbPath)) {\n return {\n status: \"info\",\n key: \"wiki.index\",\n message: \"index: not built yet (run any query command)\",\n };\n }\n try {\n const dbMtime = statSync(dbPath).mtimeMs;\n const age = Date.now() - dbMtime;\n return {\n status: \"info\",\n key: \"wiki.index\",\n message: `index: rebuilt ${formatDuration(age)} ago`,\n };\n } catch {\n return {\n status: \"info\",\n key: \"wiki.index\",\n message: \"index: present\",\n };\n }\n}\n\nfunction describeLastCapture(\n almanacDir: string,\n nowFn?: () => Date,\n): Check {\n if (!existsSync(almanacDir)) {\n return {\n status: \"info\",\n key: \"wiki.capture\",\n message: \"last capture: never\",\n };\n }\n const logDirs = [path.join(almanacDir, \"logs\"), almanacDir];\n const captures = logDirs\n .flatMap((dir) => {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n return entries\n .filter(\n (e) =>\n e.startsWith(\".capture-\") &&\n (e.endsWith(\".log\") || e.endsWith(\".jsonl\")),\n )\n .map((e) => ({ dir, name: e }));\n })\n .map((e) => {\n try {\n return {\n name: e.name,\n mtime: statSync(path.join(e.dir, e.name)).mtimeMs,\n };\n } catch {\n return null;\n }\n })\n .filter((e): e is { name: string; mtime: number } => e !== null);\n if (captures.length === 0) {\n return {\n status: \"info\",\n key: \"wiki.capture\",\n message: \"last capture: never\",\n };\n }\n captures.sort((a, b) => b.mtime - a.mtime);\n const latest = captures[0]!;\n const now = (nowFn?.() ?? new Date()).getTime();\n const age = now - latest.mtime;\n return {\n status: \"info\",\n key: \"wiki.capture\",\n message: `last capture: ${formatDuration(age)} ago (${latest.name})`,\n };\n}\n\nasync function describeHealth(\n repoRoot: string,\n options: DoctorOptions,\n): Promise<Check> {\n const healthFn = options.runHealthFn ?? runHealth;\n try {\n const healthRes = await healthFn({\n cwd: repoRoot,\n json: true,\n });\n const problems = countHealthProblems(healthRes.stdout);\n if (problems === 0) {\n return {\n status: \"ok\",\n key: \"wiki.health\",\n message: \"almanac health reports 0 problems\",\n };\n }\n return {\n status: \"problem\",\n key: \"wiki.health\",\n message: `almanac health reports ${problems} problem${problems === 1 ? \"\" : \"s\"}`,\n fix: \"run: almanac health\",\n };\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n status: \"info\",\n key: \"wiki.health\",\n message: `could not run almanac health: ${msg}`,\n };\n }\n}\n\nconst HEALTH_PROBLEM_KEYS: (keyof HealthReport)[] = [\n \"orphans\",\n \"stale\",\n \"dead_refs\",\n \"broken_links\",\n \"broken_xwiki\",\n \"empty_topics\",\n \"empty_pages\",\n \"slug_collisions\",\n];\n\nfunction countHealthProblems(jsonStdout: string): number {\n try {\n const report = JSON.parse(jsonStdout) as Partial<HealthReport>;\n let total = 0;\n for (const key of HEALTH_PROBLEM_KEYS) {\n const arr = report[key];\n if (Array.isArray(arr)) total += arr.length;\n }\n return total;\n } catch {\n return 0;\n }\n}\n"],"mappings":"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","names":[]}
|
package/guides/mini.md
CHANGED
|
@@ -210,9 +210,9 @@ Empty stdout plus `# 0 results` on stderr means the query ran and genuinely matc
|
|
|
210
210
|
```bash
|
|
211
211
|
almanac doctor # install.hook: ok/problem, wiki.capture: last capture age
|
|
212
212
|
almanac hook status # just the hook entry
|
|
213
|
-
ls -lah .almanac/.capture-*.log
|
|
213
|
+
ls -lah .almanac/logs/.capture-*.log
|
|
214
214
|
```
|
|
215
|
-
No logs at all → the hook isn't installed, or bailed before backgrounding, or `cwd` was outside any wiki (silent correct no-op). Capture ran but wrote nothing → the reviewer rejected the draft for notability, or the session was pure-read. Check
|
|
215
|
+
No logs at all → the hook isn't installed, or bailed before backgrounding, or `cwd` was outside any wiki (silent correct no-op). Capture ran but wrote nothing → the reviewer rejected the draft for notability, or the session was pure-read. Check `.almanac/logs/.capture-<id>.log` for the writer/reviewer transcript.
|
|
216
216
|
|
|
217
217
|
---
|
|
218
218
|
|
package/guides/reference.md
CHANGED
|
@@ -117,7 +117,7 @@ All topic subcommands accept `--wiki <name>`. `list` / `show` accept `--json`.
|
|
|
117
117
|
|
|
118
118
|
#### `almanac bootstrap`
|
|
119
119
|
|
|
120
|
-
Spawns an agent to create initial wiki stubs. Requires `ANTHROPIC_API_KEY` or a logged-in Claude subscription. `--quiet` suppresses per-tool streaming. `--model <model>` overrides the model. `--force` overwrites an existing populated wiki. Writes `.almanac/.bootstrap-<timestamp>.log`.
|
|
120
|
+
Spawns an agent to create initial wiki stubs. Requires `ANTHROPIC_API_KEY` or a logged-in Claude subscription. `--quiet` suppresses per-tool streaming. `--model <model>` overrides the model. `--force` overwrites an existing populated wiki. Writes `.almanac/logs/.bootstrap-<timestamp>.log`.
|
|
121
121
|
|
|
122
122
|
Bootstrap is the scaffolding path — it creates `.almanac/pages/`, `.almanac/topics.yaml`, `.almanac/README.md`, and stub entity pages based on what the agent reads in the repo.
|
|
123
123
|
|
|
@@ -132,7 +132,7 @@ Run the writer/reviewer pipeline on a Claude Code session transcript. Usually au
|
|
|
132
132
|
| `--quiet` | Suppress per-tool streaming; print only the final summary. |
|
|
133
133
|
| `--model <model>` | Override the agent model. |
|
|
134
134
|
|
|
135
|
-
Writes SDK transcript to `.almanac/.capture-<session-id>.jsonl` (one JSON message per line). When invoked manually without `--session`, falls back to `.capture-<timestamp>.jsonl` so repeated runs don't clobber each other. A writer subagent drafts pages; a reviewer subagent enforces notability + writing conventions (§9) before drafts land.
|
|
135
|
+
Writes SDK transcript to `.almanac/logs/.capture-<session-id>.jsonl` (one JSON message per line). When invoked manually without `--session`, falls back to `.capture-<timestamp>.jsonl` so repeated runs don't clobber each other. A writer subagent drafts pages; a reviewer subagent enforces notability + writing conventions (§9) before drafts land.
|
|
136
136
|
|
|
137
137
|
#### `almanac hook install | uninstall | status`
|
|
138
138
|
|
|
@@ -455,7 +455,7 @@ Claude Code invokes `SessionEnd` hooks after each session. Payload on stdin:
|
|
|
455
455
|
|
|
456
456
|
1. Parse payload with `jq`. Missing `jq` → exit 0 silently.
|
|
457
457
|
2. Walk upward from `cwd` for a `.almanac/`. Bounded at filesystem root.
|
|
458
|
-
3. Background `almanac capture "$TRANSCRIPT" --session "$SESSION_ID" --quiet`, redirect to `.almanac/.capture-$SESSION_ID.log`, `disown`.
|
|
458
|
+
3. Background `almanac capture "$TRANSCRIPT" --session "$SESSION_ID" --quiet`, redirect to `.almanac/logs/.capture-$SESSION_ID.log`, `disown`.
|
|
459
459
|
4. Exit always `0`. Capture failures must never break Claude Code's session-end path.
|
|
460
460
|
|
|
461
461
|
Falls back to `npx --no-install codealmanac` if `almanac` isn't on `PATH`.
|
|
@@ -482,7 +482,7 @@ Falls back to `npx --no-install codealmanac` if `almanac` isn't on `PATH`.
|
|
|
482
482
|
```bash
|
|
483
483
|
almanac doctor # catch-all — reports hook state + last capture age
|
|
484
484
|
almanac hook status # just the hook entry
|
|
485
|
-
ls -lah .almanac/.capture-*.log
|
|
485
|
+
ls -lah .almanac/logs/.capture-*.log
|
|
486
486
|
```
|
|
487
487
|
|
|
488
488
|
Installed but no log: `SessionEnd` didn't fire (rare, hard crash), or script bailed before backgrounding (add `set -x` to trace), or no `.almanac/` upward from `cwd` (silent correct no-op).
|
|
@@ -490,7 +490,7 @@ Installed but no log: `SessionEnd` didn't fire (rare, hard crash), or script bai
|
|
|
490
490
|
### Diagnosing "capture ran but wrote nothing"
|
|
491
491
|
|
|
492
492
|
```bash
|
|
493
|
-
tail -200 .almanac/.capture-<id>.log
|
|
493
|
+
tail -200 .almanac/logs/.capture-<id>.log
|
|
494
494
|
```
|
|
495
495
|
|
|
496
496
|
Common causes:
|
|
@@ -678,7 +678,7 @@ Missing `files:` frontmatter, OR path referenced only in inline prose (not via `
|
|
|
678
678
|
almanac doctor # reports hook state + last capture age + auth
|
|
679
679
|
claude auth status # OAuth token present?
|
|
680
680
|
echo "${ANTHROPIC_API_KEY:0:10}" # API key fallback?
|
|
681
|
-
ls -lah .almanac/.capture-*.log
|
|
681
|
+
ls -lah .almanac/logs/.capture-*.log
|
|
682
682
|
```
|
|
683
683
|
|
|
684
684
|
No logs at all → script bailed pre-background. Add `set -x` to `hooks/almanac-capture.sh` to trace. If the hook itself isn't installed, `almanac doctor` reports `install.hook: problem` with `run: almanac setup --yes`.
|
|
@@ -693,9 +693,9 @@ Case sensitivity on Linux. Schema v2 stores `original_path` for case-preserving
|
|
|
693
693
|
|
|
694
694
|
### Forensics files
|
|
695
695
|
|
|
696
|
-
- `.almanac/.capture-<session-id>.jsonl` — SDK message stream from `almanac capture` (one JSON object per line). Writer + reviewer interleaved.
|
|
697
|
-
- `.almanac/.capture-<session-id>.log` — companion sidecar written by the SessionEnd hook: stdout+stderr of `almanac capture`, human-readable. Present only for hook-invoked captures; manual invocations emit only the `.jsonl`.
|
|
698
|
-
- `.almanac/.bootstrap-<timestamp>.log` — one per bootstrap. Gitignored by default.
|
|
696
|
+
- `.almanac/logs/.capture-<session-id>.jsonl` — SDK message stream from `almanac capture` (one JSON object per line). Writer + reviewer interleaved.
|
|
697
|
+
- `.almanac/logs/.capture-<session-id>.log` — companion sidecar written by the SessionEnd hook: stdout+stderr of `almanac capture`, human-readable. Present only for hook-invoked captures; manual invocations emit only the `.jsonl`.
|
|
698
|
+
- `.almanac/logs/.bootstrap-<timestamp>.log` — one per bootstrap. Gitignored by default.
|
|
699
699
|
|
|
700
700
|
---
|
|
701
701
|
|
package/hooks/almanac-capture.sh
CHANGED
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
set -u
|
|
15
15
|
|
|
16
|
+
# CodeAlmanac's own bootstrap/capture agents run Claude Code internally.
|
|
17
|
+
# Their SessionEnd events must not trigger another capture, or one capture
|
|
18
|
+
# can become an unbounded capture chain.
|
|
19
|
+
if [ "${CODEALMANAC_INTERNAL_SESSION:-}" = "1" ]; then
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
16
23
|
# Be forgiving: if jq is missing, we can't parse the payload, so no-op.
|
|
17
24
|
if ! command -v jq >/dev/null 2>&1; then
|
|
18
25
|
exit 0
|
|
@@ -33,7 +40,8 @@ CWD=$(echo "$INPUT" | jq -r '.cwd // empty')
|
|
|
33
40
|
DIR="$CWD"
|
|
34
41
|
while [ "$DIR" != "/" ] && [ -n "$DIR" ]; do
|
|
35
42
|
if [ -d "$DIR/.almanac" ]; then
|
|
36
|
-
LOG_DIR="$DIR/.almanac"
|
|
43
|
+
LOG_DIR="$DIR/.almanac/logs"
|
|
44
|
+
mkdir -p "$LOG_DIR" || exit 0
|
|
37
45
|
# Prefer `almanac` on PATH; fall back to `npx codealmanac` if the
|
|
38
46
|
# binary isn't linked (happens with non-global installs).
|
|
39
47
|
if command -v almanac >/dev/null 2>&1; then
|
package/package.json
CHANGED
package/prompts/bootstrap.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
You are the bootstrap agent for codealmanac. Your job is to create the initial `.almanac/` wiki for a codebase — the stubs and scaffolding that future coding sessions will build on.
|
|
4
4
|
|
|
5
|
-
This runs once per repo. You're not writing a complete encyclopedia. You're setting up the anchors so the writer has something to attach knowledge to when real sessions happen.
|
|
5
|
+
This runs once per repo. You're not writing a complete encyclopedia. You're setting up the anchors and empty containers so the writer has something to attach knowledge to when real sessions happen.
|
|
6
|
+
|
|
7
|
+
Bootstrap optimizes for future usefulness, not completeness. The highest-value knowledge in an Almanac wiki is usually not "what files exist"; it is the context a future agent would otherwise have to rediscover: gotchas, why-we-do-this decisions, design philosophy, cross-file flows, upstream quirks, and constraints that are not obvious from code. During bootstrap, capture only what is observable from the repo. When the reason is not visible, create a well-labeled stub section that invites future capture instead of inventing rationale.
|
|
6
8
|
|
|
7
9
|
## Before you start
|
|
8
10
|
|
|
@@ -24,10 +26,14 @@ An anchor is a stable named thing that other pages will link to. Good anchors:
|
|
|
24
26
|
- **Major third-party dependencies** we clearly use throughout the codebase: a framework (Next.js, FastAPI), a database client (Supabase, Prisma), a payment/search/auth service
|
|
25
27
|
- **External services** referenced in config: Stripe, Claude API, Meilisearch, Redis
|
|
26
28
|
- **Custom systems** visible in the directory structure: `src/auth/` suggests an auth system, `src/checkout/` suggests a checkout system, `backend/src/services/` suggests service modules
|
|
29
|
+
- **Cross-file flows** that are visible from code structure: polling-and-rendering, checkout, publishing, ingest, sync, auth callback handling
|
|
30
|
+
- **Design systems / visual language** when the repo has UI code: typography, color tokens, spacing conventions, component composition, animation rules, visual constraints
|
|
27
31
|
- **Runtimes / deployment targets** when they matter: specific Python/Node versions, Docker orchestration
|
|
28
32
|
|
|
29
33
|
**Group related dependencies into single anchors.** `@supabase/supabase-js` + `@supabase/auth-helpers-nextjs` + `postgres` with a `src/lib/supabase.ts` utility = one page called "Supabase." Not four pages.
|
|
30
34
|
|
|
35
|
+
Prefer project-specific anchors over generic stack anchors when both are plausible. A "Results API proxy" page is usually more useful than a "Next.js" page; a "Design system" page is usually more useful than a "Tailwind CSS" page. Keep stack pages short unless the repo does something unusual or version-specific with that technology.
|
|
36
|
+
|
|
31
37
|
### What's NOT an anchor
|
|
32
38
|
|
|
33
39
|
Skip these. Creating pages for them bloats the wiki without value:
|
|
@@ -52,6 +58,8 @@ Good examples of topics that often emerge:
|
|
|
52
58
|
|
|
53
59
|
Anchor pages usually carry the `stack` or `systems` topic plus a domain topic. Example: Supabase page → `[stack, database]`. Checkout flow page → `[flows, payments]`.
|
|
54
60
|
|
|
61
|
+
For UI repos, include a topic that can hold design knowledge (`ui`, `design`, or the repo's own term). For repos with external dependencies or live upstream data, include a place for operational knowledge in the README and in future-capture sections. Only create an `incidents`, `gotchas`, or similar topic if at least one page will use that topic during this bootstrap run; `almanac health` treats unused topics as empty.
|
|
62
|
+
|
|
55
63
|
## What to produce
|
|
56
64
|
|
|
57
65
|
### `.almanac/README.md`
|
|
@@ -120,9 +128,11 @@ Stubs are fine. They should have:
|
|
|
120
128
|
- **One-paragraph intro** describing what it is in this repo (not generic docs)
|
|
121
129
|
- **A "Where we use it" or similar section** pointing to specific files
|
|
122
130
|
- **A stub marker comment** so the writer knows this page is incomplete
|
|
131
|
+
- **Future-capture sections when useful** — short empty headings such as "Known gotchas", "Design intent", "Rejected alternatives", "Operational constraints", or "Invariants" if that page is likely to accumulate that kind of knowledge. Leave the section empty with a stub comment unless the repo already proves the fact.
|
|
123
132
|
|
|
124
133
|
Do NOT:
|
|
125
134
|
- Write speculative content ("chosen for scalability" when you don't know why we chose it)
|
|
135
|
+
- Infer design rationale from aesthetics alone ("chosen to feel premium", "optimized for trust") unless a repo document says so
|
|
126
136
|
- Paste generic docs for the dependency
|
|
127
137
|
- Create one page per sub-package of a grouped dep
|
|
128
138
|
|