opencodekit 0.23.2 → 0.23.4

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 (51) hide show
  1. package/README.md +7 -14
  2. package/dist/index.js +1 -1
  3. package/dist/template/.opencode/AGENTS.md +92 -19
  4. package/dist/template/.opencode/README.md +43 -6
  5. package/dist/template/.opencode/artifacts/harness-workflows/plan.md +317 -0
  6. package/dist/template/.opencode/command/audit.md +65 -0
  7. package/dist/template/.opencode/command/init.md +19 -2
  8. package/dist/template/.opencode/command/research.md +67 -16
  9. package/dist/template/.opencode/command/ship.md +55 -5
  10. package/dist/template/.opencode/command/verify.md +5 -5
  11. package/dist/template/.opencode/opencode.json +12 -0
  12. package/dist/template/.opencode/plugin/README.md +0 -6
  13. package/dist/template/.opencode/plugin/codesearch.ts +730 -0
  14. package/dist/template/.opencode/plugin/memory/tools.ts +6 -6
  15. package/dist/template/.opencode/plugin/session-summary.ts +0 -2
  16. package/dist/template/.opencode/plugin/srcwalk.ts +22 -157
  17. package/dist/template/.opencode/skill/code-review-and-quality/SKILL.md +1 -1
  18. package/dist/template/.opencode/skill/debugging-and-error-recovery/SKILL.md +1 -1
  19. package/dist/template/.opencode/skill/deep-module-design/SKILL.md +1 -1
  20. package/dist/template/.opencode/skill/defense-in-depth/SKILL.md +0 -2
  21. package/dist/template/.opencode/skill/development-lifecycle/SKILL.md +11 -9
  22. package/dist/template/.opencode/skill/manifest.json +77 -0
  23. package/dist/template/.opencode/skill/planning-and-task-breakdown/SKILL.md +1 -1
  24. package/dist/template/.opencode/skill/srcwalk/SKILL.md +10 -13
  25. package/dist/template/.opencode/tool/grepsearch.ts +92 -103
  26. package/dist/template/.opencode/workflows/audit-pattern.md +51 -0
  27. package/dist/template/.opencode/workflows/batch-implement.md +82 -0
  28. package/dist/template/.opencode/workflows/deep-research.md +58 -0
  29. package/dist/template/.opencode/workflows/development-lifecycle-workflow.md +129 -0
  30. package/package.json +1 -1
  31. package/dist/template/.opencode/command/clarify.md +0 -46
  32. package/dist/template/.opencode/command/commit.md +0 -53
  33. package/dist/template/.opencode/command/design.md +0 -129
  34. package/dist/template/.opencode/command/explore.md +0 -169
  35. package/dist/template/.opencode/command/improve-architecture.md +0 -55
  36. package/dist/template/.opencode/command/pr.md +0 -148
  37. package/dist/template/.opencode/command/refactor.md +0 -65
  38. package/dist/template/.opencode/command/review-codebase.md +0 -128
  39. package/dist/template/.opencode/command/test.md +0 -66
  40. package/dist/template/.opencode/command/ui-review.md +0 -109
  41. package/dist/template/.opencode/opencodex-fast.jsonc +0 -3
  42. package/dist/template/.opencode/plugin/rtk.ts +0 -43
  43. package/dist/template/.opencode/skill/agent-teams/SKILL.md +0 -268
  44. package/dist/template/.opencode/skill/code-navigation/SKILL.md +0 -142
  45. package/dist/template/.opencode/skill/condition-based-waiting/SKILL.md +0 -135
  46. package/dist/template/.opencode/skill/condition-based-waiting/example.ts +0 -171
  47. package/dist/template/.opencode/skill/context-engineering/SKILL.md +0 -176
  48. package/dist/template/.opencode/skill/memory-system/SKILL.md +0 -147
  49. package/dist/template/.opencode/skill/structured-edit/SKILL.md +0 -191
  50. package/dist/template/.opencode/skill/ubiquitous-language/SKILL.md +0 -184
  51. package/dist/template/.opencode/skill/v0/SKILL.md +0 -158
@@ -1,135 +0,0 @@
1
- ---
2
- name: condition-based-waiting
3
- description: Use when tests have race conditions, timing dependencies, or inconsistent pass/fail behavior - replaces arbitrary timeouts with condition polling to wait for actual state changes, eliminating flaky tests from timing guesses
4
- version: 1.0.0
5
- tags: [testing, debugging]
6
- dependencies: []
7
- ---
8
-
9
- # Condition-Based Waiting
10
-
11
- > **Replaces** arbitrary `sleep()` / `setTimeout()` calls and hardcoded delays that cause flaky tests and slow CI
12
-
13
- ## When to Use
14
-
15
- - Tests are flaky due to arbitrary delays or timing guesses
16
- - You need to wait for async state changes (events, file writes, state transitions)
17
-
18
- ## When NOT to Use
19
-
20
- - You are explicitly testing timing behavior (debounce, throttle, intervals)
21
- - A fixed, documented timeout is part of the requirement
22
-
23
- ## Common Rationalizations
24
-
25
- | Rationalization | Rebuttal |
26
- | --------------------------------------------- | ------------------------------------------------------------------------------ |
27
- | "50ms is plenty of time" | It's plenty on YOUR machine. CI runners under load disagree |
28
- | "The sleep worked in local testing" | Local = fast SSD, idle CPU. CI = shared resources, variable latency |
29
- | "Adding a waitFor is more complex than sleep" | A 5-line waitFor is simpler than debugging a flaky test 10 times |
30
- | "This operation is always fast" | "Always" until garbage collection, disk I/O, or network latency says otherwise |
31
- | "I'll increase the timeout if it flakes" | Increasing timeouts slows the entire suite and masks the real problem |
32
- | "It only fails sometimes" | "Sometimes" = race condition. Condition-based waiting eliminates it entirely |
33
-
34
- ## Overview
35
-
36
- Flaky tests often guess at timing with arbitrary delays. This creates race conditions where tests pass on fast machines but fail under load or in CI.
37
-
38
- **Core principle:** Wait for the actual condition you care about, not a guess about how long it takes.
39
-
40
- ## Core Pattern
41
-
42
- ```typescript
43
- // ❌ BEFORE: Guessing at timing
44
- await new Promise((r) => setTimeout(r, 50));
45
- const result = getResult();
46
- expect(result).toBeDefined();
47
-
48
- // ✅ AFTER: Waiting for condition
49
- await waitFor(() => getResult() !== undefined);
50
- const result = getResult();
51
- expect(result).toBeDefined();
52
- ```
53
-
54
- ## Quick Patterns
55
-
56
- | Scenario | Pattern |
57
- | ----------------- | ---------------------------------------------------- |
58
- | Wait for event | `waitFor(() => events.find(e => e.type === 'DONE'))` |
59
- | Wait for state | `waitFor(() => machine.state === 'ready')` |
60
- | Wait for count | `waitFor(() => items.length >= 5)` |
61
- | Wait for file | `waitFor(() => fs.existsSync(path))` |
62
- | Complex condition | `waitFor(() => obj.ready && obj.value > 10)` |
63
-
64
- ## Implementation
65
-
66
- Generic polling function:
67
-
68
- ```typescript
69
- async function waitFor<T>(
70
- condition: () => T | undefined | null | false,
71
- description: string,
72
- timeoutMs = 5000,
73
- ): Promise<T> {
74
- const startTime = Date.now();
75
-
76
- while (true) {
77
- const result = condition();
78
- if (result) return result;
79
-
80
- if (Date.now() - startTime > timeoutMs) {
81
- throw new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`);
82
- }
83
-
84
- await new Promise((r) => setTimeout(r, 10)); // Poll every 10ms
85
- }
86
- }
87
- ```
88
-
89
- See @example.ts for complete implementation with domain-specific helpers (`waitForEvent`, `waitForEventCount`, `waitForEventMatch`) from actual debugging session.
90
-
91
- ## Common Mistakes
92
-
93
- **❌ Polling too fast:** `setTimeout(check, 1)` - wastes CPU
94
- **✅ Fix:** Poll every 10ms
95
-
96
- **❌ No timeout:** Loop forever if condition never met
97
- **✅ Fix:** Always include timeout with clear error
98
-
99
- **❌ Stale data:** Cache state before loop
100
- **✅ Fix:** Call getter inside loop for fresh data
101
-
102
- ## When Arbitrary Timeout IS Correct
103
-
104
- ```typescript
105
- // Tool ticks every 100ms - need 2 ticks to verify partial output
106
- await waitForEvent(manager, "TOOL_STARTED"); // First: wait for condition
107
- await new Promise((r) => setTimeout(r, 200)); // Then: wait for timed behavior
108
- // 200ms = 2 ticks at 100ms intervals - documented and justified
109
- ```
110
-
111
- **Requirements:**
112
-
113
- 1. First wait for triggering condition
114
- 2. Based on known timing (not guessing)
115
- 3. Comment explaining WHY
116
-
117
- ## Verification
118
-
119
- - **After applying:** run the previously flaky test 5+ times — should pass consistently
120
- - **Check:** no hardcoded sleep/delay values remain in the test file
121
- - **Measure:** test execution time should decrease (no wasted wait time)
122
-
123
- ## Real-World Impact
124
-
125
- From debugging session (2025-10-03):
126
-
127
- - Fixed 15 flaky tests across 3 files
128
- - Pass rate: 60% → 100%
129
- - Execution time: 40% faster
130
- - No more race conditions
131
-
132
- ## See Also
133
-
134
- - `systematic-debugging`
135
- - `test-driven-development`
@@ -1,171 +0,0 @@
1
- // Complete implementation of condition-based waiting utilities
2
- // From: Lace test infrastructure improvements (2025-10-03)
3
- // Context: Fixed 15 flaky tests by replacing arbitrary timeouts
4
- //
5
- // Self-contained — types are defined inline to avoid external dependencies.
6
-
7
- /** Minimal thread manager interface for condition polling */
8
- interface ThreadManager {
9
- getEvents(threadId: string): LaceEvent[];
10
- }
11
-
12
- /** Generic event type identifier */
13
- type LaceEventType = string;
14
-
15
- /** Generic event with type discriminator and optional data payload */
16
- interface LaceEvent {
17
- type: string;
18
- data?: Record<string, unknown>;
19
- }
20
-
21
- /**
22
- * Wait for a specific event type to appear in thread
23
- *
24
- * @param threadManager - The thread manager to query
25
- * @param threadId - Thread to check for events
26
- * @param eventType - Type of event to wait for
27
- * @param timeoutMs - Maximum time to wait (default 5000ms)
28
- * @returns Promise resolving to the first matching event
29
- *
30
- * Example:
31
- * await waitForEvent(threadManager, agentThreadId, 'TOOL_RESULT');
32
- */
33
- export function waitForEvent(
34
- threadManager: ThreadManager,
35
- threadId: string,
36
- eventType: LaceEventType,
37
- timeoutMs = 5000,
38
- ): Promise<LaceEvent> {
39
- return new Promise((resolve, reject) => {
40
- const startTime = Date.now();
41
-
42
- const check = () => {
43
- const events = threadManager.getEvents(threadId);
44
- const event = events.find((e) => e.type === eventType);
45
-
46
- if (event) {
47
- resolve(event);
48
- } else if (Date.now() - startTime > timeoutMs) {
49
- reject(new Error(`Timeout waiting for ${eventType} event after ${timeoutMs}ms`));
50
- } else {
51
- setTimeout(check, 10); // Poll every 10ms for efficiency
52
- }
53
- };
54
-
55
- check();
56
- });
57
- }
58
-
59
- /**
60
- * Wait for a specific number of events of a given type
61
- *
62
- * @param threadManager - The thread manager to query
63
- * @param threadId - Thread to check for events
64
- * @param eventType - Type of event to wait for
65
- * @param count - Number of events to wait for
66
- * @param timeoutMs - Maximum time to wait (default 5000ms)
67
- * @returns Promise resolving to all matching events once count is reached
68
- *
69
- * Example:
70
- * // Wait for 2 AGENT_MESSAGE events (initial response + continuation)
71
- * await waitForEventCount(threadManager, agentThreadId, 'AGENT_MESSAGE', 2);
72
- */
73
- export function waitForEventCount(
74
- threadManager: ThreadManager,
75
- threadId: string,
76
- eventType: LaceEventType,
77
- count: number,
78
- timeoutMs = 5000,
79
- ): Promise<LaceEvent[]> {
80
- return new Promise((resolve, reject) => {
81
- const startTime = Date.now();
82
-
83
- const check = () => {
84
- const events = threadManager.getEvents(threadId);
85
- const matchingEvents = events.filter((e) => e.type === eventType);
86
-
87
- if (matchingEvents.length >= count) {
88
- resolve(matchingEvents);
89
- } else if (Date.now() - startTime > timeoutMs) {
90
- reject(
91
- new Error(
92
- `Timeout waiting for ${count} ${eventType} events after ${timeoutMs}ms (got ${matchingEvents.length})`,
93
- ),
94
- );
95
- } else {
96
- setTimeout(check, 10);
97
- }
98
- };
99
-
100
- check();
101
- });
102
- }
103
-
104
- /**
105
- * Wait for an event matching a custom predicate
106
- * Useful when you need to check event data, not just type
107
- *
108
- * @param threadManager - The thread manager to query
109
- * @param threadId - Thread to check for events
110
- * @param predicate - Function that returns true when event matches
111
- * @param description - Human-readable description for error messages
112
- * @param timeoutMs - Maximum time to wait (default 5000ms)
113
- * @returns Promise resolving to the first matching event
114
- *
115
- * Example:
116
- * // Wait for TOOL_RESULT with specific ID
117
- * await waitForEventMatch(
118
- * threadManager,
119
- * agentThreadId,
120
- * (e) => e.type === 'TOOL_RESULT' && e.data.id === 'call_123',
121
- * 'TOOL_RESULT with id=call_123'
122
- * );
123
- */
124
- export function waitForEventMatch(
125
- threadManager: ThreadManager,
126
- threadId: string,
127
- predicate: (event: LaceEvent) => boolean,
128
- description: string,
129
- timeoutMs = 5000,
130
- ): Promise<LaceEvent> {
131
- return new Promise((resolve, reject) => {
132
- const startTime = Date.now();
133
-
134
- const check = () => {
135
- const events = threadManager.getEvents(threadId);
136
- const event = events.find(predicate);
137
-
138
- if (event) {
139
- resolve(event);
140
- } else if (Date.now() - startTime > timeoutMs) {
141
- reject(new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`));
142
- } else {
143
- setTimeout(check, 10);
144
- }
145
- };
146
-
147
- check();
148
- });
149
- }
150
-
151
- // Usage example from actual debugging session:
152
- //
153
- // BEFORE (flaky):
154
- // ---------------
155
- // const messagePromise = agent.sendMessage('Execute tools');
156
- // await new Promise(r => setTimeout(r, 300)); // Hope tools start in 300ms
157
- // agent.abort();
158
- // await messagePromise;
159
- // await new Promise(r => setTimeout(r, 50)); // Hope results arrive in 50ms
160
- // expect(toolResults.length).toBe(2); // Fails randomly
161
- //
162
- // AFTER (reliable):
163
- // ----------------
164
- // const messagePromise = agent.sendMessage('Execute tools');
165
- // await waitForEventCount(threadManager, threadId, 'TOOL_CALL', 2); // Wait for tools to start
166
- // agent.abort();
167
- // await messagePromise;
168
- // await waitForEventCount(threadManager, threadId, 'TOOL_RESULT', 2); // Wait for results
169
- // expect(toolResults.length).toBe(2); // Always succeeds
170
- //
171
- // Result: 60% pass rate → 100%, 40% faster execution
@@ -1,176 +0,0 @@
1
- ---
2
- name: context-engineering
3
- description: Use when designing AGENTS.md hierarchies, understanding autonomous duration, or writing intent layers - covers principles for extending agent work capacity
4
- version: 1.0.0
5
- tags: [context, documentation]
6
- dependencies: []
7
- ---
8
-
9
- # Context Engineering
10
-
11
- ## When to Use
12
-
13
- - Designing or refactoring AGENTS.md hierarchies and intent layers
14
- - You need to extend autonomous work duration via better context structure
15
-
16
- ## When NOT to Use
17
-
18
- - You only need pruning/distillation mechanics (use context-management)
19
- - Simple tasks where context design is not relevant
20
-
21
-
22
-
23
- ## Core Principle
24
-
25
- **Autonomous Duration**: How long can an agent work before losing the plot?
26
-
27
- Extend it by:
28
-
29
- - Binding tighter to intent (clear specs, constraints, invariants)
30
- - Providing systematic context (AGENTS.md hierarchy, memory files)
31
- - Verification loops (test → iterate → verify)
32
-
33
- ## Three Context Constraints
34
-
35
- 1. **Blind spots cause hallucinations** - Agent fills gaps with generic priors
36
- 2. **Everything influences everything** - Noise degrades ALL output quality
37
- 3. **Window is finite** - Performance degrades BEFORE hard token limits
38
-
39
- ## Intent Layer Principles
40
-
41
- ### What Belongs in Each AGENTS.md
42
-
43
- - **Purpose & Scope** - What this area does. What it DOESN'T do.
44
- - **Entry Points & Contracts** - Main APIs, invariants
45
- - **Usage Patterns** - Canonical examples
46
- - **Anti-patterns** - What NOT to do
47
- - **Dependencies & Downlinks** - Pointers to related context
48
-
49
- ### Key Mechanics
50
-
51
- | Principle | Meaning |
52
- | ------------------------ | -------------------------------------------------------- |
53
- | **Hierarchical loading** | When node loads, all ancestors load too (T-shaped view) |
54
- | **Compression** | Good nodes compress code; don't add bloat |
55
- | **LCA placement** | Place shared knowledge at shallowest node covering paths |
56
- | **Downlinks** | Point to related context without loading everything |
57
-
58
- ## Practical Implications
59
-
60
- | Instead of | Do This |
61
- | ----------------------- | --------------------------------------- |
62
- | Reading entire files | Use `lsp documentSymbol` for outline |
63
- | Loading whole documents | Read specific line ranges |
64
- | Flat file loading | Navigate AGENTS.md hierarchy |
65
- | Keeping completed work | Compress closed phases, sweep stale noise (context-management) |
66
-
67
- ## Anti-Patterns
68
-
69
- ❌ Loading "everything that might be relevant"
70
- ❌ Keeping old file reads after editing complete
71
- ❌ Reading entire files when you only need a function
72
- ❌ Ignoring AGENTS.md hierarchy
73
-
74
- ## Static vs Runtime Context (Longshot Pattern)
75
-
76
- At scale (10+ agents), the difference between **static context** and **runtime context** is the difference between a coherent swarm and chaos.
77
-
78
- ### Definitions
79
-
80
- | Type | What It Is | When Loaded | Example |
81
- | ------------------- | ------------------------------------------------------------ | ---------------------- | --------------------------------------- |
82
- | **Static Context** | Always-on knowledge — invariants, constraints, project shape | Always (auto-injected) | AGENTS.md, tech-stack.md, user.md |
83
- | **Runtime Context** | Per-task injections — what THIS task needs right now | Per-task | Delegation packet, task spec, file list |
84
-
85
- ### Why the Split Matters
86
-
87
- Without separation, context becomes soup:
88
-
89
- - Agent loads everything → hits token limit → degrades
90
- - Agents share stale context → conflicting decisions
91
- - No clear source of truth for "what is the objective"
92
-
93
- With separation:
94
-
95
- - Static = immune to session pollution (always fresh)
96
- - Runtime = scoped to task (cleaned up when done)
97
- - Result: agents stay coherent at 200-agent scale
98
-
99
- ### Task Packet Format
100
-
101
- Every task dispatched to a worker agent MUST include an explicit context block:
102
-
103
- ```markdown
104
- ## Task Packet
105
-
106
- ### Static Context (always available)
107
-
108
- - Project rules: AGENTS.md
109
- - Tech stack: .opencode/memory/project/tech-stack.md
110
- - Gotchas: .opencode/memory/project/gotchas.md
111
-
112
- ### Runtime Context (this task only)
113
-
114
- - Objective: [one sentence]
115
- - Scope: [files this task may touch]
116
- - Constraints: [must_do / must_not_do]
117
- - Dependencies: [what was produced by prior tasks]
118
- - Verification: [acceptance commands]
119
- ```
120
-
121
- ### Injection Pattern
122
-
123
- When spawning workers, always inject runtime context explicitly:
124
-
125
- ```typescript
126
- // WRONG: Vague prompt — agent guesses context
127
- Task({ prompt: "Implement auth service" });
128
-
129
- // RIGHT: Explicit static + runtime context split
130
- Task({
131
- prompt: `## Static Context
132
- AGENTS.md governs all decisions. Tech stack: Bun, TypeScript strict mode.
133
-
134
- ## Runtime Context
135
- Objective: Implement JWT auth service in src/auth/service.ts.
136
- Scope: Only modify src/auth/ directory.
137
- Dependencies: Schema defined in src/db/schema.ts (from task-1).
138
- Constraints:
139
- MUST DO: Use zod for input validation
140
- MUST NOT DO: Add new dependencies without approval
141
- Verification:
142
- npm run typecheck && npm run lint && vitest src/auth/`,
143
- });
144
- ```
145
-
146
- ### Context Pollution Anti-Patterns
147
-
148
- | Anti-Pattern | Problem | Fix |
149
- | ------------------------------------------- | --------------------------------- | ------------------------------------- |
150
- | Passing entire AGENTS.md as runtime context | Bloats token budget on every task | Load via static injection only |
151
- | Runtime state persisting across waves | Stale context poisons next wave | Clear runtime state between waves |
152
- | No objective in task packet | Agent drifts from goal | Always include one-sentence objective |
153
- | Injection without scope | Agent modifies wrong files | Always declare file scope |
154
-
155
- ### Static Context Files (Always Inject)
156
-
157
- These files are the project's invariant layer. Always available, never stale:
158
-
159
- ```
160
- .opencode/memory/project/
161
- ├── user.md # User preferences, workflow rules
162
- ├── tech-stack.md # Frameworks, constraints
163
- ├── gotchas.md # Footguns, warnings
164
- └── project.md # Vision, success criteria
165
- ```
166
-
167
- ### Runtime Context Files (Per-Task)
168
-
169
- These are created fresh per task and cleaned up after:
170
-
171
- ```
172
- .opencode/artifacts/<slug>/
173
- ├── delegation.md # Task-specific instructions
174
- ├── spec.md # Technical requirements
175
- └── progress.txt # Task state (append-only)
176
- ```
@@ -1,147 +0,0 @@
1
- ---
2
- name: memory-system
3
- description: Use when persisting learnings, loading previous context, or searching past decisions - covers memory file structure, tools, and when to update each file
4
- version: 1.2.0
5
- tags: [context, workflow]
6
- dependencies: []
7
- ---
8
-
9
- # Memory System Best Practices
10
-
11
- > **Replaces** losing context between sessions — persistent knowledge that survives session boundaries
12
-
13
- ## When to Use
14
-
15
- - Starting work and needing prior decisions, bugfixes, or patterns
16
- - Recording non-obvious decisions/learnings for future sessions
17
- - Creating handoffs so the next session can continue quickly
18
-
19
- ## When NOT to Use
20
-
21
- - Ephemeral debugging notes that won't matter after the current task
22
- - Storing generated artifacts/log dumps as long-term memory
23
-
24
- ## Core Principle
25
-
26
- **Progressive disclosure**: search compactly, fetch fully only when relevant, then record high-signal observations.
27
-
28
- ## Session Workflow
29
-
30
- 1. **Ground (search first)**
31
- - Run `memory-search` with task keywords before implementation.
32
- - Check recent handoffs when resuming interrupted work.
33
- 2. **Calibrate (progressive disclosure)**
34
- - Use search results as index; `memory-search({ file: "..." })` for file access.
35
- - Retrieve full observation details from search output.
36
- 3. **Record (high-signal only)**
37
- - Create `observation` for decisions, bugfixes, patterns, warnings, or durable learnings.
38
- - Include searchable concepts and concrete file references.
39
- 4. **Handoff (if session boundary)**
40
- - Write a concise status note with completed work, blockers, and next steps using `observation`.
41
-
42
- ## What Goes Where
43
-
44
- | Store | Put Here | Avoid Here |
45
- | --- | --- | --- |
46
- | `observation` (SQLite) | Events: decisions, bugfixes, reusable patterns, warnings, handoffs | Temporary notes, speculative ideas without evidence |
47
- | `memory-search` by file | Durable docs: handoffs, research, project notes | Every minor runtime detail from a single debug run |
48
- | Auto pipeline | Captured messages + distillations (automatic) | Manual copying of full transcripts |
49
-
50
- ## Observation Quality Bar
51
-
52
- Use this checklist before creating an observation:
53
-
54
- - Is it likely useful in a future session?
55
- - Is it non-obvious (not already in code/comments)?
56
- - Can I summarize it in one clear title + short narrative?
57
- - Did I include strong search terms in `concepts` and relevant files?
58
-
59
- If most answers are "no", skip creating the observation.
60
-
61
- ## Anti-Patterns
62
-
63
- | Anti-Pattern | Why It Fails | Instead |
64
- | --- | --- | --- |
65
- | Storing transient debugging info as permanent observations | Pollutes search results with low-value noise | Keep transient info in session context; record only durable findings |
66
- | Creating observations for every small finding (signal-to-noise) | Important items get buried and retrieval quality drops | Batch minor notes; publish one distilled observation per meaningful outcome |
67
- | Not searching memory before creating duplicate observations | Produces conflicting/duplicated records | Run `memory-search` first; update/supersede existing records when appropriate |
68
- | Using `observation` with `file` param for document-style content | Document-style content should use `memory-search({ file: "..." })` for file access | Use `observation` for events; write to memory files when structured document storage is needed |
69
-
70
- ## Verification
71
-
72
- After creating an observation: `memory-search` with relevant keywords should find it.
73
-
74
- ## Practical Defaults
75
-
76
- - Prefer specific queries over broad ones (`"auth race condition init"` > `"auth"`).
77
- - For ongoing work, append to one handoff file per task/day instead of many tiny files.
78
- - Keep observation titles concrete and action-oriented.
79
-
80
- ## Admin Operations
81
-
82
- The `memory-admin` tool supports these operations:
83
-
84
- ### Core (existing)
85
- | Operation | Purpose |
86
- |---|---|
87
- | `status` | Storage stats, FTS5 health, pipeline counts |
88
- | `full` | Full maintenance cycle (archive + checkpoint + vacuum) |
89
- | `archive` | Archive observations older than N days |
90
- | `checkpoint` | Checkpoint WAL file |
91
- | `vacuum` | Vacuum database |
92
- | `migrate` | Import .opencode/memory/observations/*.md into SQLite |
93
- | `capture-stats` | Temporal message capture statistics |
94
- | `distill-now` | Force distillation for current session |
95
- | `curate-now` | Force curator run |
96
-
97
- ### Knowledge Intelligence (new in v2.1)
98
- | Operation | Purpose |
99
- |---|---|
100
- | `lint` | Find duplicates, contradictions, stale/orphan observations |
101
- | `index` | Generate a structured catalog of all observations |
102
- | `compile` | Build concept-grouped articles from observation clusters |
103
- | `log` | View the append-only operation audit trail |
104
-
105
- Examples:
106
- ```
107
- memory-admin({ operation: "lint" })
108
- memory-admin({ operation: "lint", older_than_days: 60 })
109
- memory-admin({ operation: "index" })
110
- memory-admin({ operation: "compile" })
111
- memory-admin({ operation: "log" })
112
- ```
113
-
114
- ### Reading Compiled Artifacts
115
- ```
116
- memory-search({ file: "index" }) // Full observation catalog
117
- memory-search({ file: "compiled/auth" }) // Compiled article for "auth" concept
118
- memory-search({ file: "log" }) // Operation audit trail
119
- ```
120
-
121
- ## Validation Gate
122
-
123
- The `observation` tool now validates before storing:
124
- - **Exact duplicate** → rejected (returns duplicate ID + supersede hint)
125
- - **Near-duplicate** → stored with warning
126
- - **Contradiction** → stored with warning (for decisions sharing concepts)
127
- - **Low quality** → stored with warning (no narrative + no concepts)
128
-
129
- To update an existing observation, use `supersedes`:
130
- ```
131
- observation({ type: "decision", title: "Use JWT", supersedes: "42", ... })
132
- ```
133
-
134
- ## Idle Pipeline
135
-
136
- During `session.idle`, the memory system automatically runs:
137
- 1. Distill undistilled messages
138
- 2. Curate observations from distillations
139
- 3. Optimize FTS5 index
140
- 4. Checkpoint WAL if large
141
- 5. Compile concept articles (max 10)
142
- 6. Regenerate memory index
143
-
144
- ## See Also
145
-
146
- - `context-management`
147
- - `session-management`