Skip to content

Commit c328639

Browse files
Merge branch 'main' into dependabot/uv/urllib3-2.7.0
2 parents 4518a44 + c1a2ebc commit c328639

40 files changed

Lines changed: 4319 additions & 1442 deletions
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
---
2+
name: writing-documentation
3+
description: Write, edit, refine, or review documentation and explanatory prose — README files, docs/ pages, design docs, module/class doc comments, and architecture write-ups. Use this whenever the user asks to improve, clarify, simplify, tighten, proofread, or rewrite docs, or to remove jargon, empty phrases, vague wording, or sentence fragments. Apply it even when the request looks like a small wording tweak, because the same standards apply at any size.
4+
---
5+
6+
# Writing and refining documentation
7+
8+
The goal of documentation is to transfer understanding to a reader as quickly and reliably as possible. Every sentence should either tell the reader something they can act on or remove a question they would otherwise have. Judge prose by how much it informs, not by how it sounds.
9+
10+
These standards apply both when you write new documentation and when you refine someone else's. When in doubt, prefer fewer words that say something concrete over more words that gesture at it.
11+
12+
## Match the abstraction level to the audience
13+
14+
Before you write or edit a passage, decide who reads it and at what level it should speak. Two questions settle this, and they shape every later choice about what to include.
15+
16+
- **Is the reader a user of the system or a contributor to it?** End-user documentation explains how to operate an interface: what to call, what to pass, and what comes back. Contributor documentation explains how the system works inside, so that someone can change it safely. The same fact often belongs in one and not the other, so identify the audience first and write to it consistently.
17+
- **Does this passage describe an interface or an implementation detail?** An interface is the contract a reader can depend on. An implementation detail is how that contract happens to be met today. Keep the two clearly separated, and when it is not obvious which one you are describing, say so.
18+
19+
Once you know the level, hold it steady:
20+
21+
- In user-facing documentation, leave out implementation details. A reader who wants to call a method does not need to know how it is built, and the extra mechanism buries what they came for. The one exception is a detail that would otherwise create a wrong expectation — for example, an operation that looks atomic but is not, or a default that is about to change. Surface that detail, because hiding it misleads the reader.
22+
- In contributor documentation, implementation details are essential, but they still need a budget. Include the detail that helps a reader understand or change the code, and leave out the rest. Too much mechanism drowns the main point as surely as too little starves it, so give each passage the amount of detail its context calls for and no more.
23+
24+
A mismatch in level is itself a defect, even when every sentence is individually correct. A paragraph that slides from interface to internals and back forces the reader to keep re-orienting, which is the opposite of what the documentation is for.
25+
26+
## Writing standards
27+
28+
1. **Write to inform, not to impress.** Do not reach for elevated vocabulary, clever framing, or an authoritative tone for its own sake. If a plain word works, use the plain word. Prose that tries to sound smart usually hides that it is saying little.
29+
30+
2. **Keep jargon to a minimum.** Jargon is any term whose meaning depends on insider knowledge. Some is unavoidable in technical writing, but every term you keep is a small toll the reader pays. Spend that toll only when the term earns it.
31+
32+
3. **Make sure every technical term is either well understood by the target audience or defined where it first appears.** Before using a term, ask who reads this document and whether they already share its meaning. A term like "shim" is a bad sign: it has no agreed definition, so it lets the writer feel precise while telling the reader almost nothing. Either name the concrete thing the term stands for ("the long-running process that holds the socket and persists state") or define it on first use.
33+
34+
4. **Write in full, grammatically complete sentences.** Avoid sentence fragments and half-finished thoughts. A fragment forces the reader to reconstruct the missing subject or verb, which is exactly the work good documentation should do for them. See [Repairing sentence fragments](#repairing-sentence-fragments) below for the common shapes and how to fix them.
35+
36+
5. **Prefer active voice, but do not force it.** "Python decodes the envelope" reads more directly than "the envelope is decoded by Python." Active voice names who does what, which removes ambiguity. Where the actor is genuinely unknown or irrelevant, passive voice is fine — do not contort a sentence to avoid it.
37+
38+
6. **Replace empty phrases with real information, or delete them.** When you find a phrase that carries little meaning, first try to replace it with the fact it was standing in for. If there is no such fact, remove the phrase entirely. See [Spotting empty phrases](#spotting-empty-phrases).
39+
40+
7. **State objective facts, not subjective claims.** Documentation describes what the system is and does; it does not argue that the system is good. The reader came to understand the tool, not to be persuaded of its quality, and praise from its own documentation carries no weight anyway. Avoid value judgments and promotional words such as "powerful", "blazing fast", "simple", "elegant", "robust", and "seamless". When you find one, replace it with the concrete, verifiable property behind it: instead of "a fast, lightweight server", write "a single-threaded HTTP server that runs each request in one interpreter process", and let the reader judge. If a characterization has no fact behind it, drop it.
41+
42+
## Spotting empty phrases
43+
44+
An empty phrase occupies space without changing what the reader knows. Common kinds:
45+
46+
- **Undefined jargon**, as in standard 3 above.
47+
- **Contrasts that do not explain themselves.** "Rather than running a real validator, it ..." sets up a difference but never says what the difference is, so the reader learns nothing. Either explain the contrast concretely (for example, "real Stellar RPC is built around a mempool and ledger close, so ...") or drop it. A contrast is worth keeping only when the same sentence makes clear what is being contrasted and why it matters.
48+
- **Filler preambles and intensifiers**: "It is worth noting that", "Of course", "Simply", "Essentially", "A guiding constraint:". These announce a thought instead of delivering it. Cut them and start with the thought.
49+
- **Restatements** that repeat the previous clause in new words, and **vacuous qualifiers** ("now", "actually", "in practice") that hint at a comparison the reader cannot see.
50+
51+
Watch for words like "now survive restarts" or "those now live in K" in standalone documentation: "now" silently compares against an earlier design the reader does not know about.
52+
53+
## Repairing sentence fragments
54+
55+
Three shapes show up again and again in reference-style docs:
56+
57+
- **Label openers**: "The long-running process." or "The XDR boundary." followed by a separate sentence. Fold the label into a real sentence with a subject: "`StellarRpcServer` is the long-running process around the semantics."
58+
- **Verb-first lines with no subject**: "Decodes the envelope and returns a pair." Give it the subject it describes: "`build_tx_request` decodes the envelope and returns a pair."
59+
- **Noun-phrase or participle fragments**: "A helper that pretty-prints the config." or "Used by `demo.py` to render each step." Turn each into a full sentence, and prefer active voice for the participle form: "`demo.py` uses it to render each step."
60+
61+
An em-dash is a useful warning sign. An em-dash inside a complete sentence is fine, but an em-dash followed by a trailing fragment ("... and returns a pair — for the interpreter to splice in") often marks an underdeveloped thought that should become its own sentence or fold into the main clause. Treat a trailing em-dash as a prompt to check whether the thought after it is complete.
62+
63+
## What to leave alone
64+
65+
Restraint matters as much as correction. Do not turn every list into prose. These constructs are conventional and clear, and rewriting them as full sentences usually hurts readability:
66+
67+
- Tables and code blocks.
68+
- Glossary or reference lists in `name — short description` form (an API method index, a flags table, a list of file roles).
69+
- Rosters under a heading that already supplies the predicate, such as a "What's not yet implemented" list whose every item is implicitly "is not implemented."
70+
- Numbered procedure steps written as imperatives ("Write the request file.", "Parse the state."), which are already complete sentences.
71+
72+
A self-explaining contrast, a passive sentence with an irrelevant actor, and a deliberately terse reference entry are all fine. Change something only when the change makes the reader's job easier.
73+
74+
## How to run a refinement pass
75+
76+
When asked to clean up documentation, the user usually gives one example of a problem. Treat that example as one instance of a whole class, and handle the class.
77+
78+
1. **Read the whole document set, not just the flagged spot.** The same problem almost always recurs across files. Read every doc in scope before editing.
79+
2. **Find every instance of the class.** After reading, use `grep` to catch recurring patterns mechanically — for example, search for verb-first line starts, filler words, or contrast phrases — so you do not miss any.
80+
3. **Judge each candidate.** Separate genuine problems from acceptable constructs (see [What to leave alone](#what-to-leave-alone)). Do not invent problems to fix.
81+
4. **Fix each one in place**, keeping the original meaning. Replace empty phrases with the fact behind them, give fragments a subject, and prefer active voice.
82+
5. **Verify the docs against reality when they describe behavior.** A confident but stale sentence is worse than a wordy one. If a doc claims the code does X, confirm the code still does X. (In this codebase, that check has already caught a doc describing a `chdir` the code no longer performs.)
83+
6. **Verify your pass.** Re-run the `grep` scans to confirm no instances of the class remain, and reread the edited sections to make sure each still flows.
84+
7. **Summarize for the user.** Give a short table of the meaningful changes (before → after, with a one-line reason), and state plainly what you deliberately left unchanged and why. The "left unchanged" part shows your judgment and lets the user catch anything you misjudged.
85+
86+
## Audit prose you write, not just prose you inherit
87+
88+
[The refinement pass above](#how-to-run-a-refinement-pass) is framed around cleaning up existing text, and that framing hides a trap: it is easy to scan inherited prose for these problems while treating a sentence you just wrote as finished. The standards apply equally to both. Before you consider a passage you authored done — whether a whole new document or a single replacement sentence — run the same checks against it (abstraction level, empty phrases, fragments, and the rest) exactly as you would on someone else's draft.
89+
90+
Two failures show up most often in freshly written prose, so check for them every time:
91+
92+
- **A first sentence that announces a thought instead of delivering it.** "What makes it different is the execution engine." promises a difference without naming it — a filler preamble disguised as a topic sentence. Cut it and start the paragraph with the concrete fact.
93+
- **A clause that describes how the system is built, in user-facing prose.** Mechanism leaks in most easily here, because explaining the internals feels like supplying substance. Ask of each clause whether it tells the reader something they can act on or only how the machinery works, and move the latter to the contributor docs — unless it prevents a wrong expectation (see [Match the abstraction level](#match-the-abstraction-level-to-the-audience)).
94+
95+
Then read the passage once as its target reader and ask of each sentence: does this inform me, or does it only sound like it should? Delete or replace whatever fails.
96+
97+
## Scope discipline
98+
99+
Stay close to the class of change the user asked for. If you notice an adjacent issue (a factual error, a confusing structure, a missing section), mention it in your summary and offer to address it, rather than silently expanding the edit. Change code or behavior only when fixing a factual inaccuracy in the documentation itself, and call that out explicitly when you do.

.devcontainer/Dockerfile

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Microsoft's official devcontainer base (Ubuntu 24.04), pinned by digest for
2+
# reproducibility. It ships a non-root `vscode` user (UID 1000) with passwordless
3+
# sudo plus the common dev tools (git, curl, sudo, ca-certificates, openssh-client),
4+
# so no manual user creation is needed here.
5+
FROM mcr.microsoft.com/devcontainers/base:ubuntu24.04@sha256:4bcb1b466771b1ba1ea110e2a27daea2f6093f9527fb75ee59703ec89b5561cb
6+
7+
ENV DEBIAN_FRONTEND=noninteractive \
8+
LANG=C.UTF-8 \
9+
LC_ALL=C.UTF-8
10+
11+
# The base already provides git/sudo/ca-certificates/curl/openssh-client; we only
12+
# strictly need xz-utils for the Nix installer tarball. The rest are listed
13+
# defensively (apt is idempotent and skips already-present packages).
14+
RUN apt-get update && apt-get install -y --no-install-recommends \
15+
git sudo ca-certificates curl less xz-utils openssh-client \
16+
&& rm -rf /var/lib/apt/lists/*
17+
18+
# Pre-create /nix owned by the vscode user and a system-wide nix.conf. Two things to note:
19+
# - Sandboxing relies on CAP_SYS_ADMIN (sethostname etc.), which container
20+
# builds don't grant — without `sandbox = false` both the Nix self-install
21+
# and every subsequent `nix profile install` fail.
22+
# - The K Framework binary caches let `kup install k` pull prebuilt K binaries
23+
# instead of compiling from source. Single-user Nix (vscode owns /nix) honors
24+
# these substituters from the system config, so no per-user nix.conf needed.
25+
RUN mkdir -m 0755 /nix \
26+
&& chown vscode:vscode /nix \
27+
&& mkdir -p /etc/nix \
28+
&& printf '%s\n' \
29+
'sandbox = false' \
30+
'filter-syscalls = false' \
31+
'experimental-features = nix-command flakes' \
32+
'substituters = https://cache.nixos.org https://k-framework.cachix.org https://k-framework-binary.cachix.org' \
33+
'trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= k-framework.cachix.org-1:jeyMXB2h28gpNRjuVkehg+zLj62ma1RnyyopA/20yFE= k-framework-binary.cachix.org-1:pJedQ8iG19BW3v/DMMmiRVtwRBGO3fyMv2Ws0OpBADs=' \
34+
> /etc/nix/nix.conf
35+
36+
USER vscode
37+
WORKDIR /home/vscode
38+
39+
# Single-user Nix install — pinned to a specific release for reproducibility
40+
# and supply-chain safety. Update the version when upgrading Nix.
41+
ARG NIX_VERSION=2.28.3
42+
RUN curl -fsSL "https://releases.nixos.org/nix/nix-${NIX_VERSION}/install" -o /tmp/nix-install \
43+
&& sh /tmp/nix-install --no-daemon --no-channel-add \
44+
&& rm /tmp/nix-install
45+
46+
ENV PATH=/home/vscode/.nix-profile/bin:/home/vscode/.npm-global/bin:$PATH
47+
48+
# Point the `nixpkgs` flake registry at the nixos-25.05 release branch (matching
49+
# the project's flake.nix) — a stable channel, though not a frozen revision; it
50+
# only governs the container-level bootstrap tooling below, while the project
51+
# toolchain is pinned reproducibly via flake.nix/flake.lock. Then install only
52+
# that container-level bootstrap/runtime tooling via Nix:
53+
# - direnv + nix-direnv → auto-load and cache the flake's `nix develop` shell on
54+
# entry to /workspace; this is what actually provides the
55+
# project toolchain (python, uv, make, wabt, K Framework),
56+
# so those live in flake.nix's devShell, not here.
57+
# - nodejs_22 → host for the Claude Code CLI (run as `claude` from
58+
# anywhere, so it stays globally on PATH, not in the shell)
59+
RUN . /home/vscode/.nix-profile/etc/profile.d/nix.sh \
60+
&& nix registry add nixpkgs github:NixOS/nixpkgs/nixos-25.05 \
61+
&& nix profile install \
62+
nixpkgs#direnv \
63+
nixpkgs#nix-direnv \
64+
nixpkgs#nodejs_22
65+
66+
# Claude Code CLI — installed to a per-user prefix so root isn't required.
67+
RUN . /home/vscode/.nix-profile/etc/profile.d/nix.sh \
68+
&& npm config set prefix /home/vscode/.npm-global \
69+
&& npm install -g @anthropic-ai/claude-code@2.1.175
70+
71+
# Wire up nix-direnv, then hook direnv into interactive shells. Order matters:
72+
# Nix must be sourced before the direnv hook so the profile PATH is in place when
73+
# direnv evaluates the flake.
74+
RUN mkdir -p /home/vscode/.config/direnv \
75+
&& echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > /home/vscode/.config/direnv/direnvrc \
76+
&& printf '%s\n' \
77+
'. /home/vscode/.nix-profile/etc/profile.d/nix.sh' \
78+
'eval "$(direnv hook bash)"' \
79+
>> /home/vscode/.bashrc
80+
81+
WORKDIR /workspace

.devcontainer/devcontainer.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "komet_node",
3+
"build": {
4+
"dockerfile": "Dockerfile"
5+
},
6+
"remoteUser": "vscode",
7+
"workspaceFolder": "/workspace",
8+
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
9+
"init": true,
10+
"mounts": [
11+
"source=claude-code-config-${devcontainerId},target=/home/vscode/.claude,type=volume",
12+
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly"
13+
],
14+
"containerEnv": {
15+
"CLAUDE_CONFIG_DIR": "/home/vscode/.claude",
16+
"GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local"
17+
},
18+
"postCreateCommand": "sudo chown -R vscode:vscode /home/vscode/.claude && bash .devcontainer/setup-git.sh && direnv allow /workspace && nix develop /workspace --command bash -c 'uv sync --frozen'",
19+
"customizations": {
20+
"vscode": {
21+
"extensions": [
22+
"anthropic.claude-code",
23+
"mkhl.direnv"
24+
]
25+
}
26+
}
27+
}

.devcontainer/setup-git.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env bash
2+
# Wire up a writable global git config that inherits the host user's identity.
3+
#
4+
# devcontainer.json bind-mounts the host ~/.gitconfig read-only at
5+
# /home/vscode/.gitconfig and sets GIT_CONFIG_GLOBAL=/home/vscode/.gitconfig.local.
6+
# Git therefore writes global config to the (writable) .gitconfig.local while
7+
# still reading the host's name/email/aliases through the include set up below.
8+
#
9+
# The result: every user of this container can `git commit` with their own host
10+
# identity and run `git config --global ...` inside the container, without the
11+
# EBUSY breakage a directly-writable bind mount over ~/.gitconfig would cause.
12+
#
13+
# Adapted from trailofbits/claude-code-devcontainer's post_install.py. Invoked
14+
# from devcontainer.json's postCreateCommand. Idempotent — safe to re-run.
15+
set -euo pipefail
16+
17+
host_gitconfig="/home/vscode/.gitconfig"
18+
local_gitconfig="/home/vscode/.gitconfig.local"
19+
20+
# Ensure the writable global config exists without truncating it — a previous run
21+
# (or the user) may have written real settings here (e.g. core.editor), and this
22+
# script must not clobber them.
23+
touch "${local_gitconfig}"
24+
25+
if [ -f "${host_gitconfig}" ]; then
26+
# Host provided a ~/.gitconfig (bind-mounted as a regular file): inherit it.
27+
# Set just the include.path key via `git config` so any other keys the user
28+
# already wrote to the global config are preserved. --replace-all collapses
29+
# the entry to a single value, keeping the operation idempotent across re-runs.
30+
git config --file "${local_gitconfig}" --replace-all include.path "${host_gitconfig}"
31+
echo "setup-git: global config includes host identity from ${host_gitconfig}"
32+
else
33+
# No host ~/.gitconfig — the missing bind source is materialized as an empty
34+
# directory, which must NOT be included. Drop any stale include.path we may
35+
# have added on an earlier run, but leave every other key untouched.
36+
git config --file "${local_gitconfig}" --unset-all include.path 2>/dev/null || true
37+
echo "setup-git: no host ~/.gitconfig found; using empty ${local_gitconfig}"
38+
echo "setup-git: set your identity with 'git config --global user.name ...' / user.email"
39+
fi

0 commit comments

Comments
 (0)