ctxr-cli 0.8.0 → 0.9.0

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 (131) hide show
  1. package/README.md +303 -22
  2. package/dist/adapters/types.d.ts +0 -5
  3. package/dist/adapters/types.d.ts.map +1 -1
  4. package/dist/commands/adapters-generate.d.ts.map +1 -1
  5. package/dist/commands/adapters-generate.js +2 -3
  6. package/dist/commands/adapters-generate.js.map +1 -1
  7. package/dist/commands/context-gather.d.ts +22 -0
  8. package/dist/commands/context-gather.d.ts.map +1 -0
  9. package/dist/commands/context-gather.js +36 -0
  10. package/dist/commands/context-gather.js.map +1 -0
  11. package/dist/commands/graph-query.d.ts.map +1 -1
  12. package/dist/commands/graph-query.js +6 -8
  13. package/dist/commands/graph-query.js.map +1 -1
  14. package/dist/commands/ingest.d.ts +22 -9
  15. package/dist/commands/ingest.d.ts.map +1 -1
  16. package/dist/commands/ingest.js +86 -24
  17. package/dist/commands/ingest.js.map +1 -1
  18. package/dist/commands/init.d.ts.map +1 -1
  19. package/dist/commands/init.js +21 -5
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/source-check.d.ts.map +1 -1
  22. package/dist/commands/source-check.js +27 -9
  23. package/dist/commands/source-check.js.map +1 -1
  24. package/dist/commands/source-stamp.d.ts +6 -5
  25. package/dist/commands/source-stamp.d.ts.map +1 -1
  26. package/dist/commands/source-stamp.js +8 -7
  27. package/dist/commands/source-stamp.js.map +1 -1
  28. package/dist/config/defaults.d.ts +103 -4
  29. package/dist/config/defaults.d.ts.map +1 -1
  30. package/dist/config/defaults.js +76 -4
  31. package/dist/config/defaults.js.map +1 -1
  32. package/dist/config/render.d.ts +11 -0
  33. package/dist/config/render.d.ts.map +1 -1
  34. package/dist/config/render.js +85 -3
  35. package/dist/config/render.js.map +1 -1
  36. package/dist/config/schema.d.ts +50 -30
  37. package/dist/config/schema.d.ts.map +1 -1
  38. package/dist/config/schema.js +151 -62
  39. package/dist/config/schema.js.map +1 -1
  40. package/dist/core/agents-doc.d.ts.map +1 -1
  41. package/dist/core/agents-doc.js +4 -1
  42. package/dist/core/agents-doc.js.map +1 -1
  43. package/dist/core/checks/harness-portability-checks.d.ts +1 -1
  44. package/dist/core/checks/harness-portability-checks.d.ts.map +1 -1
  45. package/dist/core/checks/harness-portability-checks.js +2 -3
  46. package/dist/core/checks/harness-portability-checks.js.map +1 -1
  47. package/dist/core/checks/manifest.d.ts.map +1 -1
  48. package/dist/core/checks/manifest.js +2 -0
  49. package/dist/core/checks/manifest.js.map +1 -1
  50. package/dist/core/checks/organize-checks.d.ts +11 -1
  51. package/dist/core/checks/organize-checks.d.ts.map +1 -1
  52. package/dist/core/checks/organize-checks.js +42 -12
  53. package/dist/core/checks/organize-checks.js.map +1 -1
  54. package/dist/core/checks/retrieval-checks.d.ts +9 -0
  55. package/dist/core/checks/retrieval-checks.d.ts.map +1 -0
  56. package/dist/core/checks/retrieval-checks.js +30 -0
  57. package/dist/core/checks/retrieval-checks.js.map +1 -0
  58. package/dist/core/content/canonicalize.d.ts +10 -0
  59. package/dist/core/content/canonicalize.d.ts.map +1 -1
  60. package/dist/core/content/canonicalize.js +12 -0
  61. package/dist/core/content/canonicalize.js.map +1 -1
  62. package/dist/core/errors.d.ts +19 -0
  63. package/dist/core/errors.d.ts.map +1 -1
  64. package/dist/core/errors.js +53 -1
  65. package/dist/core/errors.js.map +1 -1
  66. package/dist/core/fs/prefix.d.ts +13 -0
  67. package/dist/core/fs/prefix.d.ts.map +1 -0
  68. package/dist/core/fs/prefix.js +22 -0
  69. package/dist/core/fs/prefix.js.map +1 -0
  70. package/dist/core/graph/persist.d.ts +13 -0
  71. package/dist/core/graph/persist.d.ts.map +1 -1
  72. package/dist/core/graph/persist.js +24 -0
  73. package/dist/core/graph/persist.js.map +1 -1
  74. package/dist/core/hooks.d.ts +3 -12
  75. package/dist/core/hooks.d.ts.map +1 -1
  76. package/dist/core/hooks.js +24 -20
  77. package/dist/core/hooks.js.map +1 -1
  78. package/dist/core/ingest/capture-hash.d.ts +16 -0
  79. package/dist/core/ingest/capture-hash.d.ts.map +1 -0
  80. package/dist/core/ingest/capture-hash.js +34 -0
  81. package/dist/core/ingest/capture-hash.js.map +1 -0
  82. package/dist/core/ingest/identity.d.ts +7 -1
  83. package/dist/core/ingest/identity.d.ts.map +1 -1
  84. package/dist/core/ingest/identity.js +16 -1
  85. package/dist/core/ingest/identity.js.map +1 -1
  86. package/dist/core/ingest/model.d.ts +17 -2
  87. package/dist/core/ingest/model.d.ts.map +1 -1
  88. package/dist/core/ingest/model.js +5 -0
  89. package/dist/core/ingest/model.js.map +1 -1
  90. package/dist/core/migrations/config-defaults-as-the-convention.d.ts +21 -0
  91. package/dist/core/migrations/config-defaults-as-the-convention.d.ts.map +1 -0
  92. package/dist/core/migrations/config-defaults-as-the-convention.js +54 -0
  93. package/dist/core/migrations/config-defaults-as-the-convention.js.map +1 -0
  94. package/dist/core/migrations/registry.d.ts.map +1 -1
  95. package/dist/core/migrations/registry.js +4 -0
  96. package/dist/core/migrations/registry.js.map +1 -1
  97. package/dist/core/migrations/retain-captures-as-provenance.d.ts +22 -0
  98. package/dist/core/migrations/retain-captures-as-provenance.d.ts.map +1 -0
  99. package/dist/core/migrations/retain-captures-as-provenance.js +133 -0
  100. package/dist/core/migrations/retain-captures-as-provenance.js.map +1 -0
  101. package/dist/core/notes/list.d.ts +20 -0
  102. package/dist/core/notes/list.d.ts.map +1 -1
  103. package/dist/core/notes/list.js +27 -6
  104. package/dist/core/notes/list.js.map +1 -1
  105. package/dist/core/records.d.ts +7 -0
  106. package/dist/core/records.d.ts.map +1 -1
  107. package/dist/core/records.js +12 -2
  108. package/dist/core/records.js.map +1 -1
  109. package/dist/core/retrieval/pass.d.ts +66 -0
  110. package/dist/core/retrieval/pass.d.ts.map +1 -0
  111. package/dist/core/retrieval/pass.js +171 -0
  112. package/dist/core/retrieval/pass.js.map +1 -0
  113. package/dist/core/write-lifecycle/path-gate.d.ts +2 -2
  114. package/dist/core/write-lifecycle/path-gate.d.ts.map +1 -1
  115. package/dist/core/write-lifecycle/path-gate.js +9 -10
  116. package/dist/core/write-lifecycle/path-gate.js.map +1 -1
  117. package/dist/run.d.ts +0 -5
  118. package/dist/run.d.ts.map +1 -1
  119. package/dist/run.js +38 -3
  120. package/dist/run.js.map +1 -1
  121. package/dist/version.d.ts +1 -1
  122. package/dist/version.js +1 -1
  123. package/package.json +12 -4
  124. package/templates/agents/capture-and-ingest.md +27 -8
  125. package/templates/agents/retrieval-leg-routing.md +34 -8
  126. package/templates/hooks/_resolve-ctxr.sh +37 -0
  127. package/templates/hooks/claude-code-write-gate.sh +34 -6
  128. package/templates/hooks/pre-commit.sh +15 -8
  129. package/templates/skills/ctxr-connection-finding.md +7 -5
  130. package/templates/skills/ctxr-ingest-orchestration.md +15 -9
  131. package/templates/skills/ctxr-publish.md +7 -8
@@ -1,17 +1,36 @@
1
1
  ## Capturing and ingesting new material
2
2
 
3
- To capture something new, write a plain markdown file directly into `__INBOX_PATH__` —
4
- no CLI command wraps this. That file MUST NOT contain any of these frontmatter fields; contexture assigns
5
- them once, at ingest, and never before:
3
+ Captures live under `__CAPTURE_ROOT__`, which is excluded from retrieval: nothing in it is a note, and
4
+ nothing in it is returned by a search, listed in the catalog, or drawn in the graph. It is tracked in git
5
+ all the same — a capture is the provenance behind a note, not scratch space.
6
+
7
+ To capture something new, write the material into `__INBOX_PATH__` — no CLI command wraps this. It may
8
+ already carry these two fields, since whatever fetched it usually knows them:
6
9
 
7
10
  - `source_type`
8
11
  - `source_id`
12
+
13
+ It MUST NOT carry either of these; contexture assigns them once, at ingest, and never before:
14
+
9
15
  - `source_hash`
10
16
  - `ingested`
11
17
 
12
- Before ingesting, run `ctxr source check <path> --source-id <id>` to get one of four verdicts:
13
- `new`, `already_ingested`, `alternate_source_match`, or `multiple_matches` — the last one means stop and
14
- resolve the ambiguity yourself rather than guessing which existing note it is.
18
+ Before ingesting, run `ctxr source check <path> --source-id <id>` to get one of five verdicts: `new`,
19
+ `already_ingested`, `drift` (same identity, the source's content moved), `alternate_source_match`, or
20
+ `multiple_matches` — the last one means stop and resolve the ambiguity yourself rather than guessing which
21
+ existing record it is.
22
+
23
+ Then write, or extend, the note this material informed. Ingest does not create it: deciding what the store
24
+ should know is the work, and "a new note" is only one of the answers.
25
+
26
+ ```
27
+ ctxr ingest <path> --into <note> --source-type <type> --source-id <id>
28
+ ```
29
+
30
+ That stamps the four fields onto the capture, moves it out of the inbox into `__CAPTURE_ROOT__` under the
31
+ month it was ingested, and records its path in the note's `sources` list. The note itself carries no source
32
+ identity — it is free to be rewritten, merged, or restructured without ever invalidating the frozen hash.
33
+ A note may cite as many captures as it was built from.
15
34
 
16
- To ingest, run `ctxr ingest <path> --source-type <type> --source-id <id>`. It stamps the four fields
17
- above onto the file in place and rebuilds the catalog, so the result already has a catalog entry.
35
+ Material that is not markdown cannot carry frontmatter, so it travels with a markdown sidecar beside it
36
+ naming the file in `capture_file`. The hash is taken over that file's bytes, and the two move together.
@@ -1,14 +1,40 @@
1
- ## Retrieval: which leg to use
1
+ ## Retrieval: one pass, three steps
2
2
 
3
- contexture builds and maintains two retrieval tools ahead of time — consult them first:
3
+ Retrieval is a single pass — **enter**, **expand**, **widen** — not three tools to choose between.
4
+ contexture builds and maintains the first two ahead of time; the third is yours.
4
5
 
5
- - **Catalog** (`ctxr catalog show --section <id>`): a curated, coverage-guaranteed index of every retrievable note, one section per taxonomy layer.
6
- - **Graph** (`ctxr graph query ...`): the wikilink graph between notes — neighbors, shortest path, hubs, orphans, clusters, bridges; `--type <relation>` follows one configured relation.
7
- - **Graph document** (`__GRAPH_DOCUMENT_PATH__`, rebuilt by `ctxr graph build`): hub notes by cluster, cross-cluster bridges, and orphans — read it for cluster context before writing.
6
+ **1. Enter.** Name where to start, positionally or relationally:
8
7
 
9
- For a literal or entity question the catalog and graph do not answer (a specific string, an exact identifier, a phrase),
10
- use your own direct content-matching tool (e.g. grep/ripgrep) against the store, scoped to exclude:
8
+ - `--section <id>` — every note a catalog section lists (`ctxr catalog show --section <id>` reads one directly)
9
+ - `--under <prefix>` — every retrievable note under a path prefix
10
+ - `--seed <path>` — a note you already hold, including one your own search just found
11
+ - `--entity <name>` — every note linking to a concept
12
+
13
+ **2. Expand.** `ctxr context gather` takes those selectors and walks the wikilink graph out from them,
14
+ returning each reachable note with its catalog gloss, its hop distance, and labels saying why it is
15
+ there — so you can triage the set without opening a single file, then read only what the glosses
16
+ justify:
17
+
18
+ ```
19
+ ctxr context gather --section <id> --hops 1
20
+ ctxr context gather --seed <path> --hops 2 --type <relation>
21
+ ```
22
+
23
+ Results are ordered: live material before demoted (archived) material, nearer hops before farther,
24
+ then by how the note was reached, then by path. `no_gloss` on a result means the catalog has no
25
+ description for that note yet — the pass found it structurally, not by what it says.
26
+
27
+ For structure on its own — shortest path, hubs, orphans, clusters, bridges — query the graph directly
28
+ with `ctxr graph query ...`, and read the graph document at `__GRAPH_DOCUMENT_PATH__` (rebuilt by
29
+ `ctxr graph build`) for cluster context before writing.
30
+
31
+ **3. Widen.** For a literal or entity question the first two steps do not answer — a specific string,
32
+ an exact identifier, a phrase — use your own content-matching tool (e.g. grep/ripgrep) against the
33
+ store, scoped to exclude:
11
34
 
12
35
  __EXCLUSION_PATHS__
13
36
 
14
- There is no `ctxr search` command. Ranked or semantic search is deferred to a future version — do not look for one.
37
+ Feed anything it finds back in as `--seed` to pick the pass up again from there.
38
+
39
+ There is no `ctxr search` command. Nothing here takes a free-text query, and no result carries a
40
+ relevance score. Ranked or semantic search is deferred to a future version — do not look for one.
@@ -0,0 +1,37 @@
1
+ # --- shared: resolve the ctxr CLI ------------------------------------------
2
+ # Authored in templates/hooks/_resolve-ctxr.sh and inlined verbatim into every
3
+ # hook that needs it at render time (src/core/hooks.ts) — one copy of a
4
+ # security decision, never two that can drift apart. Do not edit the inlined
5
+ # copy; re-run `ctxr init` / `ctxr update` to regenerate it.
6
+ #
7
+ # Resolves at RUN time, so the rendered hook is byte-identical in every
8
+ # checkout of this store: a path baked in when the file was generated is
9
+ # valid only on the machine that generated it, and this file is committed.
10
+ #
11
+ # Order: $CONTEXTURE_BIN, then `ctxr` on PATH. Nothing else is consulted, and
12
+ # there is no "give up and continue" branch — when neither resolves, this
13
+ # calls ctxr_unavailable <reason>, which each hook defines above and which
14
+ # must not return. A CONTEXTURE_BIN that is set but broken is terminal: it is
15
+ # a deliberate pin, and silently running a different binary instead would
16
+ # defeat the operator who set it.
17
+ #
18
+ # Leaves the resolved command in the positional parameters ("$@"). Neither
19
+ # hook that inlines this reads its own arguments.
20
+ if [ -n "${CONTEXTURE_BIN:-}" ]; then
21
+ if [ ! -f "$CONTEXTURE_BIN" ]; then
22
+ ctxr_unavailable "CONTEXTURE_BIN names a path that is not a file: $CONTEXTURE_BIN"
23
+ elif [ -x "$CONTEXTURE_BIN" ]; then
24
+ set -- "$CONTEXTURE_BIN"
25
+ elif command -v node >/dev/null 2>&1; then
26
+ # A plain dist/bin.js: tsc emits it non-executable, and only npm's
27
+ # bin-linking sets the bit, so a dev checkout needs node to run it.
28
+ set -- node "$CONTEXTURE_BIN"
29
+ else
30
+ ctxr_unavailable "CONTEXTURE_BIN is not executable and no node is on PATH to run it: $CONTEXTURE_BIN"
31
+ fi
32
+ elif command -v ctxr >/dev/null 2>&1; then
33
+ set -- ctxr
34
+ else
35
+ ctxr_unavailable "no ctxr on PATH, and CONTEXTURE_BIN is not set"
36
+ fi
37
+ # --- end shared: resolve the ctxr CLI --------------------------------------
@@ -3,13 +3,41 @@
3
3
  # controlled — do not edit by hand; re-run to regenerate. A PreToolUse hook:
4
4
  # relays the tool-call envelope on stdin to `ctxr adapters write-gate`, which
5
5
  # decides whether to deny it, and relays its stdout and exit code unchanged.
6
+ # Carries no path specific to the machine that generated it — `ctxr` is
7
+ # resolved at run time, and an edit this gate could not evaluate is denied,
8
+ # not allowed unchecked.
6
9
  set -eu
7
10
 
8
- CONTEXTURE_BIN="__CONTEXTURE_BIN__"
9
-
10
- if [ ! -f "$CONTEXTURE_BIN" ]; then
11
- echo "ctxr: warning — could not find $CONTEXTURE_BIN; skipping write-gate check." >&2
11
+ # The write gate is an enforcement primitive, and a gate that cannot run is
12
+ # not a passing decision (adapters spec). This speaks the same PreToolUse deny
13
+ # protocol `ctxr adapters write-gate` does — a JSON body on stdout, exit 0 —
14
+ # because that is the only vocabulary the harness reads as "denied, and here
15
+ # is why". A nonzero exit other than 2 is reported as a hook error and the
16
+ # edit proceeds, which is the failure this replaces; exit 2 hard-blocks
17
+ # regardless of output and would conflate a broken install with a decision.
18
+ ctxr_unavailable() {
19
+ # Drain the envelope the harness is writing to us before exiting: the
20
+ # normal path reads stdin to EOF via `ctxr adapters write-gate`, and a
21
+ # harness that writes the full envelope before checking our exit code
22
+ # would otherwise see its own write fail (EPIPE/SIGPIPE) once we exit
23
+ # without ever reading it.
24
+ cat >/dev/null 2>&1 || true
25
+ printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"contexture: the write gate could not run (%s), so this edit is denied rather than allowed unchecked. Install the CLI (npm install -g ctxr-cli) or set CONTEXTURE_BIN to its dist/bin.js, then retry."}}\n' "$1"
12
26
  exit 0
13
- fi
27
+ }
14
28
 
15
- node "$CONTEXTURE_BIN" adapters write-gate
29
+ __RESOLVE_CTXR__
30
+
31
+ # Not `exec`: a resolved command can still fail to execute (a broken symlink,
32
+ # a non-executable file found on PATH), and exec's 126/127 would reach the
33
+ # harness as an ordinary hook error — fail-open, the bug this file exists to
34
+ # close. Every other exit code and all of stdout are relayed unchanged, so the
35
+ # gate's own deliberate "I could not decide, apply normal permission flow"
36
+ # exits (never 2) keep their documented meaning.
37
+ status=0
38
+ out=$("$@" adapters write-gate) || status=$?
39
+ if [ "$status" -eq 126 ] || [ "$status" -eq 127 ]; then
40
+ ctxr_unavailable "the resolved ctxr could not be executed (exit $status)"
41
+ fi
42
+ printf '%s' "$out"
43
+ exit "$status"
@@ -1,18 +1,25 @@
1
1
  #!/bin/sh
2
2
  # Installed by `ctxr init` (write-lifecycle spec). Version-controlled —
3
3
  # do not edit by hand; re-run `ctxr init` or `ctxr doctor` to
4
- # regenerate. Runs `doctor --staged` against the exact contexture install
5
- # that wrote this hook, and refuses the commit if it finds a real problem.
4
+ # regenerate. Runs `doctor --staged` with the ctxr this machine resolves at
5
+ # run time, and refuses the commit if it finds a real problem — or if it
6
+ # could not run that validation at all.
6
7
  set -eu
7
8
 
8
- CONTEXTURE_BIN="__CONTEXTURE_BIN__"
9
+ # A commit this hook could not check is refused, not passed: the point of the
10
+ # hook is that no unvalidated change is committed, and "ctxr is not installed
11
+ # here" is not evidence that the staged diff is clean.
12
+ ctxr_unavailable() {
13
+ echo "ctxr: commit refused — $1" >&2
14
+ echo "ctxr: this hook validates staged changes with 'ctxr doctor --staged', and will not pass a commit it could not check." >&2
15
+ echo "ctxr: install the CLI with 'npm install -g ctxr-cli', or set CONTEXTURE_BIN to its dist/bin.js." >&2
16
+ echo "ctxr: 'git commit --no-verify' bypasses this hook if you must commit before repairing the install." >&2
17
+ exit 1
18
+ }
9
19
 
10
- if [ ! -f "$CONTEXTURE_BIN" ]; then
11
- echo "ctxr: warning — could not find $CONTEXTURE_BIN; skipping pre-commit validation." >&2
12
- exit 0
13
- fi
20
+ __RESOLVE_CTXR__
14
21
 
15
- if ! output=$(node "$CONTEXTURE_BIN" doctor --staged --json 2>&1); then
22
+ if ! output=$("$@" doctor --staged --json 2>&1); then
16
23
  echo "$output" >&2
17
24
  echo "ctxr: commit refused — staged changes failed 'doctor --staged'." >&2
18
25
  exit 1
@@ -5,11 +5,13 @@ to synthesize an entity's current state from its sources, use `ctxr-rollup`.
5
5
  document it writes at `__GRAPH_DOCUMENT_PATH__` — hub notes by cluster, cross-cluster bridges,
6
6
  orphans — for the cluster context the point queries below do not summarize.
7
7
  2. To find what a note connects to or from, run `ctxr graph query neighbors <path>` (add `--depth` for
8
- further hops, `--direction in|out|both`, `--type <relation>` to follow one configured relation,
9
- `--as <context>` to see only what that context can see).
10
- 3. To find a path between two notes, run `ctxr graph query path <from> <to>`.
11
- 4. To find the most-referenced notes, run `ctxr graph query hubs`; to find unlinked ones,
8
+ further hops, `--direction in|out|both`, `--type <relation>` to follow one configured relation).
9
+ 3. To read a neighbourhood rather than enumerate one — every reachable note with its catalog gloss,
10
+ hop distance, and why it was reached — run `ctxr context gather --seed <path> --hops <n>`. Use it
11
+ when you need to judge which links matter; use the point queries below when you need the structure.
12
+ 4. To find a path between two notes, run `ctxr graph query path <from> <to>`.
13
+ 5. To find the most-referenced notes, run `ctxr graph query hubs`; to find unlinked ones,
12
14
  `ctxr graph query orphans`; `ctxr graph query clusters` and `ctxr graph query bridges` show the
13
15
  cluster structure and the notes that span it.
14
- 5. Report what the graph says — it enumerates structure and ranks nothing; judgment about which links
16
+ 6. Report what the graph says — it enumerates structure and ranks nothing; judgment about which links
15
17
  matter is yours.
@@ -1,15 +1,16 @@
1
1
  Ingest is synthesis, not filing. The question is what the store should know after this source, not where
2
2
  to put the file — "create a new note" is one option among several, never the default.
3
3
 
4
- 1. Capture: write a plain markdown file directly into the inbox (see AGENTS.md's capture section) — no
5
- provenance frontmatter; contexture assigns it at ingest.
4
+ 1. Capture: write the material into the inbox (see AGENTS.md's capture section). It may already carry
5
+ `source_type` and `source_id`; it must carry neither `source_hash` nor `ingested`, which ingest assigns.
6
+ The capture is kept — it is the store's record of what arrived, and it never becomes the note.
6
7
  2. Check: run `ctxr source check <path> --source-id <id>` and read the verdict — `new`, `already_ingested`,
7
8
  `drift` (same identity, the source's content moved — read what changed and decide whether to update the
8
9
  note, then `ctxr source stamp <path> --id <id>` once resolved), `alternate_source_match`, or
9
10
  `multiple_matches`. On `multiple_matches`, stop and resolve the ambiguity yourself; never guess which
10
- existing note it is. A legacy note with an identity but no hash on file: `ctxr source stamp` backfills
11
- it. A source re-published under a new URL you've confirmed is the same material:
12
- `ctxr source add-alt <path> --id <new-id>` on the existing note, not a second ingest.
11
+ existing record it is. A record with an identity but no hash on file: `ctxr source stamp` backfills it.
12
+ A source re-published under a new URL you've confirmed is the same material:
13
+ `ctxr source add-alt <path> --id <new-id>` on the existing capture, not a second ingest.
13
14
  3. Read the source fully. Then read the existing cluster BEFORE writing anything: the catalog section for
14
15
  the domain (`ctxr catalog show --section <id>`), every related note in it (all of them, not one or two),
15
16
  and the graph (`ctxr graph build`, then read the graph document it writes at
@@ -36,7 +37,12 @@ to put the file — "create a new note" is one option among several, never the d
36
37
  headline is worse than either alone.
37
38
  - Source discipline: a field the source does not confirm is "not reported" — never inferred from
38
39
  silence in either direction.
39
- 5. Write: for a new note, `ctxr ingest <path> --source-type <type> --source-id <id>` (stamps provenance and
40
- gives it a catalog entry). Updates to existing notes are ordinary edits that preserve prior content
41
- (a deliberate ingest despite an `alternate_source_match` is the same command).
42
- 6. Verify: `ctxr lint` (orphans, broken links, leftover inbox material) and `ctxr catalog check`.
40
+ 5. Write the note the decision calls for — create it, expand it, merge, restructure, or add a section —
41
+ preserving prior content in every case but the deliberate rewrite.
42
+ 6. Register the source against whatever note you just wrote:
43
+ `ctxr ingest <path> --into <note> --source-type <type> --source-id <id>`. Every row of the table above
44
+ ends here, not just the first: the command retains the capture under the capture tier's month, freezes
45
+ its hash, cites it from the note, and rebuilds the catalog. A note that was expanded rather than created
46
+ records its provenance exactly as a new one does, and a note built from several sources cites all of
47
+ them. A deliberate ingest despite an `alternate_source_match` is the same command.
48
+ 7. Verify: `ctxr lint` (orphans, broken links, material still in the inbox) and `ctxr catalog check`.
@@ -1,7 +1,6 @@
1
- Turn store content into a shareable HTML page for a subject — a store subtree, a single note, a
2
- concept whose sources aren't co-located, or everything a named context admits. The gate below is the
3
- one step no convention can enforce by itself; everything else here is a decision procedure, not a
4
- mechanism.
1
+ Turn store content into a shareable HTML page for a subject — a store subtree, a single note, or a
2
+ concept whose sources aren't co-located. Deciding what belongs on the page is the one step no
3
+ convention can enforce for you; everything else here is a decision procedure, not a mechanism.
5
4
 
6
5
  ## 1. Does this earn a page over a note?
7
6
 
@@ -51,10 +50,10 @@ notes came from, so the notes' shorthand does not survive the copy out. Load the
51
50
  store carries by default (or whichever explanation-focused skill it's configured instead), settle who
52
51
  the reader is before writing, and pitch every definition, analogy, and level of detail at them.
53
52
 
54
- Both skills say "audience", and neither means step 3's. There, `--audience` names who the store is
55
- being asked to disclose to and the answer is a verdict; here it names how much a reader already knows
56
- and the answer is a register. A comprehension level is never a value you pass to `--audience`, and
57
- writing plainly never widens what the page may contain — the gate in step 3 already settled that.
53
+ Both skills say "audience", and here it means one thing only: how much the reader already knows,
54
+ which is a question about register. It is never a reason to put more on the page. Step 3 settled what
55
+ the page may carry, and writing that content more plainly never widens it — an explanation pitched
56
+ lower is the same material in shorter words.
58
57
 
59
58
  ## 6. Verify the output invariants
60
59