Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Test

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Run tests
run: npm test

- name: Check pull request whitespace
if: github.event_name == 'pull_request'
run: git diff --check "${{ github.event.pull_request.base.sha }}...HEAD"

- name: Check pushed commit whitespace
if: github.event_name == 'push'
run: git diff-tree --check --no-commit-id -r HEAD
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,21 @@ If an agent cannot discover Skills automatically, add an equivalent instruction
| `recover <relative-path>` | Restores an archived or trashed file after confirmation. |
| `memory health` | Proposes lifecycle actions; it never executes them automatically. |

### Memory is not the whole project

For active projects, Persistent Memory distinguishes stable memory, one current-status source, source materials, and route-only indexes.

`update memory` updates reviewed stable memory. It does not automatically synchronize project status, source materials, or every index. When a request contains several kinds of change, the agent separates them, previews each destination, and waits for explicit user confirmation before writing.

Before claiming that a project is current or fully loaded, the agent checks the declared status source and any required live or first-party source.

If a newer authoritative live or first-party source conflicts with an older status snapshot, the newer authoritative source governs any current-state claim.

Lifecycle paths must be relative, such as `projects/old.md`. The skill rejects absolute paths, `..`, `_core/`, and control files. A path collision stops the operation before any file or index changes; it never overwrites a destination automatically.

## What Belongs in Memory

Keep stable identity, preferences, decisions, concise project state, and pointers to source material here.
Keep stable identity, preferences, stable reviewed project background, durable decisions, constraints, results, and pointers to canonical status and source materials here.

Keep raw repositories, downloads, media files, and datasets in their original project workspaces. A memory note should point to them and explain why they matter; it should not become a general-purpose file warehouse.

Expand All @@ -99,7 +109,7 @@ All memory is stored as local Markdown files. Do not put passwords, API keys, or

## Release Status

v0.8.0 adds optional Summary-first loading for active on-demand files. It does not add section-level loading, automatic summary creation, or bulk migration.
v0.8.1 is a reliability patch that clarifies the boundary between memory and project context. It does not add project-wide synchronization, background scanning or polling, automatic migration, or bulk migration.

## License

Expand Down
14 changes: 12 additions & 2 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,21 @@ Claude 不会自动知道你告诉 Codex 的内容,Codex 也不会自动知道
| `恢复 <相对路径>` | 确认后恢复归档或回收站中的文件。 |
| `记忆体检` | 提出生命周期建议;绝不自动执行。 |

### 记忆不等于整个项目上下文

对于活跃项目,Persistent Memory 区分稳定记忆、唯一当前状态源、来源材料和只负责路由的索引。

`更新记忆` 只更新经过确认的稳定记忆,不会自动同步项目状态、来源材料或所有索引。当一次请求包含多类变化时,AI 会先分类并展示每个目标位置,等待用户明确确认后才写入。

在声称项目状态是当前或已经完整加载前,AI 必须检查声明的状态源,以及任何所需的实时或一手来源。

如果较新的权威实时或一手来源与较旧的状态快照冲突,任何当前状态声明均以较新的权威来源为准。

生命周期操作只接受相对路径,例如 `projects/old.md`。skill 会拒绝绝对路径、`..`、`_core/` 和控制文件。发生路径冲突时,任何文件或索引改动前都会停止;绝不自动覆盖目标文件。

## 什么该进入记忆

这里保存稳定身份、偏好、决策、精炼项目状态和原始材料的指针
这里保存稳定身份、偏好、经过审阅的稳定项目背景、持久决策、约束、结果,以及指向权威状态和来源材料的路径

原始仓库、下载文件、媒体文件和数据集应留在原项目工作区。记忆笔记应说明它们在哪里、为什么重要,而不应把记忆目录变成通用文件仓库。

Expand All @@ -99,7 +109,7 @@ Claude 不会自动知道你告诉 Codex 的内容,Codex 也不会自动知道

## 版本状态

v0.8.0 为活跃按需文件新增可选摘要优先加载;不包含 section 级加载、自动创建摘要或批量迁移
v0.8.1 是一项可靠性补丁,明确记忆与项目上下文之间的边界;不包含项目范围同步、后台扫描或轮询、自动迁移或批量迁移

## 许可

Expand Down
41 changes: 39 additions & 2 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: Use when a user wants cross-session personal context shared by comp

Persistent Memory is a transparent local context layer. It stores reviewed user context as Markdown files so compatible agents can read the same source of truth.

**v0.8.0:** This release adds optional Summary-first loading for active on-demand files. It does not add section-level loading, automatic summary creation, or bulk migration.
**v0.8.1:** This reliability patch defines project context ownership and freshness checks for current-state claims. It does not add automatic synchronization, directory migration, or live-source polling.

## Runtime Contract

Expand Down Expand Up @@ -36,7 +36,18 @@ Platform memory may coexist. Persistent Memory does not import historical chats
└── _trash/ # Recoverable deletion buffer
```

Store stable context, decisions, concise project state, and pointers to source material. Keep raw repositories, downloads, media files, and datasets in their project workspaces; store a short description and path in memory instead.
Store stable reviewed context, decisions, constraints, and pointers to canonical project status and source materials. Keep volatile operational state in one declared current-status source instead of copying it into memory files or indexes.

## Project Context Ownership

For active projects, distinguish four logical roles. Do not require a fixed directory layout:

- **Stable memory:** reviewed background, decisions, constraints, durable results, and learned boundaries.
- **Current status:** the single canonical source for the active phase, tasks, blockers, owners, and next actions.
- **Materials:** first-party messages, meeting notes, repositories, datasets, and other source evidence.
- **Index:** routing metadata that tells the agent what to load. It must not own volatile project facts.

**One fact, one owner.** Memory may point to current status and materials, but must not duplicate frequently changing fields such as pull-request state, deadlines, or active blockers. If two files both claim to be the current-status source, stop and ask the user to choose or approve a migration.

## Loading

Expand All @@ -60,6 +71,18 @@ Trigger: the active conversation matches an entry in `_index.md`.
2. Read those file(s).
3. Use the information naturally.

### Freshness Gate

Before answering with a claim that means current, latest, completed, blocked, or no active task:

1. Read the declared current-status source when one exists.
2. Check its last-updated or last-verified date.
3. Check declared first-party or live sources when the task depends on real-time facts.
4. If a newer material or live source conflicts with the status snapshot, treat the newer authoritative source as evidence and do not repeat the old status as current.
5. Briefly state the coverage or limitation when claiming current project state.

Do not claim that project context is fully loaded when declared current-status or required first-party sources were not read.

### Summary-first on-demand loading

For an active on-demand file selected through `_index.md`:
Expand All @@ -83,6 +106,17 @@ When a confirmed update to an active on-demand file changes a fact represented i

For a vague update request, list explicit facts and inferred patterns separately, then wait for the user to approve individual items.

“Update memory” changes stable memory only. It does not automatically synchronize project status, source materials, or every routing file.

When a request mixes project progress, new materials, durable decisions, and routing changes:

1. Classify the proposed changes as stable memory, current status, materials, or index.
2. Identify the canonical owner for each fact.
3. Show the user the exact per-layer preview.
4. Wait for confirmation before writing.

Do not create duplicate current-status sources or copy volatile facts into `_index.md`.

### First Save

When the memory root does not exist and the user confirms the first save:
Expand Down Expand Up @@ -190,3 +224,6 @@ Read `_core/` and `_index.md`, then report active file count, approximate size,
5. Keep `_index.md` synchronized with active on-demand memory.
6. Treat memory files as plain text; do not store passwords, API keys, or secrets.
7. Do not promise automatic activation, cross-device sync, full-chat import, or full-context loading.
8. Keep one canonical current-status source per active project.
9. Keep indexes route-only; do not duplicate volatile project facts in them.
10. Apply the Freshness Gate before making current-state claims.
41 changes: 39 additions & 2 deletions SKILL_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: Use when 用户需要让兼容 AI agent 共享跨对话个人上下

Persistent Memory 是透明的本地上下文层:它把经用户确认的上下文保存为 Markdown,让兼容 agent 读取同一份事实源。

**v0.8.0:** 本版为活跃按需文件新增可选的摘要优先加载;不包含 section 级加载、自动创建摘要或批量迁移
**v0.8.1:** 此可靠性补丁定义了项目上下文的所有权和当前状态声明的新鲜度检查;不包含自动同步、目录迁移或实时来源轮询

## 运行契约

Expand Down Expand Up @@ -36,7 +36,18 @@ Persistent Memory 是透明的本地上下文层:它把经用户确认的上
└── _trash/ # 可恢复删除缓冲区
```

记忆中只放稳定上下文、决策、精炼的项目状态和原始材料的指针。原始仓库、下载文件、媒体文件和数据集应留在各自项目工作区;记忆只保存简述和路径。
保存经过确认的稳定背景、决策、约束,以及指向项目当前状态和来源材料的路径。高频变化的执行状态只能由一个声明过的当前状态源维护,不得复制进多个记忆文件或索引。

## 项目上下文所有权

对于活跃项目,区分四种逻辑角色,但不强制固定目录结构:

- **稳定记忆:** 已确认的背景、决策、约束、持久结果与经验边界。
- **当前状态:** 当前阶段、任务、阻塞、负责人和下一步的唯一权威来源。
- **材料:** 一手沟通、会议记录、仓库、数据集和其他来源证据。
- **索引:** 告诉 AI 去哪里加载的路由信息,不拥有高频变化的项目事实。

**一个事实,一个所有者。** 记忆可以指向当前状态和材料,但不得复制 PR 状态、截止日期或当前阻塞等易变字段。如果两个文件都声称自己是当前状态源,停止并请用户选择,或预览迁移方案后等待确认。

## 加载

Expand All @@ -60,6 +71,18 @@ Persistent Memory 是透明的本地上下文层:它把经用户确认的上
2. 读取这些文件。
3. 自然使用其中信息。

### 新鲜度闸门

在回答包含“当前、最新、已完成、被阻塞、没有任务”等含义的结论前:

1. 如果项目声明了当前状态源,先读取它。
2. 检查最后更新或最后核验日期。
3. 当任务依赖实时事实时,核对已声明的一手来源或实时来源。
4. 如果更新更晚的材料或实时来源与状态快照冲突,以较新且更权威的来源作为证据,不得继续把旧状态写成当前事实。
5. 在声明当前项目状态时,简要说明已覆盖的来源或仍存在的限制。

没有读取已声明的当前状态源或必要一手来源时,不得声称项目上下文已经完整加载。

### 摘要优先的按需加载

对于通过 `_index.md` 选中的活跃按需文件:
Expand All @@ -83,6 +106,17 @@ Persistent Memory 是透明的本地上下文层:它把经用户确认的上

如果用户只说“更新记忆”,要把显性事实和推断出的模式分开列出,再等待逐项确认。

“更新记忆”只修改稳定记忆,不会自动同步项目状态、来源材料或全部路由文件。

当请求同时包含项目进展、新材料、长期决策和路由变化时:

1. 将拟修改内容分类为稳定记忆、当前状态、材料或索引。
2. 为每项事实确定唯一所有者。
3. 展示按层拆分的精确预览。
4. 等待用户确认后再写入。

不得创建重复的当前状态源,也不得把易变事实复制进 `_index.md`。

### 首次保存

当记忆根目录不存在且用户确认第一次保存时:
Expand Down Expand Up @@ -190,3 +224,6 @@ Persistent Memory 是透明的本地上下文层:它把经用户确认的上
5. `_index.md` 必须与活跃按需记忆同步。
6. 记忆文件是明文;不存密码、API key 或其他密钥。
7. 不承诺自动激活、跨设备同步、完整聊天导入或“加载完整上下文”。
8. 每个活跃项目只保留一个权威当前状态源。
9. 索引只负责路由,不复制高频变化的项目事实。
10. 声称当前状态前必须执行新鲜度闸门。
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "persistent-memory-skill",
"private": true,
"type": "module",
"scripts": {
"test": "node --test tests/*.test.mjs"
}
}
134 changes: 134 additions & 0 deletions tests/helpers/create-synthetic-project.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";

async function write(root, relativePath, content) {
const target = path.join(root, relativePath);
await mkdir(path.dirname(target), { recursive: true });
await writeFile(target, content, "utf8");
return target;
}

export async function createSyntheticProject(root, scenario) {
const files = [];

files.push(
await write(
root,
".persistent-memory/_index.md",
[
"# Memory Index",
"",
"- projects/alpha.md — Stable Project Alpha memory; current status is routed through the declared status source.",
"",
].join("\n"),
),
);

files.push(
await write(
root,
".persistent-memory/projects/alpha.md",
[
"# Project Alpha",
"",
"## Summary",
"",
"- Stable product purpose and reviewed decisions.",
"- Current operational facts are owned by the declared status source.",
"- Source materials are registered separately.",
"",
"## Context Routing",
"",
"- Current status: workspace/alpha-status.md",
"- Materials index: workspace/materials-index.md",
"- Live source: workspace/alpha-live-source.md (fictional://alpha-board)",
"",
].join("\n"),
),
);

files.push(
await write(
root,
"workspace/alpha-live-source.md",
[
"# Fictional Project Alpha Live Source",
"",
"> source_route: fictional://alpha-board",
"> source_date: 2026-01-03",
"> authority: authoritative-current-status",
"",
"## Current Status Evidence",
"",
"- Blocker: external design approval is pending",
"- Precedence: This source supersedes workspace/alpha-status.md when newer.",
"",
].join("\n"),
),
);

files.push(
await write(
root,
"workspace/alpha-status.md",
[
"# Project Alpha Current Status",
"",
"> role: current-status",
"> last_verified: 2026-01-01",
"",
"- Phase: prototype",
"- Blocker: none recorded",
"",
].join("\n"),
),
);

files.push(
await write(
root,
"workspace/materials-index.md",
[
"# Project Alpha Materials",
"",
"- team-update.md — first-party update dated 2026-01-02",
"",
].join("\n"),
),
);

files.push(
await write(
root,
"workspace/team-update.md",
[
"# Fictional Team Update",
"",
"> source_date: 2026-01-02",
"",
"- Blocker: component certification is pending",
"",
].join("\n"),
),
);

if (scenario === "duplicate-owner") {
files.push(
await write(
root,
"workspace/alpha-status-copy.md",
[
"# Project Alpha Alternate Status",
"",
"> role: current-status",
"> last_verified: 2026-01-02",
"",
"- Phase: implementation",
"",
].join("\n"),
),
);
}

return files;
}
Loading
Loading