kb-save — explicit save (the second step of the query → save loop)
Acts on an existing query-session: check the save admission gate first, then write qualifying candidates as
status: proposed pages or a Review-Queue proposal. The execution layer is scripts/pipeline.py; this
skill only orchestrates, surfaces acceptance, and marks failure stops.
1. Triggers / Non-triggers
- Triggers: "save that comparison/conclusion into the wiki", "form a synthesis", "save to the KB", "keep this as a note", "write that query-session back".
- Non-triggers: one-off facts, plain explanations, translation, source-less speculation, restating an existing page; a direct write request with no query-session (first switch to
kb-query or ask for a run_id); a new external source (use ingest).
2. Inputs
<run_id>: the user-named query-session, or the most recent kb-query run_id. It must be one direct
directory name under query-sessions/; separators, drive prefixes, ./.., and path traversal are
rejected.
- Read:
pipeline-workspace/query-sessions/<run_id>/{question.md,answer.md,related_pages.json,candidate_write_set.json,evidence_refs.json}; write_authorizations.json is created by the write guard.
- Read:
docs/skill-runtime/save-back-policy.md, docs/skill-runtime/schema.md, docs/skill-runtime/concept-resolution.md, and the relevant vault pages.
3. Outputs
- Below the admission gate: refuse clearly with a reason; write nothing.
- Above the gate: the current direct-write path creates only new allowlisted candidate pages; all are
status: proposed + managed_by: pipeline.
- Any existing target (pipeline or human), concept merge, or overwrite request fails closed: write
wiki/Review-Queue/<page>-proposal.md, do not edit the target. kb-save does not fabricate an ingest workorder to gain overwrite authority.
- Update the query-session: record what was actually written + evidence, and write
decision.md.
4. Dependencies
- CLI:
check-write --source kb-save --session, check-session --saved; the finishing publish is decided by lint. snapshot-page and write-capable resolve-concept remain ingest-only until kb-save has a session overwrite baseline.
- 记账契约(会话作用域 + 内容身份): 写下的每个页面必须①列入本 session 的
candidate_write_set.json(处理台账,与 ingest 的窗口 --writes 对等),②frontmatter 带
save_session: <run_id>(内容身份——candidate 只记路径,同路径被其他会话重写时靠此标记
fail-closed,绝不代发他会话内容)。lint --source kb-save --session <run_id> 以该会话
candidate 集同时决定发布范围与记账,并与 write_authorizations.json 的 new-only 门票
精确对账;路径缺失/非 proposed/身份不符/授权不符任一即 fail-closed,不得部分发布。历史/未保存/其他 run_id 的会话不得代记账,普通
ingest 的 lint 也完全不读会话台账;source_refs 只定归属,不算记账。kb-save 批次不背
ingest 阶段 E 义务(overview 重写/L7/topics-missing),vault 级不变量(A2 收编、渲染安全
preflight)照常。
- 严格清单契约:
candidate_write_set.json 必须是非空、无重复、canonical vault 相对路径
字符串列表;拒绝 ./、//、..、反斜杠别名和非字符串值。write_authorizations.json
每项只能含精确键 {path, mode},mode 只能是 "new",路径同样 canonical 且唯一;saved
Q1 要求候选与授权路径一一对应。
- 信任边界:
write_authorizations.json 是本机协作流程的可审计账本,不是密码学证明或
防手工伪造边界。只允许 check-write 生成/追加;不得手写、补票或把它描述成防篡改机制。
- Protocols:
save-back-policy.md (admission gate), schema.md (page structure), concept-resolution.md (concept resolution).
- Write discipline matches
ingest: never hand-write derived files (aliases.md retired — aliases in frontmatter). A candidate that would create or merge a concept is proposed to Review-Queue in the current new-only mode; never hand-create a duplicate concept. 新建 topic/comparison/synthesis 用中文文件名(与 title 一致,如 comparisons/<甲> vs <乙>.md)并带 source_refs 溯源;正文走高信息密度的学术散文、句式有起伏、结构由 purpose 与内容自然决定(不再有强制小节标题,D-4);发布正文不嵌源图(D-1)——见 ingest 的 write-pages.md「写作风格」与「页面文件名用中文」。
5. Persisted artifacts
pipeline-workspace/query-sessions/<run_id>/decision.md: why saved / which pages written / which evidence cited / why no existing concept was polluted.
candidate_write_set.json: updated to the pages actually/intended written.
evidence_refs.json: completed with the evidence actually used.
write_authorizations.json: generated by check-write, one {path, mode: "new"} per candidate.
- vault proposed pages or
wiki/Review-Queue/*-proposal.md.
6. CLI commands
python scripts/pipeline.py check-write --source kb-save --session <run_id> --path <vault-rel-path>
python scripts/pipeline.py check-session --id <run_id> --saved
After saving, prompt the user to run python scripts/pipeline.py lint --source kb-save --session <run_id>
(the finishing gate first re-checks the saved-mode session contract, then lints/promotes only the pages
listed in that session's candidate_write_set.json); do not bypass two-phase publish.
7. Workflow
| Sub-unit |
Input |
Output |
Acceptance |
Persisted |
Failure stop |
| S1 read session |
run_id |
session content + related pages |
required files present, JSON parses |
— |
session missing |
| S2 admission |
session + save-back-policy |
save/refuse decision |
at least one gate condition holds and evidence_refs non-empty |
decision.md draft |
below the gate |
| S3 resolve concepts |
candidate concepts |
Review-Queue proposal |
do not hand-create/merge concepts in new-only mode |
proposal |
concept write requested |
| S4 write proposed |
candidate pages |
new proposed pages or a proposal |
check-write --source kb-save --session ALLOW before create; existing target always proposal |
vault / Review-Queue + authorization ledger |
check-write DENY |
| S5 session self-check |
write result |
check-session --saved result |
passes the saved-mode contract |
full session dir |
self-check fails |
| S6 publish prompt |
proposed pages |
lint handoff note |
user knows lint decides promotion |
chat summary |
on lint failure hand to kb-review |
8. Failure stops / recovery
query-session missing or incomplete; evidence_refs.json empty; below save-back-policy; check-write
DENY; target already exists; concept write requested; check-session --saved fails. Recovery: keep decision.md + the session as the durable record; a DENY becomes
a Review-Queue proposal handed to kb-review. A legacy saved session with no
write_authorizations.json fails closed: if every candidate target is still absent, rerun check-write
once per candidate before creating pages; if any target already exists, do not retro-authorize it—route
the intended change to Review-Queue/kb-review, or start a fresh session with an unused new path. Never
hand-fill the authorization ledger.
9. Acceptance criteria
- When not saving, no change under
wiki/, with a stated reason.
- When saving, every written page is
status: proposed + managed_by: pipeline.
- Direct concept create/merge is unsupported in kb-save new-only mode; route it to Review-Queue.
resolve-concept remains the ingest-only write path, and no duplicate canonical_id may be created.
decision.md written; candidate_write_set.json / evidence_refs.json updated.
- Every candidate has a matching new-page entry in
write_authorizations.json.
python scripts/pipeline.py check-session --id <run_id> --saved passes.
- Prompted the finishing
lint to decide promotion; a lint failure goes to kb-review.
1---2name: kb-save3description: Save a synthesis/comparison/learning-path/self-test candidate from an existing query-session into the wiki as status:proposed (gated, two-phase publish). Use when, after a query, the user says "save that comparison/conclusion into the wiki / form a synthesis / save to the KB / keep this as a note". One-off facts, plain explanations, and restating an existing page are not saved.4---56# kb-save — explicit save (the second step of the query → save loop)78Acts on an existing query-session: check the save admission gate first, then write qualifying candidates as9`status: proposed` pages or a Review-Queue proposal. The execution layer is `scripts/pipeline.py`; this10skill only orchestrates, surfaces acceptance, and marks failure stops.1112## 1. Triggers / Non-triggers1314- **Triggers:** "save that comparison/conclusion into the wiki", "form a synthesis", "save to the KB", "keep this as a note", "write that query-session back".15- **Non-triggers:** one-off facts, plain explanations, translation, source-less speculation, restating an existing page; a direct write request with no query-session (first switch to `kb-query` or ask for a run_id); a new external source (use `ingest`).1617## 2. Inputs1819- `<run_id>`: the user-named query-session, or the most recent `kb-query` run_id. It must be one direct20 directory name under `query-sessions/`; separators, drive prefixes, `.`/`..`, and path traversal are21 rejected.22- Read: `pipeline-workspace/query-sessions/<run_id>/{question.md,answer.md,related_pages.json,candidate_write_set.json,evidence_refs.json}`; `write_authorizations.json` is created by the write guard.23- Read: `docs/skill-runtime/save-back-policy.md`, `docs/skill-runtime/schema.md`, `docs/skill-runtime/concept-resolution.md`, and the relevant vault pages.2425## 3. Outputs2627- Below the admission gate: refuse clearly with a reason; write nothing.28- Above the gate: the current direct-write path creates only **new** allowlisted candidate pages; all are `status: proposed` + `managed_by: pipeline`.29- Any existing target (pipeline or human), concept merge, or overwrite request fails closed: write `wiki/Review-Queue/<page>-proposal.md`, do not edit the target. kb-save does not fabricate an ingest workorder to gain overwrite authority.30- Update the query-session: record what was actually written + evidence, and write `decision.md`.3132## 4. Dependencies3334- CLI: `check-write --source kb-save --session`, `check-session --saved`; the finishing publish is decided by `lint`. `snapshot-page` and write-capable `resolve-concept` remain ingest-only until kb-save has a session overwrite baseline.35- **记账契约(会话作用域 + 内容身份):** 写下的每个页面必须①列入本 session 的36 `candidate_write_set.json`(处理台账,与 ingest 的窗口 `--writes` 对等),②frontmatter 带37 `save_session: <run_id>`(内容身份——candidate 只记路径,同路径被其他会话重写时靠此标记38 fail-closed,绝不代发他会话内容)。`lint --source kb-save --session <run_id>` 以该会话39 candidate 集**同时决定发布范围与记账**,并与 `write_authorizations.json` 的 new-only 门票40 精确对账;路径缺失/非 proposed/身份不符/授权不符任一即 fail-closed,不得部分发布。历史/未保存/其他 run_id 的会话不得代记账,普通41 ingest 的 lint 也完全不读会话台账;`source_refs` 只定归属,不算记账。kb-save 批次不背42 ingest 阶段 E 义务(overview 重写/L7/topics-missing),vault 级不变量(A2 收编、渲染安全43 preflight)照常。44- **严格清单契约:** `candidate_write_set.json` 必须是非空、无重复、canonical vault 相对路径45 字符串列表;拒绝 `./`、`//`、`..`、反斜杠别名和非字符串值。`write_authorizations.json`46 每项只能含精确键 `{path, mode}`,`mode` 只能是 `"new"`,路径同样 canonical 且唯一;saved47 Q1 要求候选与授权路径一一对应。48- **信任边界:** `write_authorizations.json` 是本机协作流程的可审计账本,不是密码学证明或49 防手工伪造边界。只允许 `check-write` 生成/追加;不得手写、补票或把它描述成防篡改机制。50- Protocols: `save-back-policy.md` (admission gate), `schema.md` (page structure), `concept-resolution.md` (concept resolution).51- Write discipline matches `ingest`: never hand-write derived files (`aliases.md` retired — aliases in frontmatter). A candidate that would create or merge a concept is proposed to Review-Queue in the current new-only mode; never hand-create a duplicate concept. **新建 topic/comparison/synthesis 用中文文件名(与 `title` 一致,如 `comparisons/<甲> vs <乙>.md`)并带 `source_refs` 溯源;正文走高信息密度的学术散文、句式有起伏、结构由 purpose 与内容自然决定(不再有强制小节标题,D-4);发布正文不嵌源图(D-1)——见 `ingest` 的 `write-pages.md`「写作风格」与「页面文件名用中文」。**5253## 5. Persisted artifacts5455- `pipeline-workspace/query-sessions/<run_id>/decision.md`: why saved / which pages written / which evidence cited / why no existing concept was polluted.56- `candidate_write_set.json`: updated to the pages actually/intended written.57- `evidence_refs.json`: completed with the evidence actually used.58- `write_authorizations.json`: generated by `check-write`, one `{path, mode: "new"}` per candidate.59- vault proposed pages or `wiki/Review-Queue/*-proposal.md`.6061## 6. CLI commands6263```text64python scripts/pipeline.py check-write --source kb-save --session <run_id> --path <vault-rel-path>65python scripts/pipeline.py check-session --id <run_id> --saved66```6768After saving, prompt the user to run `python scripts/pipeline.py lint --source kb-save --session <run_id>`69(the finishing gate first re-checks the saved-mode session contract, then lints/promotes **only the pages70listed in that session's `candidate_write_set.json`**); do not bypass two-phase publish.7172## 7. Workflow7374| Sub-unit | Input | Output | Acceptance | Persisted | Failure stop |75|---|---|---|---|---|---|76| S1 read session | run_id | session content + related pages | required files present, JSON parses | — | session missing |77| S2 admission | session + save-back-policy | save/refuse decision | at least one gate condition holds and evidence_refs non-empty | `decision.md` draft | below the gate |78| S3 resolve concepts | candidate concepts | Review-Queue proposal | do not hand-create/merge concepts in new-only mode | proposal | concept write requested |79| S4 write proposed | candidate pages | new proposed pages or a proposal | `check-write --source kb-save --session` ALLOW before create; existing target always proposal | vault / Review-Queue + authorization ledger | check-write DENY |80| S5 session self-check | write result | check-session --saved result | passes the saved-mode contract | full session dir | self-check fails |81| S6 publish prompt | proposed pages | lint handoff note | user knows lint decides promotion | chat summary | on lint failure hand to kb-review |8283## 8. Failure stops / recovery8485query-session missing or incomplete; `evidence_refs.json` empty; below `save-back-policy`; `check-write`86DENY; target already exists; concept write requested; `check-session --saved` fails. **Recovery:** keep `decision.md` + the session as the durable record; a DENY becomes87a `Review-Queue` proposal handed to `kb-review`. A legacy saved session with no88`write_authorizations.json` fails closed: if every candidate target is still absent, rerun `check-write`89once per candidate before creating pages; if any target already exists, do not retro-authorize it—route90the intended change to Review-Queue/`kb-review`, or start a fresh session with an unused new path. Never91hand-fill the authorization ledger.9293## 9. Acceptance criteria9495- When not saving, no change under `wiki/`, with a stated reason.96- When saving, every written page is `status: proposed` + `managed_by: pipeline`.97- Direct concept create/merge is unsupported in kb-save new-only mode; route it to Review-Queue.98 `resolve-concept` remains the ingest-only write path, and no duplicate canonical_id may be created.99- `decision.md` written; `candidate_write_set.json` / `evidence_refs.json` updated.100- Every candidate has a matching new-page entry in `write_authorizations.json`.101- `python scripts/pipeline.py check-session --id <run_id> --saved` passes.102- Prompted the finishing `lint` to decide promotion; a lint failure goes to `kb-review`.