tracecite

    Maintain Markdown references, code excerpts and source snapshots

    documentation
    markdown
    citation
    maintenance
    Download zip
    Author
    Version
    0.5.0
    License
    Apache-2.0
    Last updated
    14 hours ago
    Downloads
    6

    #TraceCite

    让文档里的引用,跟上来源的变化。

    用 MoonBit 检查 Markdown 中的本地文件、标题锚点、源码片段与网页引文。保存引用快照后,来源变化会定位到需要重新审阅的文档行。一份项目配置,让本地与 CI 使用同一套规则。

    版本说明:0.5.0 提供项目配置、init 与 check 文档维护入口。Mooncakes 安装请指定 @0.5.0;GitHub 的旧 v0.4.0 不包含这些入口。

    #为什么需要 TraceCite

    代码和配置更新了,README 中复制的片段却还停在旧版本;文件迁移、标题改名后,文档链接可能失效;网页仍能打开,引用的原文却已经改变。维护者需要知道具体哪份文档、哪一行需要复核。

    TraceCite 把引用当作文档依赖,直接读取 Markdown 与来源。本地检查默认离线,无需模型 API、Agent 事件导出、数据库或后台服务;明确添加 --online 才访问网页。

    #能做什么

    检查对象使用方式发现的问题
    本地文件与目录普通链接、引用式链接、图片或 HTML 链接来源被删除、迁移或超出工作区
    标题与来源范围标题锚点、#L10-L20、#region:name标题改名、行范围或命名区域失效
    源码与配置片段代码围栏添加 source=路径文档片段与当前来源不一致
    逐字引文双引号引文配来源链接或显式引用块引文不在当前来源中;网页需 --online
    引用快照--snapshot 保存,--baseline 复查来源区域或引用上下文改变,新增引用待复核
    项目配置init 建立配置与基线,之后只运行 check本地与 CI 复用文档范围、排除项与检查规则
    CI 检查GitHub Action 或 --github以退出码和文件行号提示文档作者

    #快速开始

    #1. 获取源码并检查仓库文档

    安装 MoonBit(moonc >= 0.10.14),然后执行:

    git clone https://github.com/Freakz2z/tracecite.git cd tracecite moon update moon run cmd/main check

    #2. 接入自己的项目

    选择需要长期维护的文件或目录,初始化一次:

    moon run cmd/main init docs README.md --root /path/to/your/repository

    来源检查通过后,会生成 tracecite.json 和 .tracecite-docs.json。将它们一起提交到项目。之后无需重复填写扫描范围与基线:

    moon run cmd/main check --root /path/to/your/repository

    默认严格、离线检查。 已有配置或基线时,init 会停止;检查失败时不会创建它们。--root 定义工作区边界,配置路径相对根目录,引用来源相对文档所在目录。

    需要回访网页来源时,在配置中设置 online: true,或临时添加 --online。配置字段与覆盖规则见 维护指南。

    #3. 构建独立可执行文件

    bash scripts/package.sh ./_build/native/release/build/cmd/main/main.exe check

    打包脚本在 dist/ 生成本机压缩包。运行独立 native 二进制无需安装 MoonBit、Python 或 Node。 打包工作流支持手动触发或版本标签触发,构建 Linux/macOS 包。

    #绑定源码片段

    在代码围栏的语言后添加 source=路径。本 README 中的这段代码已绑定 moon.mod:

    preferred_target = "native"

    它的 Markdown 写法是:

    ```text source=moon.mod preferred_target = "native" ```

    片段按完整行比较,保留引号、标点和缩进,只统一 CRLF/LF 换行。默认在整个来源文件中寻找片段,来源前面增加几行不会使仍然存在的片段失效。

    行范围、命名区域、空格路径与本地逐字引文的写法见 维护指南。

    #保存与复核引用快照

    init 已完成首次记录;以后按项目配置复查:

    moon run cmd/main check

    来源改变后:查看输出中的文档行号与前后内容,决定文档是否需要修改。完成复核后,更新配置所维护的完整范围:

    moon run cmd/main check --snapshot .tracecite-docs.json moon run cmd/main check

    --snapshot 会忽略配置中的旧基线,但仍独立检查来源;显式同时传入 --baseline 时继续比较旧基线,发生变化便拒绝写入。

    • 快照使用相对文档路径;移动文档段落不会改变引用身份。
    • 引用不匹配或来源区域改变时,输出文档行号、候选片段或前后观察值;新增引用提示复核。
    • 检查失败或存在待复核项时,不写快照,也不自动覆盖旧基线。
    • 离线快照仅覆盖实际检查的本地引用;网页引文在保存与复查时都需要 --online。

    #GitHub Action

    将项目配置和快照提交到仓库,再添加以下工作流步骤;无需重复填写规则。示例跟随 main,正式使用应固定到审核过的提交 SHA:

    - uses: actions/checkout@v5 - uses: Freakz2z/tracecite@main

    Action 安装并编译 MoonBit,在调用方仓库中解析引用,失败时产生文件行号诊断。建议设置 permissions: contents: read。

    输入默认值说明
    config自动读取 tracecite.json指定其他项目配置,路径相对调用方根目录
    path配置中的 paths;无配置为 .指定时覆盖扫描范围
    strict配置值;无配置为 true显式 true / false 覆盖规则
    online配置值;无配置为 false显式 true / false 覆盖规则
    baseline配置值指定时覆盖引用快照路径
    exclude配置值追加排除路径,每行一个

    旧的 report: 输入仍兼容,会跳过项目配置、覆盖 path 并启用联网检查。完整定义见 action.yml。

    #检查结果与边界

    默认的 LOCAL_OK 表示实际检查的本地引用通过。每次输出会列出检查数、失败数、待复核数、跳过的网页引用,以及没有 source= 的代码块数量。

    退出码含义
    0实际检查的引用通过;严格模式下没有待复核项
    1参数、输入读取或快照写入失败
    2引用失败、来源改变、没有可检查引用;严格模式下也包含待复核项

    • 本地链接检查文件或目录是否存在;片段支持 ATX 标题、行范围与命名区域。Setext 标题、自定义 HTML ID 与站点特有锚点规则暂不解析。
    • 行内引文支持中文弯双引号与英文直双引号;多行或来源关联不明确时,使用显式原文、来源块。
    • 未绑定来源的代码块只计入覆盖摘要,不执行;未关联引文或无法解析的来源会提示复核,--strict 让这些提示导致失败。
    • 网页引文只确认检查时可提取的原文是否存在,不判断论断的语义真假;动态页面、图片与登录墙可能需要人工复核。来源变化表示需要复核,不表示文档一定错误。
    • 本地来源受 --root 边界限制,包括符号链接的真实目标;文档与来源正文上限为 2 MiB。网页回源限制默认端口、公开地址、重定向、20 秒超时与 2 MiB 响应。

    详细约定见 维护指南和 HTTP 回源行为。

    联网检查支持 https_proxy / HTTPS_PROXY、http_proxy / HTTP_PROXY,小写变量优先。代理地址支持不含用户名或密码的 HTTP/HTTPS URL,可使用私有地址与非默认端口。

    no_proxy / NO_PROXY 支持逗号分隔的域名、可选端口与 *,域名匹配其自身及子域名。代理通过 CONNECT 转发;TLS 证书校验、来源地址筛选和重定向限制仍然生效。错误的代理配置不会自动退回直连。

    #MoonBit 包接入与发布

    核心库可在 MoonBit 项目中导入,支持 native、JS、Wasm;CLI 另提供独立 native 包。消费项目可执行:

    moon add Freakz2z/tracecite@0.5.0

    需要命令行时可安装 moon install Freakz2z/tracecite/cmd/tracecite@0.5.0,以后直接运行 tracecite init 和 tracecite check。

    维护者使用 bash scripts/release.sh 生成并验证源码包,包含解包后的文档检查和三个目标的消费项目测试。准备完成且登录有发布权限的 Mooncakes 账户后,使用 bash scripts/release.sh --publish 上传并确认版本。具体步骤见 发布与接入指南。

    #开发与验证

    核心库支持 native、JS 与 Wasm;文件与联网 CLI 使用 native。项目的 模块配置只声明异步 I/O 与 HTML 解析两个外部 MoonBit 库。

    moon info moon fmt moon check --deny-warn moon test --deny-warn moon test --target js --deny-warn moon test --target wasm --deny-warn python3 scripts/test_document_cli.py python3 -m unittest discover -s adapters -p 'test_*.py' sh scripts/smoke.sh

    Python 仅用于开发测试和可选的旧 Agent 适配器。JSONL 校验、verify-files、verify-urls、verify-notes、check-report 与 compare 继续兼容,见 原有输入约定。

    #项目资料

    资料内容
    文档引用维护指南片段绑定、标题锚点、引文与快照的完整用法
    Mooncakes 发布与接入源码包验证、公开 API 接入与发布步骤
    输入约定兼容接口、输入格式与 HTTP 回源规则
    第三方资料审计网页逐字引文的固定样本、结果与局限
    普通报告试验普通 Markdown 报告的核验记录
    Mooncakes 模块平台上的已发布版本

    验证记录用于说明可执行能力,不代表用户采用率或审核工时收益。

    使用 Apache-2.0 许可证。HTML 解析依赖 bobzhang/html_parser(Apache-2.0)。

    CitationEdge

    pub(all) struct CitationEdge {
    claim_index : Int
    source_id : String
    call_id : String?
    line : Int
    evidence_status : String
    } derive(ToJson)

    CitationEdge::to_json

    CitationNote

    pub(all) struct CitationNote {
    claim : String
    quote : String
    url : String
    line : Int
    } derive(ToJson)

    One citation note from the lightweight claim/quote/url input format.

    CitationNote::to_json

    CitationNotes

    pub(all) struct CitationNotes {
    notes : Array[CitationNote]
    diagnostics : Array[Diagnostic]
    } derive(ToJson)

    Parsed citation notes and line-based input diagnostics.

    CitationNotes::to_json

    Diagnostic

    pub(all) struct Diagnostic {
    line : Int
    code : String
    message : String
    } derive(ToJson)

    Diagnostic::to_json

    fn Diagnostic::to_json(Diagnostic) -> Json

    DocumentProjectConfig

    pub(all) struct DocumentProjectConfig {
    version : Int
    paths : Array[String]
    exclude : Array[String]
    baseline : String?
    strict : Bool
    online : Bool
    } derive(ToJson)

    Portable project settings shared by local checks and CI. Paths are relative to the explicit workspace root, never to the process running the CLI.

    DocumentProjectConfig::to_json

    DocumentProjectConfigResult

    pub(all) struct DocumentProjectConfigResult {
    config : DocumentProjectConfig?
    diagnostics : Array[Diagnostic]
    } derive(ToJson)

    DocumentProjectConfigResult::to_json

    DocumentReference

    pub(all) struct DocumentReference {
    line : Int
    source : String
    kind : String
    excerpt : String?
    } derive(ToJson)

    A document dependency. Snippets preserve code exactly; quotes use text matching.

    DocumentReference::to_json

    DocumentReferences

    pub(all) struct DocumentReferences {
    references : Array[DocumentReference]
    diagnostics : Array[Diagnostic]
    warnings : Array[Diagnostic]
    unbound_snippets : Int
    } derive(ToJson)

    DocumentReferences::to_json

    DocumentSnapshot

    pub(all) struct DocumentSnapshot {
    version : Int
    entries : Array[DocumentSnapshotEntry]
    } derive(ToJson,
    FromJson
    )

    DocumentSnapshotEntry

    pub(all) struct DocumentSnapshotEntry {
    document : String
    source : String
    kind : String
    excerpt : String?
    observed : String
    } derive(ToJson,
    FromJson
    )

    Portable baseline identities omit document line numbers, so moving a paragraph does not invalidate its unchanged dependency.

    DocumentSnapshotEntry::to_json

    DriftReport

    pub(all) struct DriftReport {
    old_run_id : String?
    new_run_id : String?
    old_valid : Bool
    new_valid : Bool
    unchanged_count : Int
    changes : Array[SourceChange]
    old_diagnostics : Array[Diagnostic]
    new_diagnostics : Array[Diagnostic]
    } derive(ToJson)

    DriftReport::ok

    fn DriftReport::ok(self : DriftReport) -> Bool

    DriftReport::to_json

    fn DriftReport::to_json(DriftReport) -> Json

    LiveCitationMatch

    pub(all) struct LiveCitationMatch {
    source_id : String
    claim_index : Int
    quote_present : Bool
    } derive(ToJson)

    Result for one quote checked against the current text fetched from a source.

    LiveCitationMatch::to_json

    Report

    pub(all) struct Report {
    run_id : String?
    event_count : Int
    call_count : Int
    source_count : Int
    claim_count : Int
    verified_citation_count : Int
    sources : Array[SourceRecord]
    citations : Array[CitationEdge]
    diagnostics : Array[Diagnostic]
    } derive(ToJson)

    Report::ok

    fn Report::ok(self : Report) -> Bool

    Report::to_json

    fn Report::to_json(Report) -> Json

    SelectedSource

    pub(all) struct SelectedSource {
    ok : Bool
    text : String
    code : String
    } derive(ToJson)

    SelectedSource::to_json

    SourceChange

    pub(all) struct SourceChange {
    kind : String
    uri : String
    } derive(ToJson)

    SourceChange::to_json

    SourceRecord

    pub(all) struct SourceRecord {
    id : String
    uri : String
    title : String?
    call_id : String
    line : Int
    content_present : Bool
    } derive(ToJson)

    SourceRecord::to_json

    bounded_document_text

    fn bounded_document_text(text : String) -> String

    check_live_source_quotes

    fn check_live_source_quotes(input : String, uri : String, live_content : String) -> Array[LiveCitationMatch]

    Check citations to uri against its current fetched text. Whitespace is collapsed to account for HTML layout while preserving case and punctuation.

    compare_jsonl

    fn compare_jsonl(old_input : String, new_input : String) -> DriftReport

    Compare captured source content by stable URI across two valid runs. Changes are reported without echoing source text.

    document_excerpt_matches

    fn document_excerpt_matches(content : String, excerpt : String, kind : String) -> Bool

    Code snippets are compared by complete lines. Quotes retain the existing typography/whitespace normalization. Code punctuation and indentation matter.

    document_source_context

    fn document_source_context(content : String, excerpt : String, kind : String) -> String

    A bounded candidate excerpt for review, not an automatic correction.

    markdown_heading_slug

    fn markdown_heading_slug(title : String) -> String

    normalize_web_text

    fn normalize_web_text(input : String) -> String

    Collapse Unicode whitespace, including spaces before punctuation that HTML text extraction can introduce around inline elements.

    parse_citation_notes

    fn parse_citation_notes(input : String) -> CitationNotes

    Parse the lightweight plain-text format. Each record has one claim:, quote:, and url: line; blank lines or --- separate records. Field values are single-line strings.

    parse_document

    fn parse_document(input : String) -> DocumentReferences

    Extract inline links, quoted citations, explicit quote/source blocks, and fenced snippets with source=path. Ordinary code fences are counted, not run.

    parse_document_project_config

    fn parse_document_project_config(input : String) -> DocumentProjectConfigResult

    Read version 1 project configuration. Reject misspelled keys and malformed values so a broken configuration cannot silently widen the scan or disable strict checks. New configurations default to strict, offline checks.

    parse_markdown_report

    fn parse_markdown_report(input : String) -> CitationNotes

    Parse Markdown citation blocks of the form:
    原文:“quoted text” 来源:title Inline-code backticks are Markdown formatting, not part of rendered quotes.

    quote_appears_in_text

    fn quote_appears_in_text(source : String, quote : String) -> Bool

    Check an exact quotation against visible source text. Typography-only quote mark differences are ignored. An explicit ellipsis permits omitted source text while requiring both substantial fragments in source order.

    select_document_source

    fn select_document_source(content : String, fragment : String) -> SelectedSource

    Select the whole source, a #Lx-Ly range, a Markdown heading section, or #region:name between ANCHOR: name / ANCHOR_END: name comments.

    source_snapshot_matches

    fn source_snapshot_matches(input : String, uri : String, live_content : String) -> Bool

    Check that a source snapshot in a valid trace exactly matches independently supplied content. The caller controls how the content is obtained.

    validate_evidence_jsonl

    fn validate_evidence_jsonl(input : String) -> Report

    Require each citation to quote bytes present in the captured tool output.

    validate_jsonl

    fn validate_jsonl(input : String) -> Report

    Validate one JSONL Agent run. Each diagnostic identifies an input line and a stable machine-readable code. This checks trace structure, not truth.