fix(memory): 工作日志把配方做的事归系统 —— 它不是 AI 判的,也不是人办的

不加这一条,配方的留痕会掉进 default 分支记在「任务助手」名下,日志上看着像 AI 干的。
自主率的分法要认第三档:确定的事交系统,判断交 AI,责任交人。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
xcs
2026-08-29 06:32:52 +08:00
co-authored by Claude Opus 5
parent 35ef0f1790
commit 09885c3dbd
2 changed files with 30 additions and 0 deletions
@@ -642,3 +642,26 @@ describe('进对话区的交代也只摆首句', () => {
expect(detailOf(one)).toEqual(['这句已进对话区。']);
});
});
/**
* 日志三色:配方做的事归系统(2026-08-29,ADR-014)。
*
* 用 `action-intent` 造例 —— 它在 `SKELETON` 里(能真的走完 `structuredLog` 全程,
* 不像 `result` 那样在骨架这一关就被滤掉、永远到不了 `actorOf`),
* 且不带 `detail.auto` 时会落进 `actorOf` 的 `default` 分支(署「任务助手」),
* 正好能对照出「加了 `detail.auto` 之后改署系统」这条判定是否生效。
*/
describe('日志三色:配方做的事归系统', () => {
it('带 detail.auto 的那几行是**系统**做的 —— 既不是 AI 判的,也不是人办的', () => {
const lines = structuredLog([
entry('action-intent', { title: '芯片主数据:写下 2 行', detail: { auto: 'S1#3' } }),
]);
expect(lines[0]?.who).toBe('system');
expect(lines[0]?.actor).toBe('系统');
});
it('没有 detail.auto 的 action-intent 照旧记在 AI 名下 —— 老行为一个字不变', () => {
const lines = structuredLog([entry('action-intent', { title: '我判齐了' })]);
expect(lines[0]?.who).toBe('agent');
});
});
@@ -269,6 +269,13 @@ function actionOf(e: MemoryEntry): string {
* 由上层换成名字;换不到就退回「员工」,不编名字。
*/
function actorOf(e: MemoryEntry): { actor: string; actorId: string | undefined; who: 'owner' | 'agent' | 'system' } {
/*
* **配方做的事归系统**(2026-08-29,ADR-014)。自主率的分法要认第三档:
* 它既不是 AI 判出来的,也不是人办的 —— 是系统按声明做的。
* 不加这一条的话,`kind: 'result'` 会掉进 `default` 分支被记在「任务助手」名下,
* 日志上看着像 AI 干的 —— **界面不撒谎**。
*/
if (typeof e.detail?.['auto'] === 'string') return { actor: '系统', actorId: undefined, who: 'system' as const };
const by = e.detail?.['by'];
const id = typeof by === 'string' && by !== '' ? by : undefined;
switch (e.kind) {