Files changed: - .gitignore - CHANGES.md - VERSION - docs/explanation/architecture.md - docs/explanation/pipeline-rationale.md - docs/how-to/README.md - docs/how-to/back-up-to-a-synced-folder.md - docs/how-to/collect-from-a-synced-folder.md - docs/how-to/ingest-a-source.md - docs/how-to/set-up-the-mcp-server.md - docs/reference/commands/README.md - docs/reference/commands/git.md - docs/reference/commands/instance-health.md - docs/reference/commands/raw-material-and-uploads.md - docs/reference/configuration.md - instructions/gates.md - instructions/ingest-queue.md - instructions/mcp-read-server.md - instructions/migrations/10.0.0-settings-tiers.md - instructions/wiki-ingest/SKILL.md - raw/CONTRACT.md - tools/CONTRACT.md - tools/README.md - tools/chemenu/cli_contract.py - tools/chemenu/collect.py - tools/chemenu/commands/backup_cmd.py - tools/chemenu/commands/docs_verify.py - tools/chemenu/commands/doctor.py - tools/chemenu/commands/raw/pending.py - tools/chemenu/commands/upload_cmd.py - tools/chemenu/config.py - tools/chemenu/config_shapes.py - tools/chemenu/legacy_settings.py - tools/chemenu/mcp/server.py - tools/chemenu/reference_doc.py - tools/chemenu/tests/test_backup.py - tools/chemenu/tests/test_collect.py - tools/chemenu/tests/test_config_shapes.py - tools/chemenu/tests/test_doctor.py - tools/chemenu/tests/test_legacy_settings.py - tools/chemenu/tests/test_mcp_server.py - tools/chemenu/tests/test_upload.py - tools/chemenu/tests/test_upload_cmd.py - tools/chemenu/upload.py Co-Authored-By: Claude Code <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
134 lines
5.7 KiB
Markdown
134 lines
5.7 KiB
Markdown
# Collect documents from a synced folder
|
|
|
|
Documents that are not typed or fetched at this terminal can reach the wiki through an intake
|
|
channel. Besides the MCP `submit` tool there are two channels that this checkout *pulls* from: a
|
|
folder that a sync client such as OneDrive fills (handler `folder`), and a program of your own
|
|
(handler `command`). Both end in the quarantine `quarantine/`, exactly like a submission through the
|
|
server, and both wait there for a human to run `wikitool upload accept` - the Upload Review Gate
|
|
stands between every channel and `incoming/`.
|
|
|
|
This guide creates a `folder` channel, explains what `upload collect` leaves alone and why, and shows
|
|
a `command` channel with an example script. The field reference is in the
|
|
[configuration reference](../reference/configuration.md#intake-channels); the review is in
|
|
[ingesting a source](ingest-a-source.md#a-document-submitted-from-outside).
|
|
|
|
<!-- wikitool:toc -->
|
|
## Contents
|
|
|
|
- [Create the folder channel](#create-the-folder-channel)
|
|
- [What stays in the folder, and why](#what-stays-in-the-folder-and-why)
|
|
- [`settle_seconds` and what it cannot promise](#settle_seconds-and-what-it-cannot-promise)
|
|
- [Deleting in a synced folder deletes everywhere](#deleting-in-a-synced-folder-deletes-everywhere)
|
|
- [A command channel](#a-command-channel)
|
|
- [Afterwards](#afterwards)
|
|
<!-- /wikitool:toc -->
|
|
|
|
## Create the folder channel
|
|
|
|
Channels are not written by `config set`: every field of the `intake` section opens a path into the
|
|
checkout, so it is yours to edit by hand in `.wikitool-local.json`.
|
|
|
|
```json
|
|
{
|
|
"intake": {
|
|
"channels": {
|
|
"onedrive": {
|
|
"handler": "folder",
|
|
"path": "/home/you/OneDrive/wiki-inbox",
|
|
"settle_seconds": 60,
|
|
"max_bytes": 10485760,
|
|
"allowed_extensions": [".pdf", ".docx", ".md", ".txt"]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
`path` has to be absolute and lie outside the instance. Check it with `tools/wikitool doctor`, then
|
|
pull:
|
|
|
|
```bash
|
|
tools/wikitool upload collect # every pull channel
|
|
tools/wikitool upload collect onedrive # only this one
|
|
tools/wikitool upload list
|
|
```
|
|
|
|
A collected file is stored unchanged in `quarantine/<id>/` with a manifest that names the channel,
|
|
the handler and the original path. A folder knows nobody who dropped a file, so the submitter is
|
|
recorded as `unknown`, with the handler as its source: do not trust the name or content of such a
|
|
file any more than that of a stranger's.
|
|
|
|
## What stays in the folder, and why
|
|
|
|
`upload collect` reads the top level of the folder only, and it removes one kind of thing: a file it
|
|
has just stored. Everything else stays where it is and is reported with its reason:
|
|
|
|
| Entry | Why it stays |
|
|
|---|---|
|
|
| A subdirectory | Nothing descends into it, and nothing deletes a directory. |
|
|
| A name starting with `.` or `~$`, a hidden or system file | Editors and sync clients keep temporary files like these next to the real one. |
|
|
| A symbolic link | Following it could leave the folder. |
|
|
| An extension outside `allowed_extensions`, or a file above `max_bytes` | The same limits as for `submit`. |
|
|
| A file younger than `settle_seconds` | It may still be arriving. |
|
|
| A file that changed while it was being read or copied | The source is removed only if it still has the size and sha256 that were stored. |
|
|
|
|
A file whose sha256 already waits in the quarantine is reported as a duplicate, with the id of the
|
|
existing submission, and is removed from the folder.
|
|
|
|
## `settle_seconds` and what it cannot promise
|
|
|
|
A sync client writes a file in pieces, so a file that is half there looks like a complete, smaller
|
|
one. `settle_seconds` (default 60) skips files whose modification or creation time is younger than
|
|
that, and the check that the source did not change during the copy catches the rest. Neither can see
|
|
a client that pauses for longer than the interval in the middle of a file and then resumes: such a
|
|
file is stored incomplete, and the review is where you notice it. Raise the value if your client
|
|
works over a slow link.
|
|
|
|
## Deleting in a synced folder deletes everywhere
|
|
|
|
A file removed from a folder that OneDrive syncs is removed on every device that syncs it. That is
|
|
what a collected file should do - it is now in the quarantine - but it also means a collect on one
|
|
machine empties the folder for all of them. The client's recycle bin keeps the file for a while; the
|
|
quarantine copy, which is never synced, is the one to rely on. Collect from one machine only.
|
|
|
|
## A command channel
|
|
|
|
A `command` channel runs a program of your own, which fetches documents from somewhere that has no
|
|
synced folder - a mailbox, a share, a scanner. The program is named in `commands` and referenced from
|
|
the channel; the section stays valid only if every reference resolves.
|
|
|
|
```json
|
|
{
|
|
"intake": {
|
|
"commands": {
|
|
"scan-mail": ["/home/you/bin/fetch-attachments.sh"]
|
|
},
|
|
"channels": {
|
|
"mail": { "handler": "command", "command": "scan-mail" }
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
The program is started without a shell and with stdin closed, with an empty staging directory under
|
|
`quarantine/.staging/` appended as its last argument. It has 300 seconds. It leaves the files it
|
|
wants to hand over directly in that directory:
|
|
|
|
```bash
|
|
#!/bin/sh
|
|
# fetch-attachments.sh <staging-dir>
|
|
set -eu
|
|
out="$1"
|
|
rclone copy remote:wiki-inbox "$out" --max-age 1d
|
|
```
|
|
|
|
A program that exits non-zero, does not start or times out fails its channel: nothing is taken, the
|
|
staging directory is removed, and the last 2000 characters of its stderr are shown. Other channels
|
|
still run, and `upload collect` exits 1 if any channel failed. With no pull channel configured it
|
|
exits 0 with "nothing to collect".
|
|
|
|
## Afterwards
|
|
|
|
The agent does the same at the start of an ingest when you name no file: `upload collect`, then
|
|
`upload list`, then a report of what waits. It never runs `upload accept` for you.
|