@cairnkeep/cli 1.0.2

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 (129) hide show
  1. package/AGENTS.md +45 -0
  2. package/CONTRIBUTING.md +49 -0
  3. package/LICENSE +201 -0
  4. package/README.md +149 -0
  5. package/SECURITY.md +37 -0
  6. package/bin/cairn +73 -0
  7. package/claude/agents/code-reviewer.md +174 -0
  8. package/claude/agents/security-investigator.md +112 -0
  9. package/claude/agents/security-target-selector.md +90 -0
  10. package/claude/agents/security-validator.md +97 -0
  11. package/claude/agents/wiki-ingester.md +108 -0
  12. package/claude/agents/wiki-lint-auditor.md +94 -0
  13. package/claude/agents/wiki-query-analyst.md +84 -0
  14. package/claude/commands/context-explore.md +40 -0
  15. package/claude/commands/graphify.md +150 -0
  16. package/claude/commands/memory-review.md +85 -0
  17. package/claude/commands/memory-sync.md +71 -0
  18. package/claude/commands/recall.md +32 -0
  19. package/claude/commands/remember.md +43 -0
  20. package/claude/commands/repo-review.md +191 -0
  21. package/claude/commands/security-audit.md +174 -0
  22. package/claude/commands/wiki-ingest.md +112 -0
  23. package/claude/commands/wiki-lint.md +86 -0
  24. package/claude/commands/wiki-query.md +92 -0
  25. package/claude/hooks/context-explore-pretask.sh +93 -0
  26. package/claude/hooks/memory-capture.sh +59 -0
  27. package/claude/hooks/memory-recall.sh +84 -0
  28. package/claude/hooks/memory-wakeup.sh +45 -0
  29. package/docs/building-an-overlay.md +117 -0
  30. package/docs/domain-knowledge.md +86 -0
  31. package/docs/git-providers.md +36 -0
  32. package/docs/operating.md +366 -0
  33. package/mcp-memory-server/dist/embeddings.js +122 -0
  34. package/mcp-memory-server/dist/explore-cache.js +97 -0
  35. package/mcp-memory-server/dist/index.js +1256 -0
  36. package/mcp-memory-server/package.json +36 -0
  37. package/opencode/agents/code-reviewer.md +173 -0
  38. package/opencode/agents/security-investigator.md +111 -0
  39. package/opencode/agents/security-target-selector.md +89 -0
  40. package/opencode/agents/security-validator.md +96 -0
  41. package/opencode/agents/wiki-ingester.md +107 -0
  42. package/opencode/agents/wiki-lint-auditor.md +93 -0
  43. package/opencode/agents/wiki-query-analyst.md +83 -0
  44. package/opencode/command/code-review.md +199 -0
  45. package/opencode/command/context-explore.md +42 -0
  46. package/opencode/command/graphify.md +153 -0
  47. package/opencode/command/memory-review.md +44 -0
  48. package/opencode/command/memory-sync.md +94 -0
  49. package/opencode/command/recall.md +38 -0
  50. package/opencode/command/remember.md +46 -0
  51. package/opencode/command/security-audit.md +80 -0
  52. package/opencode/command/wiki-ingest.md +55 -0
  53. package/opencode/command/wiki-lint.md +51 -0
  54. package/opencode/command/wiki-query.md +50 -0
  55. package/opencode/plugins/memory-capture.ts +188 -0
  56. package/opencode/plugins/memory-recall.ts +132 -0
  57. package/opencode/plugins/memory-wakeup.ts +102 -0
  58. package/opencode/templates/security-finding-register.yaml.template +51 -0
  59. package/opencode/templates/security-issue.md.template +43 -0
  60. package/opencode/templates/security-policy.md.template +29 -0
  61. package/opencode/templates/security-report.md.template +43 -0
  62. package/opencode/templates/wiki-contradictions.md.template +31 -0
  63. package/opencode/templates/wiki-index.md.template +40 -0
  64. package/opencode/templates/wiki-lint-report.md.template +38 -0
  65. package/opencode/templates/wiki-log.md.template +11 -0
  66. package/opencode/templates/wiki-policy.md.template +47 -0
  67. package/opencode/templates/wiki-query-answer.md.template +26 -0
  68. package/opencode/templates/wiki-source-summary.md.template +33 -0
  69. package/opencode/workflows/memory-review-workflow.md +66 -0
  70. package/opencode/workflows/security-audit-workflow.md +197 -0
  71. package/opencode/workflows/wiki-ingest-workflow.md +149 -0
  72. package/opencode/workflows/wiki-lint-workflow.md +128 -0
  73. package/opencode/workflows/wiki-query-workflow.md +122 -0
  74. package/package.json +56 -0
  75. package/scripts/bootstrap.sh +99 -0
  76. package/scripts/doctor.sh +91 -0
  77. package/scripts/install-audit-timer.sh +68 -0
  78. package/scripts/lib/assert-tool-event.mjs +50 -0
  79. package/scripts/memory-store.sh +71 -0
  80. package/scripts/memory-wiki-audit.sh +156 -0
  81. package/scripts/sync-claude-assets.sh +182 -0
  82. package/scripts/sync-opencode-explore-assets.sh +187 -0
  83. package/scripts/sync-opencode-graphify-assets.sh +152 -0
  84. package/scripts/sync-opencode-memory-assets.sh +160 -0
  85. package/scripts/sync-opencode-plugin-assets.sh +163 -0
  86. package/scripts/sync-opencode-security-assets.sh +195 -0
  87. package/scripts/sync-opencode-wiki-assets.sh +250 -0
  88. package/scripts/test-audit-timer.sh +29 -0
  89. package/scripts/test-bootstrap-untracked.sh +41 -0
  90. package/scripts/test-cli-dispatch.sh +21 -0
  91. package/scripts/test-doctor.sh +32 -0
  92. package/scripts/test-launcher-hooks.sh +66 -0
  93. package/scripts/test-memory-store.sh +44 -0
  94. package/scripts/test-remember-recall-assertions.sh +70 -0
  95. package/scripts/transcript-to-text.mjs +53 -0
  96. package/scripts/verify-docs-parity.sh +112 -0
  97. package/scripts/verify-explore-maturation.sh +288 -0
  98. package/scripts/verify-fastcontext-reliability.sh +624 -0
  99. package/scripts/verify-no-private-references.sh +145 -0
  100. package/scripts/verify-opencode-live-parity.sh +864 -0
  101. package/scripts/verify-routing-seam.sh +169 -0
  102. package/scripts/verify-token-savings-ab.sh +557 -0
  103. package/templates/PROJECT-INSTRUCTIONS.md.template +42 -0
  104. package/templates/alignment-gap-register.yaml.template +56 -0
  105. package/templates/alignment-policy.md.template +28 -0
  106. package/templates/alignment-pr-template.md.template +18 -0
  107. package/templates/alignment-report.md.template +56 -0
  108. package/templates/env.example.template +25 -0
  109. package/templates/graph-policy.md.template +37 -0
  110. package/templates/graphs-gitignore.template +3 -0
  111. package/templates/memory-wiki-audit.service.template +6 -0
  112. package/templates/memory-wiki-audit.timer.template +9 -0
  113. package/templates/memory.json.template +4 -0
  114. package/templates/planning-config.json.template +6 -0
  115. package/templates/project-brief.md.template +40 -0
  116. package/templates/security-finding-register.yaml.template +51 -0
  117. package/templates/security-issue.md.template +43 -0
  118. package/templates/security-policy.md.template +29 -0
  119. package/templates/security-report.md.template +43 -0
  120. package/templates/start-claude.sh.template +46 -0
  121. package/templates/start-opencode.sh.template +41 -0
  122. package/templates/structured-change-canvas.md.template +63 -0
  123. package/templates/wiki-contradictions.md.template +31 -0
  124. package/templates/wiki-index.md.template +40 -0
  125. package/templates/wiki-lint-report.md.template +38 -0
  126. package/templates/wiki-log.md.template +11 -0
  127. package/templates/wiki-policy.md.template +47 -0
  128. package/templates/wiki-query-answer.md.template +26 -0
  129. package/templates/wiki-source-summary.md.template +33 -0
@@ -0,0 +1,132 @@
1
+ import type { Plugin } from "@opencode-ai/plugin"
2
+ import fs from "node:fs"
3
+ import path from "node:path"
4
+
5
+ // OpenCode-native reimplementation of the Claude PreToolUse `memory-recall`
6
+ // hook (OCP-02, D-10). Before an edit/write proceeds, surface AgentFS facts
7
+ // and wiki pages that specifically mention the file about to be edited —
8
+ // high-signal / low-noise: inject nothing on routine edits, only on a
9
+ // specific stem match.
10
+ //
11
+ // Per 04-RESEARCH.md Pattern 2 / D-11, `tool.execute.before` cannot append
12
+ // freeform non-blocking context the way Claude's `additionalContext` does.
13
+ // The only confirmed mechanism is to `throw new Error(text)`, which the model
14
+ // sees as the tool call's result and must retry — this blocks the first
15
+ // attempt at a matched edit. A once-per-file-per-session guard prevents
16
+ // re-throwing (and re-blocking) the same file forever (T-04-09).
17
+ //
18
+ // Known OpenCode limitation (Pitfall 4, anomalyco/opencode#5894): this hook
19
+ // does not fire for tool calls issued by subagents spawned via the `task`
20
+ // tool — subagent-issued edits silently bypass recall injection. Documented
21
+ // scope limitation, not addressed by this phase.
22
+ //
23
+ // SERVER_ENTRY carries the install-rendered @@INFRA_ROOT@@ token (mirrors
24
+ // memory-wakeup.ts / memory-capture.ts's existing convention — the sync
25
+ // script, not this plugin, resolves it to the real repo root at install
26
+ // time; never accept a runtime-overridable server path here — T-04-03).
27
+ //
28
+ // Fail-open everywhere (D-03): a missing server binary, missing
29
+ // .agentfs/.planning, or any unexpected error must never block an edit.
30
+
31
+ const SERVER_ENTRY = "@@INFRA_ROOT@@/mcp-memory-server/dist/index.js"
32
+ const MIN_STEM_LENGTH = 4
33
+ const MAX_MEMORY_HITS = 8
34
+ const MAX_CONTEXT_LINES = 40
35
+
36
+ // Confines a candidate wiki source file to inside `.planning/wiki/sources/`
37
+ // using relative()-based containment (Phase 2 SEC-0001 pattern) — `resolve()
38
+ // === join()` misses `../` traversal, so this checks the relative path
39
+ // instead. The untrusted file path being edited is never concatenated into
40
+ // this read path; it is only used earlier to derive the `stem` grep token.
41
+ function isContained(baseDir: string, candidate: string): boolean {
42
+ const rel = path.relative(baseDir, candidate)
43
+ return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel)
44
+ }
45
+
46
+ export const MemoryRecallPlugin: Plugin = async ({ $, directory }) => {
47
+ const surfaced = new Set<string>()
48
+
49
+ return {
50
+ "tool.execute.before": async (input, output) => {
51
+ try {
52
+ if (input.tool !== "edit" && input.tool !== "write") return
53
+
54
+ const filePath: string | undefined = output.args?.filePath ?? output.args?.path
55
+ if (!filePath) return
56
+
57
+ const repo = directory
58
+ const agentfsDb = path.join(repo, ".agentfs", "project.db")
59
+ const wikiSourcesDir = path.join(repo, ".planning", "wiki", "sources")
60
+ const hasAgentfs = fs.existsSync(agentfsDb)
61
+ const hasWiki = fs.existsSync(wikiSourcesDir)
62
+ if (!hasAgentfs && !hasWiki) return
63
+
64
+ const base = path.basename(filePath)
65
+ const stem = base.slice(0, base.length - path.extname(base).length)
66
+ // Low-noise rule (D-10): skip tiny/generic stems that would match
67
+ // too broadly and turn this into noise on routine edits.
68
+ if (stem.length < MIN_STEM_LENGTH) return
69
+
70
+ // Once-per-file-per-session guard (T-04-09): after surfacing for
71
+ // this file once in this session, let subsequent edits/retries of
72
+ // the same file proceed unmodified — never re-block in a loop.
73
+ const dedupeKey = `${input.sessionID}:${filePath}`
74
+ if (surfaced.has(dedupeKey)) return
75
+
76
+ const sections: string[] = []
77
+
78
+ // 1. AgentFS project memory: filter the compact wakeup index by stem.
79
+ if (hasAgentfs && fs.existsSync(SERVER_ENTRY)) {
80
+ const res = await $`node ${SERVER_ENTRY} wakeup`.quiet().nothrow()
81
+ const idx = String(res.stdout ?? "").trim()
82
+ if (idx) {
83
+ const hitLines = idx
84
+ .split("\n")
85
+ .filter((line) => line.toLowerCase().includes(stem.toLowerCase()))
86
+ .slice(0, MAX_MEMORY_HITS)
87
+ if (hitLines.length > 0) {
88
+ sections.push(`## Relevant project memory for ${base}`, "", hitLines.join("\n"))
89
+ }
90
+ }
91
+ }
92
+
93
+ // 2. Wiki source pages (top level only) whose content mentions the
94
+ // stem. Reads are confined to wikiSourcesDir via isContained() —
95
+ // filePath (untrusted) only supplied the stem grep token above, it
96
+ // is never used to build a read path here.
97
+ if (hasWiki) {
98
+ const wikiHits: string[] = []
99
+ const entries = fs.readdirSync(wikiSourcesDir, { withFileTypes: true })
100
+ for (const entry of entries) {
101
+ if (!entry.isFile() || !entry.name.endsWith(".md")) continue
102
+ const pagePath = path.join(wikiSourcesDir, entry.name)
103
+ if (!isContained(wikiSourcesDir, pagePath)) continue
104
+ const content = fs.readFileSync(pagePath, "utf8")
105
+ if (!content.toLowerCase().includes(stem.toLowerCase())) continue
106
+ const teaserMatch = content.match(/^- \*\*.*$/m)
107
+ const teaser = teaserMatch ? teaserMatch[0].slice(0, 160) : ""
108
+ wikiHits.push(`- [${entry.name}] ${teaser}`)
109
+ }
110
+ if (wikiHits.length > 0) {
111
+ sections.push(`## Relevant wiki pages for ${base}`, "", wikiHits.join("\n"))
112
+ }
113
+ }
114
+
115
+ // Inject only when there is something specific (high-signal /
116
+ // low-noise) — routine edits with no match proceed silently.
117
+ if (sections.length === 0) return
118
+
119
+ surfaced.add(dedupeKey)
120
+ const context = sections.join("\n\n").split("\n").slice(0, MAX_CONTEXT_LINES).join("\n")
121
+ throw new Error(`Memory recall (auto-injected for this file edit):\n\n${context}`)
122
+ } catch (err) {
123
+ // Re-throw our own intentional surface-context error (it carries the
124
+ // "Memory recall" prefix); swallow everything else to fail open —
125
+ // a lookup failure must never block an edit (D-03).
126
+ if (err instanceof Error && err.message.startsWith("Memory recall (auto-injected")) {
127
+ throw err
128
+ }
129
+ }
130
+ },
131
+ }
132
+ }
@@ -0,0 +1,102 @@
1
+ import type { Plugin } from "@opencode-ai/plugin"
2
+ import fs from "node:fs"
3
+ import path from "node:path"
4
+
5
+ // OpenCode-native reimplementation of the Claude SessionStart `memory-wakeup`
6
+ // hook (OCP-05, D-04). This plugin no longer shells out to
7
+ // ~/.claude/hooks/memory-wakeup.sh — it surfaces the same four session-start
8
+ // sections natively, calling the shared cairn-memory server directly and
9
+ // reading the same .planning/ files, so OpenCode stands on its own with no
10
+ // Claude-rendered assets on disk.
11
+ //
12
+ // SERVER_ENTRY carries the install-rendered @@INFRA_ROOT@@ token (mirrors the
13
+ // Claude hook's substitution — the sync script, not this plugin, resolves it
14
+ // to the real repo root at install time; never accept a runtime-overridable
15
+ // server path here).
16
+ //
17
+ // Fail-open everywhere (D-03): a missing server binary, missing
18
+ // .agentfs/.planning, or a failed subcommand must never wedge an OpenCode
19
+ // session.
20
+
21
+ const SERVER_ENTRY = "@@INFRA_ROOT@@/mcp-memory-server/dist/index.js"
22
+
23
+ export const MemoryWakeupPlugin: Plugin = async ({ $, directory }) => {
24
+ return {
25
+ // No per-session dedupe: this hook fires more than once per session
26
+ // (including OpenCode's internal title-generation call, which happens
27
+ // before the first real agent turn and shares the same sessionID). A
28
+ // "surface once per session" Set keyed on sessionID would mark the
29
+ // session surfaced on that throwaway title-gen call and silently skip
30
+ // every real turn afterward — the OCP-05 acceptance gate never sees the
31
+ // injected context. `output.system` is a fresh array per call, so
32
+ // re-pushing on every invocation is both correct and required.
33
+ "experimental.chat.system.transform": async (input, output) => {
34
+ try {
35
+ const repo = directory
36
+ const agentfsDb = path.join(repo, ".agentfs", "project.db")
37
+ const wikiIndex = path.join(repo, ".planning", "wiki", "index.md")
38
+ const hasAgentfs = fs.existsSync(agentfsDb)
39
+ const hasWiki = fs.existsSync(wikiIndex)
40
+ if (!hasAgentfs && !hasWiki) return
41
+
42
+ const sections: string[] = []
43
+
44
+ if (hasAgentfs) {
45
+ const res = await $`node ${SERVER_ENTRY} wakeup`.quiet().nothrow()
46
+ const memory = String(res.stdout ?? "").trim()
47
+ sections.push("## Project memory (AgentFS)")
48
+ if (memory) sections.push(memory)
49
+ }
50
+
51
+ if (hasWiki) {
52
+ const wiki = fs.readFileSync(wikiIndex, "utf8").trim()
53
+ sections.push("## Wiki index")
54
+ if (wiki) sections.push(wiki)
55
+ }
56
+
57
+ // Surface open HARD wiki contradictions so the agent (and user) see
58
+ // them at session start without anyone having to remember to scan the
59
+ // register. Hard entries cannot both be correct and must be resolved
60
+ // before dependent work.
61
+ const contradictionsPath = path.join(repo, ".planning", "wiki", "CONTRADICTIONS.md")
62
+ if (fs.existsSync(contradictionsPath)) {
63
+ const raw = fs.readFileSync(contradictionsPath, "utf8")
64
+ const start = raw.indexOf("<!-- wiki:contradictions:open:start -->")
65
+ const end = raw.indexOf("<!-- wiki:contradictions:open:end -->")
66
+ if (start !== -1 && end !== -1 && end > start) {
67
+ const region = raw.slice(start, end)
68
+ const hardLines = region
69
+ .split("\n")
70
+ .filter((line) => /severity:\s*hard/i.test(line))
71
+ if (hardLines.length > 0) {
72
+ sections.push("## Open HARD contradictions — resolve before dependent work")
73
+ sections.push(hardLines.join("\n"))
74
+ }
75
+ }
76
+ }
77
+
78
+ // Surface staged memory candidates captured by the session-end
79
+ // capture plugin. These are extracted automatically from the last
80
+ // session but NOT yet written to AgentFS — /memory-review is the
81
+ // accept gate.
82
+ const stagingDir = path.join(repo, ".planning", "memory-staging")
83
+ if (fs.existsSync(stagingDir)) {
84
+ const staged = fs.readdirSync(stagingDir).filter((f) => f.endsWith(".json"))
85
+ if (staged.length > 0) {
86
+ sections.push(`## Staged memory candidates (${staged.length} session(s)) — UNREVIEWED`)
87
+ sections.push("Run /memory-review to accept (→ AgentFS) or discard these before doing other work.")
88
+ }
89
+ }
90
+
91
+ if (sections.length === 0) return
92
+
93
+ output.system.push(
94
+ "Session-start context (auto-surfaced by the memory-wakeup plugin — use it; do not ask the user to recall anything it contains):",
95
+ )
96
+ output.system.push(sections.join("\n\n"))
97
+ } catch {
98
+ // Fail open — never block a session because context surfacing failed.
99
+ }
100
+ },
101
+ }
102
+ }
@@ -0,0 +1,51 @@
1
+ version: 1
2
+ project: REPLACE_WITH_PROJECT_NAME
3
+ default_branch: REPLACE_WITH_DEFAULT_BRANCH
4
+ last_security_audit:
5
+ at: null
6
+ branch: null
7
+ report: null
8
+ findings: []
9
+
10
+ # Finding entry shape:
11
+ # - id: SEC-0001
12
+ # title: Unsafe shell command composition in archive import helper
13
+ # kind: command_execution
14
+ # severity: high
15
+ # status: open
16
+ # confidence: confirmed
17
+ # opened_by: security-audit
18
+ # opened_at: 2026-04-17T18:00:00Z
19
+ # branch: feature/archive-import
20
+ # threat_model: authenticated HTTP request
21
+ # exploit_surface: POST /api/import
22
+ # selected_file: src/import.rs
23
+ # investigation_target: shell command construction for imported filenames
24
+ # evidence:
25
+ # files:
26
+ # - src/import.rs
27
+ # commands:
28
+ # - cargo test --test import
29
+ # inputs:
30
+ # - multipart upload filename payload
31
+ # reports:
32
+ # - .planning/security/VALIDATED/SEC-0001-unsafe-shell-command-composition.md
33
+ # duplication:
34
+ # duplicate_of: null
35
+ # related_ids: []
36
+ # validation:
37
+ # verdict: accepted
38
+ # report_path: .planning/security/VALIDATED/SEC-0001-unsafe-shell-command-composition.md
39
+ # validated_at: 2026-04-17T18:00:00Z
40
+ # rationale: Reachable from the standard import flow without repo-external assumptions.
41
+ # remediation:
42
+ # status: open
43
+ # fixed_in: null
44
+ # verified_at: null
45
+ # tracking:
46
+ # provider_issue:
47
+ # project: group/project
48
+ # iid: "123"
49
+ # web_url: https://git.example.com/group/project/-/issues/123
50
+ # confidential: true
51
+ # created_at: 2026-04-17T18:00:00Z
@@ -0,0 +1,43 @@
1
+ # Confidential Security Issue Draft
2
+
3
+ - Project: REPLACE_WITH_PROJECT_NAME
4
+ - Provider project: REPLACE_WITH_PROJECT
5
+ - Finding ID: REPLACE_WITH_FINDING_ID
6
+ - Severity: REPLACE_WITH_SEVERITY
7
+ - Kind: REPLACE_WITH_KIND
8
+ - Selected file: REPLACE_WITH_SELECTED_FILE
9
+ - Investigation target: REPLACE_WITH_INVESTIGATION_TARGET
10
+ - Threat model: REPLACE_WITH_THREAT_MODEL
11
+ - Branch: REPLACE_WITH_BRANCH
12
+ - Generated at: REPLACE_WITH_TIMESTAMP
13
+ - Source command: /security-audit
14
+ - Source report: REPLACE_WITH_REPORT_PATH
15
+
16
+ ## Summary
17
+
18
+ REPLACE_WITH_SUMMARY
19
+
20
+ ## Reachability And Impact
21
+
22
+ REPLACE_WITH_REACHABILITY_AND_IMPACT
23
+
24
+ ## Evidence
25
+
26
+ REPLACE_WITH_EVIDENCE
27
+
28
+ ## Minimal Reproduction Or Exploit Sketch
29
+
30
+ REPLACE_WITH_REPRODUCTION
31
+
32
+ ## Recommended Remediation
33
+
34
+ REPLACE_WITH_REMEDIATION
35
+
36
+ ## Local Artifact References
37
+
38
+ - Validated report: REPLACE_WITH_VALIDATED_REPORT
39
+ - Audit report: REPLACE_WITH_AUDIT_REPORT
40
+
41
+ ## Confidentiality Note
42
+
43
+ This issue was generated from a governed local security assessment and should remain confidential until remediation and disclosure decisions are made.
@@ -0,0 +1,29 @@
1
+ # Security Assessment Policy
2
+
3
+ This repository uses the governed security-assessment workflow to hunt for realistic security issues without turning normal development into uncontrolled offensive automation.
4
+
5
+ ## Branch Rules
6
+
7
+ - Default branch: `REPLACE_WITH_DEFAULT_BRANCH`
8
+ - Do not push feature work directly to `REPLACE_WITH_DEFAULT_BRANCH`.
9
+ - Use feature branches for remediation and keep candidate reports under `.planning/security/CANDIDATES/` until a finding is validated.
10
+
11
+ ## Safety Rules
12
+
13
+ - Run `/security-audit` only against this repository, repo-owned local processes, local files, and localhost services launched from this repo.
14
+ - Do not target external hosts, staging, production, or third-party systems.
15
+ - Keep proofs local, minimal, and non-destructive.
16
+ - Do not attempt persistence, credential harvesting, lateral movement, or data exfiltration.
17
+
18
+ ## Finding Lifecycle
19
+
20
+ - Open or refresh findings with `/security-audit`.
21
+ - Keep rejected or duplicate candidates in run history only; do not copy them into the canonical ledger.
22
+ - Only accepted findings belong in `.planning/security/FINDINGS.yaml` and `.planning/security/VALIDATED/`.
23
+ - Close or downgrade findings only after remediation or explicit acceptance on the default branch.
24
+
25
+ ## Review Expectations
26
+
27
+ - Critical and high-severity accepted findings should block merge until triaged by a human.
28
+ - Treat the repo-local findings ledger as the shared state for severity, status, and remediation progress.
29
+ - If a run cannot prove reachability convincingly, prefer rejection over speculative acceptance.
@@ -0,0 +1,43 @@
1
+ # Security Audit Report
2
+
3
+ - Project: REPLACE_WITH_PROJECT_NAME
4
+ - Audit scope: diff | full
5
+ - Base ref: REPLACE_WITH_BASE_REF
6
+ - Head ref: REPLACE_WITH_HEAD_REF
7
+ - Threat model: REPLACE_WITH_THREAT_MODEL
8
+ - Focus path: REPLACE_WITH_FOCUS_PATH
9
+ - Generated at: REPLACE_WITH_TIMESTAMP
10
+ - Generated by: security-audit
11
+
12
+ ## Summary
13
+
14
+ - Accepted findings: 0
15
+ - Rejected candidates: 0
16
+ - Selected file: REPLACE_WITH_SELECTED_FILE
17
+ - Investigation target: REPLACE_WITH_INVESTIGATION_TARGET
18
+
19
+ ## Accepted Findings
20
+
21
+ ### SEC-0001
22
+
23
+ - Title: REPLACE_WITH_TITLE
24
+ - Kind: command_execution
25
+ - Severity: high
26
+ - Report: REPLACE_WITH_REPORT_PATH
27
+
28
+ #### Why this matters
29
+
30
+ REPLACE_WITH_IMPACT
31
+
32
+ #### Recommended next step
33
+
34
+ REPLACE_WITH_NEXT_STEP
35
+
36
+ ## Rejected Candidates
37
+
38
+ - None
39
+
40
+ ## Safety Notes
41
+
42
+ - This run was limited to repo-local analysis and locally launched services or local files.
43
+ - Proofs should stay minimal and non-destructive.
@@ -0,0 +1,31 @@
1
+ # Wiki Contradiction Register
2
+
3
+ > Persistent register of contradictions flagged across `.planning/wiki/`.
4
+ > Updated in place by `/wiki-lint`. Unlike per-run REPORTS, this file is the
5
+ > durable record — lint opens and resolves entries here rather than emitting
6
+ > write-and-forget reports. Raw repo docs, tests, interfaces, config, and code
7
+ > remain canonical; this register only tracks where the *derived* wiki disagrees.
8
+
9
+ ## Severity Taxonomy
10
+
11
+ - **soft** — non-blocking, contextual difference. Pages can coexist; flagged for awareness. No action required unless it accumulates.
12
+ - **scope-mismatch** — the two claims apply to different scopes/contexts and do not truly conflict. Recorded so a future reader is not confused; coexists.
13
+ - **hard** — genuinely conflicting claims that cannot both be correct. Must be resolved before downstream work relies on either page. Surface at session start.
14
+
15
+ ## How To Use
16
+
17
+ - `/wiki-lint` writes/updates entries here on every run. Do not delete open entries by hand — resolve the underlying page disagreement, then let lint (or a `/wiki-ingest --refresh`) flip the status to `resolved`.
18
+ - At session start, scan the **Open** section for `severity: hard`. Surface any hard/open items before doing work that depends on the affected pages.
19
+ - An entry is `resolved` only after the conflicting wiki page(s) have actually been reconciled to the canonical source — not merely acknowledged.
20
+
21
+ ## Open
22
+
23
+ <!-- wiki:contradictions:open:start -->
24
+ - None.
25
+ <!-- wiki:contradictions:open:end -->
26
+
27
+ ## Resolved
28
+
29
+ <!-- wiki:contradictions:resolved:start -->
30
+ - None.
31
+ <!-- wiki:contradictions:resolved:end -->
@@ -0,0 +1,40 @@
1
+ # REPLACE_WITH_PROJECT_NAME Knowledge Wiki
2
+
3
+ > Derived, sparse, citation-heavy notes for faster repository understanding.
4
+ > Canonical truth remains in raw repository docs, tests, interfaces, config, and code.
5
+
6
+ ## How To Use This Wiki
7
+
8
+ - Start here to find source summaries, optional topic and entity pages, reusable query answers, and lint reports.
9
+ - Follow the cited canonical sources before treating any derived claim as settled.
10
+ - If the wiki conflicts with raw sources, trust the raw sources and note the contradiction.
11
+
12
+ ## Sources
13
+
14
+ <!-- wiki:sources:start -->
15
+ - None yet.
16
+ <!-- wiki:sources:end -->
17
+
18
+ ## Topics
19
+
20
+ <!-- wiki:topics:start -->
21
+ - None yet.
22
+ <!-- wiki:topics:end -->
23
+
24
+ ## Entities
25
+
26
+ <!-- wiki:entities:start -->
27
+ - None yet.
28
+ <!-- wiki:entities:end -->
29
+
30
+ ## Queries
31
+
32
+ <!-- wiki:queries:start -->
33
+ - None yet.
34
+ <!-- wiki:queries:end -->
35
+
36
+ ## Reports
37
+
38
+ <!-- wiki:reports:start -->
39
+ - None yet.
40
+ <!-- wiki:reports:end -->
@@ -0,0 +1,38 @@
1
+ # Wiki Lint Report
2
+
3
+ - Project: REPLACE_WITH_PROJECT_NAME
4
+ - Generated at: REPLACE_WITH_TIMESTAMP
5
+ - Scope: REPLACE_WITH_SCOPE
6
+ - Advisory status: Raw repository docs, tests, interfaces, config, and code remain canonical.
7
+
8
+ ## Summary
9
+
10
+ - Missing citations: 0
11
+ - Orphan pages: 0
12
+ - Stale claims: 0
13
+ - Contradicted claims: 0 (hard: 0 / scope-mismatch: 0 / soft: 0)
14
+ - Missing obvious pages: 0
15
+ - Broken cross-references: 0
16
+
17
+ > Contradiction entries are also written to `.planning/wiki/CONTRADICTIONS.md`,
18
+ > the persistent register. This report is a snapshot; the register is durable.
19
+
20
+ ## Contradictions (severity-tagged)
21
+
22
+ - None.
23
+
24
+ ## High Signal Findings
25
+
26
+ - None.
27
+
28
+ ## Medium Signal Findings
29
+
30
+ - None.
31
+
32
+ ## Low Signal Findings
33
+
34
+ - None.
35
+
36
+ ## Suggested Next Steps
37
+
38
+ - Keep the wiki sparse, citation-heavy, and aligned to canonical raw sources.
@@ -0,0 +1,11 @@
1
+ # Wiki Log
2
+
3
+ This log records wiki ingests, reusable query writebacks, and advisory lint runs.
4
+
5
+ Raw repository docs, tests, interfaces, config, and code remain canonical.
6
+
7
+ ## Entries
8
+
9
+ <!-- wiki:entries:start -->
10
+ - Scaffold initialized. Append timestamped `wiki-ingest`, `wiki-query --writeback`, and `wiki-lint` entries below.
11
+ <!-- wiki:entries:end -->
@@ -0,0 +1,47 @@
1
+ # Project Knowledge Wiki Policy
2
+
3
+ This repository uses `.planning/wiki/` as a derived, sparse, citation-heavy compiled-knowledge layer.
4
+
5
+ ## Canonical Sources
6
+
7
+ - Raw repository docs, ADRs, tests, interfaces, config, and code remain the canonical sources of truth.
8
+ - The wiki must never overrule the default-branch implementation or canonical repository documents.
9
+ - Use wiki pages to summarize, cross-link, and preserve reusable answers, not to duplicate every file.
10
+
11
+ ## Branch Rules
12
+
13
+ - Project: `REPLACE_WITH_PROJECT_NAME`
14
+ - Default branch: `REPLACE_WITH_DEFAULT_BRANCH`
15
+ - Update wiki pages on the same branch as the source changes they depend on.
16
+ - If the wiki and raw sources disagree, record the contradiction explicitly instead of silently normalizing it.
17
+
18
+ ## Provenance Rules
19
+
20
+ - Every non-trivial claim in `sources/`, `topics/`, `entities/`, and `queries/` should cite canonical source paths.
21
+ - Prefer direct repo-relative paths and specific sections or line ranges when practical.
22
+ - Record `Last reviewed` on every derived page.
23
+ - If freshness is uncertain, say so directly.
24
+
25
+ ## Scope Rules
26
+
27
+ - Do not auto-generate a wiki page per code file.
28
+ - Prefer source summaries for repository docs, design docs, ADRs, standards, and a small number of stable interfaces or subsystems.
29
+ - Topic and entity pages are optional and rare. Create them only when the same synthesis is needed across three or more sources or recurring queries. An empty `topics/`, `entities/`, or `queries/` directory is a healthy, expected state — a sources-only wiki is the normal stable shape for engineering repos, not a gap to fill.
30
+
31
+ ## Contradiction Severity
32
+
33
+ When the wiki disagrees with a canonical source or with itself, classify and record it in `.planning/wiki/CONTRADICTIONS.md` (the persistent register) using one of:
34
+
35
+ - **soft** — non-blocking, contextual difference; coexists, no action required.
36
+ - **scope-mismatch** — the claims apply to different scopes and do not truly conflict; recorded to prevent confusion.
37
+ - **hard** — genuinely conflicting claims that cannot both be correct; must be resolved before downstream work relies on either page. Surfaced at session start.
38
+
39
+ `/wiki-lint` opens and resolves entries in the register in place. An entry is `resolved` only after the conflicting wiki page has actually been reconciled to the canonical source, not merely acknowledged. Do not silently normalize a contradiction away — if raw sources disagree, record it explicitly.
40
+
41
+ ## Maintenance
42
+
43
+ - Use `/wiki-ingest <path>` to create a source summary, or `/wiki-ingest <path> --refresh` to re-sync an existing summary to the current canonical source (re-read, update only changed stable facts, bump `Last reviewed`, log a `wiki-refresh` entry). Run a refresh whenever the MR/PR or code state behind a page changes.
44
+ - Use `/wiki-query --writeback <question>` only when the answer is likely reusable.
45
+ - Use `/wiki-lint` for advisory checks on citations, freshness, contradictions, discoverability, and link hygiene. Lint also updates the persistent `CONTRADICTIONS.md` register.
46
+ - At session start, scan `CONTRADICTIONS.md` for any `severity: hard` open entries and surface them before doing dependent work.
47
+ - Lint reports under `REPORTS/` are advisory snapshots; the register is the durable record. Raw docs and code remain the source of truth.
@@ -0,0 +1,26 @@
1
+ # Query: REPLACE_WITH_QUERY_TITLE
2
+
3
+ - Asked at: `REPLACE_WITH_TIMESTAMP`
4
+ - Question: REPLACE_WITH_QUESTION
5
+ - Answer status: `derived`
6
+ - Last reviewed: `REPLACE_WITH_TIMESTAMP`
7
+
8
+ ## Answer
9
+
10
+ REPLACE_WITH_ANSWER
11
+
12
+ ## Wiki Pages Consulted
13
+
14
+ - REPLACE_WITH_WIKI_PAGE
15
+
16
+ ## Canonical Sources
17
+
18
+ - REPLACE_WITH_CANONICAL_SOURCE
19
+
20
+ ## Contradictions And Freshness Notes
21
+
22
+ - None noted.
23
+
24
+ ## Suggested Follow-up
25
+
26
+ - None.
@@ -0,0 +1,33 @@
1
+ # Source: REPLACE_WITH_SOURCE_LABEL
2
+
3
+ - Canonical source: `REPLACE_WITH_SOURCE_PATH`
4
+ - Source kind: `REPLACE_WITH_SOURCE_KIND`
5
+ - Derived status: `active`
6
+ - Last reviewed: `REPLACE_WITH_LAST_REVIEWED`
7
+
8
+ ## Why This Source Matters
9
+
10
+ REPLACE_WITH_WHY_THIS_MATTERS
11
+
12
+ ## Stable Facts
13
+
14
+ - REPLACE_WITH_FACT_AND_CITATION
15
+
16
+ ## Contradictions And Freshness Notes
17
+
18
+ - None noted.
19
+
20
+ When a contradiction exists, tag it with a severity so `/wiki-lint` can mirror
21
+ it into `CONTRADICTIONS.md`. Use one of:
22
+ - `Contradiction severity: soft` — contextual, coexists.
23
+ - `Contradiction severity: scope-mismatch` — different scopes, not a true conflict.
24
+ - `Contradiction severity: hard` — genuinely conflicting, must resolve.
25
+ Add a `Status: open` (or `resolved`) line so lint can track it deterministically.
26
+
27
+ ## Related Wiki Pages
28
+
29
+ - None yet.
30
+
31
+ ## Canonical References
32
+
33
+ - `REPLACE_WITH_SOURCE_PATH`