tools: command records, Raw material and uploads group - one line per cause, examples, prohibitions (#142)
Files changed: - CHANGES.md - VERSION - tools/CONTRACT.md - tools/chemenu/commands/raw_cmd.py - tools/chemenu/commands/upload_cmd.py
This commit is contained in:
1 parent
be78ad20af
commit
964978a9c6
5 files changed
+305
-103
No files matched your search
+11
-1
@@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7.1.0-beta.13 - 2026-09-26 - Command records, Provenance group: examples, exit lines per cause
|
## 7.1.0-beta.14 - 2026-09-26 - Command records, Raw material and uploads group: one line per cause, examples, prohibitions
|
||||||
|
|
||||||
**Author:** Torben Nehmer
|
**Author:** Torben Nehmer
|
||||||
|
|
||||||
@@ -82,6 +82,7 @@ concern - readable here, never shipped as something to parse.
|
|||||||
- Command records, Links and citations group: one line per cause, examples, prohibitions
|
- Command records, Links and citations group: one line per cause, examples, prohibitions
|
||||||
- Command records, Finding and checking group: one line per cause, examples, prohibitions
|
- Command records, Finding and checking group: one line per cause, examples, prohibitions
|
||||||
- Command records, Provenance group: examples, exit lines per cause
|
- Command records, Provenance group: examples, exit lines per cause
|
||||||
|
- Command records, Raw material and uploads group: one line per cause, examples, prohibitions
|
||||||
<!-- /wikitool:bumps -->
|
<!-- /wikitool:bumps -->
|
||||||
|
|
||||||
### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them
|
### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them
|
||||||
@@ -269,6 +270,15 @@ only. `sources trace` separates an argument error from the uncovered-file findin
|
|||||||
1 on, since the second is a result to act on rather than an argument to fix;
|
1 on, since the second is a result to act on rather than an argument to fix;
|
||||||
`sources rebuild-index` names `kb/provenance.md` as generated in NEVER.
|
`sources rebuild-index` names `kb/provenance.md` as generated in NEVER.
|
||||||
|
|
||||||
|
### Command records, Raw material and uploads group: one line per cause, examples, prohibitions
|
||||||
|
|
||||||
|
`raw accept` and the four `upload` commands rewritten the same way; text only. `raw accept`'s
|
||||||
|
two paragraph-long variant notes became one line each, with the behaviour in NOTES bullets and
|
||||||
|
its two exit-1 lists split into seven causes. The name-occupied refusal now carries, in its own
|
||||||
|
reaction and in NEVER, what `raw/CONTRACT.md` already asks of an agent: show the message and
|
||||||
|
wait, since only the user can tell a new edition from a second source. `upload accept` states its
|
||||||
|
gate's shape itself instead of pointing at the Mass-Update Gate's.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7.0.0 - 2026-09-22 - Task-Tracker-Anbindung: Vorhaben als Seitenart, Verpflichtungsschicht, Weekly Review als Read-Time-Join
|
## 7.0.0 - 2026-09-22 - Task-Tracker-Anbindung: Vorhaben als Seitenart, Verpflichtungsschicht, Weekly Review als Read-Time-Join
|
||||||
|
|||||||
+109
-15
@@ -1357,8 +1357,8 @@ Promote one or more files from `incoming/` into `raw/`.
|
|||||||
|
|
||||||
**SYNOPSIS**
|
**SYNOPSIS**
|
||||||
|
|
||||||
- `wikitool raw accept <file> [<file> ...] --fidelity <v> --authority <v> [--page "<Title>"] [--dry-run]` - Promote one or more files from `incoming/` into `raw/<YYYY>/<MM>/`, computed from the accept date rather than chosen by hand (`raw/CONTRACT.md` "Getting a file in"): a subdirectory under `incoming/` is tolerated and ignored, not inspected - `raw/` no longer addresses by type. One file promoted alone lands with no directory of its own; several files in one call nest under `raw/<YYYY>/<MM>/<stem>/`, named after the first file's stem. `--fidelity`/`--authority` are required here (see `types describe source`; `unknown` is refused, backfill-only) - the one moment both are knowable. `--page "<Title>"` additionally extends that existing source page's `raw_files:` in the same call and writes both capture fields onto it (refused if it already carries a different value - a capture field is fixed once); if that raises the page past one file, its already-promoted file is folded into a bundle at *its own* parent directory, not today's shard, so a bundle never mixes an old and a new capture date, after checking it has no other owner (`provenance.duplicate_raw_file_owners`). The set of names occupied anywhere under `raw/` - file stems and bundle directory names alike, old type directories and date shards together - must stay unique: a promote whose target name already belongs to something this call does not itself own is refused, naming both `--replaces` and renaming-in-`incoming/` without recommending either
|
- `wikitool raw accept <file> [<file> ...] --fidelity <v> --authority <v> [--page "<Title>"] [--dry-run]` - Promote one or more files from `incoming/` into today's `raw/<YYYY>/<MM>/` shard
|
||||||
- `wikitool raw accept <file> --replaces <raw-path> [--fidelity <v>] [--authority <v>] [--dry-run]` - The one sanctioned way past that uniqueness rule, and the one sanctioned way to correct an already-set capture field: overwrites `<raw-path>` in place with the single incoming file (same filename required; there is no type directory left to match), leaving every page's `raw_files:` untouched and writing no `kb/` page - the previous edition survives only in `git log --follow <raw-path>`. `--fidelity`/`--authority` are optional here, and passing one overwrites the owning page's already-set value - the one path fill-once does not block, because a corrected capture is a new edition of the source, not an edit of the page describing it. Refuses if the target has more than one owning source page; if it has none, replaces anyway and says so. Cannot be combined with `--page` or with more than one incoming file - a replacement is one file for one file. Prints the source page (if any) and its citing pages, so their update lands in the same commit as the replacement
|
- `wikitool raw accept <file> --replaces <raw-path> [--fidelity <v>] [--authority <v>] [--dry-run]` - Overwrite one existing raw file in place with a new edition
|
||||||
|
|
||||||
**PROPERTIES**
|
**PROPERTIES**
|
||||||
|
|
||||||
@@ -1368,20 +1368,58 @@ Promote one or more files from `incoming/` into `raw/`.
|
|||||||
- budget: counted
|
- budget: counted
|
||||||
- network: no
|
- network: no
|
||||||
|
|
||||||
|
**EXAMPLES**
|
||||||
|
|
||||||
|
- `tools/wikitool raw accept incoming/docker-cheatsheet.md --fidelity verbatim --authority reporting`
|
||||||
|
- `tools/wikitool raw accept incoming/part-2.md --fidelity verbatim --authority reporting --page "Source - Docker Cheatsheet"`
|
||||||
|
- `tools/wikitool raw accept incoming/cluster.md --replaces raw/documents/cluster.md`
|
||||||
|
|
||||||
**EXIT STATUS**
|
**EXIT STATUS**
|
||||||
|
|
||||||
- 0 success
|
- 0 success
|
||||||
- 1 raw accept: A file does not exist, is not under `incoming/`, or is nested more than one level below it, two files in one call share a filename, a target path already exists, `--fidelity`/`--authority` is missing (unless `--replaces`) or names `unknown` or a value outside the schema's enum, the target name is already occupied anywhere under `raw/` by something the call does not own, `--page` names an unknown page or one with no `raw_files:` yet, an existing `raw_files:` entry is missing on disk, a file to be moved has more than one owning page, or `--page` would overwrite an already-set `fidelity`/`authority` with a different value
|
- 1 raw accept: A file does not exist, is not under `incoming/`, or is nested more than one level below it; two files in one call share a filename; or a target path already exists
|
||||||
- 1 raw accept --replaces: More than one incoming file, `--page` also given, the incoming file does not exist or is not under `incoming/` (or is nested more than one level below it), its filename differs from the target's, the target does not lie under `raw/` or does not exist, `--fidelity`/`--authority` names `unknown` or a value outside the schema's enum, or the target has more than one owning source page
|
- 1 raw accept: `--fidelity`/`--authority` is missing, or names `unknown` or a value outside the schema's enum
|
||||||
|
- 1 raw accept: The target name is already occupied anywhere under `raw/` by something the call does not own
|
||||||
|
- 1 raw accept: `--page` names an unknown page or one with no `raw_files:` yet, an existing `raw_files:` entry is missing on disk, a file to be moved has more than one owning page, or `--page` would overwrite an already-set `fidelity`/`authority` with a different value
|
||||||
|
- 1 raw accept --replaces: More than one incoming file, or `--page` also given
|
||||||
|
- 1 raw accept --replaces: The incoming file does not exist or is not under `incoming/` (or is nested more than one level below it), its filename differs from the target's, or the target does not lie under `raw/` or does not exist
|
||||||
|
- 1 raw accept --replaces: `--fidelity`/`--authority` names `unknown` or a value outside the schema's enum, or the target has more than one owning source page
|
||||||
|
|
||||||
**ON FAILURE**
|
**ON FAILURE**
|
||||||
|
|
||||||
- raw accept: A file does not exist, is not under `incoming/`, or is nested more than one level below it, two files in one call share a filename, a target path already exists, `--fidelity`/`--authority` is missing (unless `--replaces`) or names `unknown` or a value outside the schema's enum, the target name is already occupied anywhere under `raw/` by something the call does not own, `--page` names an unknown page or one with no `raw_files:` yet, an existing `raw_files:` entry is missing on disk, a file to be moved has more than one owning page, or `--page` would overwrite an already-set `fidelity`/`authority` with a different value -> Fix the named argument and retry once. Safe to retry as-is once the cause is fixed: a file already at its computed destination is what "already exists" reports, not a partial prior run to resume. A stem-occupied refusal is not fixed by retrying at all - it names `--replaces` and renaming in `incoming/` as the two routes and neither is the tool's to pick. Never choose the destination by hand instead - that is the decision this command exists to take away
|
- raw accept: A file does not exist, is not under `incoming/`, or is nested more than one level below it; two files in one call share a filename; or a target path already exists -> Fix the named argument and retry once
|
||||||
- raw accept --replaces: More than one incoming file, `--page` also given, the incoming file does not exist or is not under `incoming/` (or is nested more than one level below it), its filename differs from the target's, the target does not lie under `raw/` or does not exist, `--fidelity`/`--authority` names `unknown` or a value outside the schema's enum, or the target has more than one owning source page -> Fix the named argument and retry once. Every check runs before the filesystem is touched, so a refusal leaves both files exactly as they were
|
- raw accept: `--fidelity`/`--authority` is missing, or names `unknown` or a value outside the schema's enum -> Pass both with a valid value, then retry once
|
||||||
|
- raw accept: The target name is already occupied anywhere under `raw/` by something the call does not own -> Not fixed by retrying: the refusal names `--replaces` (same source, new edition) and renaming in `incoming/` (a separate source) as the two routes, and neither is the tool's to pick. Show the message to the user and wait
|
||||||
|
- raw accept: `--page` names an unknown page or one with no `raw_files:` yet, an existing `raw_files:` entry is missing on disk, a file to be moved has more than one owning page, or `--page` would overwrite an already-set `fidelity`/`authority` with a different value -> Fix the named argument and retry once; a different capture value on an existing page is a new edition - `--replaces`
|
||||||
|
- raw accept --replaces: More than one incoming file, or `--page` also given -> A replacement is one file for one file - fix the call and retry once
|
||||||
|
- raw accept --replaces: The incoming file does not exist or is not under `incoming/` (or is nested more than one level below it), its filename differs from the target's, or the target does not lie under `raw/` or does not exist -> Fix the named argument and retry once - every check runs before the filesystem is touched, so both files are exactly as they were
|
||||||
|
- raw accept --replaces: `--fidelity`/`--authority` names `unknown` or a value outside the schema's enum, or the target has more than one owning source page -> Fix the named argument and retry once; nothing was touched
|
||||||
|
|
||||||
|
**NEVER**
|
||||||
|
|
||||||
|
- Never choose the destination under `raw/` by hand, and never move a file into `raw/` yourself.
|
||||||
|
- Never pick between `--replaces` and renaming on your own initiative after a name-occupied refusal - the user tells the two intents apart.
|
||||||
|
|
||||||
**NOTES**
|
**NOTES**
|
||||||
|
|
||||||
See `raw/CONTRACT.md` "Getting a file in: incoming/".
|
- Promotes files from `incoming/` into `raw/<YYYY>/<MM>/`, computed from the accept date rather than chosen by hand. A subdirectory under `incoming/` is tolerated and ignored, not inspected - `raw/` does not address by type.
|
||||||
|
- One file promoted alone lands with no directory of its own; several files in one call nest under `raw/<YYYY>/<MM>/<stem>/`, named after the first file's stem.
|
||||||
|
- `--fidelity`/`--authority` are required on a plain accept (`types describe source` lists the values); `unknown` is refused - it is backfill-only.
|
||||||
|
- `--page "<Title>"` additionally extends that existing source page's `raw_files:` in the same call and writes both capture fields onto it - refused if it already carries a different value, since a capture field is fixed once.
|
||||||
|
- If `--page` raises the page past one file, its already-promoted file is folded into a bundle at *its own* parent directory, not today's shard, so a bundle never mixes an old and a new capture date - after checking that file has no other owner.
|
||||||
|
- Every name occupied anywhere under `raw/` - file stems and bundle directory names alike, old type directories and date shards together - stays unique: a promote whose target name already belongs to something this call does not itself own is refused, naming `--replaces` and renaming in `incoming/` as the two routes, without recommending either.
|
||||||
|
- `--replaces <raw-path>` overwrites that file in place with the single incoming file (same filename required), leaves every page's `raw_files:` untouched and writes no `kb/` page; the previous edition survives only in `git log --follow <raw-path>`.
|
||||||
|
- With `--replaces`, `--fidelity`/`--authority` are optional, and passing one overwrites the owning page's already-set value - the one path the fixed-once rule does not block.
|
||||||
|
- `--replaces` refuses a target with more than one owning source page; with none, it replaces anyway and says so. It cannot be combined with `--page` or with more than one incoming file.
|
||||||
|
- `--replaces` prints the source page (if any) and its citing pages, so their update lands in the same commit as the replacement.
|
||||||
|
- A file already at its computed destination is what "already exists" reports, not a partial prior run to resume - safe to retry as-is once a cause is fixed.
|
||||||
|
- `--dry-run` reports the moves without making them.
|
||||||
|
|
||||||
|
**SEE ALSO**
|
||||||
|
|
||||||
|
- `raw/CONTRACT.md` "Getting a file in: incoming/" - the rules and why
|
||||||
|
- `wikitool types describe source` - the capture field values
|
||||||
|
- `wikitool new source` - the source page for a promoted file
|
||||||
|
|
||||||
#### `upload list`
|
#### `upload list`
|
||||||
|
|
||||||
@@ -1399,13 +1437,26 @@ List every MCP submission currently waiting in the quarantine (`mcp-upload/`).
|
|||||||
- budget: counted
|
- budget: counted
|
||||||
- network: no
|
- network: no
|
||||||
|
|
||||||
|
**EXAMPLES**
|
||||||
|
|
||||||
|
- `tools/wikitool upload list`
|
||||||
|
- `tools/wikitool upload list --json`
|
||||||
|
|
||||||
**EXIT STATUS**
|
**EXIT STATUS**
|
||||||
|
|
||||||
- 0 success
|
- 0 success
|
||||||
|
|
||||||
**NOTES**
|
**NOTES**
|
||||||
|
|
||||||
Oldest id first - id, filename, size, submitter. Only ever non-empty when `.wikitool-upload.json` opts a checkout into the MCP server's `submit` tool (see the MCP read server design note). Never fails - a submission directory with a corrupt manifest is silently skipped. Safe to retry freely.
|
- Lists every submission waiting in `mcp-upload/`, oldest id first: id, filename, size, submitter.
|
||||||
|
- Only ever non-empty when `.wikitool-upload.json` opts the checkout into the MCP server's `submit` tool.
|
||||||
|
- A submission directory with a corrupt manifest is silently skipped.
|
||||||
|
- Never fails; read-only and safe to retry freely.
|
||||||
|
|
||||||
|
**SEE ALSO**
|
||||||
|
|
||||||
|
- `wikitool upload show` - one submission's manifest
|
||||||
|
- `INSTALL-MCP.md` § "Schritt 7: Optional - den `submit`-Pfad freischalten"
|
||||||
|
|
||||||
#### `upload show`
|
#### `upload show`
|
||||||
|
|
||||||
@@ -1423,6 +1474,10 @@ Print one submission's manifest in full.
|
|||||||
- budget: counted
|
- budget: counted
|
||||||
- network: no
|
- network: no
|
||||||
|
|
||||||
|
**EXAMPLES**
|
||||||
|
|
||||||
|
- `tools/wikitool upload show <id>`
|
||||||
|
|
||||||
**EXIT STATUS**
|
**EXIT STATUS**
|
||||||
|
|
||||||
- 0 success
|
- 0 success
|
||||||
@@ -1434,7 +1489,14 @@ Print one submission's manifest in full.
|
|||||||
|
|
||||||
**NOTES**
|
**NOTES**
|
||||||
|
|
||||||
Filename, size, sha256, submitter, submitter source (the header name, not a claim the header was honest), submission time. What a reviewer reads before `accept`
|
- Prints one submission's manifest in full: filename, size, sha256, submitter, submitter source (the header name, not a claim the header was honest), submission time.
|
||||||
|
- What a reviewer reads before `upload accept`.
|
||||||
|
- Read-only.
|
||||||
|
|
||||||
|
**SEE ALSO**
|
||||||
|
|
||||||
|
- `wikitool upload accept` - promotes it after review
|
||||||
|
- `wikitool upload reject` - declines it
|
||||||
|
|
||||||
#### `upload accept`
|
#### `upload accept`
|
||||||
|
|
||||||
@@ -1453,19 +1515,40 @@ Filename, size, sha256, submitter, submitter source (the header name, not a clai
|
|||||||
- network: no
|
- network: no
|
||||||
- gates: upload-review
|
- gates: upload-review
|
||||||
|
|
||||||
|
**EXAMPLES**
|
||||||
|
|
||||||
|
- `tools/wikitool upload accept <id>`
|
||||||
|
- `tools/wikitool upload accept <id> --confirm <token> # re-run after exit 42, once the user approved the manifest`
|
||||||
|
|
||||||
**EXIT STATUS**
|
**EXIT STATUS**
|
||||||
|
|
||||||
- 0 success
|
- 0 success
|
||||||
- 1 Unknown or malformed submission id, the submission's file is missing from `mcp-upload/<id>/`, or `incoming/<filename>` already exists. **Exit 42, not 1**, when `--confirm` is absent or does not match the manifest's current token - the Upload Review Gate, not a validation error
|
- 1 Unknown or malformed submission id, or the submission's file is missing from `mcp-upload/<id>/`
|
||||||
- 42 needs clearance - upload-review (see AGENTS.md § Gates)
|
- 1 `incoming/<filename>` already exists
|
||||||
|
- 42 Upload Review Gate: `--confirm` is absent or does not match the manifest's current token
|
||||||
|
|
||||||
**ON FAILURE**
|
**ON FAILURE**
|
||||||
|
|
||||||
- Unknown or malformed submission id, the submission's file is missing from `mcp-upload/<id>/`, or `incoming/<filename>` already exists. **Exit 42, not 1**, when `--confirm` is absent or does not match the manifest's current token - the Upload Review Gate, not a validation error -> For exit 42: show the user the full manifest and the exact `--confirm <token>` re-run line printed, and stop - the same rule as every other exit-42 gate (AGENTS.md invariant 6). For the three exit-1 cases: fix the named argument and retry once; an occupied `incoming/<filename>` is not fixed by retrying unchanged - rename or clear it first
|
- Unknown or malformed submission id, or the submission's file is missing from `mcp-upload/<id>/` -> Fix the named argument and retry once
|
||||||
|
- `incoming/<filename>` already exists -> Not fixed by retrying unchanged - rename or clear it first
|
||||||
|
- Upload Review Gate: `--confirm` is absent or does not match the manifest's current token -> Show the user the command's full output verbatim and stop. Once they have approved it, run the re-run line the output prints, which carries `--confirm <token>`. Without that token, or with a wrong, invented or superseded one, it exits 42 again with the current state
|
||||||
|
|
||||||
|
**NEVER**
|
||||||
|
|
||||||
|
- Never pass a `--confirm` token the user has not seen and approved.
|
||||||
|
|
||||||
**NOTES**
|
**NOTES**
|
||||||
|
|
||||||
Promote a submission's file from `mcp-upload/<id>/` into `incoming/`, delete the quarantine directory, and append an `accepted` event to `mcp-upload/ledger.jsonl`. Without a matching `--confirm`, exits **42** and prints the manifest in full plus the exact re-run line - the same shape as the Mass-Update Gate's clearance, one submission at a time. The token digests id/filename/size/sha256/submitter, so an edited or superseded manifest invalidates it. Refuses (without the gate - these are ordinary validation errors) when `incoming/<filename>` already exists
|
- Promotes a submission's file from `mcp-upload/<id>/` into `incoming/`, deletes the quarantine directory, and appends an `accepted` event to `mcp-upload/ledger.jsonl`.
|
||||||
|
- Upload Review Gate: without a matching `--confirm`, exits 42 and prints the manifest in full plus the exact re-run line - one submission at a time. The gate check runs before anything is moved.
|
||||||
|
- The token digests id, filename, size, sha256 and submitter, so an edited or superseded manifest invalidates it.
|
||||||
|
- An occupied `incoming/<filename>` is an ordinary validation error, not the gate.
|
||||||
|
|
||||||
|
**SEE ALSO**
|
||||||
|
|
||||||
|
- `wikitool upload show` - the manifest to review
|
||||||
|
- `wikitool raw accept` - the next step for the file in `incoming/`
|
||||||
|
- `instructions/gates.md` - the gate procedure
|
||||||
|
|
||||||
#### `upload reject`
|
#### `upload reject`
|
||||||
|
|
||||||
@@ -1483,6 +1566,10 @@ Delete a submission's material, keeping only its ledger trail.
|
|||||||
- budget: counted
|
- budget: counted
|
||||||
- network: no
|
- network: no
|
||||||
|
|
||||||
|
**EXAMPLES**
|
||||||
|
|
||||||
|
- `tools/wikitool upload reject <id> --reason "duplicate of raw/articles/llm-wiki.md"`
|
||||||
|
|
||||||
**EXIT STATUS**
|
**EXIT STATUS**
|
||||||
|
|
||||||
- 0 success
|
- 0 success
|
||||||
@@ -1490,11 +1577,18 @@ Delete a submission's material, keeping only its ledger trail.
|
|||||||
|
|
||||||
**ON FAILURE**
|
**ON FAILURE**
|
||||||
|
|
||||||
- Unknown or malformed submission id, or an empty `--reason` -> Fix the argument and retry once. Not idempotent against a second call with the same id: the first call already deleted the submission, so a retry reports "unknown id" - that is confirmation, not a failure
|
- Unknown or malformed submission id, or an empty `--reason` -> Fix the argument and retry once. After a first successful call, "unknown id" is confirmation, not a failure
|
||||||
|
|
||||||
**NOTES**
|
**NOTES**
|
||||||
|
|
||||||
An append-only `rejected` event naming the reason and the sha256 of what was declined. No gate - rejecting needs no clearance, only accepting does
|
- Appends an append-only `rejected` event naming the reason and the sha256 of what was declined, then deletes the submission's material.
|
||||||
|
- The ledger write happens first, so an interruption still leaves the reason on record.
|
||||||
|
- No gate - rejecting needs no clearance, only accepting does.
|
||||||
|
- Not idempotent: a second call with the same id reports "unknown id", which confirms the first call worked.
|
||||||
|
|
||||||
|
**SEE ALSO**
|
||||||
|
|
||||||
|
- `wikitool upload show` - the manifest to review
|
||||||
|
|
||||||
### Git
|
### Git
|
||||||
|
|
||||||
|
|||||||
@@ -316,42 +316,13 @@ def _replace(
|
|||||||
cli_contract.Variant(
|
cli_contract.Variant(
|
||||||
usage='raw accept <file> [<file> ...] --fidelity <v> --authority <v> '
|
usage='raw accept <file> [<file> ...] --fidelity <v> --authority <v> '
|
||||||
'[--page "<Title>"] [--dry-run]',
|
'[--page "<Title>"] [--dry-run]',
|
||||||
notes="Promote one or more files from `incoming/` into `raw/<YYYY>/<MM>/`, "
|
notes="Promote one or more files from `incoming/` into today's `raw/<YYYY>/<MM>/` "
|
||||||
"computed from the accept date rather than chosen by hand "
|
"shard",
|
||||||
"(`raw/CONTRACT.md` \"Getting a file in\"): a subdirectory under `incoming/` is "
|
|
||||||
"tolerated and ignored, not inspected - `raw/` no longer addresses by type. One "
|
|
||||||
"file promoted alone lands with no directory of its own; several files in one call "
|
|
||||||
"nest under `raw/<YYYY>/<MM>/<stem>/`, named after the first file's stem. "
|
|
||||||
"`--fidelity`/`--authority` are required here (see `types describe source`; "
|
|
||||||
"`unknown` is refused, backfill-only) - the one moment both are knowable. "
|
|
||||||
'`--page "<Title>"` additionally extends that existing source page\'s '
|
|
||||||
"`raw_files:` in the same call and writes both capture fields onto it (refused if "
|
|
||||||
"it already carries a different value - a capture field is fixed once); if that "
|
|
||||||
"raises the page past one file, its already-promoted file is folded into a bundle "
|
|
||||||
"at *its own* parent directory, not today's shard, so a bundle never mixes an old "
|
|
||||||
"and a new capture date, after checking it has no other owner "
|
|
||||||
"(`provenance.duplicate_raw_file_owners`). The set of names occupied anywhere "
|
|
||||||
"under `raw/` - file stems and bundle directory names alike, old type directories "
|
|
||||||
"and date shards together - must stay unique: a promote whose target name already "
|
|
||||||
"belongs to something this call does not itself own is refused, naming both "
|
|
||||||
"`--replaces` and renaming-in-`incoming/` without recommending either",
|
|
||||||
),
|
),
|
||||||
cli_contract.Variant(
|
cli_contract.Variant(
|
||||||
usage='raw accept <file> --replaces <raw-path> [--fidelity <v>] [--authority <v>] '
|
usage='raw accept <file> --replaces <raw-path> [--fidelity <v>] [--authority <v>] '
|
||||||
"[--dry-run]",
|
"[--dry-run]",
|
||||||
notes="The one sanctioned way past that uniqueness rule, and the one sanctioned "
|
notes="Overwrite one existing raw file in place with a new edition",
|
||||||
"way to correct an already-set capture field: overwrites `<raw-path>` in place with "
|
|
||||||
"the single incoming file (same filename required; there is no type directory left "
|
|
||||||
"to match), leaving every page's `raw_files:` untouched and writing no `kb/` page - "
|
|
||||||
"the previous edition survives only in `git log --follow <raw-path>`. "
|
|
||||||
"`--fidelity`/`--authority` are optional here, and passing one overwrites the "
|
|
||||||
"owning page's already-set value - the one path fill-once does not block, because "
|
|
||||||
"a corrected capture is a new edition of the source, not an edit of the page "
|
|
||||||
"describing it. Refuses if the target has more than one owning source page; if it "
|
|
||||||
"has none, replaces anyway and says so. Cannot be combined with `--page` or with "
|
|
||||||
"more than one incoming file - a replacement is one file for one file. Prints the "
|
|
||||||
"source page (if any) and its citing pages, so their update lands in the same "
|
|
||||||
"commit as the replacement",
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
properties=cli_contract.Properties(
|
properties=cli_contract.Properties(
|
||||||
@@ -362,35 +333,108 @@ def _replace(
|
|||||||
"`--fidelity`/`--authority` was given) one page write",
|
"`--fidelity`/`--authority` was given) one page write",
|
||||||
budget=cli_contract.Budget.COUNTED,
|
budget=cli_contract.Budget.COUNTED,
|
||||||
),
|
),
|
||||||
notes="See `raw/CONTRACT.md` \"Getting a file in: incoming/\".",
|
notes=(
|
||||||
|
"Promotes files from `incoming/` into `raw/<YYYY>/<MM>/`, computed from the accept "
|
||||||
|
"date rather than chosen by hand. A subdirectory under `incoming/` is tolerated and "
|
||||||
|
"ignored, not inspected - `raw/` does not address by type.",
|
||||||
|
"One file promoted alone lands with no directory of its own; several files in one call "
|
||||||
|
"nest under `raw/<YYYY>/<MM>/<stem>/`, named after the first file's stem.",
|
||||||
|
"`--fidelity`/`--authority` are required on a plain accept (`types describe source` "
|
||||||
|
"lists the values); `unknown` is refused - it is backfill-only.",
|
||||||
|
"`--page \"<Title>\"` additionally extends that existing source page's `raw_files:` in "
|
||||||
|
"the same call and writes both capture fields onto it - refused if it already carries a "
|
||||||
|
"different value, since a capture field is fixed once.",
|
||||||
|
"If `--page` raises the page past one file, its already-promoted file is folded into a "
|
||||||
|
"bundle at *its own* parent directory, not today's shard, so a bundle never mixes an "
|
||||||
|
"old and a new capture date - after checking that file has no other owner.",
|
||||||
|
"Every name occupied anywhere under `raw/` - file stems and bundle directory names "
|
||||||
|
"alike, old type directories and date shards together - stays unique: a promote whose "
|
||||||
|
"target name already belongs to something this call does not itself own is refused, "
|
||||||
|
"naming `--replaces` and renaming in `incoming/` as the two routes, without "
|
||||||
|
"recommending either.",
|
||||||
|
"`--replaces <raw-path>` overwrites that file in place with the single incoming file "
|
||||||
|
"(same filename required), leaves every page's `raw_files:` untouched and writes no "
|
||||||
|
"`kb/` page; the previous edition survives only in `git log --follow <raw-path>`.",
|
||||||
|
"With `--replaces`, `--fidelity`/`--authority` are optional, and passing one overwrites "
|
||||||
|
"the owning page's already-set value - the one path the fixed-once rule does not "
|
||||||
|
"block.",
|
||||||
|
"`--replaces` refuses a target with more than one owning source page; with none, it "
|
||||||
|
"replaces anyway and says so. It cannot be combined with `--page` or with more than one "
|
||||||
|
"incoming file.",
|
||||||
|
"`--replaces` prints the source page (if any) and its citing pages, so their update "
|
||||||
|
"lands in the same commit as the replacement.",
|
||||||
|
"A file already at its computed destination is what \"already exists\" reports, not a "
|
||||||
|
"partial prior run to resume - safe to retry as-is once a cause is fixed.",
|
||||||
|
"`--dry-run` reports the moves without making them.",
|
||||||
|
),
|
||||||
failures=(
|
failures=(
|
||||||
cli_contract.Failure(
|
cli_contract.Failure(
|
||||||
label="raw accept",
|
label="raw accept",
|
||||||
cause="A file does not exist, is not under `incoming/`, or is nested more than "
|
cause="A file does not exist, is not under `incoming/`, or is nested more than one "
|
||||||
"one level below it, two files in one call share a filename, a target path already "
|
"level below it; two files in one call share a filename; or a target path already "
|
||||||
"exists, `--fidelity`/`--authority` is missing (unless `--replaces`) or names "
|
"exists",
|
||||||
"`unknown` or a value outside the schema's enum, the target name is already "
|
reaction="Fix the named argument and retry once",
|
||||||
"occupied anywhere under `raw/` by something the call does not own, `--page` names "
|
),
|
||||||
"an unknown page or one with no `raw_files:` yet, an existing `raw_files:` entry is "
|
cli_contract.Failure(
|
||||||
"missing on disk, a file to be moved has more than one owning page, or `--page` "
|
label="raw accept",
|
||||||
"would overwrite an already-set `fidelity`/`authority` with a different value",
|
cause="`--fidelity`/`--authority` is missing, or names `unknown` or a value outside "
|
||||||
reaction="Fix the named argument and retry once. Safe to retry as-is once the cause is "
|
"the schema's enum",
|
||||||
"fixed: a file already at its computed destination is what \"already exists\" "
|
reaction="Pass both with a valid value, then retry once",
|
||||||
"reports, not a partial prior run to resume. A stem-occupied refusal is not fixed "
|
),
|
||||||
"by retrying at all - it names `--replaces` and renaming in `incoming/` as the two "
|
cli_contract.Failure(
|
||||||
"routes and neither is the tool's to pick. Never choose the destination by hand "
|
label="raw accept",
|
||||||
"instead - that is the decision this command exists to take away",
|
cause="The target name is already occupied anywhere under `raw/` by something the "
|
||||||
|
"call does not own",
|
||||||
|
reaction="Not fixed by retrying: the refusal names `--replaces` (same source, new "
|
||||||
|
"edition) and renaming in `incoming/` (a separate source) as the two routes, and "
|
||||||
|
"neither is the tool's to pick. Show the message to the user and wait",
|
||||||
|
),
|
||||||
|
cli_contract.Failure(
|
||||||
|
label="raw accept",
|
||||||
|
cause="`--page` names an unknown page or one with no `raw_files:` yet, an existing "
|
||||||
|
"`raw_files:` entry is missing on disk, a file to be moved has more than one owning "
|
||||||
|
"page, or `--page` would overwrite an already-set `fidelity`/`authority` with a "
|
||||||
|
"different value",
|
||||||
|
reaction="Fix the named argument and retry once; a different capture value on an "
|
||||||
|
"existing page is a new edition - `--replaces`",
|
||||||
),
|
),
|
||||||
cli_contract.Failure(
|
cli_contract.Failure(
|
||||||
label="raw accept --replaces",
|
label="raw accept --replaces",
|
||||||
cause="More than one incoming file, `--page` also given, the incoming file does "
|
cause="More than one incoming file, or `--page` also given",
|
||||||
"not exist or is not under `incoming/` (or is nested more than one level below "
|
reaction="A replacement is one file for one file - fix the call and retry once",
|
||||||
"it), its filename differs from the target's, the target does not lie under `raw/` "
|
|
||||||
"or does not exist, `--fidelity`/`--authority` names `unknown` or a value outside "
|
|
||||||
"the schema's enum, or the target has more than one owning source page",
|
|
||||||
reaction="Fix the named argument and retry once. Every check runs before the "
|
|
||||||
"filesystem is touched, so a refusal leaves both files exactly as they were",
|
|
||||||
),
|
),
|
||||||
|
cli_contract.Failure(
|
||||||
|
label="raw accept --replaces",
|
||||||
|
cause="The incoming file does not exist or is not under `incoming/` (or is nested "
|
||||||
|
"more than one level below it), its filename differs from the target's, or the "
|
||||||
|
"target does not lie under `raw/` or does not exist",
|
||||||
|
reaction="Fix the named argument and retry once - every check runs before the "
|
||||||
|
"filesystem is touched, so both files are exactly as they were",
|
||||||
|
),
|
||||||
|
cli_contract.Failure(
|
||||||
|
label="raw accept --replaces",
|
||||||
|
cause="`--fidelity`/`--authority` names `unknown` or a value outside the schema's "
|
||||||
|
"enum, or the target has more than one owning source page",
|
||||||
|
reaction="Fix the named argument and retry once; nothing was touched",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
examples=(
|
||||||
|
"tools/wikitool raw accept incoming/docker-cheatsheet.md --fidelity verbatim "
|
||||||
|
"--authority reporting",
|
||||||
|
'tools/wikitool raw accept incoming/part-2.md --fidelity verbatim --authority reporting '
|
||||||
|
'--page "Source - Docker Cheatsheet"',
|
||||||
|
"tools/wikitool raw accept incoming/cluster.md --replaces raw/documents/cluster.md",
|
||||||
|
),
|
||||||
|
never=(
|
||||||
|
"Never choose the destination under `raw/` by hand, and never move a file into `raw/` "
|
||||||
|
"yourself.",
|
||||||
|
"Never pick between `--replaces` and renaming on your own initiative after a "
|
||||||
|
"name-occupied refusal - the user tells the two intents apart.",
|
||||||
|
),
|
||||||
|
see_also=(
|
||||||
|
"`raw/CONTRACT.md` \"Getting a file in: incoming/\" - the rules and why",
|
||||||
|
"`wikitool types describe source` - the capture field values",
|
||||||
|
"`wikitool new source` - the source page for a promoted file",
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
@app.command("accept")
|
@app.command("accept")
|
||||||
|
|||||||
@@ -43,11 +43,23 @@ def _call(fn, *args, **kwargs):
|
|||||||
atomic="Read-only",
|
atomic="Read-only",
|
||||||
budget=cli_contract.Budget.COUNTED,
|
budget=cli_contract.Budget.COUNTED,
|
||||||
),
|
),
|
||||||
notes="Oldest id first - id, filename, size, submitter. Only ever non-empty when "
|
notes=(
|
||||||
"`.wikitool-upload.json` opts a checkout into the MCP server's `submit` tool (see the MCP "
|
"Lists every submission waiting in `mcp-upload/`, oldest id first: id, filename, size, "
|
||||||
"read server design note). Never fails - a submission directory with a corrupt manifest is "
|
"submitter.",
|
||||||
"silently skipped. Safe to retry freely.",
|
"Only ever non-empty when `.wikitool-upload.json` opts the checkout into the MCP "
|
||||||
|
"server's `submit` tool.",
|
||||||
|
"A submission directory with a corrupt manifest is silently skipped.",
|
||||||
|
"Never fails; read-only and safe to retry freely.",
|
||||||
|
),
|
||||||
failures=(),
|
failures=(),
|
||||||
|
examples=(
|
||||||
|
"tools/wikitool upload list",
|
||||||
|
"tools/wikitool upload list --json",
|
||||||
|
),
|
||||||
|
see_also=(
|
||||||
|
"`wikitool upload show` - one submission's manifest",
|
||||||
|
"`INSTALL-MCP.md` § \"Schritt 7: Optional - den `submit`-Pfad freischalten\"",
|
||||||
|
),
|
||||||
))
|
))
|
||||||
def upload_list_command(
|
def upload_list_command(
|
||||||
json_out: bool = typer.Option(False, "--json", help="Print every waiting submission as JSON"),
|
json_out: bool = typer.Option(False, "--json", help="Print every waiting submission as JSON"),
|
||||||
@@ -78,13 +90,24 @@ def upload_list_command(
|
|||||||
atomic="Read-only",
|
atomic="Read-only",
|
||||||
budget=cli_contract.Budget.COUNTED,
|
budget=cli_contract.Budget.COUNTED,
|
||||||
),
|
),
|
||||||
notes="Filename, size, sha256, submitter, submitter source (the header name, not a claim "
|
notes=(
|
||||||
"the header was honest), submission time. What a reviewer reads before `accept`",
|
"Prints one submission's manifest in full: filename, size, sha256, submitter, "
|
||||||
|
"submitter source (the header name, not a claim the header was honest), submission "
|
||||||
|
"time.",
|
||||||
|
"What a reviewer reads before `upload accept`.",
|
||||||
|
"Read-only.",
|
||||||
|
),
|
||||||
failures=(cli_contract.Failure(
|
failures=(cli_contract.Failure(
|
||||||
label="",
|
|
||||||
cause="Unknown or malformed submission id",
|
cause="Unknown or malformed submission id",
|
||||||
reaction="Fix the id (see `upload list`) and retry",
|
reaction="Fix the id (see `upload list`) and retry",
|
||||||
),),
|
),),
|
||||||
|
examples=(
|
||||||
|
"tools/wikitool upload show <id>",
|
||||||
|
),
|
||||||
|
see_also=(
|
||||||
|
"`wikitool upload accept` - promotes it after review",
|
||||||
|
"`wikitool upload reject` - declines it",
|
||||||
|
),
|
||||||
))
|
))
|
||||||
def upload_show_command(
|
def upload_show_command(
|
||||||
submission_id: str = typer.Argument(..., help="A submission id from `upload list`"),
|
submission_id: str = typer.Argument(..., help="A submission id from `upload list`"),
|
||||||
@@ -141,25 +164,46 @@ def _clearance_message(manifest: dict, token: str, stale: Optional[str]) -> str:
|
|||||||
budget=cli_contract.Budget.COUNTED,
|
budget=cli_contract.Budget.COUNTED,
|
||||||
gates=("upload-review",),
|
gates=("upload-review",),
|
||||||
),
|
),
|
||||||
notes="Promote a submission's file from `mcp-upload/<id>/` into `incoming/`, delete the "
|
notes=(
|
||||||
"quarantine directory, and append an `accepted` event to `mcp-upload/ledger.jsonl`. Without "
|
"Promotes a submission's file from `mcp-upload/<id>/` into `incoming/`, deletes the "
|
||||||
"a matching `--confirm`, exits **42** and prints the manifest in full plus the exact re-run "
|
"quarantine directory, and appends an `accepted` event to `mcp-upload/ledger.jsonl`.",
|
||||||
"line - the same shape as the Mass-Update Gate's clearance, one submission at a time. The "
|
"Upload Review Gate: without a matching `--confirm`, exits 42 and prints the manifest "
|
||||||
"token digests id/filename/size/sha256/submitter, so an edited or superseded manifest "
|
"in full plus the exact re-run line - one submission at a time. The gate check runs "
|
||||||
"invalidates it. Refuses (without the gate - these are ordinary validation errors) when "
|
"before anything is moved.",
|
||||||
"`incoming/<filename>` already exists",
|
"The token digests id, filename, size, sha256 and submitter, so an edited or "
|
||||||
failures=(cli_contract.Failure(
|
"superseded manifest invalidates it.",
|
||||||
label="",
|
"An occupied `incoming/<filename>` is an ordinary validation error, not the gate.",
|
||||||
cause="Unknown or malformed submission id, the submission's file is missing from "
|
),
|
||||||
"`mcp-upload/<id>/`, or `incoming/<filename>` already exists. **Exit 42, not 1**, when "
|
failures=(
|
||||||
"`--confirm` is absent or does not match the manifest's current token - the Upload "
|
cli_contract.Failure(
|
||||||
"Review Gate, not a validation error",
|
cause="Unknown or malformed submission id, or the submission's file is missing from "
|
||||||
reaction="For exit 42: show the user the full manifest and the exact `--confirm <token>` "
|
"`mcp-upload/<id>/`",
|
||||||
"re-run line printed, and stop - the same rule as every other exit-42 gate (AGENTS.md "
|
reaction="Fix the named argument and retry once",
|
||||||
"invariant 6). For the three exit-1 cases: fix the named argument and retry once; an "
|
),
|
||||||
"occupied `incoming/<filename>` is not fixed by retrying unchanged - rename or clear it "
|
cli_contract.Failure(
|
||||||
"first",
|
cause="`incoming/<filename>` already exists",
|
||||||
),),
|
reaction="Not fixed by retrying unchanged - rename or clear it first",
|
||||||
|
),
|
||||||
|
cli_contract.Failure(
|
||||||
|
cause="Upload Review Gate: `--confirm` is absent or does not match the manifest's "
|
||||||
|
"current token",
|
||||||
|
reaction=cli_contract.token_gate_reaction("--confirm"),
|
||||||
|
code=42,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
examples=(
|
||||||
|
"tools/wikitool upload accept <id>",
|
||||||
|
"tools/wikitool upload accept <id> --confirm <token> # re-run after exit 42, once "
|
||||||
|
"the user approved the manifest",
|
||||||
|
),
|
||||||
|
never=(
|
||||||
|
"Never pass a `--confirm` token the user has not seen and approved.",
|
||||||
|
),
|
||||||
|
see_also=(
|
||||||
|
"`wikitool upload show` - the manifest to review",
|
||||||
|
"`wikitool raw accept` - the next step for the file in `incoming/`",
|
||||||
|
"`instructions/gates.md` - the gate procedure",
|
||||||
|
),
|
||||||
))
|
))
|
||||||
def upload_accept_command(
|
def upload_accept_command(
|
||||||
submission_id: str = typer.Argument(..., help="A submission id from `upload list`"),
|
submission_id: str = typer.Argument(..., help="A submission id from `upload list`"),
|
||||||
@@ -190,15 +234,25 @@ def upload_accept_command(
|
|||||||
"first, so an interruption still leaves the reason on record",
|
"first, so an interruption still leaves the reason on record",
|
||||||
budget=cli_contract.Budget.COUNTED,
|
budget=cli_contract.Budget.COUNTED,
|
||||||
),
|
),
|
||||||
notes="An append-only `rejected` event naming the reason and the sha256 of what was "
|
notes=(
|
||||||
"declined. No gate - rejecting needs no clearance, only accepting does",
|
"Appends an append-only `rejected` event naming the reason and the sha256 of what was "
|
||||||
|
"declined, then deletes the submission's material.",
|
||||||
|
"The ledger write happens first, so an interruption still leaves the reason on record.",
|
||||||
|
"No gate - rejecting needs no clearance, only accepting does.",
|
||||||
|
"Not idempotent: a second call with the same id reports \"unknown id\", which "
|
||||||
|
"confirms the first call worked.",
|
||||||
|
),
|
||||||
failures=(cli_contract.Failure(
|
failures=(cli_contract.Failure(
|
||||||
label="",
|
|
||||||
cause="Unknown or malformed submission id, or an empty `--reason`",
|
cause="Unknown or malformed submission id, or an empty `--reason`",
|
||||||
reaction="Fix the argument and retry once. Not idempotent against a second call with the "
|
reaction="Fix the argument and retry once. After a first successful call, \"unknown "
|
||||||
"same id: the first call already deleted the submission, so a retry reports \"unknown "
|
"id\" is confirmation, not a failure",
|
||||||
"id\" - that is confirmation, not a failure",
|
|
||||||
),),
|
),),
|
||||||
|
examples=(
|
||||||
|
'tools/wikitool upload reject <id> --reason "duplicate of raw/articles/llm-wiki.md"',
|
||||||
|
),
|
||||||
|
see_also=(
|
||||||
|
"`wikitool upload show` - the manifest to review",
|
||||||
|
),
|
||||||
))
|
))
|
||||||
def upload_reject_command(
|
def upload_reject_command(
|
||||||
submission_id: str = typer.Argument(..., help="A submission id from `upload list`"),
|
submission_id: str = typer.Argument(..., help="A submission id from `upload list`"),
|
||||||
|
|||||||
Reference in new issue
Block a user