The telemetry, remotes, upload and tasks loaders take field names, defaults, coercions and their Expected: text from the record and accept exactly what they did before. doctor WARNs on a telemetry file the policy silently reads differently. configuration.md carries one generated region per file, also checked in an instance. docs reference --apply now refreshes several regions in one page in sequence instead of keeping only the last. Files changed: - CHANGES.md - VERSION - docs/how-to/set-up-the-mcp-server.md - docs/reference/README.md - docs/reference/commands/instance-health.md - docs/reference/commands/types-instructions-and-docs.md - docs/reference/configuration.md - instructions/dev/doc-pull-through.md - instructions/setup-instance.md - tools/CONTRACT.md - tools/README.md - tools/chemenu/commands/docs_verify.py - tools/chemenu/commands/doctor.py - tools/chemenu/commands/git_publish.py - tools/chemenu/config.py - tools/chemenu/config_shapes.py - tools/chemenu/reference_doc.py - tools/chemenu/tasks/caldav.py - tools/chemenu/tasks/config.py - tools/chemenu/tasks/superproductivity.py - tools/chemenu/telemetry/policy.py - tools/chemenu/tests/test_config_shapes.py - tools/chemenu/tests/test_doctor.py - tools/chemenu/tests/test_reference_doc.py - tools/chemenu/upload.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
333 lines
17 KiB
Markdown
333 lines
17 KiB
Markdown
# Configuration
|
|
|
|
The environment variables `wikitool` reads, and the per-checkout files an operator may create at the
|
|
root of an instance. None of them is needed for an instance to work: an instance installed from a
|
|
release runs against the origin's public release feed with no configuration at all.
|
|
|
|
The shape of each file - its fields, what an absent or a malformed file means, an example - is
|
|
generated from the record `wikitool` itself reads the file with, so it cannot describe a field the
|
|
tool does not take. What a setting does around it is written by hand.
|
|
|
|
Three more files at the root are written by `wikitool` and never by hand: `.wikitool-release.json`,
|
|
`.wikitool-kb.json` and `.wikitool-tools.json` ([AGENTS.md invariant 1](../../AGENTS.md#invariants)).
|
|
The last one has its own section [below](#tool-paths), because the preflight that writes it is
|
|
something you run.
|
|
|
|
<!-- wikitool:toc -->
|
|
## Contents
|
|
|
|
- [Environment variables](#environment-variables)
|
|
- [Telemetry](#telemetry)
|
|
- [Tool paths](#tool-paths)
|
|
- [Allowed push URLs](#allowed-push-urls)
|
|
- [Task tracker](#task-tracker)
|
|
- [Super Productivity](#super-productivity)
|
|
- [CalDAV](#caldav)
|
|
- [MCP submissions](#mcp-submissions)
|
|
<!-- /wikitool:toc -->
|
|
|
|
## Environment variables
|
|
|
|
| Variable | Purpose | Fallback |
|
|
|----------|---------|----------|
|
|
| `WIKI_AUTHOR` | Overrides the author name of new source pages | `git config user.name`. Without either, `new` stops with an `ERROR` |
|
|
| `WIKITOOL_SESSION_ID` | Scopes the Iteration Budget Gate to a task rather than a terminal, and names the directory a trace lands in | A session variable the harness sets itself, where one is known (for example `CLAUDE_CODE_SESSION_ID`), else the parent process id ([instructions/session-setup.md](../../instructions/session-setup.md)) |
|
|
| `WIKITOOL_UPDATE_URL` | The release feed `version check`, `version notes` and `dist upgrade --latest` ask | The value in `.wikitool-release.json`, else the origin's feed |
|
|
| `WIKITOOL_UPDATE_TOKEN` | A Gitea token for the release feed, sent only to downloads on the feed's own host | None. Not needed against the origin, only against a feed in a private repository |
|
|
| `WIKITOOL_TASKS_CONFIG` | Path to a tracker configuration that `task`, `review` and `doctor` read instead of `.wikitool-tasks.json`, for running one checkout against several trackers in turn | `.wikitool-tasks.json` at the root. A path to a file that does not exist is an error, never "no tracker configured" |
|
|
| `CHEMENU_ROOT` | Which corpus the package works on, for a caller outside the checkout itself | The checkout the package sits in. `tools/wikitool` behaves the same without it |
|
|
| `WIKI_TRACE` | `0`/`1` turns telemetry off or on, beating both the default and `.wikitool-telemetry.json` | The installation-form default ([below](#telemetry)) |
|
|
| `WIKI_TRACE_DIR` | Writes traces somewhere other than `reports/telemetry/` | `reports/telemetry/` |
|
|
| `WIKI_TRACE_CONTENT` | `0` keeps only lengths and digests of prompts and replies instead of their text | Text is kept |
|
|
| `WIKI_TRACE_MAX_CONTENT` | Per-attribute content cap in characters | 60 KiB |
|
|
| `WIKI_TRACE_MAX_SESSION_BYTES` | Byte cap per session trace; beats the file's `max_session_bytes` | 5 MiB |
|
|
| `WIKI_TRACE_KEEP_SESSIONS` | How many session directories retention keeps; beats the file's `keep_sessions` | 250 |
|
|
|
|
## Telemetry
|
|
|
|
**Telemetry is off in an instance.** Every instance carries the `.wikitool-release.json` of its
|
|
release, and that is how `chemenu.telemetry.policy` recognises that nobody asked for telemetry. The
|
|
same holds for every further clone of the instance, because the file is committed with the first
|
|
publish. Only a clone of the origin repository, used to develop the stack, has no stamp. There the
|
|
traces are the instrument the stack measures itself with, and they are **on**.
|
|
|
|
To turn the default around, create `.wikitool-telemetry.json` at the root. It is per checkout and
|
|
gitignored, with no `.template`, like `.wikitool-remotes.json`.
|
|
|
|
<!-- wikitool:reference-config-telemetry -->
|
|
| Field | Type | Required | Default | Description |
|
|
|-------|------|----------|---------|-------------|
|
|
| `schema` | any | no | - | Accepted and not read. Conventionally `1`; no version of this file is told apart by it yet. |
|
|
| `enabled` | boolean | no | the installation-form default | Telemetry on or off. `WIKI_TRACE` beats it in both directions. Any other value is read by its truthiness, so `"no"` means on. |
|
|
| `max_session_bytes` | positive integer | no | `5242880` | Byte cap per session trace. `WIKI_TRACE_MAX_SESSION_BYTES` beats it. A number written as a string is accepted. |
|
|
| `keep_sessions` | positive integer | no | `250` | How many session directories retention keeps. `WIKI_TRACE_KEEP_SESSIONS` beats it. A number written as a string is accepted. |
|
|
|
|
- **Absent:** The installation-form default applies: on in a clone of the origin repository, off in an instance.
|
|
- **Malformed:** Never an error. Unreadable JSON or a non-object counts as an absent file, and a cap that is not a positive integer as an absent cap. `doctor` reports each such deviation as a `WARN` under `telemetry`, naming the field.
|
|
- **Unknown keys:** Ignored; `doctor` reports them as a `WARN`.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"enabled": true,
|
|
"max_session_bytes": 5242880,
|
|
"keep_sessions": 250
|
|
}
|
|
```
|
|
<!-- /wikitool:reference-config-telemetry -->
|
|
|
|
`wikitool doctor` reports the current state: on or off, why, and how much has been recorded against
|
|
both caps. What the caps do and what never reaches a trace:
|
|
[telemetry-and-evaluation.md](../explanation/telemetry-and-evaluation.md).
|
|
|
|
## Tool paths
|
|
|
|
**The preflight writes this file, not you.** `.wikitool-tools.json` at the root holds the absolute
|
|
paths of Python, git and ripgrep, as the preflight found them on this machine. `wikitool` starts git
|
|
and rg from there instead of from `PATH`. It is per checkout and gitignored, because a path on one
|
|
machine says nothing about the next.
|
|
|
|
If the file is missing or incomplete, `tools/wikitool` does not start (exit 42) and names the
|
|
preflight. If a tool sits somewhere the preflight does not look, name it:
|
|
`tools/preflight.sh --set rg=<path>`, or in PowerShell
|
|
`pwsh -NoProfile -ExecutionPolicy Bypass -File tools/preflight.ps1 --set rg=<path>`. Never edit the
|
|
file by hand. `doctor` reports under `tool-paths` whether every path still works.
|
|
|
|
## Allowed push URLs
|
|
|
|
`.wikitool-remotes.json` at the root restricts where `publish` may push, per checkout and
|
|
gitignored.
|
|
|
|
<!-- wikitool:reference-config-remotes -->
|
|
| Field | Type | Required | Default | Description |
|
|
|-------|------|----------|---------|-------------|
|
|
| `schema` | any | no | - | Accepted and not read. Conventionally `1`; no version of this file is told apart by it yet. |
|
|
| `allowed_push_urls` | array of strings | yes | - | The push URLs `publish` may write to, compared with the URL the remote resolves to for a push - not with its name. An empty list allows no push at all. |
|
|
|
|
- **Absent:** Any remote is allowed; the Publish-Remote Gate does not apply.
|
|
- **Malformed:** `publish` stops with an error before committing anything. A broken file is never read as "no restriction".
|
|
- **Unknown keys:** Ignored.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"schema": 1,
|
|
"allowed_push_urls": [
|
|
"<url>"
|
|
]
|
|
}
|
|
```
|
|
<!-- /wikitool:reference-config-remotes -->
|
|
|
|
With the file, `publish` refuses with exit 42 (the Publish-Remote Gate) before pushing to a URL not
|
|
on the list. Only you edit this file. The gate has no token and no flag, and an agent that edits the
|
|
list to get past a refusal is opening a gate on its own initiative.
|
|
|
|
## Task tracker
|
|
|
|
**Optional.** The weekly review (`tools/wikitool review`, skill `gtd-weekly-review`) checks the
|
|
project pages under `kb/gtd/` against a task tracker. Which tracker that is goes in
|
|
`.wikitool-tasks.json` at the root. Like the telemetry and remotes files, it is per checkout and has
|
|
no `.template`, and it is **gitignored as soon as a token is in it**.
|
|
|
|
<!-- wikitool:reference-config-tasks -->
|
|
| Field | Type | Required | Default | Description |
|
|
|-------|------|----------|---------|-------------|
|
|
| `schema` | any | no | - | Accepted and not read. Conventionally `1`; no version of this file is told apart by it yet. |
|
|
| `provider` | string | yes | - | Which adapter the review goes through. The provider's own settings sit in a section of the same name. |
|
|
| `thresholds` | object | yes | - | The weekly review's three thresholds. |
|
|
| `thresholds.stalled_waiting_days` | positive integer | yes | - | A waiting-for item older than this many days counts as overdue. A number written as a string is accepted. |
|
|
| `thresholds.unpaged_project_weeks` | positive integer | yes | - | A tracker project with no `kb/` page is reported once it is older than this many weeks. A number written as a string is accepted. |
|
|
| `thresholds.someday_stale_months` | positive integer | yes | - | A someday/maybe item untouched for this many months counts as stale. A number written as a string is accepted. |
|
|
|
|
- **Absent:** No tracker is configured - a valid state. The weekly review has nothing to check against.
|
|
- **Malformed:** An error, and `doctor` reports `FAIL`. A broken file is never read as "no tracker configured".
|
|
- **Unknown keys:** Ignored, so the section of a provider not chosen right now can stay in the file.
|
|
|
|
**The `superproductivity` section.** Connection settings for the Super Productivity adapter.
|
|
|
|
| Field | Type | Required | Default | Description |
|
|
|-------|------|----------|---------|-------------|
|
|
| `access` | string | yes | - | Which way in: `api`, the local REST API (reads the current state and carries the write path), or `snapshot`, the newest backup snapshot (read-only). |
|
|
|
|
With `access: "api"`:
|
|
|
|
| Field | Type | Required | Default | Description |
|
|
|-------|------|----------|---------|-------------|
|
|
| `api_base_url` | string | no | `"http://127.0.0.1:3876"` | Where the app's REST API listens. An empty value means the default. |
|
|
| `api_token` | string | yes, not empty | - | The token from the app's settings. |
|
|
|
|
With `access: "snapshot"`:
|
|
|
|
| Field | Type | Required | Default | Description |
|
|
|-------|------|----------|---------|-------------|
|
|
| `backups_dir` | string | yes, not empty | - | The directory the app writes its backup snapshots to; `~` is expanded. |
|
|
|
|
- **Unknown keys:** Rejected: a section names only one access path's own fields, so a `backups_dir` beside `access: "api"` is an error, not ignored.
|
|
|
|
```json
|
|
{
|
|
"superproductivity": {
|
|
"access": "api",
|
|
"api_base_url": "http://127.0.0.1:3876",
|
|
"api_token": "..."
|
|
}
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"superproductivity": {
|
|
"access": "snapshot",
|
|
"backups_dir": "~/.config/superProductivity/backups"
|
|
}
|
|
}
|
|
```
|
|
|
|
**The `caldav` section.** Connection settings for the CalDAV adapter.
|
|
|
|
| Field | Type | Required | Default | Description |
|
|
|-------|------|----------|---------|-------------|
|
|
| `url` | string | yes, not empty | - | The account's CalDAV calendar home set. A trailing `/` is added when missing. |
|
|
| `username` | string | yes, not empty | - | The login name, which can differ from the user id in the URL. |
|
|
| `app_password` | string | yes, not empty | - | An app password is recommended over the account password. |
|
|
| `inbox_list` | string | yes, not empty | - | The name of the one inbox list. |
|
|
| `someday_list` | string | yes, not empty | - | The name of the one someday/maybe list. |
|
|
| `exclude_lists` | array of strings | no | `[]` | Existing plain task lists that are not projects. |
|
|
|
|
- **Unknown keys:** Rejected.
|
|
|
|
```json
|
|
{
|
|
"caldav": {
|
|
"url": "https://<host>/remote.php/dav/calendars/<user>/",
|
|
"username": "<login>",
|
|
"app_password": "...",
|
|
"inbox_list": "Inbox",
|
|
"someday_list": "Someday",
|
|
"exclude_lists": [
|
|
"..."
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
A complete example:
|
|
|
|
```json
|
|
{
|
|
"schema": 1,
|
|
"provider": "superproductivity",
|
|
"thresholds": {
|
|
"stalled_waiting_days": 14,
|
|
"unpaged_project_weeks": 3,
|
|
"someday_stale_months": 5
|
|
},
|
|
"superproductivity": {
|
|
"access": "api",
|
|
"api_base_url": "http://127.0.0.1:3876",
|
|
"api_token": "..."
|
|
}
|
|
}
|
|
```
|
|
<!-- /wikitool:reference-config-tasks -->
|
|
|
|
The `thresholds` block is configuration, not schema. A value the provider cannot supply at all - a
|
|
waiting item with no follow-up date, a tracker project with no creation date - is not skipped silently
|
|
but reported as its own finding (`waiting_no_follow_up`/`project_age_unknown`).
|
|
|
|
### Super Productivity
|
|
|
|
`access` is **mandatory and has no fallback**. It chooses between two mutually exclusive ways in:
|
|
|
|
- **`access: "snapshot"`** for an instance run headless: it reads only the newest backup snapshot
|
|
under `backups_dir`. That works without the app running, but only reads. The tracker cannot be
|
|
written from there.
|
|
|
|
- **`access: "api"`** for a desktop instance: it talks only to the local REST API. That answers only
|
|
while the app runs, but it gives the current state and carries the write path. `api_token` is
|
|
required, because every endpoint except `GET /health` demands `Authorization: Bearer <token>`.
|
|
|
|
What can write:
|
|
|
|
- `tools/wikitool new project` creates a tracker project of the same name only with `access: "api"`,
|
|
and even there not automatically. The command stops (exit 42) and asks a human to create it, then
|
|
continues with `--resume`.
|
|
- `tools/wikitool task new` creates a single item without a `kb/` page.
|
|
- `tools/wikitool task close --id` marks an item done. It never deletes one.
|
|
|
|
With `access: "snapshot"`, all three refuse completely with exit 1, because the tracker is read-only
|
|
from there. `tools/wikitool task list --project` only reads, so it answers with either `access`.
|
|
|
|
### CalDAV
|
|
|
|
`caldav` is the standards-based second adapter (RFC 4791/5545). It is verified against Nextcloud
|
|
Tasks, with iOS *Reminders* as the mobile client, and built for that split: tick items off on the
|
|
phone, maintain them at the desk. Unlike Super Productivity, there is only one way in. CalDAV is
|
|
always a network access, so no `access` field is needed.
|
|
|
|
- `url` may be an alias - Nextcloud accepts a short name there - because every later address comes
|
|
only from the `href`s the server returns, never from this URL joined with a name.
|
|
- `inbox_list` and `someday_list` name the two fixed lists, exactly one of each per instance.
|
|
- A list with a `VEVENT` component never counts as a project, so `exclude_lists` is needed only for
|
|
plain task lists.
|
|
|
|
A project is a list whose only supported component is `VTODO`. `tools/wikitool new project` creates
|
|
it automatically with `MKCALENDAR` - without asking, unlike Super Productivity, because CalDAV has a
|
|
real create command for lists. The uniqueness check runs against **every** list in the account,
|
|
including excluded lists, the inbox, someday and mixed calendars. If a new name collides with any of
|
|
them, nothing is created and the collision is named. Renaming the existing list in Nextcloud stays a
|
|
manual step.
|
|
|
|
`task new` and `task close` work without restriction on `caldav`, which has no read-only mode like
|
|
`access: "snapshot"`. When ticking an item off, `task close` changes only `STATUS`, `COMPLETED`,
|
|
`PERCENT-COMPLETE`, `LAST-MODIFIED` and `DTSTAMP` on the existing `.ics` resource. Every other
|
|
property, including an unknown `X-` property or an alarm, is kept unchanged. A resource that changed
|
|
since it was read (an ETag conflict) is not written, and the command stops with exit 1. Nothing is
|
|
ever deleted. The lists of finished projects stay after archiving, because the adapter never deletes
|
|
a list. Remove them by hand in Nextcloud when you want to.
|
|
|
|
## MCP submissions
|
|
|
|
`.wikitool-upload.json` in the served corpus turns on the MCP read server's optional `submit` tool.
|
|
What the authentication in front of the server has to guarantee before you create it is in
|
|
[Set up the MCP read server](../how-to/set-up-the-mcp-server.md#optional-the-submit-path).
|
|
|
|
<!-- wikitool:reference-config-upload -->
|
|
| Field | Type | Required | Default | Description |
|
|
|-------|------|----------|---------|-------------|
|
|
| `schema` | any | no | - | Accepted and not read. Conventionally `1`; no version of this file is told apart by it yet. |
|
|
| `identity_header` | string | no | `"X-Forwarded-User"` | The request header the authenticating middleware sets to the submitter's identity on every request. An empty value means the default. |
|
|
| `max_bytes` | positive integer | yes | - | Largest single submission, in bytes. A number written as a string is accepted. |
|
|
| `allowed_extensions` | array of strings | yes | - | The file extensions `submit` accepts, each starting with `.`. Compared case-insensitively. Entries are read as strings and lower-cased; duplicates collapse. |
|
|
| `quota` | object | yes | - | Per-submitter limits over any rolling 24 hours. |
|
|
| `quota.submissions_per_day` | positive integer | yes | - | Submissions one submitter may make. A number written as a string is accepted. |
|
|
| `quota.bytes_per_day` | positive integer | yes | - | Bytes one submitter may submit. A number written as a string is accepted. |
|
|
|
|
- **Absent:** The `submit` tool is not registered: the write path does not exist.
|
|
- **Malformed:** An error. The MCP server does not start and `doctor` reports `FAIL`. A broken file is never read as "no limits".
|
|
- **Unknown keys:** Ignored.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"schema": 1,
|
|
"identity_header": "X-Forwarded-User",
|
|
"max_bytes": 10485760,
|
|
"allowed_extensions": [
|
|
".md",
|
|
".pdf"
|
|
],
|
|
"quota": {
|
|
"submissions_per_day": 20,
|
|
"bytes_per_day": 52428800
|
|
}
|
|
}
|
|
```
|
|
<!-- /wikitool:reference-config-upload -->
|
|
|
|
Size and quota are your deployment's decision, not defaults worth copying blindly.
|