@c4a/context-cli 0.5.29-alpha.2 → 0.5.29-beta.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +71 -34
  2. package/cli.js +43511 -20153
  3. package/package.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json.template +1 -1
  5. package/plugin/.codex-plugin/plugin.json.template +8 -5
  6. package/plugin/.cursor-plugin/plugin.json.template +29 -0
  7. package/plugin/README.md +127 -0
  8. package/plugin/README_CN.md +125 -0
  9. package/plugin/assets/icon.svg +3 -0
  10. package/plugin/assets/logo.svg +3 -0
  11. package/plugin/assets/workflow-en.png +0 -0
  12. package/plugin/assets/workflow.png +0 -0
  13. package/plugin/commands/align.md +50 -42
  14. package/plugin/commands/capture.md +27 -11
  15. package/plugin/commands/compile.md +91 -36
  16. package/plugin/commands/context.md +7 -4
  17. package/plugin/commands/drop.md +14 -6
  18. package/plugin/commands/extract.md +3 -3
  19. package/plugin/commands/init.md +48 -9
  20. package/plugin/commands/purge.md +6 -6
  21. package/plugin/commands/query.md +11 -3
  22. package/plugin/commands/status.md +4 -2
  23. package/plugin/skills/skill-align-workflow/SKILL.md +61 -0
  24. package/plugin/skills/skill-align-workflow/references/candidate-resolution.md +60 -0
  25. package/plugin/skills/skill-align-workflow/references/density-profile.md +23 -0
  26. package/plugin/skills/skill-align-workflow/references/gates.md +95 -0
  27. package/plugin/skills/skill-compile-close/SKILL.md +127 -0
  28. package/plugin/skills/skill-compile-draft/SKILL.md +409 -0
  29. package/plugin/skills/skill-context-query/SKILL.md +184 -0
  30. package/plugin/skills/skill-drop/SKILL.md +190 -0
  31. package/plugin/skills/skill-semantic-reconcile/SKILL.md +251 -0
  32. package/scripts/build-plugin.ts +674 -45
  33. package/templates/aspects/code/aspect.yaml +21 -0
  34. package/templates/aspects/code/prompt.md +33 -18
  35. package/templates/aspects/design-system/prompt.md +2 -3
  36. package/templates/aspects/graphql/prompt.md +2 -2
  37. package/templates/aspects/openapi/prompt.md +2 -2
  38. package/plugin/.claude-plugin/plugin.json +0 -16
  39. package/plugin/.codex-plugin/plugin.json +0 -35
  40. package/plugin/commands/capture-aspect.md +0 -17
  41. package/plugin/commands/capture-code.md +0 -25
  42. package/plugin/skills/align-finalize/SKILL.md +0 -137
  43. package/plugin/skills/align-propose/SKILL.md +0 -163
  44. package/plugin/skills/align-scan/SKILL.md +0 -161
  45. package/plugin/skills/align-scan/references/data-model.md +0 -343
  46. package/plugin/skills/align-scan/references/user-question-contract.md +0 -159
  47. package/plugin/skills/compile-close/SKILL.md +0 -122
  48. package/plugin/skills/compile-draft/SKILL.md +0 -252
  49. package/plugin/skills/context-query/SKILL.md +0 -166
  50. package/plugin/skills/drop/SKILL.md +0 -170
  51. package/plugin/skills/semantic-reconcile/SKILL.md +0 -129
@@ -0,0 +1,21 @@
1
+ schema_version: "1.0"
2
+ name: code
3
+ runner:
4
+ package: "@c4a/extract"
5
+ bin: c4a-extract-code
6
+ plugins:
7
+ - package: "@c4a/extract-ts"
8
+ export: TypeScriptPlugin
9
+ output:
10
+ bucket: raw/aspect/code/<source-slug>/<snapshot-id>/
11
+ files:
12
+ - source.yaml
13
+ - manifest.json
14
+ - digests.jsonl
15
+ - source-files.jsonl
16
+ - packages.jsonl
17
+ - symbols.jsonl
18
+ - edges.jsonl
19
+ - _meta.yaml
20
+ evidence:
21
+ mode: none
@@ -1,29 +1,44 @@
1
1
  # aspect: code
2
2
 
3
- > Code structure aspect. The `context capture-code` command invokes the
4
- > `@c4a/extract` / `@c4a/extract-ts` pipeline directly — it does not read
5
- > this file. The prompt exists as a placeholder so the init flow is
6
- > uniform and so a future scripted-aspect mechanism can take over without
7
- > reshuffling directory structure.
3
+ > Code structure aspect. The public entrypoint is `context capture --code`.
8
4
 
9
5
  ## Target
10
6
 
11
- Extract `Package` / `Symbol` nodes and `imports` / `calls` / `extends` edges
12
- from the current git worktree.
7
+ Extract package, symbol, edge, digest, and source-file rows from the local
8
+ worktree. The runner reads JSON from stdin and returns NDJSON events; the
9
+ context CLI owns `.context` writes.
13
10
 
14
- ## Schema
11
+ ## Raw Snapshot
15
12
 
16
- Payloads are written by the CLI as JSONL files under each dated bucket:
13
+ Each capture writes a source-scoped bucket:
17
14
 
18
- - `packages.jsonl` — one `Package` per line
19
- - `symbols.jsonl` — one `Symbol` per line (recursive `members` flattened)
20
- - `edges.jsonl` — one relation per line (optional)
21
- - `_meta.yaml` — bucket metadata (`commit`, `captured_at`, `script_hash`,
22
- `content_hash`)
15
+ ```text
16
+ raw/aspect/code/<source-slug>/<snapshot-id>/
17
+ ```
18
+
19
+ The bucket contains:
20
+
21
+ - `source.yaml` — local source identity and publish upsert hints
22
+ - `manifest.json` — snapshot identity, runner hash, counts, and content hash
23
+ - `digests.jsonl` — module digest rows with `hash_id`, `module_path`, and `dir_commit`
24
+ - `source-files.jsonl` — service-compatible source file mapping
25
+ - `packages.jsonl` — flat package/module view
26
+ - `symbols.jsonl` — flat symbol view, with nested members flattened
27
+ - `edges.jsonl` — flat code relation view
28
+ - `_meta.yaml` — compact copy of snapshot metadata for legacy readers
23
29
 
24
30
  ## Notes
25
31
 
26
- - Currently driven by the CLI, not by a per-aspect script. The scripted
27
- `capture.ts` contract for non-code aspects is planned for a later release.
28
- - source-id is always `aspect:code`.
29
- - Bucket name is `YYYY-MM-DD-<sha7>` where `<sha7>` is `git rev-parse --short HEAD`.
32
+ - New writes use `aspect:code:<source-slug>` as the source id.
33
+ - Repeated captures of the same commit, script hash, and snapshot content are
34
+ reported as unchanged and do not create duplicate snapshots.
35
+ - Dirty worktrees are allowed; dirty module rows keep the real `dir_commit`
36
+ and use a content-sensitive `hash_id`.
37
+ - The code aspect uses `evidence.mode: none`: it does not generate
38
+ `raw/.evidence` block manifests. Symbols, files, and edges are already
39
+ represented by the bucket JSONL indexes.
40
+ - Formal code DSL query and logical code `source_ref` support are tracked by a
41
+ later version; this aspect only prepares the raw snapshot contract.
42
+ - Runner raw extraction results are intentionally not written as a separate
43
+ bucket file. Future consumers should read `digests.jsonl` and the flat
44
+ package/symbol/edge projections instead.
@@ -1,9 +1,8 @@
1
1
  # aspect: design-system
2
2
 
3
3
  > Design-system aspect placeholder. The current CLI does not yet provide
4
- > a scripted capture driver for this aspect `context capture-aspect
5
- > design-system` will report that scripted aspects beyond `code` are not
6
- > implemented. This file exists so the init flow keeps the aspect slot
4
+ > a scripted capture driver for this aspect. Only code capture is exposed
5
+ > today via `context capture --code`. This file exists so the init flow keeps the aspect slot
7
6
  > consistent for a future scripted-capture release.
8
7
 
9
8
  ## Target
@@ -1,8 +1,8 @@
1
1
  # aspect: graphql
2
2
 
3
3
  > GraphQL aspect placeholder. The current CLI does not yet provide a
4
- > scripted capture driver `context capture-aspect graphql` will report
5
- > that scripted aspects beyond `code` are not implemented.
4
+ > scripted capture driver for this aspect; only code capture is exposed via
5
+ > `context capture --code`.
6
6
 
7
7
  ## Target
8
8
 
@@ -1,8 +1,8 @@
1
1
  # aspect: openapi
2
2
 
3
3
  > OpenAPI aspect placeholder. The current CLI does not yet provide a
4
- > scripted capture driver `context capture-aspect openapi` will report
5
- > that scripted aspects beyond `code` are not implemented.
4
+ > scripted capture driver for this aspect; only code capture is exposed via
5
+ > `context capture --code`.
6
6
 
7
7
  ## Target
8
8
 
@@ -1,16 +0,0 @@
1
- {
2
- "name": "context",
3
- "description": "Context For AI — local knowledge workspace. Capture docs, extract code structure, compile into an interlinked wiki with source-traced facts. CLI does mechanical work; agent drives compile/drop via skill protocols.",
4
- "version": "0.5.29-alpha.2",
5
- "author": {
6
- "name": "c4a"
7
- },
8
- "license": "MIT",
9
- "keywords": [
10
- "context",
11
- "knowledge-base",
12
- "wiki",
13
- "claude-plugin",
14
- "c4a"
15
- ]
16
- }
@@ -1,35 +0,0 @@
1
- {
2
- "name": "context",
3
- "version": "0.5.29-alpha.2",
4
- "description": "Context For AI — local knowledge workspace. Capture docs, extract code structure, compile into source-traced local knowledge, query with citations, and govern source updates.",
5
- "author": {
6
- "name": "c4a"
7
- },
8
- "homepage": "https://github.com/context4ai/c4a",
9
- "repository": "https://github.com/context4ai/c4a/tree/main/packages/context-cli",
10
- "license": "MIT",
11
- "keywords": [
12
- "context",
13
- "knowledge-base",
14
- "codex-plugin",
15
- "agent-skills",
16
- "c4a"
17
- ],
18
- "skills": "./skills/",
19
- "interface": {
20
- "displayName": "C4A Context",
21
- "shortDescription": "Local knowledge workspace skills for C4A.",
22
- "longDescription": "Use the context CLI and bundled skills to capture project sources, align them to a Node tree, compile source-traced local knowledge, query with citations, and govern source drop or refresh workflows.",
23
- "developerName": "c4a",
24
- "category": "Productivity",
25
- "capabilities": [
26
- "Read",
27
- "Write"
28
- ],
29
- "websiteURL": "https://github.com/context4ai/c4a",
30
- "defaultPrompt": [
31
- "Use C4A Context to answer from local knowledge with citations.",
32
- "Use C4A Context to compile newly captured sources into local knowledge."
33
- ]
34
- }
35
- }
@@ -1,17 +0,0 @@
1
- ---
2
- description: "Run a named aspect capture pipeline. Stub in the current CLI — every invocation exits non-zero with 'planned for a future release'."
3
- argument-hint: "<aspect-name>"
4
- allowed-tools: Bash(context:*)
5
- ---
6
-
7
- ## Your task
8
-
9
- `capture-aspect` is a placeholder. Every invocation, including `capture-aspect code`, exits non-zero with a deferred-to-a-future-release message; the pluggable aspect pipeline lands later.
10
-
11
- Run `context capture-aspect $ARGUMENTS` and relay the CLI's exit code + stderr verbatim. Do NOT route `code` to `/context:capture-code` — the two commands have different semantics, and the stub intentionally blocks this entry point so future aspect kinds land behind one consistent surface.
12
-
13
- Users who actually want to snapshot the repo's code structure today should be told to run `/context:capture-code` directly; mention it as a pointer in your reply. Do not auto-invoke it.
14
-
15
- Language policy: your explanatory prose follows the user's conversation language. Relay CLI stderr/stdout verbatim; command names, paths, source-ids, and quoted CLI phrases stay as printed.
16
-
17
- Never work around the stub by shelling into extraction directly or writing into workspace `raw/aspect/<name>/` by hand — the CLI is the sole writer of `raw/`.
@@ -1,25 +0,0 @@
1
- ---
2
- description: "Extract TypeScript code structure (Package / Symbol / Edge) from the current git repository into raw/aspect/code/."
3
- argument-hint: ""
4
- allowed-tools: Bash(context:*)
5
- ---
6
-
7
- ## Your task
8
-
9
- Run `context capture-code` from anywhere inside the workspace — the CLI always scans the resolved workspace root, not the current subdirectory, so invoking it from a subfolder like `apps/web/src/` still snapshots the full repo. The CLI:
10
-
11
- - Requires the workspace to live inside a git checkout (needs `HEAD` sha).
12
- - Writes a bucket `raw/aspect/code/<YYYY-MM-DD>-<sha7>/` containing `packages.jsonl`, `symbols.jsonl`, `edges.jsonl`, `_meta.yaml`.
13
- - Updates `raw/aspect/code/latest` symlink and registers the snapshot in `raw/_sources.yaml` under source-id `aspect:code` (snapshots array entry carries `commit` + `script_hash`). Raw-side audit is kept via `_sources.yaml` snapshots + git history — the standalone `log.md` from earlier CLI generations is no longer maintained.
14
- - Skips with `unchanged, skipped` when the commit + script hash combination has already been captured.
15
- - Refuses with an actionable error when extraction finds zero scannable packages (rather than writing an empty bucket that Compile would later mistake for the full repo).
16
-
17
- Report whether a new bucket was produced or the run was a skipped idempotent re-run. When a fresh bucket lands, suggest the right next step:
18
-
19
- - Run `context workspace locate --format json` and use `ctxDir` as `WORKSPACE_DIR`.
20
- - `WORKSPACE_DIR/output/align.md` does not exist → suggest `/context:align` (code snapshot becomes align / compile material, but compile requires the structural plan from align first).
21
- - `WORKSPACE_DIR/output/align.md` exists → suggest `/context:compile`.
22
-
23
- Language policy: your explanatory prose follows the user's conversation language. CLI output, paths, source-ids, command names, commit hashes, and status tokens such as `unchanged, skipped` stay as printed.
24
-
25
- Do NOT write workspace `raw/aspect/code/` directly — only the CLI mutates that tree.
@@ -1,137 +0,0 @@
1
- ---
2
- name: align-finalize
3
- description: >
4
- Third skill of `/context:align`. Reconciles `WORKSPACE_DIR/output/align.propose.yaml`
5
- with the user's `WORKSPACE_DIR/output/align.user-decisions.yaml`, applies every pick
6
- (ambiguous / orphan / accept-reject / addition / rename), re-validates
7
- the tree against identity / establishment / tag / mount / layout rules,
8
- and writes the final AlignRenderInput YAML. The CLI renders
9
- `WORKSPACE_DIR/output/align.md` and binds raw mentions to `WORKSPACE_DIR/output/bind.yaml`.
10
- Activates after the user has reviewed `align.propose.md` and written
11
- their decisions file.
12
- tools:
13
- - Read
14
- - Bash
15
- ---
16
-
17
- # align-finalize — apply user decisions, emit the final plan
18
-
19
- Reconcile propose YAML with user-decisions YAML, settle every
20
- ambiguity, and produce the final `AlignRenderInput`. The CLI does
21
- all rendering.
22
-
23
- `WORKSPACE_DIR` is provided by the caller from `context workspace locate --format json` (`ctxDir`). It is `.context/` in embedded layout and the repository root in root layout.
24
-
25
- ## TL;DR — Non-negotiables
26
-
27
- - Inputs must both exist: `output/align.propose.yaml` (CLI render) + `output/align.user-decisions.yaml` (user pick), read through `context workspace read`. Missing → stop and say which one.
28
- - Output: final `AlignRenderInput` passed to `context align --finalize -`; add `--save-input` when the caller wants a durable scratch copy.
29
- - Respect user decisions literally; a demoted candidate does not appear in `nodes[]` even if you disagreed during propose.
30
- - `type` values are closed to `domain / entity / action / concept` (see [Node types](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#node-types)); reject anything else rather than silently mapping.
31
- - Domain hard rule: no Node sits inline under a Domain file; every child of a Domain is a standalone root (see [Layout rules](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#layout-rules)).
32
- - `planned_sections[]` must satisfy the [Section mount matrix](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#section-mount-matrix) for each Node's type.
33
- - Slugs reuse across loops for `existing` / `modified`; new slugs only for `new`; user rename decisions carry through verbatim.
34
- - Every edge endpoint references a slug in `nodes[]`; no dangling edges.
35
- - No cycles across `contains` + `refines`; user-decision-induced cycles reject with the cycle path.
36
- - User decisions are business-intent answers; map them back to the closed model yourself instead of asking follow-up questions with model terms.
37
- - Output language: user-facing prose and `decisions.notes` match the user's language; slug / tag / type / edge values stay English.
38
-
39
- <reference>
40
-
41
- ## User-decisions shape
42
-
43
- ```yaml
44
- decisions:
45
- ambiguous:
46
- - phrase: "..."
47
- choice: A # A / B / other
48
- custom: { ... } # optional for "other"
49
- orphans:
50
- - slug: "..."
51
- choice: deprecate # deprecate | keep | modify | supersede
52
- new_status: deprecated
53
- valid_until: <date>
54
- supersede_with: <slug> # when choice=supersede
55
- new_candidates:
56
- - name: "..."
57
- choice: accept # accept | reject | reclassify
58
- reclassify: { type: ..., tags: [...] }
59
- rename: <new-slug> # optional
60
- additions:
61
- - name: "..."
62
- suggested_type: ...
63
- suggested_tags: [...]
64
- ```
65
-
66
- Missing sections = "no change from propose". Read tolerantly.
67
-
68
- ## Action establishment recheck
69
-
70
- User decisions can drop an Action below its establishment bar (e.g.
71
- rejecting a sub-Action leaves the parent with a single Section
72
- intent). After applying all decisions, re-verify every `action` Node
73
- against [Node establishment thresholds](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#node-establishment-thresholds). Below bar → leave the Node in place (the user made the call) but flag the weakness in `decisions.notes`. The caller's next `context doctor` pass (run at the end of `/context:align`) surfaces the note via the output-align diagnostic group, giving the user a chance to reopen the decision on the next loop.
74
-
75
- ## Alias carry-through
76
-
77
- For every `new` Node, carry over the aliases the raw called it by
78
- (derived from scan mentions) so the close-time glossary build picks
79
- them up. Without aliases, later retrieval misses language variants.
80
-
81
- </reference>
82
-
83
- <procedures>
84
-
85
- ### Step 1 — Load inputs
86
-
87
- Read `output/align.propose.yaml` and `output/align.user-decisions.yaml` in full with `context workspace read <path> --format text`. Build a `decisions` lookup keyed by slug / phrase / name. If either file is missing, stop and name it.
88
-
89
- ### Step 2 — Apply `new_candidates` decisions
90
-
91
- For each propose `new` candidate:
92
-
93
- - `accept` → keep in the plan.
94
- - `reject` → drop from the plan; its mentions still show in `bind.yaml` as unbound raw.
95
- - `reclassify` → swap `type` + `tags`; re-check tags per the [Entity tag groups](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#entity-tag-groups) and concept gate rules.
96
- - `rename` → change `slug` / `title`; propagate to every edge + parent pointer.
97
-
98
- ### Step 3 — Apply `ambiguous` decisions
99
-
100
- Pick the matching candidate per the user's `choice`; drop the others. `custom` branches go through Step 2's `reclassify` path.
101
-
102
- ### Step 4 — Apply `orphans` decisions
103
-
104
- - `keep` → `status: existing`, no change.
105
- - `deprecate` → `status: orphan` + copy `new_status` / `valid_until`.
106
- - `modify` → `status: modified` + record intent in `decisions.notes`.
107
- - `supersede` → `status: orphan` on the old slug + add a new Node for the supersession target from `supersede_with`.
108
-
109
- ### Step 5 — Apply `additions`
110
-
111
- For every user addition, run it through the same gates as propose ([establishment](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#node-establishment-thresholds), [tag rules](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#entity-tag-groups), [concept gate](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#concept-three-test-gate)). Gate failure → surface to the caller; the user needs to revise. Do not silently drop.
112
-
113
- ### Step 6 — Fill and verify `planned_sections[]`
114
-
115
- Carry propose's hint when set; extend from raw evidence only with kinds that satisfy the [Section mount matrix](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#section-mount-matrix) for the Node's type. Drop mismatches and note them in `decisions.notes`.
116
-
117
- ### Step 7 — Layout + cycle check
118
-
119
- - Confirm no Node carries a `contains_parent` that would require inline nesting under a Domain (see [Layout rules](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#layout-rules)).
120
- - Run a cycle check over `contains` + `refines`. Any cycle → reject with the offending path in the output; do not auto-break.
121
-
122
- ### Step 8 — Write the final input
123
-
124
- Emit the confirmed `AlignRenderInput` shape (same shape as align-propose's output) for the caller to pass to `context align --finalize -` (or `context align --finalize - --save-input` when a durable scratch copy is needed). Return control to the caller.
125
-
126
- ### Step 9 — Self-verify
127
-
128
- - [ ] Every `type` is one of the four closed values — if not, **Step 2/3/5**.
129
- - [ ] Every `entity` has ≥1 A/B tag — if not, **Step 2/3/5**.
130
- - [ ] Every `action` cleared the establishment bar OR carries a `decisions.notes` override — if not, **Step 2**.
131
- - [ ] Every `planned_sections[]` entry is legal for the Node type — if not, **Step 6**.
132
- - [ ] No Node sits inline under a Domain — if any, **Step 7**.
133
- - [ ] No cycles — if any, **Step 7**.
134
- - [ ] Every edge endpoint appears in `nodes[]` — if not, **Step 8**.
135
- - [ ] No Read / Glob / Grep / Write was used against `WORKSPACE_DIR` — if any, restart with `context workspace` / `context mdrive` commands.
136
-
137
- </procedures>
@@ -1,163 +0,0 @@
1
- ---
2
- name: align-propose
3
- description: >
4
- Second skill of `/context:align`. Reads the align-scan decisions file +
5
- the existing `knowledge/` contains tree from `context mdrive query
6
- --contains-tree --format json`, assembles a candidate Node tree, validates every candidate
7
- against identity / establishment / tag / Edge rules, marks each node
8
- new / existing / modified / orphan, and writes an AlignRenderInput
9
- YAML. The CLI renders `WORKSPACE_DIR/output/align.propose.md` + `.yaml` for the user
10
- to review. Activates when align-scan decisions are on disk and the
11
- pipeline needs a reviewable tree before align-finalize.
12
- tools:
13
- - Read
14
- - Bash
15
- ---
16
-
17
- # align-propose — assemble a reviewable Node tree
18
-
19
- Promote classified mentions into a contains-rooted tree with explicit
20
- Edges, applying the identity / establishment / tag rules. Output is
21
- pure YAML; the CLI does the rendering.
22
-
23
- `WORKSPACE_DIR` is provided by the caller from `context workspace locate --format json` (`ctxDir`). It is `.context/` in embedded layout and the repository root in root layout.
24
-
25
- ## TL;DR — Non-negotiables
26
-
27
- - Input: `output/align.scan-decisions.json` read through `context workspace read` + the current contains tree from `context mdrive query --contains-tree --format json`.
28
- - Output: an `AlignRenderInput` passed to `context align --propose -`; add `--save-input` when the caller wants a durable scratch copy.
29
- - Every candidate carries a `status` (see [Status markers](#status-markers)); `new` is never the default.
30
- - Entity candidates pass [Entity tag groups](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#entity-tag-groups); re-route false Entities per [False-Entity smell](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#false-entity-smell).
31
- - Action candidates clear [Node establishment thresholds](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#node-establishment-thresholds); below-bar demote to Section suggestions.
32
- - Concept candidates pass the [Concept three-test gate](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#concept-three-test-gate); failures demote.
33
- - Edges come from the five logical types only (see [Edge types](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#edge-types)); `refers_to_nodes` is a Section field, not an Edge.
34
- - No `contains` edge from a Domain to a Node that would nest inline — Domain children are always standalone files (see [Layout rules](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#layout-rules)).
35
- - No cycles in `contains` + `refines`.
36
- - Slugs are kebab-case, ≤60 chars, stable across loops.
37
- - User review questions follow `${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/user-question-contract.md`: ask for business intent, never storage mechanics.
38
- - Output language: user-facing prose and `decisions.notes` match the user's language; slugs / tag / type / edge values stay English.
39
-
40
- <reference>
41
-
42
- ## Status markers
43
-
44
- | marker | When to use |
45
- |---|---|
46
- | `existing` | Exact slug / title match in workspace AND raw does not contradict the glossary entry |
47
- | `new` | No existing Node; raw material justifies creating it |
48
- | `modified` | Matches an existing Node AND raw adds information (new Sections incoming, or raw flips a decision) |
49
- | `orphan` | Existing Node whose raw footprint says sunset / deprecated / superseded; user decides deprecate / keep / supersede |
50
-
51
- ## AlignRenderInput shape
52
-
53
- ```yaml
54
- workspace: <workspace-name>
55
- nodes:
56
- - slug: <kebab-case>
57
- type: domain | entity | action | concept
58
- tags: [...]
59
- title: <human title>
60
- sources: [<source-id>, ...]
61
- aliases: [...]
62
- summary: <optional one-liner>
63
- status: new | existing | modified | orphan
64
- contains_parent: <slug, optional>
65
- planned_sections: [description, spec, warning]
66
- edges:
67
- - type: contains | depends_on | realizes | refines | related_to
68
- from: <slug>
69
- to: <slug>
70
- note: <optional>
71
- decisions:
72
- notes: |
73
- <brief rationale + demotions + unresolved questions>
74
- new_candidates:
75
- - slug: <accepted-node-slug>
76
- mentions:
77
- - file: <raw-file>
78
- line: <line-number>
79
- quote: <verbatim raw quote>
80
- demotions:
81
- - heading: <candidate or heading that was not promoted>
82
- outcome: kept as Section | merged into <slug> | ignored as navigation
83
- mentions:
84
- - file: <raw-file>
85
- line: <line-number>
86
- quote: <verbatim raw quote>
87
- ambiguous: [ ... carried from scan + discovered re-routes ... ]
88
- orphans: [ ... carried from scan ... ]
89
- ```
90
-
91
- ## User-review shape
92
-
93
- The CLI renders the proposal to `align.propose.md`. Shape your output
94
- so the caller can ask a small number of business-intent questions. The
95
- user should never need to know the internal Node / Section / Edge model.
96
-
97
- 1. **Same thing or separate topic?** Pre-resolve with the identity tests.
98
- Surface only hard cases in `decisions.ambiguous[]`.
99
- 2. **Standalone lookup or parent detail?** Set the internal parent / demotion
100
- fields yourself. Ask the user only when future user-visible lookup or
101
- organization is unclear.
102
- 3. **Business relationship?** Ask only when raw evidence leaves multiple
103
- plausible meanings, such as alternatives vs dependency vs co-location.
104
- 4. **Validity change?** Ask when old and new source documents conflict about
105
- what readers should trust now.
106
- 5. **Navigation-only duplication?** Do not ask. Repeated "Related links",
107
- "References", and similar blocks are handled as structure unless they carry
108
- new business prose.
109
-
110
- </reference>
111
-
112
- <procedures>
113
-
114
- ### Step 1 — Load inputs
115
-
116
- 1. Read `output/align.scan-decisions.json` with `context workspace read output/align.scan-decisions.json --format text`.
117
- 2. Run `context mdrive query --contains-tree --format json` and use the returned `nodes[]` + `edges[]` as the current workspace skeleton. `contains` edges encode both same-file heading children and cross-file `## Contains` links; do not rebuild the tree with Glob + Read.
118
-
119
- ### Step 2 — Seat `existing_nodes`
120
-
121
- Copy every `existing` entry into `nodes[]` with its current parent pointer from the tree. Set `status: existing`, or `modified` if new mentions imply incoming Sections. Do not re-parent without explicit raw evidence.
122
-
123
- ### Step 3 — Promote new candidates
124
-
125
- For each `new_candidates[]` from `output/align.scan-decisions.json`:
126
-
127
- 1. Check [Node establishment thresholds](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#node-establishment-thresholds). Below bar → skip the Node; record a Section demotion in `decisions.notes`.
128
- 2. For `entity`, verify tags per the data-model reference. Neither-group hits → re-route via [False-Entity smell](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#false-entity-smell) into `decisions.ambiguous[]`.
129
- 3. For `concept`, run the [Concept three-test gate](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#concept-three-test-gate). Any ❌ → demote; do not emit.
130
- 4. Pick `contains_parent` from the scan's `suggested_parent` or a matching existing Domain.
131
- 5. Fill `planned_sections[]` only where raw clearly justifies kinds that fit the target type per the [Section mount matrix](${CLAUDE_PLUGIN_ROOT}/skills/align-scan/references/data-model.md#section-mount-matrix).
132
- 6. For every candidate you promote into `nodes[]`, copy its raw `mentions[]`
133
- into `decisions.new_candidates[]` under the final `slug`. For every
134
- candidate or heading you do not promote, add `decisions.demotions[]` with
135
- the raw mention and the outcome. These are review evidence, not new
136
- summaries.
137
-
138
- ### Step 4 — Enumerate edges
139
-
140
- One `edges[]` entry per `contains` edge in the tree. Add `depends_on` / `realizes` / `refines` / `related_to` only where raw explicitly justifies the relationship AND both endpoints appear in `nodes[]`. Never emit `refers_to_nodes`-style edges. Run a cycle check over `contains` + `refines`; reject any cycle.
141
-
142
- ### Step 5 — Carry ambiguous and orphans
143
-
144
- Copy `scan.ambiguous[]` into `decisions.ambiguous[]` plus any discoveries from Step 3 re-routes. Copy `scan.orphans[]` into `decisions.orphans[]`. Do not make deprecation calls yourself; the user decides in the next gate.
145
-
146
- ### Step 6 — Write the proposal input
147
-
148
- Emit the [AlignRenderInput shape](#alignrenderinput-shape) as YAML for the caller to pass to `context align --propose -` (or `context align --propose - --save-input` when a durable scratch copy is needed). Return control to the caller; the caller renders `align.propose.md` + `.yaml`.
149
-
150
- ### Step 7 — Self-verify
151
-
152
- - [ ] Every `entity` has ≥1 A/B tag — if not, **Step 3**.
153
- - [ ] Every `action` clears the establishment bar OR is demoted — if not, **Step 3**.
154
- - [ ] Every `concept` passes the three-test gate — if not, **Step 3**.
155
- - [ ] Every promoted new candidate has `decisions.new_candidates[].mentions[]` copied from scan evidence — if not, **Step 3**.
156
- - [ ] Every demoted candidate/heading has `decisions.demotions[]` with raw mentions — if not, **Step 3**.
157
- - [ ] No cycles in `contains` + `refines` — if any, **Step 4**.
158
- - [ ] No `refers_to_nodes` edges — if any, **Step 4**.
159
- - [ ] No `new` slug collides with an existing slug — if any, treat as `modified` in **Step 3**.
160
- - [ ] `decisions.ambiguous[]` + `decisions.orphans[]` carried through — if not, **Step 5**.
161
- - [ ] No Read / Glob / Grep / Write was used against `WORKSPACE_DIR` — if any, restart with `context workspace` / `context mdrive` commands.
162
-
163
- </procedures>