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
28 changes: 22 additions & 6 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,39 @@

### 本地质量检查

- [ ] `npm run verify:static` 通过(build / package:check / tsc / lint / format:check / docs:check / agent:context:check / complexity:gate)
- [ ] `npm run verify:static` 通过(build / package:check / check / check:lock / lint / format:check / docs:check / agent:context:check / complexity:gate / verify:packages
- [ ] `npm run test:product` 通过(或按改动路由跑 `npm run agent:verify -- <路径>`)
- [ ] 改了依赖或 lockfile 时:`npm run check:lock` 通过、根 `package-lock.json` 已同步、`npm audit --omit=dev --audit-level=high` 无漏洞
- [ ] 改了子包(`dsh/dsh-nmg` 等)时:`npm run verify:packages` 通过(frozen-lockfile install + build),`pnpm-lock.yaml`/lockfile 随 package.json 同步
- [ ] 文档改动跑过 `npm run docs:check`;决策/设计改动遵循 doc-maintenance 规范
- [ ] 新代码方法圈复杂度不超阈值(CodeFactor / `npm run complexity:gate`)
- [ ] 未提交可再生产物:`dist/`、`dsh/dsh-nmg/lib/`、`src/prompts/nmg-prompts.generated.ts`、`.nmg-search-scope` 不入库(见 `docs/decisions/rejected/2026-09-02-track-build-artifacts-in-git.md`)

### RCP(Repository Control Plane)

- [ ] 首个实质写入前已在 `repo-development` 黑板登记 in-flight goal(条目已 resolve)
- [ ] `npm run agent:verify -- <改动路径>` 跑过 reconcile,`.nmg/verification/latest.json` 覆盖改动路由
<!-- 逐项给出可执行命令:模板是操作层检查表,照命令跑完即完成该项自证。
命令细节见 skills/repo-development/SKILL.md 的
"Repository Control Plane beyond agent:verify" 节。 -->

- [ ] 首个实质写入前已在 `repo-development` 黑板登记 in-flight goal:
`nmg board put repo-development "goal=…; approach=…; scope=…" --agent <id> --kind goal`(已 resolve)
- [ ] 改动路由的 reconcile 已通过并写入证据:
`npm run agent:verify -- <改动路径>`(或 `nmg-rcp reconcile <contract> --apply --workspace-ready`)
→ `.nmg/verification/latest.json` 覆盖改动路由
- [ ] CI 全绿是用 RCP 观察确认的,不是人肉轮询:
`nmg-rcp forge-status --pr <PR号>` 的 checks 全为 SUCCESS
- [ ] 只提交本 PR 拥有的文件;未吞并行 Agent 的暂存/工作树改动
(`git status --short` 核对无他人文件)

### CI 完成确认

<!-- CI 完成后无需逐个 job 轮询:读 CI Status Snapshot 即可确认。 -->
- [ ] CI Status Snapshot(`.nmg-ci/status.json`)结论为 `workflow.conclusion: "success"` 且 `failures: []`
—— 或 `gh pr checks <PR> | Select-String "All checks passed"` 出现且为 pass
<!-- 用 RCP/forge 观察确认,不逐个 job 轮询。RCP 节的
`nmg-rcp forge-status --pr <PR号>` 是权威读法;下面两项是其补充。 -->

- [ ] `nmg-rcp forge-status --pr <PR号>` 的 `All checks passed` 为 SUCCESS
(或 CI Status Snapshot `.nmg-ci/status.json`:`conclusion: "success"` 且 `failures: []`)
- [ ] CodeFactor 通过
- [ ] Static job 通过(含 `verify:static` 全部子检查 + Dependency audit;audit 因上游新 advisory 失败时,先 `npm audit fix` 再更新 `package-lock.json` 提交,不要改 audit 门槛)

> CI Status Snapshot 是 GitHub 状态的只读观察(`authority: observation-only`),
> 不是授权或合并决定;合并仍需显式操作。
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ jobs:
node-version: "24"
cache: npm
- run: npm ci
# Subpackage builds (verify:packages) use pnpm via the dsh/dsh-nmg
# pnpm-lock.yaml; corepack is bundled with the Node distribution.
- name: Enable corepack pnpm
run: |
corepack enable
pnpm --version
- name: Shared static verification contract
run: npm run verify:static
- name: Dependency audit
Expand Down
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ evals/snapshots/
*.sqlite-wal
.wrangler/

# File-content hot-zone manifest (runtime state; contains absolute paths)
/.nmg-search-scope

# Generated build artifacts (regenerated by npm run build / dsh-nmg tsdown)
/src/prompts/nmg-prompts.generated.ts
dsh/dsh-nmg/lib/
# dsh/dsh-nmg's real lockfile is pnpm-lock.yaml (tracked); package-lock.json
# only appears if someone runs `npm install` inside the subpackage by mistake.
dsh/dsh-nmg/package-lock.json

# Large/generated binary artifacts (should never be tracked)
*.sqlite
*.safetensors
Expand Down
2 changes: 0 additions & 2 deletions .nmg-search-scope

This file was deleted.

4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ Before modifying the repository, follow `skills/repo-development/SKILL.md`:

For documentation changes, also follow `skills/doc-maintenance/SKILL.md`. For using
NMG memory or its coordination board, follow `skills/nmg-memory/SKILL.md`.

Build outputs (`dist/`, `dsh/dsh-nmg/lib/`, generated prompts) are not tracked;
see the "Builds and generated artifacts" section of `skills/repo-development/SKILL.md`
for reproduction order and `verify:packages` / `check:lock`.
5 changes: 5 additions & 0 deletions docs/decisions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,8 @@ to one another. Missing translations are reported as warnings, not hard errors.
## Implemented decisions

- [External Repository Control Plane](implemented/2026-08-29-repository-control-plane.md)

## Rejected decisions

- [Track build artifacts in version control](rejected/2026-09-02-track-build-artifacts-in-git.md) — regenerable outputs stay untracked; buildability is verified, not committed
- [Keep the bookmark feature named "anchors"](rejected/2026-09-02-keep-bookmarks-named-anchors.md) — renamed to tesserae to end collision with surface/task/support anchors
5 changes: 5 additions & 0 deletions docs/decisions/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@
## 已实现决策

- [外部 Repository Control Plane](implemented/2026-08-29-repository-control-plane.zh-CN.md)

## 被拒决策

- [将构建产物纳入版本控制](rejected/2026-09-02-track-build-artifacts-in-git.zh-CN.md) — 可再生输出保持不入库;可构建性靠验证而非提交
- [书签功能继续命名为 "anchors"](rejected/2026-09-02-keep-bookmarks-named-anchors.zh-CN.md) — 改名为 tessera,终结与 surface/task/support anchors 的撞名
77 changes: 77 additions & 0 deletions docs/decisions/rejected/2026-09-02-keep-bookmarks-named-anchors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Keep the bookmark feature named "anchors"

[中文](2026-09-02-keep-bookmarks-named-anchors.zh-CN.md)

**Status:** rejected
**Date:** 2026-09-02

## Problem

The memory-bookmark feature (file locations a memory points into, content-anchored
by snippet) shipped as "anchors" (`anchors` table, `anchor_ref` markers,
`--anchor` CLI flag). While reviewing the name for long-term clarity, three
unrelated "anchor" concepts were found already living in the same codebase and
ecosystem:

- **Surface anchors** (`surfaceAnchorCandidates` / `surfaceAnchors` in
`src/core/store/`): retrieval-side explicit quoted phrases/paths/IDs indexed
for exact-match search — an unrelated, pre-existing product concept in the
_same search path_ as the bookmark hits.
- **Pi task anchors** (`state.anchors` in the Pi extension): recent substantive
user-task context carried across terse turns.
- **Support anchors** (`reasoning-workspace`, `qpp`, `hierarchical-activation`):
"a stable evidence reference" in Lab reasoning, the top-1 query anchor in QPP,
LTG node vectors as anchors.

One word, four meanings; two of them inside the same file
(`src/core/store/retrieval.ts` hosts both `surfaceAnchorCandidates` and
`searchAnchors`).

## Proposal

Keep the shipped name "anchors" for bookmarks, relying on context and the
`(bookmark)` parenthetical to disambiguate.

## Alternatives considered

- **Qualified name, e.g. `memory-anchor` / `file-anchor`.** Rejected: it is
longer on every CLI flag and still overloads the shared word; grep and search
results would need the qualifier to be meaningful.
- **Keep "anchor" only in code, rename user-facing surfaces.** Rejected: the
confusion is worst at the rendering boundary (`anchor=` lines), so partial
renaming leaves the collision where readers actually see it.
- **Rename the other concepts instead.** Rejected: `surfaceAnchor` is an older,
widely-referenced retrieval term (design docs, benchmark notes), and Pi's
task-anchor lives in another repository we do not own; renaming bookmarks was
the single change fully inside our control.

## Why rejected

- **Same search path, two meanings.** Bookmark hits and surface-anchor hits both
flow through the retrieval context; a reader or agent seeing `anchor=` render
lines cannot tell which concept produced them without reading the code.
- **Retrieval quality discussion needs the distinction.** The retrieval
benchmark and design notes distinguish surface anchors (explicit-token
retrieval) from ordinary word overlap; overloading "anchor" makes that
discussion ambiguous.
- **Rename cost was lowest at this moment.** The feature was merged days earlier,
had no external consumers, and all real-store rows were test data — a rename
was a mechanical, low-risk operation (see the rename PR). Naming debt only
compounds with age.
- The replacement name chosen was **tessera** (plural _tesserae_), from the
Latin _tessera hospitalis_ — a token broken in two so matching the halves
proves identity — matching the snippet-relocation model (the bookmark's
snippet half must match the file's content half). The word had zero prior
usage in the codebase, so it cannot collide.

## Consequences

- The feature is now `tesserae` end-to-end: table, FTS, markers
(`tessera_ref`), CLI (`--tessera`), search rendering (`tessera=`), types
(`TesseraRecord/Input/Hit`), and the design doc
(`docs/design/memory-tesserae-design.md`).
- A forward migration renames a pre-rename `anchors` table in place and rewrites
`anchor_ref` markers to `tessera_ref`, so existing stores upgrade without data
loss.
- "Anchor" remains in the codebase only where it means one of the other three
concepts (surface / task / support), each now unambiguous.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 书签功能继续命名为 "anchors"

[English](2026-09-02-keep-bookmarks-named-anchors.md)

**Status:** rejected
**Date:** 2026-09-02

## Problem

记忆书签功能(记忆指向的文件位置,以内容 snippet 为锚)以 "anchors" 之名发布(`anchors` 表、`anchor_ref` markers、`--anchor` CLI 参数)。在审视长期清晰度时,发现代码库与生态中已存在三个无关的 "anchor" 概念:

- **Surface anchors**(`src/core/store/` 的 `surfaceAnchorCandidates` / `surfaceAnchors`):检索侧的显式引号短语/路径/ID,为精确匹配搜索建索引——一个无关的、先于本功能存在的产品概念,且位于与书签命中**同一条搜索路径**。
- **Pi task anchors**(Pi 扩展的 `state.anchors`):跨简洁轮次携带的近期实质性用户任务上下文。
- **Support anchors**(`reasoning-workspace`、`qpp`、`hierarchical-activation`):Lab 推理中的"稳定证据引用"、QPP 的 top-1 查询锚、LTG 节点向量锚。

一个词,四种含义;其中两种在同一个文件里(`src/core/store/retrieval.ts` 同时承载 `surfaceAnchorCandidates` 与 `searchAnchors`)。

## 提案

保留已发布的 "anchors" 命名,依靠上下文与 `(bookmark)` 括注消歧。

## 考虑过的替代方案

- **限定名,如 `memory-anchor` / `file-anchor`。** 拒绝:每个 CLI 参数更长,且仍重载共享词;grep 与搜索结果必须带限定词才有意义。
- **代码里保留 "anchor",只改用户可见表面。** 拒绝:混淆最严重处正是渲染边界(`anchor=` 行),部分改名把撞名留在读者实际看到的位置。
- **改为改其他概念。** 拒绝:`surfaceAnchor` 是更早、被广泛引用的检索术语(设计文档、benchmark 笔记),Pi 的 task-anchor 位于我们无权修改的另一个仓库;改书签名是我们完全掌控内的唯一改动。

## 为什么拒绝

- **同一条搜索路径,两种含义。** 书签命中与 surface-anchor 命中都流经检索上下文;读者或 agent 看到 `anchor=` 渲染行,不读代码无法分辨是哪一种概念产生的。
- **检索质量讨论需要区分。** 检索 benchmark 与设计笔记区分 surface anchors(显式 token 检索)与普通词重叠;重载 "anchor" 使该讨论含混。
- **改名成本此时最低。** 功能几天前才合入、无外部消费者、真实库存量行全是测试数据——改名是机械、低风险操作(见改名 PR)。命名债务只随年龄增长。
- 替代名选定为 **tessera**(复数 _tesserae_),源自拉丁语 _tessera hospitalis_——一分为二、相合证身份的凭证牌——契合 snippet 重定位模型(书签的 snippet 半必须与文件内容半相合)。该词在代码库零先例,不可能撞名。

## Consequences

- 功能现以 `tesserae` 全链路命名:表、FTS、markers(`tessera_ref`)、CLI(`--tessera`)、检索渲染(`tessera=`)、类型(`TesseraRecord/Input/Hit`)与设计文档(`docs/design/memory-tesserae-design.md`)。
- 前向迁移将改名前的 `anchors` 表原位 RENAME 并把 `anchor_ref` markers 重写为 `tessera_ref`,存量库无数据丢失升级。
- "Anchor" 仅在指代其余三种概念(surface / task / support)时留在代码库中,各自不再含混。
64 changes: 64 additions & 0 deletions docs/decisions/rejected/2026-09-02-track-build-artifacts-in-git.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Track build artifacts in version control

[中文](2026-09-02-track-build-artifacts-in-git.zh-CN.md)

**Status:** rejected
**Date:** 2026-09-02

## Problem

The repository contains several regenerable outputs: `dsh/dsh-nmg/lib/` (tsdown
build output of the DSH host plugin), `src/prompts/nmg-prompts.generated.ts`
(emitted from `nmg-prompts.yaml` by `scripts/generate-prompts.ts`), and
`.nmg-search-scope` (a runtime hot-zone manifest). For a long time the working
tree carried modified-but-uncommitted copies of `lib/` and `package-lock.json`,
because PRs committed only `src/` while the tracked artifacts drifted behind.

That produced the worst of both worlds: artifacts were tracked (so a fresh clone
carried stale copies), yet never updated in step with their sources (so the
tracked copy was wrong and the tree was permanently dirty). The drift surfaced
as real failures — a stale `lib/index.js` that still spoke the old `anchors`
RPC surface while the daemon and CLI had already moved to `tesserae`.

## Proposal

Commit build artifacts (`dsh/dsh-nmg/lib/`, generated prompts) in the same
commit as their sources, keeping the repository always-buildable from a clean
clone and the tree permanently clean.

## Alternatives considered

- **Ignore the artifacts but keep them tracked as they are today.** Rejected:
this was the status quo that produced permanent tree dirt and stale tracked
copies — tracked yet never in step with their sources.
- **Generate into a separate location outside the repository.** Rejected for
now: `dsh/dsh-nmg` is consumed by a `link:` install that expects `lib/` next
to `package.json`; moving the output would break the DSH plugin contract.

## Why rejected

- **Generated outputs are not source.** They carry no design intent and cannot
be reviewed meaningfully; a diff over `lib/index.js` is noise that obscures
the real `src/` change.
- **They drift by construction.** Every artifact regenerates with a different
timestamp/content hash per machine and toolchain version, so "commit them with
every source change" is an unenforceable discipline that will silently lapse
again (as it did before).
- **The right guarantee is buildability, not committed artifacts.** A clean
clone must be able to regenerate everything — that is a _verification_
property, owned by CI and the Repository Control Plane (`verify:packages`,
`verify:static`), not a _content_ property of the tree.
- Lockfiles are the deliberate exception: `package-lock.json` / `pnpm-lock.yaml`
pin the dependency graph and are configuration, not regenerable build output.
They stay tracked, and drift between `package.json` and the lockfile is caught
by `npm ci` and `check:lock`.

## Consequences

- `dsh/dsh-nmg/lib/`, `src/prompts/nmg-prompts.generated.ts`, and
`.nmg-search-scope` are untracked and ignored.
- Fresh consumers of `dsh/dsh-nmg` must run `pnpm install --frozen-lockfile &&
pnpm run build` before the package is usable (see
`skills/repo-development/SKILL.md`).
- CI verifies subpackage buildability from a clean checkout via
`verify:packages`, so artifact exclusion cannot silently rot the build.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 将构建产物纳入版本控制

[English](2026-09-02-track-build-artifacts-in-git.md)

**Status:** rejected
**Date:** 2026-09-02

## Problem

仓库中存在若干可再生的输出:`dsh/dsh-nmg/lib/`(DSH 宿主插件的 tsdown 构建产物)、`src/prompts/nmg-prompts.generated.ts`(由 `scripts/generate-prompts.ts` 从 `nmg-prompts.yaml` 生成)、`.nmg-search-scope`(运行时热区清单)。很长一段时间里,工作树一直带着已修改但未提交的 `lib/` 与 `package-lock.json` 副本——因为 PR 只提交 `src/`,而被跟踪的产物持续落后于源码。

这造成了最差的两难:产物被跟踪(全新 clone 会拿到过时副本),却从不与其源同步更新(被跟踪副本是错的,工作树永远脏)。漂移以真实故障形式暴露——过时的 `lib/index.js` 仍在讲旧 `anchors` RPC 表面,而 daemon 与 CLI 早已迁到 `tesserae`。

## 提案

将构建产物(`dsh/dsh-nmg/lib/`、生成的 prompts)与其源放在同一提交中入库,使仓库从干净 clone 即可构建、工作树始终干净。

## 考虑过的替代方案

- **忽略产物但仍像现在这样跟踪它们。** 拒绝:这正是造成工作树永久脏、被跟踪副本过时的现状——被跟踪却从不与其源同步。
- **生成到仓库外的独立位置。** 暂拒:`dsh/dsh-nmg` 由 `link:` 安装消费,期望 `lib/` 与 `package.json` 相邻;移动输出会破坏 DSH 插件契约。

## 为什么拒绝

- **生成产物不是源码。** 它们不携带设计意图,无法被有意义的评审;对 `lib/index.js` 的 diff 是淹没真实 `src/` 变更的噪音。
- **它们按构造就会漂移。** 每个产物随机器与工具链版本以不同的时间戳/内容哈希再生,因此"每次源码变更都随提交"是无法执行的纪律,会像过去一样悄然失效。
- **正确的保证是可构建性,而非提交产物。** 干净 clone 必须能再生一切——这是**验证**属性,由 CI 与 Repository Control Plane(`verify:packages`、`verify:static`)拥有,而非工作树的内容属性。
- Lockfile 是刻意的例外:`package-lock.json` / `pnpm-lock.yaml` 固定依赖图,是配置而非可再生的构建输出。它们保持跟踪;`package.json` 与 lockfile 的漂移由 `npm ci` 与 `check:lock` 捕获。

## Consequences

- `dsh/dsh-nmg/lib/`、`src/prompts/nmg-prompts.generated.ts`、`.nmg-search-scope` 不再跟踪并被忽略。
- `dsh/dsh-nmg` 的新消费者必须先运行 `pnpm install --frozen-lockfile && pnpm run build` 才能使用该包(见 `skills/repo-development/SKILL.md`)。
- CI 通过 `verify:packages` 在干净 checkout 上验证子包可构建性,因此产物排除不会悄然腐蚀构建。
9 changes: 5 additions & 4 deletions docs/design/file-content-source-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

**Status:** superseded
**Updated:** 2026-09-01
**Superseded by:** [memory-anchors-design.md](memory-anchors-design.md) — the
full-text file index is dropped in favor of sparse, Agent-authored anchors as an
independent searchable source. This document is kept for lineage; its
"files are not memory" and separated-presentation conclusions carry forward.
**Superseded by:** [memory-tesserae-design.md](memory-tesserae-design.md) — the
full-text file index is dropped in favor of sparse, Agent-authored tesserae
(bookmarks) as an independent searchable source. This document is kept for
lineage; its "files are not memory" and separated-presentation conclusions
carry forward.

This document proposes giving `nmg search` a second content source — the
project's own files and documents — so an Agent does not have to re-discover the
Expand Down
Loading