docs(notes): archive low-value decision records

This commit is contained in:
Tianyi Cui
2026-07-26 23:06:00 +08:00
parent 7c038f275d
commit 37140bf823
281 changed files with 1033 additions and 707 deletions

View File

@@ -8,15 +8,18 @@ import { resolve, sep } from 'node:path'
export const agentNoteRoot = resolve(import.meta.dirname, '../.agents/notes')
/** The closed set of Agent Note lifecycles (top-level folders under .agents/notes/). */
const LIFECYCLES = ['proposed', 'implemented', 'rejected'] as const
/** The closed set of active Agent Note lifecycles (top-level folders under .agents/notes/). */
export const AGENT_NOTE_LIFECYCLES = ['proposed', 'implemented', 'rejected'] as const
/**
* The closed set of Agent Note classes (nested folder under each lifecycle). Adding a
* class is a deliberate act: extend this list AND the README's Classification
* section. The gate rejects any folder not listed here.
*/
const CLASSES = ['feature', 'bug-fix', 'simplification', 'architecture', 'process', 'testing'] as const
export const AGENT_NOTE_CLASSES = ['feature', 'bug-fix', 'simplification', 'architecture', 'process', 'testing'] as const
/** Historical implemented notes live outside the active lifecycle tree. */
export const AGENT_NOTE_ARCHIVE = 'archived'
/** Non-Agent Note Markdown allowed to sit directly at a lifecycle root. */
const ROOT_ALLOWLIST = new Set(['AGENTS.md', 'CLAUDE.md'])
@@ -45,11 +48,13 @@ export function walkAgentNoteTree(): { notes: AgentNote[]; errors: string[] } {
errors.push('structure: INDEX.md — centralized Agent Note indexes are forbidden; browse the lifecycle/class tree or search the repository')
continue
}
if (entry.isDirectory() && !(LIFECYCLES as readonly string[]).includes(entry.name)) {
errors.push(`structure: ${entry.name}/ — unknown lifecycle folder (allowed: ${LIFECYCLES.join(', ')})`)
if (entry.isDirectory()
&& entry.name !== AGENT_NOTE_ARCHIVE
&& !(AGENT_NOTE_LIFECYCLES as readonly string[]).includes(entry.name)) {
errors.push(`structure: ${entry.name}/ — unknown lifecycle folder (allowed: ${AGENT_NOTE_LIFECYCLES.join(', ')}, plus ${AGENT_NOTE_ARCHIVE}/)`)
}
}
for (const lifecycle of LIFECYCLES) {
for (const lifecycle of AGENT_NOTE_LIFECYCLES) {
for (const match of globSync(`${lifecycle}/**/*.md`, { cwd: agentNoteRoot }).map(path => path.split(sep).join('/')).sort()) {
const segs = match.split('/')
// Allowlisted file directly at the lifecycle root (e.g. implemented/AGENTS.md).
@@ -63,8 +68,8 @@ export function walkAgentNoteTree(): { notes: AgentNote[]; errors: string[] } {
errors.push(`structure: ${match} — expected {lifecycle}/{class}/file.md (got depth ${segs.length})`)
continue
}
if (!(CLASSES as readonly string[]).includes(cls)) {
errors.push(`structure: ${match} — unknown class folder "${cls}" (allowed: ${CLASSES.join(', ')})`)
if (!(AGENT_NOTE_CLASSES as readonly string[]).includes(cls)) {
errors.push(`structure: ${match} — unknown class folder "${cls}" (allowed: ${AGENT_NOTE_CLASSES.join(', ')})`)
continue
}
if (!/^\d{4}-\d{2}-\d{2}-.+\.md$/.test(base)) {

View File

@@ -0,0 +1,64 @@
import { describe, expect, it } from 'vitest'
import {
extendArchiveManifest,
gitBlobHash,
parseArchiveManifest,
renderArchiveManifest,
validateArchiveArtifacts,
type ArchiveManifest,
} from './archived-agent-notes.ts'
function fixture(): Map<string, Buffer> {
const base = '2026-07-26-example'
const source = Buffer.from(`# Agent Note: Example\n\nStatus: implemented\nArchived: 2026-07-26\n\nEnglish | [中文](${base}.zh.md)\n\n## Problem\n\nExample.\n`)
const zh = Buffer.from(`# Agent Note: 示例\n\nStatus: implemented\nArchived: 2026-07-26\n\n[English](${base}.md) | 中文\n\n## 问题\n\n示例。\n`)
const meta = Buffer.from(`${base}.md: ${gitBlobHash(source)}\n${base}.zh.md: ${gitBlobHash(zh)}\n`)
return new Map([
[`process/${base}.md`, source],
[`process/${base}.zh.md`, zh],
[`process/${base}.i18n.yaml`, meta],
])
}
describe('archived Agent Notes', () => {
it('accepts one complete implemented triplet with matching archive metadata', () => {
expect(validateArchiveArtifacts(fixture())).toEqual([])
})
it('rejects incomplete triplets and invalid archive headers', () => {
const artifacts = fixture()
artifacts.delete('process/2026-07-26-example.i18n.yaml')
artifacts.set(
'process/2026-07-26-example.md',
Buffer.from('# Agent Note: Example\n\nStatus: proposed\nArchived: yesterday\n'),
)
expect(validateArchiveArtifacts(artifacts).join('\n')).toMatch(/incomplete archived triplet/)
})
it('extends the manifest without permitting a sealed change or removal', () => {
const artifacts = fixture()
const empty: ArchiveManifest = { version: 1, files: {} }
const first = extendArchiveManifest(empty, artifacts)
expect(first.errors).toEqual([])
expect(first.added).toHaveLength(3)
const sealed: ArchiveManifest = { version: 1, files: first.files }
const changed = new Map(artifacts)
changed.set('process/2026-07-26-example.md', Buffer.from('changed'))
expect(extendArchiveManifest(sealed, changed).errors).toEqual([
'process/2026-07-26-example.md: sealed content hash changed',
])
changed.delete('process/2026-07-26-example.zh.md')
expect(extendArchiveManifest(sealed, changed).errors).toContain(
'process/2026-07-26-example.zh.md: sealed artifact is missing',
)
})
it('round-trips the deterministic manifest schema', () => {
const content = renderArchiveManifest({ 'process/z.md': `sha256:${'a'.repeat(64)}` })
expect(parseArchiveManifest(content)).toEqual({
version: 1,
files: { 'process/z.md': `sha256:${'a'.repeat(64)}` },
})
})
})

View File

@@ -0,0 +1,175 @@
/** Pure archive-format, triplet, and immutable-manifest helpers. */
import { createHash } from 'node:crypto'
import { basename } from 'node:path'
import { AGENT_NOTE_CLASSES } from './agent-note-tree.ts'
/** Versioned shape of the frozen-content manifest. */
export interface ArchiveManifest {
version: 1
files: Readonly<Record<string, string>>
}
/** Hash one archived artifact independently of the repository's Git object format. */
export function archiveContentHash(content: Buffer): string {
return `sha256:${createHash('sha256').update(content).digest('hex')}`
}
/** Compute the SHA-1 Git blob id used by bilingual consistency sidecars. */
export function gitBlobHash(content: Buffer): string {
const hash = createHash('sha1')
hash.update(`blob ${content.byteLength}\0`)
hash.update(content)
return hash.digest('hex')
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
/** Parse the archive manifest and reject fields or hashes outside its closed schema. */
export function parseArchiveManifest(content: string): ArchiveManifest {
const value: unknown = JSON.parse(content)
if (!isRecord(value)) throw new Error('expected a JSON object')
const fields = Object.keys(value).sort()
if (fields.join(',') !== 'files,version') throw new Error('expected exactly the fields `version` and `files`')
if (value.version !== 1) throw new Error('unsupported manifest version (expected 1)')
if (!isRecord(value.files)) throw new Error('`files` must be an object')
const files: Record<string, string> = {}
for (const [path, hash] of Object.entries(value.files)) {
if (typeof hash !== 'string' || !/^sha256:[0-9a-f]{64}$/.test(hash)) {
throw new Error(`invalid content hash for ${path}`)
}
files[path] = hash
}
return { version: 1, files }
}
/** Render the archive manifest with deterministic path ordering. */
export function renderArchiveManifest(files: Readonly<Record<string, string>>): string {
return `${JSON.stringify({
version: 1,
files: Object.fromEntries(Object.entries(files).sort(([left], [right]) => left.localeCompare(right))),
}, null, 2)}\n`
}
function validDate(value: string): boolean {
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value)
if (match === null) return false
const year = Number(match[1])
const month = Number(match[2])
const day = Number(match[3])
const date = new Date(Date.UTC(year, month - 1, day))
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day
}
interface Triplet {
source?: Buffer
zh?: Buffer
meta?: Buffer
}
function pairMeta(content: string): Map<string, string> | undefined {
const entries = new Map<string, string>()
for (const line of content.split('\n')) {
if (line === '' || line.startsWith('#')) continue
const match = /^([^:#]+\.md): ([0-9a-f]{40})$/.exec(line)
if (match?.[1] === undefined || match[2] === undefined) return undefined
entries.set(match[1], match[2])
}
return entries
}
function validateHeader(path: string, content: Buffer, sourceBase: string, chinese: boolean): string[] {
const errors: string[] = []
const lines = content.toString('utf8').split('\n')
if (!/^# Agent Note: \S/.test(lines[0] ?? '')) errors.push(`${path}: line 1 must be \`# Agent Note: <title>\``)
if (lines[1] !== '') errors.push(`${path}: line 2 must be blank`)
if (lines[2] !== 'Status: implemented') errors.push(`${path}: line 3 must be \`Status: implemented\``)
const archived = /^Archived: (\d{4}-\d{2}-\d{2})$/.exec(lines[3] ?? '')?.[1]
if (archived === undefined || !validDate(archived)) {
errors.push(`${path}: line 4 must be \`Archived: YYYY-MM-DD\` with a valid date`)
} else if (archived < sourceBase.slice(0, 10)) {
errors.push(`${path}: archive date ${archived} predates the note filename`)
}
if (lines[4] !== '') errors.push(`${path}: line 5 must be blank`)
const switcher = chinese
? `[English](${sourceBase}.md) | 中文`
: `English | [中文](${sourceBase}.zh.md)`
if (lines[5] !== switcher) errors.push(`${path}: line 6 must be ${JSON.stringify(switcher)}`)
return errors
}
/** Validate the closed kind tree, implemented/archive headers, and complete bilingual triplets. */
export function validateArchiveArtifacts(artifacts: ReadonlyMap<string, Buffer>): string[] {
const errors: string[] = []
const triplets = new Map<string, Triplet>()
for (const [path, content] of artifacts) {
const match = /^([^/]+)\/(\d{4}-\d{2}-\d{2}-.+?)(\.zh\.md|\.i18n\.yaml|\.md)$/.exec(path)
if (match?.[1] === undefined || match[2] === undefined || match[3] === undefined) {
errors.push(`${path}: expected {kind}/yyyy-mm-dd-topic.{md,zh.md,i18n.yaml}`)
continue
}
if (!(AGENT_NOTE_CLASSES as readonly string[]).includes(match[1])) {
errors.push(`${path}: unknown Agent Note kind ${JSON.stringify(match[1])}`)
continue
}
const key = `${match[1]}/${match[2]}`
const triplet = triplets.get(key) ?? {}
if (match[3] === '.md') triplet.source = content
else if (match[3] === '.zh.md') triplet.zh = content
else triplet.meta = content
triplets.set(key, triplet)
}
for (const [key, triplet] of [...triplets].sort(([left], [right]) => left.localeCompare(right))) {
const sourcePath = `${key}.md`
const zhPath = `${key}.zh.md`
const metaPath = `${key}.i18n.yaml`
const missing = [
triplet.source === undefined ? sourcePath : undefined,
triplet.zh === undefined ? zhPath : undefined,
triplet.meta === undefined ? metaPath : undefined,
].filter((path): path is string => path !== undefined)
if (missing.length > 0) {
errors.push(`${key}: incomplete archived triplet; missing ${missing.join(', ')}`)
continue
}
const sourceBase = basename(key)
errors.push(...validateHeader(sourcePath, triplet.source, sourceBase, false))
errors.push(...validateHeader(zhPath, triplet.zh, sourceBase, true))
const sourceDate = /^Archived: (\d{4}-\d{2}-\d{2})$/m.exec(triplet.source.toString('utf8'))?.[1]
const zhDate = /^Archived: (\d{4}-\d{2}-\d{2})$/m.exec(triplet.zh.toString('utf8'))?.[1]
if (sourceDate !== undefined && zhDate !== undefined && sourceDate !== zhDate) {
errors.push(`${key}: English and Chinese archive dates differ (${sourceDate} vs ${zhDate})`)
}
const meta = pairMeta(triplet.meta.toString('utf8'))
if (meta === undefined || meta.size !== 2
|| meta.get(`${sourceBase}.md`) !== gitBlobHash(triplet.source)
|| meta.get(`${sourceBase}.zh.md`) !== gitBlobHash(triplet.zh)) {
errors.push(`${metaPath}: consistency record must contain the current Git blob hashes of both archived sides`)
}
}
return errors
}
/** Preserve every sealed path/hash and append hashes for newly archived artifacts. */
export function extendArchiveManifest(
existing: ArchiveManifest,
artifacts: ReadonlyMap<string, Buffer>,
): { files: Record<string, string>; added: string[]; errors: string[] } {
const errors: string[] = []
const files: Record<string, string> = { ...existing.files }
for (const [path, expected] of Object.entries(existing.files)) {
const content = artifacts.get(path)
if (content === undefined) errors.push(`${path}: sealed artifact is missing`)
else if (archiveContentHash(content) !== expected) errors.push(`${path}: sealed content hash changed`)
}
const added: string[] = []
for (const [path, content] of [...artifacts].sort(([left], [right]) => left.localeCompare(right))) {
if (files[path] !== undefined) continue
files[path] = archiveContentHash(content)
added.push(path)
}
return { files, added, errors }
}

View File

@@ -12,6 +12,7 @@ import ts from 'typescript'
import { builtDeclarationPath } from './doc-typecheck-paths.ts'
import { extractFences } from './md-fences.ts'
import { partitionPairedMarkdownDerivatives } from './paired-markdown-derivatives.ts'
import { isArchivedAgentNotePath } from './repo-files.ts'
const root = resolve(import.meta.dirname, '..')
@@ -204,7 +205,9 @@ const markdownGlobs = ['README.md', '.agents/notes/**/*.md', 'docs/**/*.md', 'pa
const files: string[] = []
for (const pattern of markdownGlobs) {
for (const match of globSync(pattern, { cwd: root })) files.push(resolve(root, match))
for (const match of globSync(pattern, { cwd: root })) {
if (!isArchivedAgentNotePath(match)) files.push(resolve(root, match))
}
}
files.sort()

View File

@@ -21,6 +21,11 @@ export interface ReferenceViolation {
ref: string
}
/** Whether a repository path is frozen Agent Note history, not evolving source prose. */
export function isArchivedAgentNotePath(path: string): boolean {
return path.startsWith('.agents/notes/archived/')
}
/**
* Expand repository-relative globs and deduplicate symlinked files.
* @param root - absolute repository root.

View File

@@ -454,6 +454,7 @@ function docSyncLeafGates(options: {
pnpmScript('mermaid', 'verify-mermaid'),
pnpmScript('agent-note-classification', 'verify-agent-note-classification', { label: 'agent note classification' }),
pnpmScript('agent-note-format', 'verify-agent-note-format', { label: 'agent note format' }),
pnpmScript('archived-agent-notes', 'verify-archived-agent-notes', { label: 'archived agent notes' }),
pnpmScript('type-equivalence', 'verify-type-equiv', { label: 'type equivalence' }),
pnpmScript('translation-prompt', 'verify-translation-prompt', { label: 'translation prompt' }),
pnpmScript('translation-pairing', 'verify-translation-pairing', { label: 'translation pairing' }),

View File

@@ -34,6 +34,7 @@ const NON_SOURCE_DIRECTORIES = new Set([
/** Glob traversal exclusions corresponding to the non-source path predicate. */
export const TRANSLATION_SCOPE_GLOB_EXCLUDES = [
'.agents/notes/archived/**',
'**/node_modules/**',
'**/lib/**',
'**/.pnpm-store/**',
@@ -67,7 +68,8 @@ function isTranslationSourceExcluded(file: string): boolean {
/** Whether one discovered Markdown or sidecar path belongs to the bilingual source corpus. */
export function isTranslationScopeFile(file: string): boolean {
return !isTranslationSourceExcluded(file) && (README_ARTIFACT.test(file)
return !file.startsWith('.agents/notes/archived/')
&& !isTranslationSourceExcluded(file) && (README_ARTIFACT.test(file)
|| file.startsWith('.agents/notes/')
|| file.startsWith('docs/')
|| file.startsWith('python/'))

View File

@@ -0,0 +1,88 @@
/** Verify and append-seal the frozen Agent Note archive. */
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { AGENT_NOTE_CLASSES, agentNoteRoot } from './agent-note-tree.ts'
import {
extendArchiveManifest,
parseArchiveManifest,
renderArchiveManifest,
validateArchiveArtifacts,
type ArchiveManifest,
} from './archived-agent-notes.ts'
const args = process.argv.slice(2)
const writeMode = args.length === 1 && args[0] === '--write'
if (args.length > 0 && !writeMode) {
console.error('verify-archived-agent-notes: usage: tsx scripts/verify-archived-agent-notes.ts [--write]')
process.exit(1)
}
const archiveRoot = resolve(agentNoteRoot, 'archived')
const manifestPath = resolve(archiveRoot, 'manifest.json')
const errors: string[] = []
const allowedRootFiles = new Set(['AGENTS.md', 'manifest.json'])
const kinds = new Set<string>()
if (!existsSync(resolve(archiveRoot, 'AGENTS.md'))) errors.push('archived/AGENTS.md is required')
const artifacts = new Map<string, Buffer>()
for (const entry of readdirSync(archiveRoot, { withFileTypes: true })) {
if (entry.isFile()) {
if (!allowedRootFiles.has(entry.name)) errors.push(`archived/${entry.name}: unexpected root file`)
continue
}
if (!entry.isDirectory()) {
errors.push(`archived/${entry.name}: only regular files and kind directories are allowed`)
continue
}
if (!(AGENT_NOTE_CLASSES as readonly string[]).includes(entry.name)) {
errors.push(`archived/${entry.name}/: unknown Agent Note kind`)
continue
}
kinds.add(entry.name)
for (const child of readdirSync(resolve(archiveRoot, entry.name), { withFileTypes: true })) {
const rel = `${entry.name}/${child.name}`
if (!child.isFile()) {
errors.push(`${rel}: archived kind directories contain regular files only`)
continue
}
artifacts.set(rel, readFileSync(resolve(archiveRoot, rel)))
}
}
for (const kind of AGENT_NOTE_CLASSES) {
if (!kinds.has(kind)) errors.push(`archived/${kind}/: required kind directory is missing`)
}
errors.push(...validateArchiveArtifacts(artifacts))
let manifest: ArchiveManifest = { version: 1, files: {} }
if (existsSync(manifestPath)) {
try {
manifest = parseArchiveManifest(readFileSync(manifestPath, 'utf8'))
} catch (error: unknown) {
errors.push(`archived/manifest.json: ${error instanceof Error ? error.message : String(error)}`)
}
} else if (!writeMode) {
errors.push('archived/manifest.json is required; seal new artifacts with `pnpm run verify-archived-agent-notes --write`')
}
const extended = extendArchiveManifest(manifest, artifacts)
errors.push(...extended.errors)
if (!writeMode) {
for (const path of extended.added) errors.push(`${path}: archived artifact is not sealed in manifest.json`)
}
if (errors.length > 0) {
console.error('verify-archived-agent-notes: archive contract violated:')
for (const error of errors) console.error(` ${error}`)
process.exit(1)
}
if (writeMode) {
const rendered = renderArchiveManifest(extended.files)
if (!existsSync(manifestPath) || readFileSync(manifestPath, 'utf8') !== rendered) {
writeFileSync(manifestPath, rendered)
}
console.log(`verify-archived-agent-notes: sealed ${extended.added.length} new artifact(s); existing seals unchanged.`)
} else {
console.log(`verify-archived-agent-notes: ${artifacts.size} frozen artifact(s) checked across ${kinds.size} kind(s).`)
}

View File

@@ -9,7 +9,7 @@ import { existsSync, readFileSync } from 'node:fs'
import { dirname, relative, resolve } from 'node:path'
import type { Nodes } from 'mdast'
import { parseMarkdown, visitMarkdown } from './markdown.ts'
import { uniqueRepoFiles } from './repo-files.ts'
import { isArchivedAgentNotePath, uniqueRepoFiles } from './repo-files.ts'
const root = resolve(import.meta.dirname, '..')
@@ -95,7 +95,8 @@ function findViolations(absPath: string): Violation[] {
return out
}
const files = uniqueRepoFiles(root, PATTERNS)
// Archived notes remain valid link targets, but their historical outbound links are frozen.
const files = uniqueRepoFiles(root, PATTERNS, isArchivedAgentNotePath)
const all = files.flatMap(file => findViolations(file.abs))
const checked = files.length

View File

@@ -10,7 +10,7 @@ import { readFileSync } from 'node:fs'
import { relative, resolve } from 'node:path'
import type { Nodes } from 'mdast'
import { parseMarkdown, visitMarkdown } from './markdown.ts'
import { uniqueRepoFiles } from './repo-files.ts'
import { isArchivedAgentNotePath, uniqueRepoFiles } from './repo-files.ts'
const root = resolve(import.meta.dirname, '..')
@@ -69,7 +69,7 @@ function findViolations(absPath: string): Violation[] {
return out
}
const files = uniqueRepoFiles(root, PATTERNS)
const files = uniqueRepoFiles(root, PATTERNS, isArchivedAgentNotePath)
const all = files.flatMap(file => findViolations(file.abs))
const checked = files.length

View File

@@ -11,6 +11,7 @@ import { gfmFromMarkdown } from 'mdast-util-gfm'
import { gfm } from 'micromark-extension-gfm'
import { JSDOM } from 'jsdom'
import type { Nodes } from 'mdast'
import { isArchivedAgentNotePath } from './repo-files.ts'
const root = resolve(import.meta.dirname, '..')
@@ -65,6 +66,7 @@ const seen = new Set<string>()
let checkedFiles = 0
for (const pattern of PATTERNS) {
for (const match of globSync(pattern, { cwd: root })) {
if (isArchivedAgentNotePath(match)) continue
const real = realpathSync(resolve(root, match))
if (seen.has(real)) continue
seen.add(real)

View File

@@ -7,7 +7,12 @@
import { existsSync, readdirSync } from 'node:fs'
import { resolve } from 'node:path'
import { findReferenceViolations, uniqueRepoFiles, type ReferenceViolation as Violation } from './repo-files.ts'
import {
findReferenceViolations,
isArchivedAgentNotePath,
uniqueRepoFiles,
type ReferenceViolation as Violation,
} from './repo-files.ts'
const root = resolve(import.meta.dirname, '..')
@@ -26,7 +31,7 @@ const PATTERNS = [
/** Paths excluded from the scan: built output and vendored upstream source. */
const isExcluded = (p: string): boolean =>
p.includes('/lib/') || p.endsWith('.d.ts') || p.startsWith('vendor/')
isArchivedAgentNotePath(p) || p.includes('/lib/') || p.endsWith('.d.ts') || p.startsWith('vendor/')
/**
* Directory names of every real package, `packages/<group>/<pkg>`. A broken

View File

@@ -12,6 +12,7 @@ import { globSync, readFileSync, existsSync } from 'node:fs'
import { resolve, sep } from 'node:path'
import ts from 'typescript'
import { partitionPairedMarkdownDerivatives } from './paired-markdown-derivatives.ts'
import { isArchivedAgentNotePath } from './repo-files.ts'
const root = resolve(import.meta.dirname, '..')
@@ -223,7 +224,10 @@ const keyOf = (x: { doc: string; symbol: string; projection?: 'public-api' }): s
// as an orphan rather than silently skipped.
const docSet = new Set<string>()
for (const pattern of MARKDOWN_GLOBS) {
for (const match of globSync(pattern, { cwd: root })) docSet.add(match.split(sep).join('/'))
for (const match of globSync(pattern, { cwd: root })) {
const normalized = match.split(sep).join('/')
if (!isArchivedAgentNotePath(normalized)) docSet.add(normalized)
}
}
const extractedBlocks: EquivBlock[] = [...docSet].sort().flatMap(extractEquivBlocks)
const { primary: blocks, derivatives } = partitionPairedMarkdownDerivatives(