Просмотр исходного кода

fix: include package-index links and align residual metadata

Tianyi Cui 2 недель назад
Родитель
Сommit
3b27563290

+ 6 - 0
.agents/notes/implemented/process/2026-09-08-package-index-link-discovery.i18n.yaml

@@ -0,0 +1,6 @@
+# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
+# side as of the last confirmed-consistent state. Both languages carry equal authority;
+# after editing either side, bring the other along and re-record with:
+#   pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-09-08-package-index-link-discovery.md
+2026-09-08-package-index-link-discovery.md: be9084f72e468b914bd035ce62e95aa6a89983dc
+2026-09-08-package-index-link-discovery.zh.md: 93d6c2163183b4fb6391b731e982002b1938a704

+ 23 - 0
.agents/notes/implemented/process/2026-09-08-package-index-link-discovery.md

@@ -0,0 +1,23 @@
+# Agent Note: Include package indexes in Markdown link discovery
+
+Status: implemented
+
+English | [中文](2026-09-08-package-index-link-discovery.zh.md)
+
+## Problem
+
+In `verify-md-links`, nested group and package globs cannot match `packages/README.md` or its Chinese counterpart. A broken link in either index escapes validation unless the check also discovers those sources.
+
+## Decision
+
+[`verify-md-links`](../../../../scripts/verify-md-links.ts) includes `packages/*.md` in its existing source discovery. The command and its tests share that discovery function. Symlink deduplication and frozen Agent Note exclusion remain owned by [`repo-files`](../../../../scripts/repo-files.ts).
+
+## Alternatives considered
+
+**Repair individual links only.** This leaves the source-discovery omission in place, so a later broken index link can pass the same check.
+
+**Add a separate package-inventory checker.** Index completeness is a different requirement from link validity. It needs to support the current table formats and bilingual link targets; it does not replace checking links in every discovered source.
+
+## Consequences
+
+Broken relative links in both package indexes fail the existing documentation command. Discovery tests exercise those failures, preserve package-instruction and nested-document coverage, and exclude frozen notes. This check validates links; it does not require indexes to enumerate every package or define the source sets of other documentation checks.

+ 23 - 0
.agents/notes/implemented/process/2026-09-08-package-index-link-discovery.zh.md

@@ -0,0 +1,23 @@
+# Agent Note: 将包索引纳入 Markdown 链接发现范围
+
+Status: implemented
+
+[English](2026-09-08-package-index-link-discovery.md) | 中文
+
+## 问题
+
+在 `verify-md-links` 中,嵌套的组级与包级 glob 无法匹配 `packages/README.md` 及其中文对应文件。除非检查器也发现这些源文件,否则两份索引中的死链都不会进入校验。
+
+## 决策
+
+[`verify-md-links`](../../../../scripts/verify-md-links.ts) 在现有源文件发现范围中加入 `packages/*.md`。命令与测试共用同一个发现函数。符号链接去重与冻结 Agent Note 的排除仍由 [`repo-files`](../../../../scripts/repo-files.ts) 负责。
+
+## 考虑过的替代方案
+
+**仅修复个别链接。** 这会保留源文件发现遗漏,后续新增的索引死链仍可能通过同一检查。
+
+**另加包清单检查器。** 索引完整性与链接有效性是不同要求。前者需要支持当前表格格式与双语链接目标,不能替代对每个已发现源文件的链接检查。
+
+## 后果
+
+两份包索引中的相对死链会使现有文档命令失败。发现测试覆盖这些失败,保留包级指令文件与嵌套文档的检查范围,并排除冻结笔记。本检查验证链接,不要求索引枚举每个包,也不定义其他文档检查的源文件集合。

+ 57 - 41
AGENTS.md

@@ -13,50 +13,66 @@ Acknowledge [declared persistence-type changes](docs/cookbook/reviewing-persiste
 ## Repository layout
 
 ```
-vendor/      Vendored Cordis source — manifest + sync procedure in vendor/README.md
+vendor/      Vendored Cordis (vendor/README.md)
 packages/    @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
-  core/        product API spine: session, system-prompt, tools, agent, agent-loop
-  api/         Remote BFF assembly and Typert RPC gateway
-  typert/      type graph generator, loader, and runtime registry
-  llm/         LLM capability: Service Definition/Consumer + DeepSeek providers
-  shell/        bash capability: Service Definition + local/pwsh providers + shell Consumers
-  subprocess/  subprocess capability + local process-tree provider + shared Win32 library
-  ssh/         SSH connection + remote filesystem/subprocess/sandbox providers
-  terminal/         persistent sessions
-  fs/          filesystem capability + policy
-  lsp/         language-server capability
-  skill/       skill provider registry + local impl + catalog/loader tool
-  web/         web capability: Service Definition + search/fetch providers + tool Consumer
-  compaction/     compaction capability + basic provider
-  context/     request-context plugins
-  subagent/    subagent capability: Service Definition + providers + delegation Consumers
-  bundle/      installable dsh --profile patch-layer bundles
-  workflow/    workflow capability + worker-thread provider + tool Consumer
-  webhook/     webhook ingress
-  todo/        todo_write tool
-  plan/        plan mode as logged state
-  preset/      per-session agent composition from preset cordis.yml files
-  guard/       loop-hygiene + tool-timeout plugins
-  self-modification/  the agent inspects/mounts its own plugins
-  hooks/       Claude Code/Codex hook bridges + wire-protocol library
-  session/     durable session data: persistence, projection, titles, telemetry
-  identity/    anonymous identity
-  settings/    user-settings capability + file provider
-  credentials/ credential/authorization capabilities + env/.env provider
-  acp/         automation-only Agent Client Protocol server
-  interaction/ approval/interaction capabilities, permission, commands, ask-user
-  boot/        shared profile/application boot glue
-  sdk/         JSON-RPC protocol + TypeScript client/server
-  experimental/ pre-stable prototypes; private by default with explicit public exceptions
-  support/     dev/test infrastructure
-  util/        zero-dependency utilities
-python/      Python SDK/runtime (see python/README.md)
-native/      @deepseek-ai/node-addon-system source of record (see native/README.md)
+  core/                 agent/session API
+  api/                  remote BFF
+  typert/               type graphs
+  llm/                  model providers
+  shell/                command execution
+  subprocess/           child-process management
+  ssh/                  SSH execution providers
+  terminal/             persistent terminals
+  ptc-runtime/          PTC execution
+  sandbox/              process confinement
+  fs/                   filesystem access
+  lsp/                  language servers
+  skill/                skill loading
+  web/                  search/fetch tools
+  computer-use/         computer interaction
+  compaction/           context compaction
+  context/              request context
+  subagent/             delegated agents
+  jobs/                 background jobs
+  bundle/               profile bundles
+  workflow/             workflow execution
+  webhook/              webhook ingress
+  todo/                 todo_write tool
+  plan/                 logged planning
+  goal/                 session goals
+  schedule/             scheduled follow-ups
+  preset/               agent composition
+  guard/                loop/tool guards
+  extensions/           runtime self-modification
+  hooks/                Claude Code/Codex bridges
+  session/              durable sessions
+  session-query/        browsing/search/export
+  attachment/           binary attachments
+  spill/                output spill
+  storage/              non-session storage
+  workspace/            workspace entities
+  feedback/             human feedback
+  identity/             anonymous identity
+  settings/             user settings
+  credentials/          credentials/authorization
+  acp/                  automation-only ACP
+  interaction/          human interaction
+  boot/                 application boot
+  sdk/                  JSON-RPC SDK
+  host/                 GUI host
+  client/               GUI client
+  mcp/                  external tools
+  experimental/         pre-stable prototypes; private by default with explicit public exceptions
+  test-support/         test infrastructure
+  runtime-diagnostics/  runtime invariants
+  util/                 zero-dependency utilities
+python/      Python SDK/runtime (python/README.md)
+native/      @deepseek-ai/node-addon-system source (native/README.md)
 benchmarks/  performance gates
-.agents/     Agent workflows and Agent Notes (`notes/`)
-docs/        architecture, generated catalogs, postmortems, cookbook (see docs/AGENTS.md)
+.agents/     Agent workflows/notes
+docs/        Documentation (docs/AGENTS.md)
 scripts/     gates and generators
-website/     VitePress projection of selected bilingual docs/ sources
+website/     VitePress documentation projection
 ```
 
 Package groups: [packages/README.md](packages/README.md).

+ 2 - 2
packages/schedule/schedule/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/schedule/schedule/README.md
-README.md: 7a8b14fcf1e335aab2c9a0c717e38ae08c81eb6c
-README.zh.md: 10ac96cb6d569a0cdac2eebf8414f7e0ca058c52
+README.md: 962c343111849ef20fd085dbe8ba454e57a78f8b
+README.zh.md: 8913bc53d68ad2eb7406f1ce6780d69ec522b956

+ 1 - 1
packages/schedule/schedule/README.md

@@ -94,7 +94,7 @@ The package rests on one separation and three commitments:
 | [`src/projection.ts`](src/projection.ts) | Optional seed-aware Session projection and strict checkpoint schema |
 | [`src/client.ts`](src/client.ts) | Browser-safe type-only `ScheduleRecord` export |
 | [`src/transaction.ts`](src/transaction.ts) | Agent-scoped serialization for reads and durable mutations |
-| [`src/invariant.ts`](src/invariant.ts) | `./invariant` companion applying replay policy to existing logs and candidate events |
+| [`src/invariant.ts`](src/invariant.ts) | `schedule-invariant` companion at `./invariant`, applying replay policy to existing logs and candidate events |
 
 ### Durable state and replay
 

+ 1 - 1
packages/schedule/schedule/README.zh.md

@@ -94,7 +94,7 @@ Session projection 是可选能力。`ctx.sessionProjections` 存在时,插件
 | [`src/projection.ts`](src/projection.ts) | 可选的 seed-aware Session projection 与严格检查点 schema |
 | [`src/client.ts`](src/client.ts) | 浏览器安全的纯类型 `ScheduleRecord` 导出 |
 | [`src/transaction.ts`](src/transaction.ts) | 读取与持久变更的 agent 范围串行化 |
-| [`src/invariant.ts`](src/invariant.ts) | `./invariant` 配套模块,对现有日志与候选事件应用回放策略 |
+| [`src/invariant.ts`](src/invariant.ts) | 位于 `./invariant` 的 `schedule-invariant` 配套模块,对现有日志与候选事件应用回放策略 |
 
 ### 持久状态与回放
 

+ 1 - 1
packages/schedule/schedule/src/invariant.ts

@@ -11,7 +11,7 @@ import { foldScheduleEvents, ScheduleLogError } from './domain.ts'
 const PACKAGE_NAME = '@deepseek-ai/dsh-schedule'
 
 /** Cordis invariant-companion plugin name. */
-export const name = 'tool-schedule-invariant'
+export const name = 'schedule-invariant'
 /** Service required before reserving this package's invariant ownership. */
 export const inject = ['invariants']
 

+ 34 - 2
scripts/verify-md-links.spec.ts

@@ -1,5 +1,5 @@
 /**
- * Acceptance-path coverage for fragment validation in `verify-md-links`: a
+ * Source-discovery and fragment validation coverage for `verify-md-links`: a
  * `#fragment` onto a Markdown target — same-file anchors included — must name
  * a real heading slug or explicit `<a id>`, while non-Markdown fragments and
  * external targets stay out of scope.
@@ -9,7 +9,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
 import { tmpdir } from 'node:os'
 import { join } from 'node:path'
 import { afterEach, describe, expect, it } from 'vitest'
-import { anchorCache, documentAnchors, findViolations, githubSlug } from './verify-md-links.ts'
+import { anchorCache, documentAnchors, findViolations, githubSlug, markdownLinkSourcePaths } from './verify-md-links.ts'
 
 const roots: string[] = []
 afterEach(() => {
@@ -30,6 +30,38 @@ function violationsIn(root: string, rel: string): { url: string; reason: string
   return findViolations(join(root, rel), anchorCache(), root).map(({ url, reason }) => ({ url, reason }))
 }
 
+describe('Markdown source discovery', () => {
+  it('rejects broken links in both top-level package indexes', () => {
+    const root = layout({
+      'packages/README.md': '[removed group](removed/README.md)\n',
+      'packages/README.zh.md': '[removed group](removed/README.zh.md)\n',
+    })
+    const sources = markdownLinkSourcePaths(root)
+    const violations = sources.flatMap(file => findViolations(join(root, file), anchorCache(), root))
+    expect(violations.map(({ file, reason }) => ({ file, reason })).sort((a, b) => a.file.localeCompare(b.file)))
+      .toEqual([
+        { file: join('packages', 'README.md'), reason: 'target' },
+        { file: join('packages', 'README.zh.md'), reason: 'target' },
+      ])
+  })
+
+  it('checks package instructions and nested docs while excluding frozen notes', () => {
+    const root = layout({
+      'packages/AGENTS.md': '[missing](missing.md)\n',
+      'packages/core/README.md': '[missing](missing.md)\n',
+      'packages/core/probe/README.md': '[missing](missing.md)\n',
+      '.agents/notes/archived/process/frozen.md': '[historical](missing.md)\n',
+    })
+    const sources = markdownLinkSourcePaths(root)
+    const violations = sources.flatMap(file => findViolations(join(root, file), anchorCache(), root))
+    expect(violations.map(({ file }) => file).sort()).toEqual([
+      join('packages', 'AGENTS.md'),
+      join('packages', 'core', 'README.md'),
+      join('packages', 'core', 'probe', 'README.md'),
+    ])
+  })
+})
+
 describe('documentAnchors', () => {
   it('slugs rendered heading text, suffixes repeats, and reads explicit <a id> anchors', () => {
     const anchors = documentAnchors([

+ 14 - 4
scripts/verify-md-links.ts

@@ -21,13 +21,24 @@ const PATTERNS = [
   'README.zh.md',
   '.agents/notes/**/*.md',
   'docs/**/*.md',
+  'packages/*.md',
   'packages/*/*.md',
   'packages/*/*/*.md',
   'AGENTS.md',
-  'packages/AGENTS.md',
   '.agents/skills/**/*.md',
 ]
 
+/**
+ * Discover authored Markdown sources, deduplicating symlinks.
+ * Archived notes are excluded as sources, but links to them remain checked.
+ * @param scanRoot - absolute repository root; pass the same root to findViolations for matching diagnostics.
+ * @returns forward-slash source paths relative to scanRoot.
+ */
+export function markdownLinkSourcePaths(scanRoot: string = root): string[] {
+  return uniqueRepoFiles(scanRoot, PATTERNS, isArchivedAgentNotePath)
+    .map(file => relative(scanRoot, file.abs).replaceAll('\\', '/'))
+}
+
 /** A broken relative link: a missing target path or a missing anchor on it. */
 interface Violation {
   file: string
@@ -195,10 +206,9 @@ export function findViolations(
 }
 
 if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
-  // Archived notes remain valid link targets, but their historical outbound links are frozen.
-  const files = uniqueRepoFiles(root, PATTERNS, isArchivedAgentNotePath)
+  const files = markdownLinkSourcePaths(root)
   const anchorsOf = anchorCache()
-  const all = files.flatMap(file => findViolations(file.abs, anchorsOf))
+  const all = files.flatMap(file => findViolations(resolve(root, file), anchorsOf))
   const checked = files.length
 
   if (all.length === 0) {