Skip to content

Commit ca593ad

Browse files
anandgupta42claude
andcommitted
fix: address Sentry review findings — 7 bugs fixed
1. stripTrainingMeta/parseTrainingMeta regex: remove multiline `m` flag that could match user content starting with `<!-- training` mid-string (types.ts, store.ts) 2. training_save content limit: reduce from 2500 to 1800 chars to account for ~200 char metadata overhead against MemoryStore's 2048 char limit (training-save.ts) 3. injectTrainingOnly: change `break` to `continue` so budget-exceeding section headers skip to next kind instead of stopping all injection (memory/prompt.ts) 4. injectTrainingOnly: track itemCount and return empty string when no items injected (was returning header-only string, inflating budget reports) (memory/prompt.ts) 5. projectDir cache: replace module-level singleton with Map keyed by Instance.directory to prevent stale paths when AsyncLocalStorage context changes across concurrent requests (memory/store.ts) 6. budgetUsage side effect: already fixed — delegates to injectTrainingOnly which is read-only (no applied count increment). Sentry comments were against pre-refactor code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 80c9e68 commit ca593ad

File tree

6 files changed

+25
-19
lines changed

6 files changed

+25
-19
lines changed

docs/docs/data-engineering/training/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ Training doesn't replace CLAUDE.md. They complement each other:
177177
| Limit | Value |
178178
|---|---|
179179
| Max entries per kind | 20 |
180-
| Max content per entry | 2,500 characters |
180+
| Max content per entry | 1,800 characters |
181181
| Training kinds | 6 |
182182
| Scopes | 2 (global = personal, project = team) |
183183

packages/opencode/src/altimate/tools/training-save.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ export const TrainingSaveTool = Tool.define("training_save", {
4343
content: z
4444
.string()
4545
.min(1)
46-
.max(2500)
47-
.describe("The knowledge to save. Be specific and actionable. Use markdown for structure. Max 2500 chars."),
46+
.max(1800)
47+
.describe("The knowledge to save. Be specific and actionable. Use markdown for structure. Max 1800 chars."),
4848
scope: z
4949
.enum(["global", "project"])
5050
.default("project")

packages/opencode/src/altimate/training/store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,5 +164,5 @@ export namespace TrainingStore {
164164
}
165165

166166
function stripTrainingMeta(content: string): string {
167-
return content.replace(/^<!--\s*training\n[\s\S]*?-->\n*/m, "").trim()
167+
return content.replace(/^<!--\s*training\n[\s\S]*?-->\n*/, "").trim()
168168
}

packages/opencode/src/altimate/training/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function trainingKind(block: { tags: string[] }): TrainingKind | undefine
3939

4040
export function parseTrainingMeta(content: string): TrainingBlockMeta | undefined {
4141
// Training blocks store structured metadata in the first YAML-like section
42-
const match = content.match(/^<!--\s*training\n([\s\S]*?)\n-->/m)
42+
const match = content.match(/^<!--\s*training\n([\s\S]*?)\n-->/)
4343
if (!match) return undefined
4444
const meta: Record<string, unknown> = {}
4545
for (const line of match[1].split("\n")) {
@@ -63,6 +63,6 @@ export function embedTrainingMeta(content: string, meta: TrainingBlockMeta): str
6363
"-->",
6464
].join("\n")
6565
// Strip existing training meta block if present
66-
const stripped = content.replace(/^<!--\s*training\n[\s\S]*?-->\n*/m, "")
66+
const stripped = content.replace(/^<!--\s*training\n[\s\S]*?-->\n*/, "")
6767
return header + "\n" + stripped
6868
}

packages/opencode/src/memory/prompt.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ export namespace MemoryPrompt {
253253
const header = "## Teammate Training\n\nYou have been trained on the following knowledge by your team. Apply it consistently.\n"
254254
let result = header
255255
let used = header.length
256+
let itemCount = 0
256257

257258
const byKind = new Map<TrainingKind, MemoryBlock[]>()
258259
for (const block of training) {
@@ -269,7 +270,7 @@ export namespace MemoryPrompt {
269270

270271
const section = KIND_HEADERS[kind]
271272
const sectionHeader = `\n### ${section.header}\n_${section.instruction}_\n`
272-
if (used + sectionHeader.length > budget) break
273+
if (used + sectionHeader.length > budget) continue
273274
result += sectionHeader
274275
used += sectionHeader.length
275276

@@ -285,9 +286,10 @@ export namespace MemoryPrompt {
285286
if (used + needed > budget) break
286287
result += "\n" + formatted + "\n"
287288
used += needed
289+
itemCount++
288290
}
289291
}
290292

291-
return result
293+
return itemCount > 0 ? result : ""
292294
}
293295
}

packages/opencode/src/memory/store.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,24 @@ function globalDir(): string {
1414
}
1515

1616
// altimate_change start - use .altimate-code (primary) with .opencode (fallback)
17-
let _cachedProjectDir: string | undefined
17+
// Cache keyed by Instance.directory to avoid stale paths when context changes
18+
const _projectDirCache = new Map<string, string>()
1819
function projectDir(): string {
19-
if (_cachedProjectDir) return _cachedProjectDir
20-
const primary = path.join(Instance.directory, ".altimate-code", "memory")
21-
const fallback = path.join(Instance.directory, ".opencode", "memory")
22-
// Use .altimate-code if it exists, fall back to .opencode, default to .altimate-code for new projects
23-
if (fsSync.existsSync(path.join(Instance.directory, ".altimate-code"))) {
24-
_cachedProjectDir = primary
25-
} else if (fsSync.existsSync(path.join(Instance.directory, ".opencode"))) {
26-
_cachedProjectDir = fallback
20+
const dir = Instance.directory
21+
const cached = _projectDirCache.get(dir)
22+
if (cached) return cached
23+
const primary = path.join(dir, ".altimate-code", "memory")
24+
const fallback = path.join(dir, ".opencode", "memory")
25+
let result: string
26+
if (fsSync.existsSync(path.join(dir, ".altimate-code"))) {
27+
result = primary
28+
} else if (fsSync.existsSync(path.join(dir, ".opencode"))) {
29+
result = fallback
2730
} else {
28-
_cachedProjectDir = primary
31+
result = primary
2932
}
30-
return _cachedProjectDir
33+
_projectDirCache.set(dir, result)
34+
return result
3135
}
3236
// altimate_change end
3337

0 commit comments

Comments
 (0)