1. 摘要

本书定义 inkrail runtime 的软件架构、模块边界、公共库 API、CLI 消费者关系、上游依赖适配器、状态存储、查询执行管线、输出投影、错误传播、测试分层、文档同步和发布门禁。

本书以 inkrail CLI 设计书 的对象契约为输入,不改写其需求边界;本书为后续 implementation WBS 提供工程基线。

版本说明

本书属于 inkrail 设计书架。v0.1.0 定义实现前的软件架构与工程质量基线。

本书使用对象语言描述当前架构基线。实现批次、审核记录和状态台账属于后续 implementation WBS,不进入本书核心定义。

前言

本书位于需求契约之后、实现分解之前。10 号书 定义 inkrail 应当成立的对象契约;本书定义这些契约在软件工程中由哪些模块、公共 API、服务、适配器、状态对象、运行流和质量门禁承载。

本书依赖四类输入。查询动作来源于 08 号 RDF 投影查询工作流。标准书架生成与本地 runtime 来源于 create-asciidoc-multi-book-workspace。Book-entry parsing、source coordinate recovery、RDF12 TTL 和 RDF12 JSON-LD 投影来源于 asciidoc-abundant-tree。本地 SPARQL 执行由 Oxigraph adapter 承担。

本书不定义实现批次、提交顺序或工作包状态。后续 implementation WBS 可以引用本书拆分工作包;工作包不得用执行顺序改写本书的架构对象。

本书使用结构化写作约定。part 目录使用源文件编排号;关键标题使用稳定 ID、role 和受控 xref 关系谓词,以便后续通过 inkrail 查询本书自身的架构对象和依赖关系。

第一部:架构表面

本部用一次 project mode 内置查询展示 inkrail runtime 的最小完整架构切片,并定义本书的设计输入和源文件编排规则。

2. 架构切片

本章用一次 project mode 内置查询展示 inkrail runtime 的最小完整架构切片。该切片从公共库调用进入,经由项目记忆、投影新鲜度、查询执行和输出投影,返回结构化查询结果。

const result = await queryProjectBook({
  cwd,
  book: "10-inkrail-cli-design",
  action: builtInActions.outline({ levels: 2 }),
});

该调用表面属于 public API。CLI 命令、脚本消费者、编辑器插件、CI 检查器和 agent 工具应消费同一组 public API 对象,而不是解析 CLI pretty 输出。

public API caller
  -> project context resolver
  -> config loader
  -> book resolver
  -> freshness service
  -> asciidoc-abundant-tree adapter
  -> projection state store
  -> Oxigraph adapter
  -> query result model
  -> consumer projection

该路径中的节点承担不同身份。Project context resolver 解析 .inkrail/config.toml 所在项目。Config loader 读取并规范化项目记忆。Book resolver 选择目标 book 并推导标准 profile 路径。Freshness service 判断 RDF12 TTL 是否代表当前 source set。asciidoc-abundant-tree adapter 生成 book-entry RDF12 投影。Projection state store 保存上次成功投影的 fingerprint。Oxigraph adapter 加载 TTL 并执行 SPARQL。Query result model 保存结构化 RDF term。Consumer projection 把同一结果投影为 CLI JSON、CLI pretty 或其它下游表面。

CLI 使用同一个架构切片:

inkrail outline --book 10-inkrail-cli-design --levels 2 --json

CLI command handler 只负责参数解析、调用 public API、投影 stdout/stderr 和设置 exit code。CLI command handler 不拥有查询语义、项目选择语义、新鲜度语义或 RDF term 语义。

成功的 JSON 投影保持 10 号书输出表面 定义的 envelope:

{
  "ok": true,
  "data": {
    "mode": "project",
    "book": {
      "id": "10-inkrail-cli-design",
      "bookshelf": "docs"
    },
    "projection": {
      "format": "rdf12",
      "freshness": "fresh"
    },
    "query": {
      "kind": "select",
      "variables": ["level", "headline", "path", "headingLine"]
    },
    "rows": []
  }
}

该 JSON 是 public API result 的消费者投影。公共库内部的 result model 可以包含更多运行证据,例如 refresh events、state path、projector identity 和 elapsed time;CLI JSON 只公开契约允许消费者依赖的字段。

本书后续章节分别定义该切片中的对象身份、模块边界、数据模型、状态模型、运行流、适配器和质量门禁。后续 implementation WBS 以这些架构对象为拆分坐标。

3. 设计输入

本书的设计输入分为需求契约、查询语句来源、上游 runtime、上游 projection、查询执行器和工程骨架。

输入 对象 进入本书的位置

需求契约

10 号书:inkrail CLI 设计书

人工制品身份、项目记忆、投影新鲜度、查询模式、输出表面、错误语义和测试合同。

查询语句来源

08 号书:RDF 投影查询工作流

内置查询动作 registry 的 SPARQL 模板、变量、排序和 source coordinate 语义。

标准书架生成与 runtime

create-asciidoc-multi-book-workspace

书架初始化 adapter、标准书架固定输出路径、本地 buildcheckclean script 调度。

Book-entry parser 与 RDF12 projector

asciidoc-abundant-tree

Book-entry source set、origin source coordinate、RDF12 TTL 和 RDF12 JSON-LD 投影。

本地 SPARQL 执行器

Oxigraph

TTL load、SPARQL query、RDF/JS term 读取和 triple term 结果结构化。

工程骨架

Rootward TypeScript template

TypeScript package、CLI runner、测试入口、文件系统抽象和输出投影模式。

需求契约提供目标对象。查询语句来源提供内置动作的 SPARQL 语义。上游 runtime 和 projection 包提供可复用能力。Oxigraph 提供本地查询执行能力。工程骨架提供初始 TypeScript 承载结构。

这些输入不具有相同层位。10 号书和 08 号书是书籍化 body object。两个 npm 包和 Oxigraph 是运行时依赖。Rootward TypeScript template 是工程起点,不是 inkrail 的业务语义来源。

本书只在架构对象需要时引用输入。引用必须说明关系:rel=depends-on 表示当前定义依赖目标对象;rel=implements-contract 表示当前架构对象承载目标需求契约;rel=sources-query-from 表示查询动作来源于目标查询语句;rel=uses-adapter 表示运行流使用目标 adapter;rel=verifies 表示测试对象验证目标契约。

上游依赖能力必须由 adapter contract 和测试锁定。实现不得把未被公开导出的上游内部模块扩散到 domain core。实现不得用工程模板的 sample scanner 语义污染 inkrail 的 book、bookshelf、projection 和 query 语义。

4. 源文件编排号

本书使用源文件编排号保持结构稳定。Part 目录使用 100-200-300-。Chapter 文件在所属 part 内使用 010-020-030-

parts/100-architecture-surface/
parts/200-identity-and-boundary/
parts/300-package-and-module-structure/

010-architecture-slice.adoc
020-design-inputs.adoc
030-source-order-notation.adoc

中间插入新主题时,使用相邻编号之间的空位。新增主题不得通过重命名既有文件制造无意义 diff。

010-architecture-slice.adoc
015-architecture-slice-constraints.adoc
020-design-inputs.adoc

标题文本保留对象语义。长期引用的标题必须使用显式 ID。架构章节可以使用 role 标明标题身份。

id: oxigraph-adapter
role: adapter-contract
owner: query-runtime
package: oxigraph
heading: Oxigraph 适配器

上例使用字段化写法描述标题元数据,避免示例锚点或示例标题进入本书目录与 xref 图。实际章节使用 AsciiDoc attribute list 和 heading line。

本书采用以下 role:

architecture-object

架构对象定义。

module-contract

模块职责契约。

api-contract

公共 API 契约。

adapter-contract

外部依赖适配器契约。

data-model

数据模型。

runtime-flow

运行流。

quality-gate

工程质量门禁。

test-contract

测试契约。

boundary-rule

边界规则。

traceability

需求追踪表。

本书采用以下 xref relation predicate:

depends-on

当前章节的判断、规则或契约依赖目标章节。

implements-contract

当前架构对象承载目标需求契约。

sources-query-from

当前查询 registry 或内置动作来源于目标查询语句。

uses-adapter

当前运行流使用目标 adapter。

projects-to

当前对象投影为目标输出表面。

verifies

当前测试契约验证目标对象。

constrains

当前章节约束目标对象合法范围。

Role 描述标题身份。Relation predicate 描述章节之间的边。Named attributes 保存本书架构表面的附加字段。本书第一版使用 ownerpackagesurface 三个附加字段;新增字段必须先在本章登记。

第二部:身份与边界

本部定义本书的 architecture baseline 身份、消费者角色、模块 role 和架构边界。

5. 架构基线身份

本书是 inkrail runtime 的架构基线。它定义实现前必须成立的软件结构、公共契约承载位置、模块职责、依赖方向、运行数据流和工程质量门禁。

架构基线的上游输入是 10 号书需求契约。10 号书定义 inkrail 应当对消费者承诺什么;本书定义这些承诺由哪些工程对象承担。

架构基线的下游消费者是 implementation WBS。Implementation WBS 可以引用本书拆分工作包、安排提交顺序和记录审核证据。Implementation WBS 不得用执行阶段、批次编号或局部实现便利改写本书定义的对象边界。

本书属于 current body。它描述当前可依赖的架构事实,不记录实现过程状态。工作包状态、允许失败集合、审核打回、提交 hash 和关闭记录属于后续治理材料。

本书的完成声明依赖三类可检查事实:

  • 每个 10 号书关键契约都有架构承载对象。

  • 每个架构承载对象都有模块位置、数据输入、输出表面、失败面和测试位置。

  • 每个外部依赖都通过 adapter contract 进入 runtime,不穿透 domain core。

缺少这些事实时,本书不能作为 implementation WBS 的工程基线。

6. 消费者角色

inkrail runtime 面向多个消费者。消费者角色决定公共表面,不决定内部模块身份。

Library consumer

直接 import inkrail public API 的 TypeScript/JavaScript 调用方。它依赖结构化结果、结构化错误、RDF term JSON 和可测试函数边界。

CLI consumer

通过 inkrail 命令读取 pretty 或 JSON 输出的终端、脚本和人工作者。它依赖 stdout/stderr 分离、exit code、错误码和稳定命令参数。

Author consumer

使用查询结果回到 source file 的书稿作者。它依赖 origin source coordinate、heading line、line span、column、raw 和刷新后的当前事实。

Automation consumer

在 CI、检查脚本或 agent 工具中调用 inkrail 的机器消费者。它依赖 JSON envelope、稳定错误码和可重复查询语义。

Documentation consumer

阅读 README、用户指南和设计书的维护者。它依赖当前对象语言、source coordinate 说明、边界说明和测试矩阵。

Library consumer 是核心消费者。CLI consumer 通过 CLI adapter 消费 public API。Automation consumer 可以直接消费 public API,也可以消费 CLI JSON;CLI pretty 不属于机器契约。

消费者不能倒置对象层位。CLI command surface 不定义 domain model。README 不定义内部状态格式。测试 fixture 不定义公共 API。Public API、状态模型、运行流和输出投影由本书定义。

同一事实可以有多个投影。Project mode query result 可以投影为 public API result、CLI JSON、CLI pretty、测试观察对象和文档示例。投影之间必须回到同一 result model;实现不得让不同消费者各自计算查询语义。

7. 模块身份

inkrail 源码模块按职责分层。模块 role 描述模块在 runtime 中承担的身份。

domain core

定义纯对象、规则、类型和可确定函数。Domain core 不读取进程参数,不写 stdout,不执行外部命令,不 import 上游 parser 内部模块。

application service

组合 domain core 与 adapters,形成可由 public API 和 CLI 共同消费的用例。Application service 可以读取文件系统抽象,可以调用 adapters,可以返回结构化 result。

adapter

封装外部能力或运行环境。Adapter 负责把上游 package、文件系统、进程、Oxigraph 或 package script 转换为 inkrail 内部契约。

command handler

把 CLI 参数映射为 application service 输入。Command handler 不持有 domain 语义,不直接构造 RDF term,不直接判断 projection freshness。

output projector

把结构化 result 投影为 CLI JSON、pretty table、stderr diagnostic 或测试观察表面。Output projector 不执行查询。

test support

提供 fixture、fake adapter 和测试辅助对象。Test support 不进入发布公共 API。

模块只能承担与自身 role 相符的职责。一个文件可以包含多个小函数,但不能跨越 role 边界混合进程输入、domain rule、外部 adapter 和输出投影。

Role 边界服务可替换性。Oxigraph adapter 可以替换查询执行器而不改变 public API。Workspace initializer adapter 可以调整上游集成方式而不改变 project config model。CLI 可以替换 commander 解析器而不改变 application service。

Role 边界也服务测试。Domain core 通过纯单元测试锁定。Application service 通过 fake adapter 和 fixture 测试锁定。Adapter 通过真实依赖 contract test 锁定。Command handler 和 output projector 通过 CLI surface test 锁定。

8. 边界与非目标

本书的架构边界继承 10 号书对象边界。架构实现不得扩大 inkrail 的人工制品边界。

inkrail 不实现 AsciiDoc parser。Parsing、book-entry input construction、include resolution、origin source coordinate recovery、RDF12 TTL 和 RDF12 JSON-LD projection 由 asciidoc-abundant-tree adapter 调用上游 public API 完成。

inkrail 不实现标准书架模板。标准书架内容、七本样本书、tools/adoc-books.mjs、本地 buildcheckclean runtime 来源于 create-asciidoc-multi-book-workspaceinkrail 只提供 initializer adapter、项目登记和错误映射。

inkrail 不把 CLI stdout 作为核心契约。Pretty output 是 CLI consumer 投影。机器消费者应消费 public API result 或 CLI JSON envelope。

inkrail 不暴露 Oxigraph store 作为 public API。Oxigraph 是 query adapter。Public API 暴露 inkrail query result model 和 RDF term JSON。

inkrail 不解释 payload raw。Payload 的 JSON、YAML、TOML、XML 或其它格式作为 raw 文本和 RDF fact 保留。业务解释属于下游消费者。

inkrail project mode 一次查询一本书。跨书联合图、跨书 label namespace、跨书 provenance 和跨书 result ownership 不属于当前 architecture baseline。

inkrail 不允许 stale projection 回答 project mode 查询。Freshness service 已知 RDF12 TTL 不代表当前 source set 时,query service 必须刷新或失败。

这些非目标不是能力缺失说明,而是当前人工制品边界。未来对象要进入边界,必须先形成新的需求契约和 architecture baseline 更新。

第三部:包与模块结构

本部定义 packages/inkrail 的包边界、源码树、核心模块、应用服务、适配器、CLI consumer 和公共导出表面。

9. 包边界

packages/inkrailinkrail runtime 的 npm package 边界。该 package 同时发布 public library API 和 CLI binary。

Package root 承担以下职责:

  • 声明 package metadata、license、engines、exports、bin、files 和 scripts。

  • 承载 src/ runtime source。

  • 承载 runtime tests 和 fixtures。

  • 构建 dist/ 中的 ESM library entry、CLI entry 和类型声明。

  • 通过 package boundary test 验证发布包内容。

Workspace root 承担 monorepo 协调职责。它可以运行全仓检查、版本检查和文档构建;它不定义 inkrail runtime public API。

docs/bookshelf 承担书籍化设计材料职责。设计书可以约束 runtime,但不进入 npm runtime files。构建输出、HTML、展开后 ADOC 和临时 RDF projection artifacts 不进入 package publish boundary。

发布包必须包含:

dist/
README.md
LICENSE
package.json

发布包不得包含:

src/
test/
coverage/
tmp/
docs/bookshelf/build/
node_modules/

Package boundary 的证据表面是 npm pack --dry-runpnpm pack --dry-run。架构验收要求 package boundary test 检查 required files 和 forbidden files。

10. 源码树

src/ 按 module role 分区。目录名表达模块身份,不表达实现批次。

src/
  index.ts
  bin.ts
  cli.ts
  core/
    config.ts
    profiles.ts
    project.ts
    books.ts
    paths.ts
    fingerprint.ts
    freshness.ts
    errors.ts
    result.ts
    rdf-terms.ts
    query-actions.ts
  services/
    load-project.ts
    init-project.ts
    bookshelf-init.ts
    bookshelf-runtime.ts
    projection.ts
    refresh.ts
    query.ts
    built-in-actions.ts
  adapters/
    filesystem.ts
    toml.ts
    abundant-tree.ts
    workspace-initializer.ts
    oxigraph.ts
    process-runner.ts
  commands/
    init.ts
    bookshelves.ts
    books.ts
    bookshelf.ts
    refresh.ts
    project.ts
    query.ts
    builtins.ts
    status.ts
    doctor.ts
    config-print.ts
  io/
    json-output.ts
    pretty-output.ts
    table.ts
    diagnostics.ts
  test-support/

index.ts 是 public library API entry。bin.ts 是 Node executable entry。cli.ts 是 CLI adapter entry。core/services/adapters/ 组成 public API 可消费的 runtime。commands/io/ 组成 CLI consumer surface。

依赖方向如下:

core
  <- services
  <- commands
  <- cli

adapters
  <- services
  <- commands
  <- cli

io
  <- cli

core/ 不依赖 services/adapters/commands/io/services/ 可以依赖 core/ 和 adapter interfaces。commands/ 可以依赖 services/io/ 可以依赖 public result types,但不得执行 domain logic。

test-support/ 提供 fake adapters、fixture builders 和 assertions。src/index.ts 不导出 test-support/

11. Domain Core 模块

Domain core 保存不依赖进程环境的对象和规则。

模块 职责 不得承担的职责

config.ts

定义 config raw schema、normalized config、serialize/parse shape 和 validation issue mapping。

不得读取磁盘、查找项目根或修改配置文件。

profiles.ts

定义 standard profile 与 custom profile 的合法字段、派生能力和 profile identity。

不得访问实际书架目录。

project.ts

定义 project context 类型、config dir 名称、project discovery result 类型。

不得执行 upward search。

books.ts

定义 bookshelf、book、target book selection 输入输出和选择规则。

不得读取 book entry 文件内容。

paths.ts

定义 POSIX path validation、project-relative path、bookshelf-relative path 和 absolute operation path 的转换规则。

不得直接调用 Node filesystem。

fingerprint.ts

定义 source file fingerprint、source set fingerprint、projection fingerprint 和稳定 hash 输入序列。

不得自行构造 source set。

freshness.ts

定义 freshness decision、refresh reason 和 stale projection 判断。

不得生成 projection artifact。

errors.ts

定义 InkrailErrorCodeInkrailErrorInkrailFailure 和 error details 类型。

不得定义 CLI exit code 为 domain 本体。

result.ts

定义 InkrailResult<T>、success、failure 和 result helpers。

不得写 stdout 或 stderr。

rdf-terms.ts

定义 RDF term JSON、quad term JSON 和 Oxigraph term 转换目标类型。

不得 import Oxigraph runtime class 作为 public type。

query-actions.ts

定义内置查询 action registry、参数 schema、SPARQL template、variables、query kind 和 source reference。

不得执行 SPARQL。

Domain core 的测试使用纯输入输出。测试不得依赖真实 .inkrail/ 目录、真实 Oxigraph store 或真实上游 parser。

Domain core 是 public API 的类型来源。下游消费者能依赖的 object shape 应从 domain core 或 service result 类型导出。

12. Application Services 模块

Application services 把 domain core 规则和 adapters 组合为可调用用例。Public API 和 CLI command handler 共同消费 application services。

模块 职责 主要输出

load-project.ts

解析 project context、读取 config、规范化 config、应用显式 project 路径。

LoadedProject

init-project.ts

创建 .inkrail/config.toml.inkrail/cache/.inkrail/state/ 和 runtime ignore 文件。

ProjectInitializationResult

bookshelf-init.ts

调用 workspace initializer adapter 创建标准书架,并在 --register 时写入 bookshelves 配置。

BookshelfInitializationResult

bookshelf-runtime.ts

调度已登记标准书架的 local buildcheckclean script。

BookshelfRuntimeResult

projection.ts

执行 explicit projection 或 project projection,调用 abundant-tree adapter 生成 RDF12 TTL/JSON-LD。

ProjectionResult

refresh.ts

执行 project mode refresh state machine,判断 freshness、生成 artifact、写 state。

RefreshResult

query.ts

执行 project mode query 或 TTL-only query,调用 Oxigraph adapter 并返回 query result model。

QueryResult

built-in-actions.ts

根据 query action registry 渲染 SPARQL,执行 --print-query 或调用 query service。

BuiltInActionResult

Application service 接收 adapter set。默认 adapter set 使用 Node filesystem、TOML parser、asciidoc-abundant-tree、workspace initializer、Oxigraph 和 Node process runner。测试可以注入 fake adapter set。

Application service 返回结构化 result,不返回 CLI 字符串。刷新事件和诊断作为 result 附加对象返回,由 CLI output projector 决定写 stderr 还是 JSON details。

Application service 不能越过 domain core 修改规则。发现上游限制、配置缺口或运行失败时,service 必须返回 InkrailFailure 或 diagnostic,而不是即兴改变目标契约。

13. 适配器

Adapter 封装外部能力和运行环境。Adapter 的输出必须转换为 inkrail 内部对象。

Adapter 外部对象 内部契约

filesystem.ts

Node filesystem

FileSystem interface、path existence、read/write text、mkdirp、stat、realpath。

toml.ts

TOML parser/stringifier

Raw config parse result 和 serialized config text。

abundant-tree.ts

asciidoc-abundant-tree

Book-entry parse、source files、RDF12 TTL、RDF12 JSON-LD、projector identity。

workspace-initializer.ts

create-asciidoc-multi-book-workspace

标准书架初始化结果、fixed template output path、upstream error mapping。

oxigraph.ts

Oxigraph Store

TTL load、SPARQL query、query result classification、RDF term conversion。

process-runner.ts

Node child process

Package script dispatch result、stdout、stderr、exit status。

Adapter 是外部依赖变化的隔离层。上游 package 版本、API 形状或错误消息变化时,优先修改 adapter 和 adapter contract tests。

Adapter 不定义 inkrail domain identity。asciidoc-abundant-tree adapter 不定义 book selection。Workspace initializer adapter 不定义 project memory。Oxigraph adapter 不定义 built-in query action。

Adapter contract tests 使用真实依赖。Application service tests 可以使用 fake adapters。两类测试不得互相替代。

14. CLI 消费者

CLI consumer 由 bin.tscli.tscommands/io/ 承担。它消费 public API 和 application services。

bin.ts 是 executable entry。它读取 process.argv、调用 runCli、写 stdout/stderr,并设置 process.exitCode

cli.ts 是 CLI adapter。它创建 command parser、注册 commands、捕获 usage error、调用 command handler,并把 InkrailResult 投影为 CliRunResult

commands/ 保存 command handler。Command handler 把 CLI 参数转换为 service input。Command handler 不读写 stdout,不直接访问 Oxigraph,不直接解析 .inkrail/config.toml

io/ 保存 output projector。Output projector 根据 result 和 output mode 生成 stdout/stderr。Pretty output 面向终端读者。JSON output 面向机器消费者。错误 JSON 写 stderr。

CLI consumer 必须保持 stdout/stderr 纪律:

  • 成功 pretty result 写 stdout。

  • 成功 JSON envelope 写 stdout。

  • Refresh event、diagnostic 和 error 写 stderr。

  • Failure JSON 写 stderr。

  • Stack trace 不进入普通 pretty failure surface。

CLI consumer 的命令表面实现 10 号书查询模式10 号书输出表面。CLI consumer 不扩大 public API 契约。

15. 公共导出

src/index.ts 是 library consumer 的公共入口。它导出可稳定依赖的 API、类型、错误对象和 query action registry。

公共导出分为五类:

Project API

resolveProjectContextloadInkrailProjectnormalizeInkrailConfigselectTargetBookderiveBookPaths

Mutation API

initInkrailProjectinitBookshelfregisterBookshelfregisterBooksetDefaultBook

Projection API

projectAdocrefreshBookProjectionrefreshProjectBook

Query API

queryProjectBookqueryTtlexecuteSparqlrunBuiltInAction

Model API

Result、error、diagnostic、config、book、path, fingerprint、projection、query result、RDF term JSON 和 built-in action types。

runCli 可以导出为测试和嵌入式 CLI consumer 表面,但它不是 domain core API。Library consumer 不应为了执行 query 而构造 CLI args。

公共导出不包含:

  • commands/ 内部 command handler。

  • io/ pretty formatter internals。

  • test-support/

  • Oxigraph Store instance。

  • asciidoc-abundant-tree 内部 source files 以外的 parser IR。

  • Workspace initializer deep import path。

公共导出的类型声明来自 build output。Package exports 必须暴露 ".""./package.json"。其它 subpath export 只有在存在明确消费者动作和稳定契约时才进入 package boundary。

第四部:数据与状态契约

本部定义配置、项目、书架、路径、指纹、状态存储、查询结果和 RDF term JSON 的数据契约。

16. 配置模型

Config model 表达 .inkrail/config.toml 的当前项目记忆。它承载 project、bookshelves 和 books 三类对象。

[project]
default_book = "10-inkrail-cli-design"

[[bookshelves]]
id = "docs"
root = "docs/bookshelf"
profile = "inkrail-asciidoc-multi-book"

[[books]]
id = "10-inkrail-cli-design"
bookshelf = "docs"

Raw config 是 TOML 解析结果。Normalized config 是 runtime 内部对象。Public API 返回 normalized config,不要求下游消费者理解 TOML parser 的 raw shape。

Config validation 必须覆盖以下规则:

  • bookshelves.idbooks.id 使用 CLI identifier。

  • bookshelves.id 在项目内唯一。

  • books.id 在项目内唯一。

  • bookshelves.root 是相对 project root 的 POSIX 路径。

  • 路径不得为绝对路径。

  • 路径不得包含反斜杠。

  • 路径不得包含 .. segment。

  • project.default_book 必须指向已登记 book。

  • Standard profile 禁止手写 book_entrydocument_rootrdf12_ttlrdf12_jsonldwatch

  • Custom profile 必须完整声明 book_entrydocument_rootrdf12_ttlrdf12_jsonldwatch

Standard profile 和 custom profile 之间不存在半自动层。Standard profile 的路径来自 profile derivation。Custom profile 的路径来自 config。实现不得在 custom profile 中猜测标准路径,也不得允许 standard profile 同时声明派生字段。

Config serializer 必须输出 runtime 支持的 config surface。Serializer 可以规范化字段顺序,但不得丢失语义字段。

17. Project 与 Book 模型

Project context 描述 .inkrail/config.toml 所在项目。

type ProjectContext = {
  projectRoot: AbsolutePath;
  configDir: AbsolutePath;
  configPath: AbsolutePath;
  cacheDir: AbsolutePath;
  stateDir: AbsolutePath;
  discoveryMode: "explicit" | "cwd-upward";
  startDirectory: AbsolutePath;
};

Project context 由显式 --project <path> 或 cwd upward search 得到。显式 project 不存在 config 时返回 PROJECT_CONFIG_NOT_FOUND。Upward search 找不到 config 时返回 PROJECT_NOT_FOUND

Bookshelf model 描述已登记书架:

type Bookshelf = {
  id: BookshelfId;
  root: ProjectRelativePath;
  profile: "inkrail-asciidoc-multi-book" | "custom";
};

Book model 描述已登记 book:

type Book = {
  id: BookId;
  bookshelf: BookshelfId;
};

Resolved book 是 query、refresh 和 projection 的运行对象:

type ResolvedBook = {
  id: BookId;
  bookshelf: Bookshelf;
  bookEntry: BookshelfRelativePath;
  documentRoot: AbsolutePath;
  rdf12Ttl: BookshelfRelativePath;
  rdf12JsonLd: BookshelfRelativePath;
  html: BookshelfRelativePath;
  expandedAdoc: BookshelfRelativePath;
  watch: BookshelfRelativePath[];
};

Target book selection 按以下顺序解析目标:

  1. 调用输入中的 book id。

  2. project.default_book

  3. 项目只登记一本 book 时的唯一 book。

多 book 项目没有目标 book 时返回 BOOK_REQUIRED。指定未知 book 时返回 BOOK_NOT_FOUND。Book selection 是 domain rule,不属于 CLI command handler。

18. 路径模型

Path model 区分 authored path、display path 和 operation path。

Project-relative path

相对 project root 的 POSIX path。Config 中的 bookshelves.root 使用该路径类型。

Bookshelf-relative path

相对 bookshelf root 的 POSIX path。Standard profile 派生的 book entry、RDF12 TTL、JSON-LD、HTML、expanded ADOC 和 watch glob 使用该路径类型。

DocumentRoot-relative path

相对 documentRoot 的 source coordinate path。RDF projection 中的 aat:relativePath 使用该路径类型。

Absolute path

文件系统操作路径。Runtime 读写文件时使用该路径类型。

Path validation 使用 segment 规则。路径不得包含反斜杠。路径不得为绝对路径。路径不得包含 .. segment。路径可以包含 . segment only when explicitly allowed by field contract;standard profile 的 documentRoot 在 display surface 中可以显示为 .

Runtime 对外显示相对路径,文件操作使用 absolute path。任何 file read、write、stat、realpath 或 process cwd 都必须在 path model 中完成物化。

Standard profile 的派生路径如下:

对象 Bookshelf-relative path

Book entry

books/<book-id>/book.adoc

DocumentRoot display

.

RDF12 TTL

build/rdf12/books/<book-id>.ttl

RDF12 JSON-LD

build/rdf12/books/<book-id>.jsonld

HTML

build/html/books/<book-id>/book.html

Expanded ADOC

build/adoc/books/<book-id>.adoc

Watch

books/<book-id>//.adoc, shared//.adoc

Watch paths 是 glob 表面。Source set paths 是具体 source file path。Watch globs 不定义 source set membership。

19. Source Set 指纹

Source set fingerprint 描述 book-entry projection 实际到达的 source files。它由 asciidoc-abundant-tree book-entry parse result 的 source file reconstruction surface 和 filesystem metadata 共同构造。

type SourceFileFingerprint = {
  relativePath: DocumentRootRelativePath;
  size: number;
  mtimeMs: number;
  contentHash: string;
};

type SourceSetFingerprint = {
  hash: string;
  files: SourceFileFingerprint[];
};

relativePath 相对于 documentRoot。sizemtimeMs 来自 filesystem stat。contentHash 来自 source file raw content。hash 由按 relativePath 排序后的 file fingerprints 计算。

排序是 fingerprint 契约的一部分。Include graph traversal order、filesystem listing order 或 parser internal order 不得影响 source set hash。

mtimeMs 是快速变化线索。contentHash 是内容事实。Runtime 可以使用 mtimeMs 做快速预检,但 freshness 判定必须以 content hash 和 projection fingerprint 条件为准。

Source set 包含 entry file、frontmatter、chapters、backmatter、shared attributes 和递归 include 到达的 source files。只贡献 attributes 或其它非结构节点的 source file 仍属于 source set。

Source set 构造失败返回 SOURCE_SET_FAILED。失败时 project mode query 不得读取旧 TTL 回答当前查询。

20. Projection 指纹

Projection fingerprint 描述一个 projection artifact 与其输入、投影器和选项之间的关系。

type ProjectionFingerprint = {
  format: "rdf12" | "rdf12-json-ld";
  outputPath: ProjectRelativePath;
  outputHash: string;
  projectedFromSourceSetHash: string;
  documentRoot: ProjectRelativePath;
  projector: {
    name: "asciidoc-abundant-tree";
    version: string;
  };
  options: Record<string, unknown>;
};

format 区分 RDF12 TTL 与 RDF12 JSON-LD。两个格式有独立 projection fingerprint。它们可以来自同一 source set hash,但不能共享同一个 state record。

outputPath 是 project-relative display path。文件操作时由 path model 物化为 absolute path。outputHash 是 projection artifact 的 content hash。

projectedFromSourceSetHash 连接 projection artifact 与 source set fingerprint。该字段不证明 artifact 存在。TTL 文件缺失时,即使 state 中有 projection fingerprint,project mode query 仍必须刷新。

documentRootprojectoroptions 是 freshness 条件。任一字段与当前 projection request 不一致时,projection artifact 过期。

Projector identity 由 asciidoc-abundant-tree adapter 提供。Adapter 必须从 package metadata 或注入版本读取 projector version。Runtime 不从 TTL 文本推断 projector version。

21. State Store 模型

State store 位于 .inkrail/state/。它保存 project mode 下上次成功 source set fingerprint 和 projection fingerprint。

State store 使用 project memory 身份组织文件。标准 state path 为:

.inkrail/state/books/<book-id>/rdf12.json
.inkrail/state/books/<book-id>/rdf12-json-ld.json

books.id 在 config model 中保持项目内唯一,因此 state path 可以使用 book id。若未来允许不同 bookshelf 下同名 book,state path 必须改为包含 bookshelf id 的结构;该变化需要更新 config model 和 migration contract。

State record shape:

type ProjectionStateRecord = {
  schemaVersion: 1;
  book: {
    id: BookId;
    bookshelf: BookshelfId;
  };
  sourceSet: SourceSetFingerprint;
  projection: ProjectionFingerprint;
  updatedAt: string;
};

State file 不替代 projection artifact。State file 缺失、schemaVersion 不支持、JSON parse 失败、projection artifact 缺失或 fingerprint 不匹配,都进入 refresh decision。

State write 发生在 projection artifact 成功写入并计算 outputHash 之后。查询失败不回滚已经成功写入的新鲜 projection artifact 和 state record。

State store 不记录书架本地 buildcheckclean 状态。书架 local runtime 的生成物和 RDF projection state 是不同对象。

22. Query Result 模型

Query result model 是 SPARQL 执行后的内部结构化结果。它独立于 CLI output surface。

type QueryMode = "project" | "ttl-only";

type QueryResult =
  | SelectQueryResult
  | AskQueryResult
  | GraphQueryResult;

type QueryResultBase = {
  mode: QueryMode;
  query: {
    kind: "select" | "ask" | "construct" | "describe";
    source: "raw" | "built-in";
    variables?: string[];
  };
};

SELECT result 保存 variables 和 rows:

type SelectQueryResult = QueryResultBase & {
  query: {
    kind: "select";
    source: "raw" | "built-in";
    variables: string[];
  };
  rows: Array<Record<string, RdfTermJson>>;
};

ASK result 保存 boolean:

type AskQueryResult = QueryResultBase & {
  query: {
    kind: "ask";
    source: "raw" | "built-in";
  };
  value: boolean;
};

CONSTRUCT 和 DESCRIBE result 保存 quads:

type GraphQueryResult = QueryResultBase & {
  query: {
    kind: "construct" | "describe";
    source: "raw" | "built-in";
  };
  quads: RdfQuadJson[];
};

Project mode result 扩展 book 和 projection 信息:

type ProjectQueryEnvelope = {
  mode: "project";
  book: {
    id: BookId;
    bookshelf: BookshelfId;
  };
  projection: {
    format: "rdf12";
    path: ProjectRelativePath;
    freshness: "fresh" | "refreshed";
  };
  result: QueryResult;
};

TTL-only result 不包含 book 和 freshness 声明:

type TtlOnlyQueryEnvelope = {
  mode: "ttl-only";
  ttl: {
    path: ProjectRelativePath | AbsolutePath;
  };
  result: QueryResult;
};

Oxigraph adapter 返回值必须被分类后进入 Query result model。Runtime 不向 public API 暴露 Store.query() 的原始 union return type。

Query kind classification 来源于 Oxigraph 返回值和调用输入。Boolean 返回 ASK。Map<string, Term>[] 返回 SELECT。Quad[] 返回 CONSTRUCT 或 DESCRIBE,kind 由 query classifier 或调用输入决定。Runtime 默认不请求 string results_format,以保留 RDF term structure。

23. RDF Term JSON 模型

RDF term JSON 是 public API 和 CLI JSON 共同使用的 RDF term 表示。它保留 RDF/JS term 结构,避免把 NamedNode、Literal、BlankNode、DefaultGraph、Variable 或 Quad term 压成字符串。

NamedNode:

{
  "termType": "NamedNode",
  "value": "urn:aat:doc:example#heading-l17-o0"
}

Literal:

{
  "termType": "Literal",
  "value": "工作流对象",
  "datatype": "http://www.w3.org/2001/XMLSchema#string",
  "language": "",
  "direction": ""
}

BlankNode:

{
  "termType": "BlankNode",
  "value": "b0"
}

DefaultGraph:

{
  "termType": "DefaultGraph",
  "value": ""
}

Variable:

{
  "termType": "Variable",
  "value": "heading"
}

Quad term:

{
  "termType": "Quad",
  "value": "",
  "subject": {
    "termType": "NamedNode",
    "value": "urn:aat:doc:example#heading-l3-o0"
  },
  "predicate": {
    "termType": "NamedNode",
    "value": "https://micheng.dev/ns/asciidoc-relation#depends-on"
  },
  "object": {
    "termType": "NamedNode",
    "value": "urn:aat:doc:example#heading-l14-o0"
  },
  "graph": {
    "termType": "DefaultGraph",
    "value": ""
  }
}

Quad term 表示 RDF/JS Quad。当 Oxigraph 用 Quad 表达 RDF-star / RDF 1.2 triple term 时,graph 为 DefaultGraph。Public API 不引入单独的 ad hoc TripleTerm surface;它使用 RDF/JS-compatible Quad JSON 保存 subject、predicate、object 和 graph。

RDF term JSON serializer 必须是 total function over supported Oxigraph term classes。遇到未知 term shape 时,adapter 返回 QUERY_FAILED with details,而不是输出部分字符串化结果。

Pretty output 可以把 RDF term 渲染为短文本。Pretty 渲染不改变 JSON 契约,也不能作为机器消费者的解析来源。

第五部:运行流与关系

本部定义 project mode、TTL-only mode、explicit projection mode、刷新状态机、书架初始化、书架 runtime 调度、内置动作和输出投影的运行流。

24. Project Mode 查询流

Project mode 查询流实现 10 号书 Project mode。该运行流在查询前确保 RDF12 TTL 代表当前 target book source set。

queryProjectBook input
  -> resolve project context
  -> load normalized config
  -> select target book
  -> derive book paths
  -> refresh RDF12 projection
  -> read fresh TTL
  -> load Oxigraph store
  -> execute SPARQL
  -> classify query result
  -> return project query envelope

输入包括 cwd、optional project path、optional book id、raw query 或 built-in action。输出是 project query envelope。失败输出是 InkrailFailure

Project context、normalized config、target book selection 和 path derivation 使用 domain core 规则。Refresh RDF12 projection 使用 刷新状态机。SPARQL execution 使用 Oxigraph 适配器

Project mode 不接受 stale TTL fallback。Refresh 失败、TTL 缺失、TTL load 失败或 query execution 失败都阻止当前查询结果产生。

Project mode 可以产生 refresh events。Public API 返回结构化 events。CLI consumer 将 events 写入 stderr。

25. TTL-only 查询流

TTL-only 查询流直接查询调用者提供的 Turtle 文件。该运行流实现 10 号书 TTL-only mode

queryTtl input
  -> read TTL path
  -> load Oxigraph store
  -> execute SPARQL
  -> classify query result
  -> return ttl-only query envelope

TTL-only mode 不查找 .inkrail/config.toml,不读取 project memory,不构造 source set,不计算 freshness,不写 state,不声称给定 TTL 代表某个当前书稿。

TTL-only result envelope 只声明 mode、TTL path 和 query result。它不得包含 book identity、bookshelf identity 或 projection freshness。

TTL read failure 返回 TTL_READ_FAILED。TTL load failure 返回 TTL_LOAD_FAILED。SPARQL execution failure 返回 QUERY_FAILED

TTL-only mode 服务临时实验、外部 TTL 审查和跨项目复制查询。该 mode 不参与 project mode 的 stale projection 规则。

26. Explicit Projection 投影流

Explicit projection 投影流读取调用者指定的 AsciiDoc input,并生成指定 RDF projection artifact。该运行流实现 10 号书 explicit projection mode

projectAdoc input
  -> resolve input path
  -> select input semantics
  -> parse abundant document
  -> project RDF12 graph
  -> serialize requested format
  -> write output file
  -> return projection result

Input semantics 有两种:

single-file

默认语义。Projection adapter 只读取调用者指定 source file。

book-entry

显式语义。Projection adapter 使用 entry file 和 documentRoot 构造 book-entry logical document。

Explicit projection 投影流不读取 project memory。它不写 .inkrail/state/。它不判断 project freshness。它不登记 book。

Format 为 rdf12 时输出 Turtle。Format 为 rdf12-json-ld 时输出 JSON-LD。Output path 由调用者提供;runtime 可以创建父目录。

Projection failure 返回 PROJECTION_FAILED。Book-entry source set construction failure 返回 SOURCE_SET_FAILED。输入路径越界或 documentRoot 不合法返回对应 config/path failure。

27. 刷新状态机运行流

刷新状态机运行流在 project mode query 和 explicit refresh command 中复用。它实现 10 号书刷新状态机

refresh input
  -> resolve project context
  -> load normalized config
  -> select target book
  -> derive book paths
  -> parse book-entry source set
  -> calculate source set fingerprint
  -> read projection state
  -> inspect projection artifact
  -> decide freshness
  -> project missing or stale artifact
  -> write projection artifact
  -> calculate output hash
  -> write state record
  -> return refresh result

Freshness decision 检查以下条件:

  • Book entry exists.

  • Current source set can be constructed.

  • Requested artifact exists.

  • Current source set hash equals state projectedFromSourceSetHash.

  • Current documentRoot equals state documentRoot.

  • Current projection options equal state options.

  • Current projector identity equals state projector.

  • Current artifact output hash equals state outputHash.

refresh --projection rdf12 refreshes TTL state. refresh --projection rdf12-json-ld refreshes JSON-LD state. refresh --projection all refreshes both formats with independent state records.

Project mode query 只要求 RDF12 freshness。JSON-LD freshness 不是 query precondition。

Projection success 后发生 query failure 时,不回滚 projection artifact 或 state。Query failure 属于 query execution layer。

28. 书架初始化流

书架初始化流通过 workspace initializer adapter 创建标准 AsciiDoc multi-book workspace,并按调用参数把书架登记到 project memory。

bookshelf init input
  -> resolve target path
  -> validate target path state
  -> call workspace initializer adapter
  -> if register requested, load project config
  -> append bookshelves entry
  -> write config
  -> return initialization result

Target path rules:

  • Missing target directory is created.

  • Existing empty directory is used.

  • Existing non-directory path fails with BOOKSHELF_INIT_TARGET_INVALID.

  • Existing non-empty directory without --force fails with BOOKSHELF_INIT_TARGET_NOT_EMPTY.

  • --force 覆盖 fixed template output paths,并保留未知用户文件。

Standard workspace content 来源于 create-asciidoc-multi-book-workspaceinkrail 不把 maintainer source、tests、release configuration 或 design documents 复制到 generated workspace。

--register <bookshelf-id> 写入 ,其中 profile 为 inkrail-asciidoc-multi-book,root 为 target path 相对于 project root 的路径。

Bookshelf initialization 不登记 sample books。Book registration 保持显式。

29. 书架 Runtime 调度流

书架 Runtime 调度流执行标准书架本地 package scripts。它实现 10 号书书架本地构建调度

bookshelf runtime input
  -> load project
  -> resolve registered bookshelf
  -> require standard profile
  -> validate package script
  -> validate tools/adoc-books.mjs
  -> run package script in bookshelf root
  -> forward stdout/stderr/status
  -> return dispatch result

支持的 actions 是 buildcheckclean。Dispatch 使用登记的 bookshelves.root 作为 process cwd。

未知 bookshelf 返回 BOOKSHELF_NOT_FOUND。Non-standard profile 返回 BOOKSHELF_PROFILE_UNSUPPORTED。缺失 package script 返回 BOOKSHELF_SCRIPT_NOT_FOUND。缺失 tools/adoc-books.mjs 返回 BOOKSHELF_RUNTIME_NOT_FOUND。Non-zero script status 返回 BOOKSHELF_BUILD_FAILED

Bookshelf runtime dispatch 不写 RDF projection state。Project mode query 不隐式执行 bookshelf local buildcheckclean

30. 内置动作流

内置动作流把命令化 author actions 映射为 SPARQL templates。它实现 10 号书查询模板契约,并从 08 号书查询语句集 取得 SPARQL 语义。

built-in action input
  -> lookup action definition
  -> validate action parameters
  -> render SPARQL template
  -> if printQuery, return rendered query
  -> else call project mode or ttl-only query flow
  -> return action result

Action definition 包含:

  • Action id。

  • CLI command path。

  • Query kind。

  • Parameter schema。

  • SPARQL template。

  • Query kind 为 select 时的 SELECT variables。

  • Sort contract。

  • Source workflow reference。

--print-query 在 SPARQL rendering 之后停止。它不 resolve project context,不 read TTL,不 run refresh state machine,不 execute SPARQL。

Action parameters 只替换已记录的 literal placeholders 或 scope labels。Action parameter handling 不得改变 query fact boundary、variables、sorting、OPTIONALFILTER NOT EXISTSGROUP BYHAVING 或 property path semantics。

Built-in action result 使用与 raw query 相同的 query result model。Built-in action identity 记录在 query.source = "built-in" 和 action metadata 中。

31. 输出投影流

输出投影流把 structured result 或 failure 转换为 CLI stdout/stderr。它实现 10 号书输出表面

InkrailResult
  -> select output mode
  -> project success to stdout
  -> project diagnostics/events to stderr
  -> project failure to stderr
  -> set exit code

Pretty output:

  • Writes successful query table or graph rows to stdout.

  • Uses SPARQL variable names as table field names.

  • Uses single-column ask table for ASK.

  • Renders CONSTRUCT and DESCRIBE quads as RDF term text.

  • Writes errors to stderr with stable error code and details.

JSON output:

  • Writes success envelope to stdout.

  • Writes failure envelope to stderr.

  • Preserves RDF term JSON.

  • Preserves mode、book、projection、query kind、variables、rows、value 或 quads.

Refresh events 与 diagnostics 不混入 successful stdout result。Teaching text 属于 README 和 user guide,不属于 command output。

Exit code mapping 是 CLI projection。Domain error code 保持为稳定 recovery index。

第六部:适配器与依赖契约

本部定义标准书架 initializer、asciidoc-abundant-tree、Oxigraph、文件系统、进程运行器和版本能力锁定的适配器契约。

32. Workspace Initializer 适配器

Workspace initializer adapter 集成 create-asciidoc-multi-book-workspace。该 adapter 创建标准 AsciiDoc multi-book workspace,并把上游错误映射为 inkrail 错误码。

上游 package 提供 npm initializer、默认 workspace template 和本地 runtime。发布包包含 dist/init-workspace.jsdist/runtime/adoc-books.mjstemplates/。当前 package boundary 未通过 package exports 显式声明 library subpath。

Adapter contract:

  • 接收 absolute target directory 和 force

  • 调用上游 workspace initializer。

  • 保留未知用户文件。

  • 覆盖上游固定模板输出路径。

  • 返回 created workspace root。

  • 把 target path 状态错误映射为 BOOKSHELF_INIT_TARGET_INVALIDBOOKSHELF_INIT_TARGET_NOT_EMPTY

Adapter 是唯一允许接触上游 initializer import path 的位置。若上游发布正式 library export,adapter 使用正式 export。若实现阶段必须使用发布包 subpath,subpath import 不得扩散到 services、commands 或 domain core。

inkrail 不复制标准 workspace template。模板内容、七本样本书、user README、package script 和 tools/adoc-books.mjs 由上游 package 负责。

Adapter contract test 必须验证生成 workspace 包含 catalog.adocbooks/shared/package.json.gitignoretools/adoc-books.mjs,并验证 maintainer-only files 不进入生成 workspace。

33. asciidoc-abundant-tree 适配器

asciidoc-abundant-tree adapter 集成 book-entry parser、source coordinate recovery 和 RDF12 projector。

Adapter 使用上游 public API:

parseAbundantTree({
  sourcePath,
  mode: "book-entry",
  documentRoot,
});

rdf12(document, { documentRoot });

Adapter contract:

  • 使用 explicit mode,不从 :doctype: book、文件名或 include directive 自动切换模式。

  • 在 project mode projection 中使用 mode: "book-entry"

  • 在 explicit projection single-file mode 中保持 single-file semantics。

  • AbundantDocument.sourceFiles 读取 source set reconstruction surface。

  • 输出 RDF12 TTL 和 RDF12 JSON-LD。

  • 提供 projector identity。

  • 把 book-entry construction failure 映射为 SOURCE_SET_FAILED

  • 把 RDF projection failure 映射为 PROJECTION_FAILED

Adapter 不 import asciidoc-abundant-tree 内部 source modules。Book-entry source set、logical document、line origin 和 origin recovery 是上游内部或上游 public model 的职责。

Adapter 不解释 payload raw。Adapter 不修改 source files。Adapter 不执行 SPARQL。

Adapter contract test 必须覆盖 book-entry source files、shared attributes inclusion、RDF12 TTL output、RDF12 JSON-LD output、source coordinate preservation 和 single-file mode 不展开 include。

34. Oxigraph 适配器

Oxigraph adapter 封装本地 SPARQL 查询执行器。Public API 不暴露 Oxigraph Store。

Adapter contract:

  • 创建 in-memory store。

  • 使用 Turtle format 加载 RDF12 TTL。

  • 执行 SELECT、ASK、CONSTRUCT 和 DESCRIBE 查询。

  • 支持 property path 查询。

  • 保留 RDF/JS term structure。

  • 把 RDF/JS term 转换为 RDF Term JSON 模型

  • 把 TTL load failure 映射为 TTL_LOAD_FAILED

  • 把 SPARQL execution failure 映射为 QUERY_FAILED

Oxigraph Store.query() 返回 union value。Adapter 必须分类:

Oxigraph return Inkrail result

boolean

ASK result

Map<string, Term>[]

SELECT result

Quad[]

CONSTRUCT or DESCRIBE graph result

string

Unsupported default runtime surface unless explicitly requested by adapter-internal test

Runtime 默认不设置 string results_format。String result 会丢失 RDF term structure,不进入 public query result model。

RDF-star / RDF 1.2 triple term capability 必须由 fixture test 锁定。测试应加载包含 rdf:reifies 和 embedded triple term 的 TTL,并验证 query result 中的 Quad term JSON。

Adapter 不定义 built-in query action。Adapter 只执行 SPARQL text。

35. Filesystem 与 Process 适配器

Filesystem adapter 封装 Node filesystem。Process adapter 封装 child process execution。

Filesystem interface:

type FileSystem = {
  pathExists(path: AbsolutePath): Promise<boolean>;
  readText(path: AbsolutePath): Promise<string>;
  writeText(path: AbsolutePath, content: string): Promise<void>;
  mkdirp(path: AbsolutePath): Promise<void>;
  stat(path: AbsolutePath): Promise<FileStat>;
  lstat(path: AbsolutePath): Promise<FileStat>;
  realpath(path: AbsolutePath): Promise<AbsolutePath>;
  listDirectory(path: AbsolutePath): Promise<DirectoryEntry[]>;
};

Filesystem adapter 不执行 path validation。Path validation 属于 domain core。Filesystem adapter 接收已经物化的 absolute path。

Process runner interface:

type ProcessRunner = {
  run(options: {
    cwd: AbsolutePath;
    command: string;
    args: string[];
    env?: Record<string, string>;
  }): Promise<ProcessResult>;
};

Process result 保存 stdout、stderr、exit status 和 signal。Bookshelf runtime dispatch 把 process result 映射为 dispatch result 或 BOOKSHELF_BUILD_FAILED

Service tests 使用 fake filesystem 和 fake process runner。Adapter contract tests 在 repository fixture directories 内使用真实 filesystem 和真实 process execution。

Temporary test files 属于 project 或 subproject tmp/,也可以属于 fixture directories。Tests 不得在 system temp directories 或 workspace-external paths 创建文件,除非测试对象明确验证 path rejection。

36. 版本与能力锁定

Runtime dependency versions 是 package-level facts。Capability locks 是 test-level facts。

inkrail package manifest declares runtime dependencies for commandersmol-toml or equivalent TOML parser、zod or equivalent validation library、asciidoc-abundant-treecreate-asciidoc-multi-book-workspace and oxigraph

Version ranges 不能单独证明 capability。Adapter contract tests 锁定以下必需行为:

  • Workspace initializer 创建 standard workspace,并在 force 模式下保留未知文件。

  • asciidoc-abundant-tree 解析 book-entry input,并暴露 sourceFiles

  • asciidoc-abundant-tree 从 book-entry document 生成 RDF12 TTL 和 RDF12 JSON-LD。

  • Oxigraph 加载 RDF12 TTL,并执行 SELECT、ASK、CONSTRUCT 或 DESCRIBE。

  • Oxigraph 为 triple term query results 保留 Quad term structure。

Projector identity 进入 projection fingerprint。Query engine version 不进入 projection fingerprint,因为 query engine 不生成 projection artifacts。Query engine version 可以进入 diagnostics 和 doctor output。

Dependency upgrades 必须运行 adapter contract tests 和 package boundary tests。缺少 capability evidence 的 version bump 不能作为架构完成证据。

第七部:质量与验收

本部定义测试分层、fixture 策略、契约测试矩阵、负契约、输出测试、文档质量、构建门禁和架构验收条件。

37. 测试分层

inkrail tests 遵循 module role boundaries。

Layer Target Evidence

Core unit tests

Pure domain rules 与 data serializers。

Config validation、path validation、book selection、fingerprint hash、freshness decision、query action rendering、RDF term JSON conversion.

Service integration tests

带 fake adapters 的 application services。

Project load、init、refresh decision、projection write、query flow branching、failure propagation.

Adapter contract tests

真实 external dependencies 与 runtime environment。

Workspace initializer output、abundant-tree book-entry projection、Oxigraph query execution、process runner script dispatch.

CLI surface tests

runCli(args, { cwd }) and stdout/stderr projection.

Argument parsing、JSON/pretty output、exit code、usage failure、--print-query stop point.

End-to-end fixture tests

Minimal standard bookshelf 上的 full runtime path。

Init/register/book/refresh/query/built-in action closure。

Package boundary tests

Published package content。

pnpm pack --dry-run required and forbidden files.

Lower layer tests 不替代 higher layer tests。Adapter contract tests 证明 dependency capability;它们不证明 service branching。CLI tests 证明 command projection;它们不证明 domain rule completeness。

Tests 必须标识其验证对象。当断言行为属于 config model、freshness model、RDF term model 或 adapter contract 时,只按 command 命名的测试不足以表达对象边界。

38. Fixture 策略

Fixtures 为 architecture contracts 提供稳定 evidence surfaces。

Minimal project fixture

包含 .inkrail/config.toml,且不包含 bookshelf。用于 config、project discovery 和 init tests。

Minimal standard bookshelf fixture

包含 catalog、books、shared、package scripts 和一个或多个 book entries。用于 profile path derivation、refresh 和 query tests。

Book-entry source fixture

包含 entry file、frontmatter、chapters、backmatter 和 shared attributes。用于 source set 和 projection tests。

Stale projection fixture

包含 source files、old state 和 old TTL。用于 freshness tests。

RDF term fixture

包含带有 literal、named node、blank node 和 quad term evidence 的 TTL。用于 Oxigraph 和 RDF term JSON tests。

Query action fixture

包含 built-in actions 所需的 RDF facts。用于 registry 和 CLI command tests。

Fixtures 在 test execution 期间位于 package test fixture directories 或 project-local tmp/。Tests 不得扫描或修改 workspace-external paths。

Fixture source files 应当小而有明确目的。Fixture 的存在理由是验证 contract,而不是模拟完整 user project。

Fixture 中的 generated artifacts 必须作为 explicit expected artifacts 提交,或在 test setup 内生成。Tests 不得静默依赖 previous runs 留下的 stale build output。

39. 契约测试矩阵

契约测试矩阵把 10 号书 contracts 映射到 architecture test locations。

Contract Primary layer Required evidence

Config validation

Core unit

Invalid id、duplicate id、bad path、profile field rules.

Book selection

Core unit

CLI book override、default book、single book、multi book failure.

Standard path derivation

Core unit

Book entry、documentRoot、TTL、JSON-LD、HTML、expanded ADOC、watch globs.

Source set fingerprint

Service integration

Shared attributes included、content hash changes、stable sorted hash.

Projection fingerprint

Service integration

Source hash、documentRoot、options、projector identity mismatch refreshes.

Refresh state machine

Service integration

Missing TTL、stale TTL、projection failure、no stale query branch.

RDF12 projection

Adapter contract

Book-entry TTL、JSON-LD、source file reconstruction surface.

Oxigraph query

Adapter contract

SELECT、ASK、CONSTRUCT/DESCRIBE、property path、quad term.

Output surfaces

CLI surface

stdout/stderr separation、JSON envelope、RDF term JSON preservation.

Built-in query actions

Core unit and service integration

SPARQL template、variables、sort、--print-query stop point.

Bookshelf initialization

Adapter contract and service integration

Target state rules、force preservation、register without sample books.

Bookshelf runtime dispatch

Service integration and adapter contract

Script validation、stdout/stderr forwarding、non-zero status mapping.

附录 D 把本矩阵展开为 review checklist。附录不替代本章契约。

40. 负契约测试

Negative tests 验证 invalid states 不会静默通过。

Config negative tests:

  • Invalid book id.

  • Duplicate bookshelf id.

  • Duplicate book id.

  • Absolute path.

  • Backslash path.

  • Parent traversal path.

  • default_book points to missing book.

  • Standard profile declares derived fields.

  • Custom profile omits required paths.

Runtime negative tests:

  • Project config not found.

  • Book not found.

  • Book entry not found.

  • Source set construction failure.

  • Projection failure.

  • TTL read failure.

  • TTL load failure.

  • Query read failure.

  • Query execution failure.

  • Stale projection 不能刷新。

Bookshelf negative tests:

  • Init target is file.

  • Init target is non-empty without force.

  • Bookshelf not registered.

  • Non-standard profile build dispatch.

  • Missing package script.

  • Missing tools/adoc-books.mjs.

  • Non-zero local runtime exit status.

Negative tests 必须断言 stable error code 和 recovery-relevant details。除非 adapter contract 定义了上游错误消息文本,否则 negative tests 不应断言不稳定的 upstream error message text。

41. 输出测试契约

Output tests 验证 consumer projections。

Pretty output tests 必须验证:

  • Successful result 写入 stdout。

  • Refresh event 写入 stderr。

  • Failure 写入 stderr。

  • SELECT table 使用 SPARQL variable names。

  • ASK table 使用 ask

  • CONSTRUCT/DESCRIBE surface 渲染 RDF term text。

  • Failure output 包含 stable error code。

  • Failure output 默认省略 stack trace。

JSON output tests 必须验证:

  • Success envelope 包含 ok: truedata

  • Failure envelope 包含 ok: falseerror

  • Failure JSON 写入 stderr。

  • Project result 包含 mode、book、projection 和 query metadata。

  • TTL-only result 不包含 book freshness claims。

  • SELECT rows 保留 RDF term JSON。

  • ASK 使用 value

  • CONSTRUCT/DESCRIBE 使用 quads

  • Quad term 不转换为 string。

Output tests 使用 CLI surface。Public API tests 单独验证 raw structured result。

Command output 不得包含 tutorial text。README 和 user guide 承载 teaching material。

42. 文档质量

Documentation surfaces 属于 runtime quality。

README 必须说明:

  • Tool identity。

  • Install 与 command entry。

  • Project initialization。

  • Bookshelf initialization 与 registration。

  • Book registration 与 default book。

  • Basic query examples。

  • Config file location。

  • Source-aware output。

  • Boundary statements。

User guide 必须说明:

  • Project mode refresh behavior。

  • TTL-only mode freshness boundary。

  • Explicit projection mode state boundary。

  • Source coordinate fields。

  • 从 query result 回读 source files 的方法。

  • Built-in query action groups。

  • Structural facts 与 quality judgments 的边界。

Design books 定义 object contracts 和 architecture contracts。README 与 user guide 说明 usage surfaces。Command output 不承载 source coordinate tutorial text。

Documentation tests 至少必须验证 README examples 引用 supported commands,并验证文档不承诺 cross-book union query、payload business interpretation、remote federation 或 stale TTL fallback。

43. Build、Lint 与 Package 门禁

Runtime package 必须暴露 stable check commands。

必需门禁:

pnpm --filter inkrail typecheck
pnpm --filter inkrail test
pnpm --filter inkrail build
pnpm --filter inkrail pack:check

Runtime source 存在时,TypeScript project 应当使用 Biome 执行 lint 和 formatting:

pnpm --filter inkrail lint

Full workspace check 应当包含 runtime checks 和 docs bookshelf checks:

pnpm check

Package gate 验证 dist/ 和 type declarations 已生成、CLI bin 指向 built file、package exports 可解析,并验证 publish boundary 排除 forbidden files。

Build gate 不能单独证明 architecture contract。Contract tests 和 adapter tests 提供 behavioral evidence。

44. 架构验收

架构基线满足以下条件时,才具备进入 implementation WBS 的资格:

  • 10 号书的每个构成性条件都有架构承载对象。

  • Public library API 与 CLI consumer 边界明确。

  • Domain core、application services、adapters、commands 和 output projectors 职责分离。

  • Config、project、book、path、fingerprint、state、query result 和 RDF term JSON 模型已定义。

  • Project mode、TTL-only mode 和 explicit projection mode 拥有独立 runtime flow。

  • Refresh state machine 禁止 stale projection fallback。

  • Built-in action flow 的 SPARQL 语义来源于 08 号书。

  • Workspace initializer、asciidoc-abundant-tree 和 Oxigraph 通过 adapters 进入 runtime。

  • Test layering、fixture strategy、negative tests、output tests 和 package gates 已定义。

  • Backmatter 为 implementation WBS 提供 API surface、module responsibility map、error mapping 和 test matrix。

架构验收不要求实现文件已经存在。架构验收要求本书足以支撑 implementation WBS,且不存在未归档的对象边界缺口。

实现活动发现缺失架构对象时,该发现必须回到本书或后继架构修订。Work package 不得依赖未进入架构基线的对象。

Appendix A: 附录 A:公共 API 表面

API 输入 输出 消费者

loadInkrailProject

cwd、optional project path、adapter set

Loaded project context and normalized config

Library、CLI、automation

selectTargetBook

Normalized config、optional book id

Resolved book identity

Library、services

deriveBookPaths

Project context、bookshelf、book

Resolved book paths

Projection、query、doctor

initInkrailProject

Project root、force

Project initialization result

CLI、library

initBookshelf

Target path、force、optional register id

Bookshelf initialization result

CLI、library

refreshProjectBook

Project context、book、projection format

Refresh result

Query、CLI refresh

projectAdoc

Input file、mode、documentRoot、format、out path

Projection result

CLI project、library

queryProjectBook

Project query input

Project query envelope

CLI、library、automation

queryTtl

TTL path、query text

TTL-only query envelope

CLI、library、automation

runBuiltInAction

Action id、parameters、mode input

Rendered query or query envelope

CLI built-ins、library

本附录是查找表面。API 定义仍以 公共导出 为准。

Appendix B: 附录 B:Module Responsibility Map

Module area Responsibility Forbidden responsibility

core/

纯模型、规则、validation 和 serialization targets。

Process IO、filesystem IO、external package calls、stdout/stderr.

services/

围绕 core 与 adapters 编排 use-case。

CLI formatting、commander parsing、upstream internal imports.

adapters/

External package 与 runtime environment boundary。

Domain identity、query action semantics、project config rules.

commands/

CLI option 到 service input 的映射。

Freshness decision、SPARQL execution、RDF term serialization.

io/

CLI JSON、pretty、diagnostic projection.

Project loading、projection generation、query execution.

cli.ts

Command parser 与 CliRunResult assembly。

Domain rule ownership。

bin.ts

Node process entry。

Business logic。

test-support/

Fake adapters 与 fixture helpers。

Public package export。

Appendix C: 附录 C:Error 与 Exit Code Map

Domain error code 是恢复索引。Exit code 是 CLI projection。

Error code CLI exit code Recovery surface

BOOK_REQUIRED

3

传入 --book 或设置 project.default_book

BOOK_NOT_FOUND

3

检查 registered book 与 standard book entry。

BOOK_CONFIG_INVALID

4

修正 .inkrail/config.toml

WORKSPACE_NOT_FOUND

3

检查 bookshelf root。

BOOKSHELF_INIT_TARGET_INVALID

3

使用不存在的 target path 或 directory target path。

BOOKSHELF_INIT_TARGET_NOT_EMPTY

3

使用 empty directory 或传入 --force

BOOKSHELF_NOT_FOUND

3

使用 registered bookshelf id。

BOOKSHELF_PROFILE_UNSUPPORTED

4

runtime dispatch 使用 standard profile。

BOOKSHELF_SCRIPT_NOT_FOUND

5

修正 bookshelf package.json

BOOKSHELF_RUNTIME_NOT_FOUND

5

恢复 tools/adoc-books.mjs

BOOKSHELF_BUILD_FAILED

5

读取 forwarded local runtime output。

BOOK_ENTRY_NOT_FOUND

4

创建或修正 book entry path。

SOURCE_SET_FAILED

5

修正 include graph 或 documentRoot boundary。

PROJECTION_FAILED

5

修正 source file 或 projector capability issue。

TTL_READ_FAILED

4

检查 TTL path 与 permissions。

TTL_LOAD_FAILED

5

重新生成或修正 Turtle。

QUERY_READ_FAILED

4

检查 query argument、file 或 stdin。

QUERY_FAILED

5

修正 SPARQL 或 query capability assumption。

STALE_PROJECTION

5

修复 refresh blocker;stale TTL 不得回答 project query。

TOOL_NOT_FOUND

5

安装或配置 required tool。

实现阶段可以细化 exit code groups。细化结果必须保留稳定 domain error codes。

Appendix D: 附录 D:Test Matrix

Object Test layer Fixture Gate

Config model

Core unit

Config text fixtures

pnpm --filter inkrail test

Book selection

Core unit

Normalized config fixtures

pnpm --filter inkrail test

Path derivation

Core unit

Book/profile fixtures

pnpm --filter inkrail test

Source set fingerprint

Service integration

Book-entry source fixture

pnpm --filter inkrail test

Projection fingerprint

Service integration

Projection state fixture

pnpm --filter inkrail test

State store

Service integration

Temporary project fixture

pnpm --filter inkrail test

Workspace initializer adapter

Adapter contract

Generated workspace fixture

pnpm --filter inkrail test

asciidoc-abundant-tree adapter

Adapter contract

Book-entry fixture

pnpm --filter inkrail test

Oxigraph adapter

Adapter contract

RDF term TTL fixture

pnpm --filter inkrail test

CLI output

CLI surface

Minimal project fixture

pnpm --filter inkrail test

Package boundary

Package boundary

Dry-run package output

pnpm --filter inkrail pack:check

本矩阵是 implementation WBS 的审核查找表面。它不记录 work package 状态。

术语表

architecture baseline

实现前的软件架构与工程质量基线。它定义模块、API、数据流、适配器和质量门禁。

public API

Library consumer 直接 import 的结构化 TypeScript/JavaScript 表面。

CLI consumer

通过 inkrail 命令消费 pretty 或 JSON 输出的消费者。

domain core

保存纯模型、规则和类型的模块层。

application service

组合 domain core 和 adapters 的用例层。

adapter

封装外部 package、文件系统、进程或查询执行器的边界模块。

query action registry

保存内置查询动作、SPARQL 模板、变量、参数和来源引用的 registry。

source set fingerprint

描述 book-entry source files 当前内容事实的 fingerprint。

projection fingerprint

描述 projection artifact 与 source set、documentRoot、projector 和 options 关系的 fingerprint。

state store

.inkrail/state/ 中保存 projection state record 的存储面。

query result model

SPARQL 查询执行后的内部结构化结果。

RDF term JSON

保留 RDF/JS term 结构的 JSON 表示。

capability lock

用 adapter contract test 锁定的依赖能力事实。

package boundary

npm publish artifact 的文件边界。

quality gate

完成声明前必须通过的检查命令或证据条件。

参考坐标

  • [inkrail-cli-design] inkrail CLI 设计书

  • [rdf12-query-workflow] AsciiDoc 书稿 RDF 投影查询工作流

  • [structured-writing-conventions] create-asciidoc-multi-book-workspace default workspace, 07-structured-writing-conventions

  • [asciidoc-multi-book-workspace] MichengLiang, create-asciidoc-multi-book-workspace

  • [asciidoc-abundant-tree] MichengLiang, asciidoc-abundant-tree

  • [oxigraph] Oxigraph, RDF store and SPARQL engine。

  • [rdfjs] RDF/JS, Data model and Dataset interfaces。

  • [rdf12-concepts] W3C, RDF 1.2 Concepts and Abstract Data Model

  • [sparql12-query] W3C, SPARQL 1.2 Query Language

  • [configuration-management] ISO/IEC/IEEE 828, Systems and software engineering — Configuration management

索引

本索引 section 位于书籍后置区域。正文中的 index term 是索引入口来源。

本书第一版使用显式 heading ID、role 和 xref relation predicate 支持结构查询。后续实现可以使用 inkrail 查询 .adapter-contract.api-contract.runtime-flow.quality-gate 等标题身份。

核心索引词:

indexterm:[architecture baseline]
indexterm:[public API]
indexterm:[adapter]
indexterm:[projection fingerprint]
indexterm:[source set fingerprint]
indexterm:[state store]
indexterm:[RDF term JSON]
indexterm:[quality gate]