fix: resolve remaining architect issues - split feedback keys, remove generic config key, supersession mode

- Split feedbackTopicKey: server-error now separate from port-occupied-environment
- Remove generic plugin.*config entity key (too broad), fall back to canonical dedup
- Feedback topic conflicts now use supersession mode (newer beats longer)
- Add 3 regression tests: English port/split, unrelated configs, feedback supersession

70/70 tests pass.
This commit is contained in:
Ralph Chang
2026-04-26 16:54:24 +08:00
parent a154139b27
commit 3d44269228
2 changed files with 47 additions and 9 deletions
+14 -9
View File
@@ -84,10 +84,7 @@ function extractEntityKey(text: string): string | null {
if (/opencode.*agenthub/i.test(normalized)) {
return "opencode-agenthub plugin system";
}
// plugin config variations
if (/plugin.*config|config.*plugin/i.test(normalized)) {
return "plugin config";
}
// For generic config references, fall back to canonical text dedup — no entity key
return null;
}
@@ -120,9 +117,13 @@ function feedbackTopicKey(text: string): string | null {
if (/purple.*italic/i.test(normalized)) {
return "purple-italic-rendering";
}
// Browser login/server errors
if (/login.*500|500.*internal|server.*error|port.*occup/i.test(normalized)) {
return "server-error-port-issue";
// Browser login/server errors (500 internal_error)
if (/login.*500|500.*internal|internal_error|server.*error/i.test(normalized)) {
return "server-error";
}
// Port occupied / environment issues
if (/port.*occup|9473|端口|舊進程|旧进程/i.test(normalized)) {
return "port-occupied-environment";
}
// Theme preferences
if (/theme|dark.*light|prefer.*theme/i.test(normalized)) {
@@ -198,8 +199,12 @@ export function enforceLongTermLimits(entries: LongTermMemoryEntry[]): LongTermM
const existing = entityDeduped.get(key);
if (!existing) {
entityDeduped.set(key, entry);
} else if (chooseBetterMemory(entry, existing) === entry) {
entityDeduped.set(key, entry);
} else {
// Feedback topic conflicts use supersession mode (newer beats longer)
const mode = entry.type === "feedback" && entityKey ? "supersession" as const : "entity" as const;
if (chooseBetterMemory(entry, existing, mode) === entry) {
entityDeduped.set(key, entry);
}
}
}
+33
View File
@@ -381,3 +381,36 @@ test("enforceLongTermLimits supersession: newer shorter decision beats older lon
assert.equal(decisions.length, 1, "Newer shorter decision should supersede older longer one");
assert.ok(decisions[0].text.includes("4 formats"), "Kept entry should be the newer 4-formats");
});
test("enforceLongTermLimits feedback: English port issue does NOT collapse with server error", () => {
const entries = [
agedEntry("e1", "Browser login 500 internal_error, code correct but cause unknown", "feedback", { daysAgo: 0 }),
agedEntry("e2", "Port 9473 occupied by old process, may need to kill and restart", "feedback", { daysAgo: 0 }),
];
const kept = enforceLongTermLimits(entries);
const feedbackEntries = kept.filter(e => e.type === "feedback");
assert.equal(feedbackEntries.length, 2, "English port issue and server error should remain separate");
});
test("enforceLongTermLimits config: unrelated generic plugin configs do NOT collapse", () => {
const entries = [
agedEntry("c1", "Vite plugin config location: vite.config.ts at project root", "reference", { daysAgo: 0 }),
agedEntry("c2", "ESLint plugin config location: eslint.config.js at project root", "reference", { daysAgo: 0 }),
];
const kept = enforceLongTermLimits(entries);
const refEntries = kept.filter(e => e.type === "reference");
assert.equal(refEntries.length, 2, "Unrelated plugin configs without entity key should remain separate");
});
test("enforceLongTermLimits feedback: supersession prefers newer shorter over older longer", () => {
// Same purple/italic issue, newer shorter fix supersedes older verbose fix
const older = agedEntry("f1", "Purple/italic text issue resolved by using plain text labels instead of any special markup syntax in the prompt", "feedback", { daysAgo: 5 });
const newer = agedEntry("f2", "Purple/italic text fixed via template replacement", "feedback", { daysAgo: 0 });
const kept = enforceLongTermLimits([older, newer]);
const feedbackEntries = kept.filter(e => e.type === "feedback");
assert.equal(feedbackEntries.length, 1, "Newer shorter feedback should supersede older longer");
assert.ok(feedbackEntries[0].text.includes("template replacement"), "Kept entry should be the newer fix");
});