Files
chemenu/docs/reference/configuration.md
T
torbenandClaude Opus 5.5 893db6e640
CI / verify (push) Successful in 6m17s
CI / pwsh (push) Successful in 2m0s
Release / release (push) Successful in 34s
feat: the shapes of the four hand-written .wikitool-*.json files live in one record each (config_shapes.py); loaders, doctor and docs/reference/configuration.md read them (#193)
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
2026-10-07 08:44:50 +02:00

17 KiB

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). The last one has its own section below, because the preflight that writes it is something you run.

Contents

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)
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)
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.

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:

{
  "enabled": true,
  "max_session_bytes": 5242880,
  "keep_sessions": 250
}

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.

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.

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:

{
  "schema": 1,
  "allowed_push_urls": [
    "<url>"
  ]
}

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.

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.
{
  "superproductivity": {
    "access": "api",
    "api_base_url": "http://127.0.0.1:3876",
    "api_token": "..."
  }
}
{
  "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.
{
  "caldav": {
    "url": "https://<host>/remote.php/dav/calendars/<user>/",
    "username": "<login>",
    "app_password": "...",
    "inbox_list": "Inbox",
    "someday_list": "Someday",
    "exclude_lists": [
      "..."
    ]
  }
}

A complete example:

{
  "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": "..."
  }
}

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 hrefs 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.

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:

{
  "schema": 1,
  "identity_header": "X-Forwarded-User",
  "max_bytes": 10485760,
  "allowed_extensions": [
    ".md",
    ".pdf"
  ],
  "quota": {
    "submissions_per_day": 20,
    "bytes_per_day": 52428800
  }
}

Size and quota are your deployment's decision, not defaults worth copying blindly.