cartograph

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cartograph

Cartograph

Extract a structural map of a codebase: surfaces, features, entities, relationships, operations, flows, compartments, and tech stack. Four orthogonal axes — surfaces are where you go (pages/entry points), features are what you can do (standalone capabilities), entities are what the app works with (data), and compartments are how the code is organized (logical file groupings that bridge product concepts to the underlying codebase). The tech stack provides a comprehensive inventory of all technologies, frameworks, and libraries the project uses.
提取代码库的结构映射:包括surfaces(界面入口)、features(功能模块)、entities(实体)、关系、操作、flows(业务流程)、compartments(逻辑代码模块)以及技术栈。四个核心维度——surfaces是用户访问的入口(页面/接入点),features是用户可执行的独立功能,entities是应用处理的数据对象,compartments是代码的逻辑组织方式(连接产品概念与底层代码库的文件分组)。技术栈则列出项目使用的所有技术、框架和库的完整清单。

Workflow

工作流程

Intent Detection

意图检测

The default mode is Full Scan — produce a full structural map of the codebase plus code health. Run this unless the user's message clearly asks for one of the invariant-specific modes below.
Two narrower modes override the default:
  1. Add Invariant — message contains "add invariant", "new invariant", or "add this invariant: '...'" → Add Invariant Flow.
  2. Standalone Verify — message contains "verify", "check invariants", "run invariants", or similar → Standalone Verify Flow.

默认模式为完整扫描——生成代码库的完整结构映射及代码健康报告。除非用户明确要求以下特定的不变量模式,否则默认执行此模式。
两种窄范围模式会覆盖默认设置:
  1. 添加不变量——消息包含"add invariant"、"new invariant"或"add this invariant: '...'" → 执行添加不变量流程
  2. 独立验证——消息包含"verify"、"check invariants"、"run invariants"或类似表述 → 执行独立验证流程

Full Scan

完整扫描

A scan has four steps:
  1. Discover — read the codebase to build the context every extract task needs.
  2. Extract — produce the structural map.
  3. Analyze — assess the assembled map for code health and invariants.
  4. Assemble — merge everything into
    .cartograph/mapping.json
    .
Within Extract and Analyze, fan out via subagents on anything whose inputs are ready — don't run them serially in the orchestrator, wall time matters. The natural parallel batches are:
{ Surfaces, Entities }
  → { Features, Operations }
  → { Flows, Compartments, File Tree Weights }
  → { Compartment Dependencies }
  → { Co-location, DRYness, Dead Code, Invariants }
Every task below names its inputs and what it returns. All return shapes conform to
references/json-schema.md
.

扫描包含四个步骤:
  1. 发现阶段——读取代码库,构建所有提取任务所需的上下文。
  2. 提取阶段——生成结构映射。
  3. 分析阶段——评估已组装的映射,生成代码健康度和不变量报告。
  4. 组装阶段——将所有结果合并到
    .cartograph/mapping.json
    中。
在提取和分析阶段,只要子任务的输入准备就绪,就通过子代理并行执行——不要在编排器中串行运行,耗时至关重要。天然的并行批次如下:
{ Surfaces, Entities }
  → { Features, Operations }
  → { Flows, Compartments, File Tree Weights }
  → { Compartment Dependencies }
  → { Co-location, DRYness, Dead Code, Invariants }
以下每个任务都会说明其输入和返回内容。所有返回格式均符合
references/json-schema.md
的定义。

1. Discover

1. 发现阶段

Run this yourself before fanning out — it's fast and every Extract task needs it.
  1. Read
    package.json
    for project name and dependencies (framework detection).
  2. Glob for key structural files:
    • Schema:
      **/*.prisma
      ,
      **/schema.*
      ,
      **/models/**
    • Routes/Pages:
      app/**/page.{tsx,ts,jsx,js}
      ,
      app/api/**/*.{ts,js}
      ,
      pages/**/*.{tsx,ts}
    • Server actions: grep for
      "use server"
    • Components:
      components/**/*.{tsx,jsx}
    • Lib/services:
      lib/**/*.{ts,js}
      ,
      services/**/*.{ts,js}
  3. Read the directory tree to understand the overall shape.
  4. Detect the tech stack. For each technology in use, record name, version, category, source (where you found it), and confidence (high = explicit dependency + matching config; medium = dependency only; low = inferred from patterns). The full catalog of detection signals — config files, file patterns, import patterns — lives in
    references/tech-stack-detection.md
    . The category list lives in
    references/json-schema.md
    .
  5. Collect the full non-generated file inventory.
Returns the discover bundle: file inventory + tech stack array. Pass this to every Extract and Analyze task.

在并行执行前自行运行此阶段——速度快,且所有提取任务都需要它的结果。
  1. 读取
    package.json
    获取项目名称和依赖项(用于框架检测)。
  2. 查找关键结构文件:
    • Schema:
      **/*.prisma
      **/schema.*
      **/models/**
    • 路由/页面:
      app/**/page.{tsx,ts,jsx,js}
      app/api/**/*.{ts,js}
      pages/**/*.{tsx,ts}
    • 服务器操作:搜索
      "use server"
      关键字
    • 组件:
      components/**/*.{tsx,jsx}
    • 库/服务:
      lib/**/*.{ts,js}
      services/**/*.{ts,js}
  3. 读取目录树,了解代码库的整体结构。
  4. 检测技术栈。对于每个使用的技术,记录名称、版本、类别、来源(发现位置)和置信度(高=明确依赖+匹配配置;中=仅依赖;低=从模式推断)。完整的检测信号目录——配置文件、文件模式、导入模式——位于
    references/tech-stack-detection.md
    。类别列表位于
    references/json-schema.md
  5. 收集所有非生成文件的清单。
返回发现包:文件清单 + 技术栈数组。将此传递给所有提取和分析任务。

2. Extract

2. 提取阶段

Surfaces
Surfaces(界面入口)
Needs: discover.
Surfaces are entry points — self-contained user-facing experiences. Each app is fundamentally a collection of surfaces.
  1. Walk the route tree (
    app/**/page.tsx
    ) and identify each distinct user-facing experience.
  2. Group related routes into surfaces (e.g.,
    /create
    +
    /create/[id]/edit
    = one "Creation Studio" surface).
  3. Look for admin-only areas, standalone tools, dashboards, and onboarding flows.
  4. For each surface determine: entrypoint (main page file and route), actor (user/admin/system), description (what this surface does as a standalone experience).
Returns: surfaces array (without
entityIds
,
operationIds
,
flowIds
, or
compartmentIds
— those get back-filled by later tasks and Assemble).
所需输入:发现包。
Surfaces是入口点——独立的用户面向体验。每个应用本质上都是一组Surfaces的集合。
  1. 遍历路由树(
    app/**/page.tsx
    ),识别每个独特的用户面向体验。
  2. 将相关路由分组为Surfaces(例如:
    /create
    +
    /create/[id]/edit
    = 一个"创作工作室"Surface)。
  3. 查找仅管理员可用的区域、独立工具、仪表板和引导流程。
  4. 为每个Surface确定:入口点(主页面文件和路由)、角色(用户/管理员/系统)、描述(此Surface作为独立体验的功能)。
返回:surfaces数组(不含
entityIds
operationIds
flowIds
compartmentIds
——这些会在后续任务和组装阶段补全)。
Entities + Relationships
Entities(实体)+ 关系
Needs: discover.
Entities — read schema/type definitions and extract domain objects:
  1. DB models (high confidence) — Prisma models, TypeORM entities, Mongoose schemas.
  2. TypeScript types/interfaces (medium confidence) — types used as API payloads, form data, state.
  3. Enums (high confidence) — enum definitions representing domain concepts.
  4. Derived types (medium confidence) — transformed versions like
    PostWithAuthor
    .
For each entity: id, name, kind, description, source location, key fields (3–8 most important), confidence.
Relationships — map connections between entities:
  1. Foreign keys and references in schema →
    has-many
    ,
    belongs-to
    ,
    has-one
    .
  2. Nested includes/joins → confirm relationships.
  3. Type compositions →
    derives-from
    .
  4. Looser references →
    references
    .
Returns: two arrays —
entities
and
relationships
.
所需输入:发现包。
Entities——读取Schema/类型定义,提取领域对象:
  1. 数据库模型(高置信度)——Prisma模型、TypeORM实体、Mongoose Schema。
  2. TypeScript类型/接口(中置信度)——用作API负载、表单数据、状态的类型。
  3. 枚举(高置信度)——代表领域概念的枚举定义。
  4. 派生类型(中置信度)——如
    PostWithAuthor
    这类转换后的类型。
为每个实体记录:id、名称、类型、描述、源位置、关键字段(3–8个最重要的)、置信度。
关系——映射实体之间的关联:
  1. Schema中的外键和引用 →
    has-many
    belongs-to
    has-one
  2. 嵌套包含/连接 → 确认关系。
  3. 类型组合 →
    derives-from
  4. 松散引用 →
    references
返回:两个数组——
entities
relationships
Features
Features(功能模块)
Needs: discover, surfaces, entities.
Features are standalone capabilities embedded within surfaces — what you can do in the app, as distinct from where you go. A like button, a prompt wizard, a credit purchase, an age gate are all features. They compose into surfaces; they aren't themselves pages.
If you can't describe a feature without naming a specific page, it's probably part of a surface, not a feature.
The six kinds — tool, interaction, transaction, gate, infrastructure, workflow — and the code patterns that signal each are catalogued in
references/feature-kinds.md
. Use that as your scanning checklist.
Separate implementations are separate features. The same conceptual capability often exists as independent implementations in different surfaces — a user-facing "Prompt Wizard" modal in chat and an admin "Prompt Remix Wizard" in the post-management area. Always create separate feature entries; name them distinctly. After extracting from one surface, scan the others' component trees for similar patterns and grep for shared service imports — this is the easiest class of feature to miss.
For each feature record: name, description, kind,
surfaceIds
,
entityIds
, implementations (2–5 most important files, not every file).
Returns: features array (without
compartmentIds
— Compartment Dependencies fills that in).
所需输入:发现包、surfaces、entities。
Features是嵌入在Surfaces中的独立功能——用户在应用中可以执行的操作,区别于用户访问的入口。点赞按钮、提示向导、积分购买、年龄验证都是Features。它们组合成Surfaces,但本身不是页面。
如果描述某个功能时必须提到特定页面,那它可能是Surface的一部分,而非独立Feature。
六种类型——工具、交互、事务、验证、基础设施、工作流——以及每种类型对应的代码模式都记录在
references/feature-kinds.md
中。以此作为扫描清单。
独立实现即为独立Feature。同一概念功能通常在不同Surfaces中有独立实现——例如聊天中的用户端"提示向导"模态框,和帖子管理区域的管理员端"提示重制向导"。始终创建独立的Feature条目;名称要区分开。从一个Surface提取后,扫描其他Surface的组件树寻找相似模式,并搜索共享服务的导入——这是最容易遗漏的Feature类别。
为每个Feature记录:名称、描述、类型、
surfaceIds
entityIds
、实现文件(2–5个最重要的文件,无需全部)。
返回:features数组(不含
compartmentIds
——由模块依赖阶段补全)。
Operations
Operations(操作)
Needs: discover, entities.
For each entry point (route handler, server action, API endpoint):
  1. Which entity it targets.
  2. Operation type:
    create
    ,
    read
    ,
    update
    ,
    delete
    , or
    domain
    .
  3. Descriptive name (e.g., "Publish Post", "Generate Preview").
  4. Side effects on other entities.
  5. Implementation location (file + function).
Returns: operations array.
所需输入:发现包、entities。
针对每个入口点(路由处理器、服务器操作、API端点):
  1. 它针对的实体。
  2. 操作类型:
    create
    read
    update
    delete
    domain
  3. 描述性名称(例如:"发布帖子"、"生成预览")。
  4. 对其他实体的副作用。
  5. 实现位置(文件 + 函数)。
返回:operations数组。
Flows
Flows(业务流程)
Needs: discover, surfaces, entities, features, operations.
  1. Start from UI pages — what can a user do on each page?
  2. Trace: UI action → handler → service → DB.
  3. Name each flow by its user-visible goal.
  4. Identify trigger and actor (user/admin/system).
  5. List steps in order, linking to operations and entities.
Returns: flows array.
所需输入:发现包、surfaces、entities、features、operations。
  1. 从UI页面开始——用户在每个页面可以执行什么操作?
  2. 追踪:UI操作 → 处理器 → 服务 → 数据库。
  3. 根据用户可见的目标为每个流程命名。
  4. 识别触发因素和角色(用户/管理员/系统)。
  5. 按顺序列出步骤,关联到对应的操作和实体。
返回:flows数组。
Compartments
Compartments(逻辑代码模块)
Needs: discover, surfaces, features, entities, operations.
Compartments are logical groupings of related files that form cohesive units of functionality. They bridge the product-side view (surfaces, features) with the underlying code structure, so a developer can navigate from "what does this feature do?" to "where does that code live?".
  1. Scan the file tree, using surfaces, features, entities, and operations as context.
  2. Group files using multiple signals:
    • Folder structure — files in the same directory often belong together.
    • Import graph — files that heavily import each other are likely in the same compartment.
    • Feature alignment — files belonging to a feature should cluster into compartments mapping to those features.
    • Domain proximity — files dealing with the same entity or business concept belong together.
    • Naming conventions — files with related names (e.g.,
      image-*.ts
      ,
      *-generation.*
      ) suggest a compartment.
    • Shared infrastructure — files used by 3+ features may warrant their own compartment, or may appear in multiple compartments.
  3. Compartments are nestable — sub-compartments can be nested to any depth. A typical web app has 2–3 levels.
  4. Files are non-exclusive — a file can appear in multiple compartments (e.g.,
    lib/prisma.ts
    in both "Database Access" and "Shared Infrastructure").
  5. Every non-generated file must appear in at least one compartment. Config files, build tooling, etc. go into a "Project Infrastructure" compartment. Exclude
    generated/
    ,
    node_modules/
    ,
    .next/
    ,
    dist/
    .
  6. For each compartment: name and description (name after what it does, not folder names — "Image Generation Pipeline" not "app/chat/actions"), tags (from the vocabulary in
    references/json-schema.md
    , plus custom as needed), files (with role: component, hook, action, api, lib, type, config, style, test, other), parentId (null for top-level), featureIds, surfaceIds.
Guidelines that keep compartments useful:
  • Don't create compartments with only 1 file unless it's a genuinely standalone module — merge small groupings into their parent.
  • Keep top-level compartments to 8–15 for a typical web app. Sub-compartments can be more.
  • Prefer meaningful groupings over 1:1 folder mapping. If a folder mixes unrelated files, split them. If related files span folders, group them.
Returns: compartments array (without
dependsOn
— Compartment Dependencies fills that in).
所需输入:发现包、surfaces、features、entities、operations。
Compartments是相关文件的逻辑分组,形成内聚的功能单元。它们连接产品视角(surfaces、features)与底层代码结构,让开发者可以从"这个功能做什么?"导航到"代码在哪里?"。
  1. 扫描文件树,以surfaces、features、entities和operations为上下文。
  2. 通过多种信号分组文件:
    • 文件夹结构——同一目录下的文件通常属于同一组。
    • 导入图谱——频繁互相导入的文件可能属于同一模块。
    • 功能对齐——属于某个Feature的文件应聚类到对应Feature的模块中。
    • 领域相关性——处理同一实体或业务概念的文件属于同一组。
    • 命名约定——名称相关的文件(例如
      image-*.ts
      *-generation.*
      )暗示属于同一模块。
    • 共享基础设施——被3个以上Feature使用的文件可能需要独立模块,或属于多个模块。
  3. Compartments可嵌套——子模块可以嵌套任意深度。典型Web应用有2–3层。
  4. 文件非排他——一个文件可以属于多个模块(例如
    lib/prisma.ts
    同时属于"数据库访问"和"共享基础设施"模块)。
  5. 所有非生成文件必须至少属于一个模块。配置文件、构建工具等归入"项目基础设施"模块。排除
    generated/
    node_modules/
    .next/
    dist/
    目录。
  6. 为每个模块记录:名称和描述(按功能命名,而非文件夹名——如"图片生成流水线"而非"app/chat/actions")、标签(来自
    references/json-schema.md
    中的词汇,必要时可自定义)、文件(带角色:组件、钩子、操作、API、库、类型、配置、样式、测试、其他)、parentId(顶级模块为null)、featureIdssurfaceIds
保持模块实用性的准则:
  • 除非是真正独立的模块,否则不要创建仅含1个文件的模块——将小组合并入父模块。
  • 典型Web应用的顶级模块保持在8–15个。子模块可以更多。
  • 优先选择有意义的分组,而非与文件夹1:1映射。如果文件夹混合了无关文件,拆分它们;如果相关文件跨文件夹,合并它们。
返回:compartments数组(不含
dependsOn
——由模块依赖阶段补全)。
File Tree Weights
File Tree Weights(文件树权重)
Needs: discover, features.
For every non-generated file, estimate what proportion of the file's purpose serves each feature.
  1. Take the full file list from the discover bundle.
  2. For each file, read it (or sample very large files) and estimate proportions.
  3. Files that don't belong to any product feature get
    "__infrastructure__"
    as their sole feature weight.
  4. Files serving multiple features get proportional weights (e.g., a shared hook → 50/50).
  5. All weights for a file must sum to 1.0.
Estimation guidance:
  • Look at imports, function names, component names, and the overall purpose of the file.
  • A file 100% dedicated to one feature →
    [{featureId: "that-feature", weight: 1.0}]
    .
  • A shared utility used by multiple features → split proportionally.
  • Config files, generic type definitions, build config, middleware →
    __infrastructure__
    .
  • Prefer fewer features per file with higher weights over many features with tiny weights.
Returns: array of
{file, featureWeights: [{featureId, weight}]}
entries — one per file.
所需输入:发现包、features。
针对每个非生成文件,估算文件用途对每个Feature的贡献比例。
  1. 从发现包中获取完整文件列表。
  2. 读取每个文件(大文件可抽样)并估算比例。
  3. 不属于任何产品Feature的文件,其唯一Feature权重为
    "__infrastructure__"
  4. 服务于多个Feature的文件按比例分配权重(例如共享钩子 → 50/50)。
  5. 单个文件的所有权重总和必须为1.0。
估算指南:
  • 查看导入、函数名、组件名以及文件的整体用途。
  • 100%专注于一个Feature的文件 →
    [{featureId: "that-feature", weight: 1.0}]
  • 被多个Feature使用的共享工具 → 按比例拆分。
  • 配置文件、通用类型定义、构建配置、中间件 →
    __infrastructure__
  • 优先选择每个文件关联较少Feature且权重较高,而非关联多个Feature且权重极低。
返回
{file, featureWeights: [{featureId, weight}]}
条目数组——每个文件对应一条。
Compartment Dependencies
Compartment Dependencies(模块依赖)
Needs: compartments (plus surfaces and features for back-filling).
  1. Walk the imports of every file in every compartment.
  2. Map each imported file to the compartment(s) it belongs to.
  3. Record these as
    dependsOn
    edges on each compartment (inter-compartment only, no self-references).
  4. Populate
    compartmentIds
    on features — for each feature, determine which compartments implement it.
  5. Populate
    compartmentIds
    on surfaces — for each surface, determine which compartments serve it.
Returns: updated compartments array (with
dependsOn
populated), plus a
featureCompartmentIds
map and a
surfaceCompartmentIds
map.

所需输入:compartments(加上surfaces和features用于补全)。
  1. 遍历每个模块中所有文件的导入。
  2. 将每个导入的文件映射到其所属的模块。
  3. 在每个模块上记录这些
    dependsOn
    依赖边(仅模块间依赖,无自引用)。
  4. 为features填充
    compartmentIds
    ——针对每个Feature,确定哪些模块实现了它。
  5. 为surfaces填充
    compartmentIds
    ——针对每个Surface,确定哪些模块为它提供支持。
返回:更新后的compartments数组(已填充
dependsOn
),以及
featureCompartmentIds
映射和
surfaceCompartmentIds
映射。

3. Analyze

3. 分析阶段

Runs on the assembled extract.
How analysis tasks report. Each analysis task emits a metric record with a score, a thresholds object, a one-line
summary
, and a
findings[]
array. The findings are the explanation for the score — every file or item that pulled the score below 100 must appear as a finding, with a concrete recommendation. The summary must include exact counts ("8 of 103 evaluated files are misplaced"), not vague prose ("most files are correctly placed").
A sub-100 score with no findings is unactionable — treat it as a bug in your output, not a valid result.
Each task's specific finding shape lives in
references/json-schema.md
under the metric's name.
基于已组装的提取结果运行。
分析任务的报告方式。每个分析任务都会生成一条指标记录,包含分数、阈值对象、一行
summary
摘要和
findings[]
数组。
findings
是分数的解释依据——任何导致分数低于100的文件或项都必须作为finding列出,并附带具体建议。摘要必须包含精确计数("103个评估文件中有8个位置错误"),而非模糊表述("大多数文件位置正确")。
分数低于100但无findings的结果是不可执行的——视为输出错误,而非有效结果。
每个任务的具体finding格式位于
references/json-schema.md
中对应指标的名称下。
Co-location
Co-location(代码共置)
Read project instructions (
AGENTS.md
,
CLAUDE.md
, or equivalent) and extract explicit co-location conventions. If none are found, fall back to:
  • Files used by a single surface should live inside that surface's directory.
  • Files shared by multiple surfaces but representing one capability belong in
    features/<capability>/
    .
  • Root
    components/
    ,
    lib/
    , and
    actions/
    are reserved for truly global code used by 3+ surfaces/features.
  • components/ui/*
    is exempt and considered correctly placed.
Evaluate every non-generated, non-infrastructure file:
  1. Trace which files import it.
  2. Determine which surfaces/features actually consume it.
  3. Compare its current location to where the rules say it should live.
  4. Assign a binary
    pass
    /
    fail
    verdict.
For each failing file, emit a finding with
action: "move"
(co-locate inside a surface or feature directory) or
action: "promote"
(move up into
features/
because it serves multiple surfaces). Include
file
,
verdict
,
reason
,
consumers[]
, and
recommendation
.
Score:
(passing files / total evaluated files) * 100
.
Returns: one metric object with
id: "co-location"
.
读取项目说明(
AGENTS.md
CLAUDE.md
或等效文件),提取明确的代码共置约定。如果未找到,默认遵循:
  • 仅被单个Surface使用的文件应放在该Surface的目录内。
  • 被多个Surface使用但代表单一功能的文件应放在
    features/<capability>/
    目录下。
  • 根目录的
    components/
    lib/
    actions/
    保留给真正被3个以上Surfaces/Features使用的全局代码。
  • components/ui/*
    目录例外,视为位置正确。
评估所有非生成、非基础设施文件:
  1. 追踪哪些文件导入了它。
  2. 确定哪些Surfaces/Features实际使用了它。
  3. 将其当前位置与规则要求的位置进行比较。
  4. 分配
    pass
    /
    fail
    二元判定。
针对每个判定为fail的文件,生成一条finding,包含
action: "move"
(移至Surface或Feature目录内共置)或
action: "promote"
(移至
features/
目录,因为它服务于多个Surfaces)。包含
file
verdict
reason
consumers[]
recommendation
分数:
(通过文件数 / 总评估文件数) * 100
返回:一个
id: "co-location"
的指标对象。
DRYness
DRYness(无重复代码)
  1. Use features and compartments as the starting map.
  2. Look for candidate duplication before reading files:
    • Features with the same
      kind
      and overlapping
      entityIds
      across different surfaces.
    • Files in different surfaces with similar names or import patterns.
    • Compartments with similar descriptions, tags, or overlapping
      featureIds
      .
    • Hooks/actions/clients that wrap the same external API or workflow.
  3. Read the candidates to confirm real overlap — weigh both functional overlap (same product problem solved twice) and structural similarity (same technical pattern repeated with light variation).
  4. For each confirmed duplication: identify what's genuinely shared, what must stay implementation-specific, and where shared logic should live (respecting co-location rules).
Each finding includes
id
,
title
,
severity
,
implementations[]
,
sharedLogic[]
, and
recommendation
.
Score:
K = 200 / totalNonInfrastructureFiles
;
score = max(0, 100 - (findingCount * K))
.
Returns: one metric object with
id: "dryness"
and
scalingFactor
.
  1. 以features和compartments为起始映射。
  2. 在读取文件前寻找重复候选:
    • 不同Surfaces中类型相同且
      entityIds
      重叠的Features。
    • 不同Surfaces中名称或导入模式相似的文件。
    • 描述、标签或
      featureIds
      重叠的Compartments。
    • 包装同一外部API或工作流的钩子/操作/客户端。
  3. 读取候选文件确认真实重叠——同时考量功能重叠(同一产品问题被解决两次)和结构相似性(相同技术模式重复,仅轻微变化)。
  4. 针对每个确认的重复:识别真正共享的部分、必须保留的实现特定部分,以及共享逻辑应放置的位置(遵循代码共置规则)。
每个finding包含
id
title
severity
implementations[]
sharedLogic[]
recommendation
分数:
K = 200 / 非基础设施文件总数
score = max(0, 100 - (finding数量 * K))
返回:一个
id: "dryness"
的指标对象,包含
scalingFactor
Dead Code
Dead Code(死代码)
  1. Build an import map for every non-generated file (resolve relative imports and
    @/
    path aliases).
  2. Use the always-live entry-point list from
    references/dead-code-detection.md
    — those never count as dead even with zero importers.
  3. Walk the codebase looking for the five finding kinds documented in
    references/dead-code-detection.md
    :
    • dead-file
      — non-entry-point with zero importers.
    • test-only-file
      — non-test file imported only by tests (informational; doesn't affect the score).
    • orphaned-surface
      — surface with no inbound navigation references.
    • orphaned-feature
      — feature with empty
      surfaceIds
      or all-orphaned surfaces.
    • dead-entity
      — DB model or DTO with no operation, feature, or query reference.
Each finding includes
id
,
kind
,
severity
,
target
,
reason
,
evidence
,
recommendation
.
Score:
totalEvaluated = filesEvaluated + surfacesEvaluated + featuresEvaluated + entitiesEvaluated
;
deadItems = deadFiles + orphanedSurfaces + orphanedFeatures + deadEntities
;
score = ((totalEvaluated - deadItems) / totalEvaluated) * 100
. Exclude test-only files from both numerator and denominator.
Returns: one metric object with
id: "dead-code"
.
  1. 为每个非生成文件构建导入图谱(解析相对导入和
    @/
    路径别名)。
  2. 使用
    references/dead-code-detection.md
    中的永久活跃入口点列表——即使没有导入者,这些文件也永远不会被视为死代码。
  3. 遍历代码库,寻找
    references/dead-code-detection.md
    中记录的五种finding类型:
    • dead-file
      ——非入口点且无导入者的文件。
    • test-only-file
      ——仅被测试文件导入的非测试文件(信息性,不影响分数)。
    • orphaned-surface
      ——无入站导航引用的Surface。
    • orphaned-feature
      ——
      surfaceIds
      为空或所有关联Surfaces均为孤立状态的Feature。
    • dead-entity
      ——无操作、Feature或查询引用的数据库模型或DTO。
每个finding包含
id
kind
severity
target
reason
evidence
recommendation
分数:
总评估数 = 文件评估数 + Surface评估数 + Feature评估数 + 实体评估数
死项数 = 死文件数 + 孤立Surface数 + 孤立Feature数 + 死实体数
score = ((总评估数 - 死项数) / 总评估数) * 100
。测试专用文件从分子和分母中排除。
返回:一个
id: "dead-code"
的指标对象。
Invariants
Invariants(不变量)
Runs only if
cartograph-invariants.md
exists at the repo root. Otherwise return
null
and Assemble omits the
invariants
key entirely.
  1. Read
    cartograph-invariants.md
    .
  2. For every invariant, run the Verifying an invariant procedure below.
  3. Compute summary counts (total, passing, failing, skipped).
  4. Set
    verifiedAt
    to the current ISO 8601 timestamp and
    definitionsFile
    to
    "cartograph-invariants.md"
    .
Returns: the
invariants
object matching the schema in
references/json-schema.md
, or
null
.

仅当仓库根目录存在
cartograph-invariants.md
时运行。否则返回
null
,组装阶段会完全省略
invariants
键。
  1. 读取
    cartograph-invariants.md
  2. 针对每个不变量,执行下面的验证不变量流程。
  3. 计算汇总统计(总数、通过数、失败数、跳过数)。
  4. verifiedAt
    设置为当前ISO 8601时间戳,
    definitionsFile
    设置为
    "cartograph-invariants.md"
返回:符合
references/json-schema.md
中schema的
invariants
对象,或
null

4. Assemble

4. 组装阶段

Run this yourself. Merge everything into
.cartograph/mapping.json
:
  1. Populate
    entityIds
    ,
    operationIds
    ,
    flowIds
    , and
    compartmentIds
    on each surface — from operations, flows, and the Compartment Dependencies output.
  2. Populate
    compartmentIds
    on each feature — from the Compartment Dependencies output. Set
    "files": []
    (the empty array is the back-compat shape;
    compartmentIds
    is the primary code mapping).
  3. Include the
    techStack
    array from Discover as-is.
  4. Include the File Tree Weights array as-is under
    fileTree
    .
  5. Add a top-level
    codeHealth
    object:
    analyzedAt = ISO timestamp
    ,
    metrics = [coLocationMetric, drynessMetric, deadCodeMetric]
    .
  6. If the Invariants analysis returned a non-null result, include
    "invariants": <result>
    . If
    null
    , omit the key.
  7. Write
    .cartograph/mapping.json
    at the repo root, creating the
    .cartograph/
    directory if needed. The final shape lives in
    references/json-schema.md
    .
  8. Tell the user: "Start the Cartograph UI from your project root with
    npm --prefix skills/cartograph/app install
    once, then
    npm --prefix skills/cartograph/app start
    ." If invariants were verified, also print the invariant summary (same format as Standalone Verify).

自行运行此阶段。将所有结果合并到
.cartograph/mapping.json
中:
  1. 为每个Surface填充
    entityIds
    operationIds
    flowIds
    compartmentIds
    ——来自operations、flows和模块依赖阶段的输出。
  2. 为每个Feature填充
    compartmentIds
    ——来自模块依赖阶段的输出。设置
    "files": []
    (空数组为兼容格式;
    compartmentIds
    是主要代码映射)。
  3. 原样包含发现阶段的
    techStack
    数组。
  4. 原样包含File Tree Weights数组,放在
    fileTree
    下。
  5. 添加顶级
    codeHealth
    对象:
    analyzedAt = ISO时间戳
    metrics = [coLocationMetric, drynessMetric, deadCodeMetric]
  6. 如果不变量分析返回非null结果,包含
    "invariants": <result>
    。如果为
    null
    ,省略该键。
  7. 在仓库根目录写入
    .cartograph/mapping.json
    ,必要时创建
    .cartograph/
    目录。最终格式位于
    references/json-schema.md
  8. 告知用户:"从项目根目录启动Cartograph UI,先执行一次
    npm --prefix skills/cartograph/app install
    ,然后执行
    npm --prefix skills/cartograph/app start
    。"如果验证了不变量,还需打印不变量摘要(格式与独立验证相同)。

Add Invariant Flow

添加不变量流程

When the user wants to add a new invariant:
  1. Extract the user's assertion text (the natural-language claim after "add invariant:" or similar phrasing).
  2. Read the codebase to understand the assertion:
    • Identify relevant files, functions, and patterns related to the assertion.
    • Determine which surfaces and features are involved (if a previous
      .cartograph/mapping.json
      exists, reference its IDs for
      surfaceIds
      and
      featureIds
      ).
    • Map out the verification approach.
  3. Expand the one-liner into a full invariant definition following
    references/invariant-definitions-format.md
    :
    • Write the YAML frontmatter: generate a unique kebab-case
      id
      ; set
      severity
      (critical for money/security/data integrity, high for core product logic, low for conventions); add relevant
      tags
      ; optionally add
      surfaceIds
      /
      featureIds
      .
    • Write all body sections: Assertion, Verification steps, Pass criteria, Known scope, Verification prompt.
  4. Append the invariant to
    cartograph-invariants.md
    at the repo root. Create the file with a
    # Cartograph Invariants
    heading if it doesn't exist.
  5. Run an initial verification using the Verifying an invariant procedure below.
  6. Report the result:
    • Passing: "Invariant added and verified. Definition saved to
      cartograph-invariants.md
      ."
    • Failing: "Invariant added but does NOT currently hold — definition saved anyway. Violations: [details]. Fix the code to make it pass, or edit the definition if the assertion needs adjusting."
If the assertion is too vague to determine verification steps, ask a clarifying question before writing the definition.

当用户想要添加新不变量时:
  1. 提取用户的断言文本("add invariant:"或类似表述后的自然语言声明)。
  2. 读取代码库以理解断言:
    • 识别与断言相关的文件、函数和模式。
    • 确定涉及的Surfaces和Features(如果之前存在
      .cartograph/mapping.json
      ,引用其ID作为
      surfaceIds
      featureIds
      )。
    • 规划验证方法。
  3. 将单行断言扩展为完整的不变量定义,遵循
    references/invariant-definitions-format.md
    • 编写YAML前置元数据:生成唯一的短横线命名
      id
      ;设置
      severity
      (关键=涉及资金/安全/数据完整性,高=核心产品逻辑,低=约定);添加相关
      tags
      ;可选添加
      surfaceIds
      /
      featureIds
    • 编写所有正文部分:断言验证步骤通过标准已知范围验证提示
  4. 将不变量追加到仓库根目录的
    cartograph-invariants.md
    中。如果文件不存在,创建并添加
    # Cartograph Invariants
    标题。
  5. 使用下面的验证不变量流程执行初始验证。
  6. 报告结果:
    • 通过:"不变量已添加并验证。定义已保存到
      cartograph-invariants.md
      。"
    • 失败:"不变量已添加,但当前不成立——定义仍已保存。违规详情:[具体内容]。修复代码使其通过,或调整断言以修改定义。"
如果断言过于模糊无法确定验证步骤,在编写定义前先询问澄清问题。

Standalone Verify Flow

独立验证流程

When the user wants to verify existing invariants without a full scan:
  1. Read
    cartograph-invariants.md
    from the repo root. If the file doesn't exist: respond "No invariant definitions found. Add one with:
    /cartograph add this invariant: '...'
    ".
  2. Run the Verifying an invariant procedure below on every invariant in the file.
  3. Print a pass/fail summary to the console:
    Invariant Results (N checked)
    ──────────────────────────────────
    ✓ CRITICAL  Invariant name
      Summary of passing result
    
    ✗ HIGH  Invariant name
      Violation in file:line
      Brief description of violation
    
    N of M invariants passing.
  4. If
    .cartograph/mapping.json
    exists, update only the
    invariants
    key (leave all other data untouched). Write the
    invariants
    object following the schema in
    references/json-schema.md
    .
  5. If
    .cartograph/mapping.json
    doesn't exist, create the
    .cartograph/
    directory if needed and write a minimal JSON with only
    meta
    and
    invariants
    keys.

当用户想要验证现有不变量而不执行完整扫描时:
  1. 读取仓库根目录的
    cartograph-invariants.md
    。如果文件不存在:回复"未找到不变量定义。使用
    /cartograph add this invariant: '...'
    添加一个。"
  2. 对文件中的每个不变量执行下面的验证不变量流程。
  3. 在控制台打印通过/失败摘要:
    不变量结果(已检查N个)
    ──────────────────────────────────
    ✓ CRITICAL  不变量名称
      通过结果摘要
    
    ✗ HIGH  不变量名称
      违规位置:文件:行号
      违规简要描述
    
    M个不变量中有N个通过。
  4. 如果
    .cartograph/mapping.json
    存在,仅更新**
    invariants
    键**(保留其他所有数据不变)。按照
    references/json-schema.md
    中的schema写入
    invariants
    对象。
  5. 如果
    .cartograph/mapping.json
    不存在,必要时创建
    .cartograph/
    目录,并写入仅包含
    meta
    invariants
    键的最小JSON文件。

Verifying an invariant

验证不变量

The shared procedure used by Add Invariant Flow, Standalone Verify Flow, and the Invariants analysis task in Full Scan.
  1. Parse the invariant: extract frontmatter fields and body sections (see
    references/invariant-definitions-format.md
    ).
  2. If
    enabled: false
    , emit a
    "skipped"
    result and stop.
  3. Follow the Verification steps section as a guide; read files listed in Known scope plus anything the steps reference.
  4. Evaluate whether the Pass criteria hold:
    • Passing: record checked files, an empty violations array, and set
      fixPrompt
      to
      null
      .
    • Failing: record specific violations with file paths, line numbers, what was expected, what was found, and a suggestion. Generate a self-contained
      fixPrompt
      that an AI agent can use to fix the specific violations — include the invariant name, violation details, affected paths/lines, and what needs to change.
  5. Set
    verificationPrompt
    on every result (passing or failing) to the Verification prompt from the invariant definition.
The result shape lives in
references/json-schema.md
under the
invariants
key.

添加不变量流程、独立验证流程和完整扫描中不变量分析任务共用的流程。
  1. 解析不变量:提取前置元数据字段和正文部分(参见
    references/invariant-definitions-format.md
    )。
  2. 如果
    enabled: false
    ,生成
    "skipped"
    结果并停止。
  3. 按照验证步骤部分的指引操作;读取已知范围中列出的文件以及步骤引用的任何文件。
  4. 评估通过标准是否成立:
    • 通过:记录已检查文件,违规数组为空,
      fixPrompt
      设为
      null
    • 失败:记录具体违规,包含文件路径、行号、预期结果、实际结果和建议。生成AI代理可用于修复特定违规的独立
      fixPrompt
      ——包含不变量名称、违规详情、受影响路径/行号以及需要修改的内容。
  5. 为每个结果(通过或失败)设置
    verificationPrompt
    ,值为不变量定义中的验证提示
结果格式位于
references/json-schema.md
invariants
键下。

Important

重要说明

  • Read-only on the analyzed codebase — never modify it. Only
    .cartograph/mapping.json
    and (on user request)
    cartograph-invariants.md
    are written.
  • Prefer inclusion with lower confidence over omission when unsure.
  • Plain-language descriptions — a PM should be able to read them.
  • Relative paths — all file paths relative to repo root.
  • Large repos — analyze by feature/route directory and merge.
  • 仅读取被分析的代码库——绝不修改它。仅写入
    .cartograph/mapping.json
    和(用户请求时)
    cartograph-invariants.md
  • 不确定时优先低置信度包含,而非遗漏
  • 使用通俗易懂的描述——产品经理应能读懂。
  • 使用相对路径——所有文件路径相对于仓库根目录。
  • 大型仓库——按功能/路由目录分析后合并结果。