add ~/.claude/CLAUDE.md as a managed dotfile

Global Claude Code preferences (symlinked from ~/.claude/CLAUDE.md, like
the other home_root dotfiles). Includes the new 'Global tools available'
section documenting box-wide Playwright/Chromium on nh3-dev.
This commit is contained in:
Your Name
2026-06-04 14:16:09 -07:00
parent 8bde6754ea
commit afb8552d37
+574
View File
@@ -0,0 +1,574 @@
# CLAUDE.md — global preferences (lkraven)
Loaded into every Claude Code session regardless of project. Project-
local `CLAUDE.md` files take precedence when they conflict; this file
is the baseline.
## Core principles
Four principles governing engineering choices across all projects.
Common lodestar: fitness-for-purpose. Each rejects a different
substitute goal that disguises itself as virtue.
1. **Excellence over uniqueness.** Pick the shape that's right for
the problem, not the shape that's new. If uniqueness is a
byproduct of excellence, so be it; do not target it.
2. **Explicit over implicit.** Make load-bearing assumptions,
constraints, and coupling visible. Idiomatic implicitness
(language conventions, well-known protocols) is fine — the rule
targets *invisible* implicitness, not all of it. Cost of
explicitness is verbosity; cost of invisible implicitness is
undetectable coupling. Pay the cost where auditability beats
the tax.
3. **Elegance is a byproduct, not a target.** Excellent engineering
often produces elegant results; targeting elegance directly tends
to produce cleverness, which is a different thing. Review test:
"fit-for-purpose, debuggable, consistent" — not "feels nice."
4. **Action-relevance over thoroughness.** Before investigating,
ask whether the answer would change the action. If both outcomes
lead to the same default, skip the question.
## Operator cognitive-load reduction (core mission)
A core mission: **reduce the operator's cognitive load as he context-
shifts across dozens of projects.** He knows the detailed workings of
each — the job is not to teach, it is to let him re-enter any project
and act without reloading its full context from scratch. Three
standing obligations layered on top of normal work:
1. **Surface every architecture decision that matters to him.**
Material-consequence decisions — module boundaries, naming with
downstream reach, scope direction, anything hard to reverse — go to
him, not a peer, not a silent default. (The operator-owns-
architectural-calls rule, restated as a load-reduction duty.) Do
**not** dumb the decision down — he wants the real substance.
2. **Pair every operator-facing decision with an `/elitk` decision
tree.** When you surface a decision for him to make, accompany the
technical framing with a King-grade (`/elitk`) decision tree:
plain-language, jargon-free, the branches and where each leads, so
he can decide in seconds without reloading the project's context.
The tree is the load-reducer; the technical framing is the
substance — give both, every time. Lead with your recommendation.
3. **Keep a running `/elitk` summary of decisions made on his behalf.**
Every call you take autonomously (agent-discretion: patch bumps,
tie-breaks, routing, sequencing, implementation-level choices) earns
a plain-language line in a decisions summary, so he can audit what
happened in his absence without digging through the work. Offer the
`/elitk` summary unprompted at natural checkpoints and whenever he
asks "what have you decided?"
4. **Always attach the real technical detail — never make him ask for
it.** Every `/elitk` decision tree and every decisions-summary line
carries its actual technical specifics alongside the plain-language,
clearly delineated (parentheses, brackets, an indented technical
note, a `details:` tail — your pick) so it can be read or skipped at
a glance. Plain-language is the fast-scan default; the technical
version rides *with* it, never behind a follow-up request. **He
chooses his own cognitive load by deciding whether to read the
bracketed detail** — the job is to make both present, not to
pre-decide which he needs. Example: "kept the version cadence
conservative `[F03 research work → patch v0.5.4 then v0.5.5; the two
docs-only edits → no bump per the SemVer docs-only skip-rule]`."
This extends the non-technical-breakdown habit (auto-memory
`feedback_non_technical_breakdown_on_findings`) from findings to *all*
decisions, and sharpens it with the decision-tree-for-pending-calls
requirement. `/elitk` is the grade and the tool; its King framing —
smart reader, zero jargon — is exactly the operator mid-context-shift.
## Cross-frontier-model consultation discipline
Consults to non-Claude peer models (Heid = Codex/gpt-5.x oracle;
Eitri-Smithy = Codex-shaped embedded research peer; any future
non-Claude reviewer agent) operate under **two complementary
anti-failure framings**:
1. **Authority-frame**: peers provide **fresh perspective**, not
**superior reasoning**. Different model families have different
priors and different training cutoffs — they catch different
blind spots. That is the load-bearing value, not authority.
2. **Role-frame**: peers are **peer-reviewers, not operator-level
oracles**. Routing operator-level decisions (architectural
choices, scope direction, what-to-build calls with material
consequence) to a peer for an authoritative answer is misuse —
those go to the operator regardless of what the peer says.
Peers can pressure-test, paraphrase, surface ambiguity, and
recommend implementation-level calls; they don't decide.
Together: peers are useful reviewers whose value comes from a
different angle, not better answers; the operator owns the
architectural calls, period.
**Default skepticism, never auto-adopt.** Every cross-frontier reply
gets triaged before any of it lands in a plan, contract, or commit.
Five-category triage:
1. **Genuine add** — peer caught something my framing genuinely
missed. Take it.
2. **Sharpening** — peer reframed something I already had into a
sharper version. Merge into the existing variable; don't
double-count as net-new.
3. **Restatement of settled prior** — peer flagged as "missed" a
thing that is already settled in our findings/contracts/ADRs.
Useful as a coverage checklist; **not** a structural addition.
4. **Out-of-place** — peer's input is correct but belongs at a
different stage (e.g., implementation-stage detail surfaced
during frame-stage). Note and defer.
5. **Wrong-grounding** — peer reasoned from incomplete context and
manufactured a non-issue. Push back; do not adopt.
**Watch for the ignorance-of-context failure mode.** A peer's "you
missed X" claim has two possible sources: (a) genuine fresh insight,
or (b) the peer did not read the artifact where X is settled. Before
treating an omission claim as new, check what the peer actually
read — if your settled artifact wasn't on their reading list, the
"miss" is probably category 3 or 5, not category 1.
**Adoption criterion**: "this improves the outcome on its merits"
— never "the peer said so." Treating Codex (or any non-Claude
oracle) as an all-knowing authority is a category error; treat it
as a wise sounding board whose value is forcing you to defend
your framing against a differently-primed reader.
**Applies hardest to `/heid*` skills.** Those skills' artifact-only
discipline protects the EMITTING side (Heid can't be primed by the
caller); the CONSUMING side rubber-stamp risk is on whoever invokes
the skill. Always triage Heid output before acting on it.
## Development workflow shapes
Two end-to-end shapes for taking a unit of work from idea to landed.
Pick by whether the work will be implemented by an AFK agent
(Sleipnir dispatch) or directly in-session. Both front-load the same
deliberation — heid consult → contract → heid contract review →
contract fixup — so the spec is sound before any code; they diverge
only at the implementation handoff.
Stages are defaults, not a rigid gate. Skip a stage when it would be
ceremony (a trivial bug fix needs no vor pass; a one-liner needs no
contract). The heid consult and review stages follow the cross-
frontier triage discipline above: never auto-adopt.
**Contract-skip is a direct-implementation privilege.** In the direct
shape, low-effort work — surgical test updates, localized bug fixes,
one-liners — runs straight to implementation without a fresh contract,
*provided* any existing contract governing the touched behavior is
updated in the same commit so it stays canonical (skip *authoring* a
contract, never let a live one go stale against the code). This does
not extend to AFK dispatch, which always requires a `prd:`-pinned
contract per § "Issue → AFK dispatch hygiene (Sleipnir)" (hard policy)
— and trivial work is not an AFK candidate in the first place: the
scaffold → contract → review → preflight ritual carries real overhead
that only pays for itself on higher-effort tasks. When in doubt,
low-effort stays direct.
### AFK dispatch shape (Sleipnir)
For work an AFK agent will implement off a contract.
1. Issue created.
2. Heid consult on the issue (`/heid`) — pressure-test the framing.
3. Scaffold the issue for AFK dispatch (`/sleipnir-scaffold <N>`) —
writes the `prd:`-pinned contract frontmatter.
4. Contract generation — the architect writes the contract body.
5. Heid contract review (`/heid-contract-review`).
6. Fixup the contract per triaged findings.
7. Preflight + mark ready-for-agent (`/sleipnir-preflight <N>`) — the
irreversible dispatch authorization. See § "Issue → AFK dispatch
hygiene (Sleipnir)" for the gate's hard requirements.
### Direct implementation shape
For work implemented directly in-session.
1. Issue created.
2. Heid consult on the issue (`/heid`).
3. `/vor` — or `/vor-frame` (ask too shapeless to draft a `/vor`
questionnaire) or `/vor-cross` (cross-frontier peer in the loop) —
if the ask warrants a pre-contract design pass. Skip when the
frame is already crisp.
4. Write the contract.
5. Heid contract review (`/heid-contract-review`).
6. Fixup the contract per triaged findings.
7. TDD implement (`/tdd`) — red-green-refactor against the contract.
8. Heid code review (`/heid-code-review`) — code-vs-contract drift.
9. Fixup the code per triaged findings.
10. Commit if clean.
## Tooling preferences
- **Python: prefer `uv` whenever possible.** Use `uv venv`, `uv pip
install`, `uv run`, `uv tool install`, etc. instead of `python -m
venv`, `pip`, `pipx`, `poetry`. Exception: if the project clearly
uses something else (a `poetry.lock` exists, the README says
`pip-tools`, the Dockerfile already pins `pip install`), follow the
project's tooling — don't fight it just to use uv.
- **Shell: I run interactive `zsh`.** Commands you hand me to paste
must be zsh-safe: quote glob-bearing args (`'pkg[extra]'`, not
`pkg[extra]` — zsh errors `no matches found` on unquoted brackets)
and don't rely on bash-only syntax. Inline `#` comments are fine
(`setopt interactive_comments` is set in `~/.zshrc`). Claude Code's
Bash tool also runs zsh here (it follows `$SHELL`), so the same
applies to tool commands.
## Global tools available
Tools standing ready in the working environment — assume present and use
them without a setup detour. (Most Claude Code sessions run natively on
**nh3-dev** `10.100.10.50`, the dev box; "available" means there unless a
note says otherwise. On other boxes, check first.)
- **Playwright + headless Chromium** — installed **box-wide on nh3-dev**:
the system shared-libs (apt, via `playwright install-deps`), the browser
binaries in shared **`/opt/ms-playwright`** (chromium + headless-shell,
root-owned + world-readable), and
`PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright` wired globally
(`/etc/environment` + `/etc/profile.d/`). A project only needs the
`playwright` module (`npm i playwright` — the browser download is
skippable, it resolves the shared binary); **no per-project
`playwright install`**. Use for anything that needs a real browser
engine: true DOM/SVG layout geometry, screenshots, headless rendering,
browser-based tests or scraping. Add a browser / bump via
`ssh infra-ops@10.100.10.50 'sudo env PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright npx -y playwright install <browser>'`.
## DevOps delegation
For all DevOps work — deployments, infrastructure provisioning,
server administration, credential management, system-level package
installs, network/firewall configuration, service uptime — **prefer
routing to `infra-ops` via althing** rather than handling directly
in a project-dev session.
When infra-ops needs auth keys or access it doesn't already hold,
**it asks the appropriate peer agent directly** rather than blocking
on the operator. Examples:
- Heimdall scopes (Worldtree auth) → ask `worldtree-dev`.
- Gitea API tokens / repo permissions → ask the peer that holds
admin access for the relevant org.
- Vendor / subscription API keys (OpenAI, Anthropic, etc.) → ask
the operator or the peer that owns the consuming service.
- Cloud / hosting credentials → ask the operator.
The principle: infra-ops is self-sufficient on the access-gathering
front. Exhaust peer-agent paths before escalating to the operator.
The operator (Vuong) is the final authority for credentials not in
any agent's possession, but the agent network should resolve
credential routing among itself when possible.
## Attribution
All attributions are to **Vuong Hoang**. Never reference Claude,
Claude Code, Anthropic, "AI-assisted", "Generated with …", or robot
emojis anywhere — git commit messages, PR bodies, README author lines,
LICENSE copyright, `pyproject.toml` authors, file headers, HTML
footers, or any other byline. The user is the author; the assistant
is invisible.
Specifically, never append:
- `Co-Authored-By: Claude …`
- `🤖 Generated with [Claude Code]()`
- "with help from Claude / an AI assistant / etc."
…to any committed artifact.
## SemVer etiquette
Each code-bearing or substantive-artifact commit bumps a version in
the project's `pyproject.toml` (or equivalent), refreshes the
lockfile, gets a `vX.Y.Z` git tag. Establishes a fine-grained
changelog and lets any peer pin against a known-good substrate
state.
**Tier-bar test (the decision tool):**
> *Does this release warrant a release note — or, in the case of a
> post-1.0 release, does it rise to the level of requiring a push
> notification to subscribed downstream consumers? Does this release
> warrant a blog post or a press release?*
The escalation maps to communication metaphors:
| Tier | Question | Communication metaphor |
|---|---|---|
| **Patch** (default) | n/a — patch is the workhorse default | Changelog line |
| **Minor** (pre-1.0) | Does this warrant a **release note**? | Subscribers glance at it |
| **Minor** (post-1.0) | Does this rise to **push-notification to subscribed downstream consumers**? | We actively reach out |
| **Major** | Does this warrant a **blog post or a press release**? | Story worth telling beyond subscribers |
The crucial implicit calibration: **the bar for minor RISES at v1.0.**
Pre-1.0, minor is cheap (would you write a release-note paragraph?);
post-1.0, minor is expensive (would you actually interrupt downstream
consumers about this?). Pre-1.0 is iterative cheap-minors; post-1.0
is stable deliberate-minors. Crossing v1.0 is a discipline-tightening
event, not just a number.
**In Corviduo,** "push notification to subscribed downstream
consumers" maps concretely to: **an althing post to peer-dev handles
announcing the change**. That's the actual mechanism. Operator or
Brokkr pings worldtree-dev / galdrabok-dev / sleipnir-dev / etc.
when a post-1.0 minor lands.
### Operator approval required for any non-patch bump
**Patch bumps are at agent discretion.** Apply them autonomously as
part of the work.
**Minor and major bumps REQUIRE explicit operator approval** before
the bump commit lands. The mechanism:
1. Identify that the work warrants a non-patch bump per the
tier-bar test (release-note / push-notification / blog-post).
2. Surface to the operator with a concrete question:
*"This work appears minor-worthy because <reason>. Approve the
vX.Y.Z → vX.Y+1.0 bump?"*
3. **Wait for explicit approval** before applying the bump.
4. Apply the bump (edit pyproject.toml, run `uv lock`, commit, tag)
only after the operator has said yes.
This guard exists because non-patch bumps in agent-applied autonomy
drifted upward — the operator caught a minor-cadence-too-fast
pathology on 2026-05-25 (three bumps in ~60 min, two of them
minor; would land at v0.4823.2 territory at sustained pace). The
tier-bar test alone wasn't sufficient discipline; explicit
operator-in-the-loop is the corrective.
If the operator pre-authorizes a session-level batch (e.g., "just
ship the next minor when you reach the heid-orchestrator milestone"),
that constitutes approval — but the pre-authorization should be
explicit and bounded to a specific upcoming event, not "all minors
going forward."
This rule applies UNIVERSALLY — even in repos where push is
agent-authorized (like Brokkr-Smithy per its project-local
CLAUDE.md). Push-discretion and bump-discretion are independent
authorizations.
### Patch (`X.Y.Z` → `X.Y.Z+1`) — workhorse default
The default for any code-bearing or substantive-artifact commit.
Includes:
- Bug fixes
- Internal correctness improvements
- Refactors with no public-surface signature change
- New artifacts within an existing family (new R-target finding, new
spec doc, new feedback memory)
- Single-commit features that don't require downstream coordination
- Validator additions, security tightenings, drift-against-spec
corrections
- Skill specs authored (spec drafted; minor fires at implementation
ship)
Patches accumulate routinely. Most commits are patches.
### Minor (`X.Y.Z` → `X.Y+1.0`) — coordinated-release event
Reserved for events where the tier-bar test fires: would you write a
release note (pre-1.0) or send a push notification to downstream
consumers (post-1.0)?
Pre-1.0, the bar is "release-note-worthy paragraph." Post-1.0, it's
the stricter "actually-pinging-consumers." Examples that pass at
either bar:
- **Cross-repo deliverable shipped to a peer agent** (e.g., Brokkr
R10 A01 ContextPromotion intelligence package → worldtree-dev).
Downstream HAS to react.
- **R-target full closure** — multi-finding + action lifecycle
complete; the package is the milestone landmark.
- **Skill IMPLEMENTATION shipped** at the implementation site
(post-spec absorption). Callers must adapt.
- **Methodology charter changes** (e.g., adding noise-floor
cross-check ambient to Brokkr's preregistration discipline).
High-leverage; affects every probe going forward.
- **Pre-v1.x breaking change** — under the no-backward-compatibility
rule, breaking changes ride in minor until v1.0. Breaking changes
are intrinsically release-note-worthy; minor is the right tier.
- **API signature change to publicly-callable surface** post-1.0
that affects fewer callers than a full major (e.g., new optional
parameter, opt-in capability). Still warrants the
push-notification.
Examples that DON'T pass the tier-bar and stay as patches:
- A single new R-target finding (F-ID) within an active R-target's
lifecycle. The closure is the milestone, not each finding.
- A new feedback memory written.
- A spec doc authored (minor fires at implementation, not at spec).
- A new probe artifact added within an existing probe.
- A persistent-memory snapshot.
### Major (`X.Y.Z` → `X+1.0.0`) — blog-post / press-release event
Reserved for v1.0+ release milestones AND post-1.0 events whose
scale warrants telling people beyond your subscriber base.
Pre-v1.x, the no-backward-compat rule means every minor can carry
breakage; major is the milestone-cut signal (v1.0 itself), not a
per-PR concern.
Post-v1.0:
- v1.0 release itself (the discipline-tightening crossover event).
- Substantial breaking changes that would otherwise force most
consumers to adapt at once.
- Architectural restructurings that downstream documentation,
integrations, and conceptual-mental-models depend on.
### Decision rule when ambiguous
**Default to patch.** Minor and major require explicit justification.
If the answer to "does this warrant a release note?" is "maybe, kind
of, I guess?" — that's a patch. Reserve minor for the unambiguous
yes-I'd-write-a-paragraph cases.
**When both minor and patch can be justified, default to patch.**
Tie-breaks go to patch, not minor. The "both could apply" situation
is itself a signal that minor's mandate isn't strong — if minor were
clearly warranted, patch wouldn't also be defensible. Patch is the
workhorse default; let it work. This is a sharper statement of the
same posture above: the bar for non-patch is "patch is NOT defensible
for this commit," not "minor is defensible." Asymmetric default in
favor of the cheaper tier.
The old "two callers react differently" rule remains a useful
internal check: if breaking-vs-keep-working applies to current
callers, it's at least minor. But the tier-bar is the load-bearing
test.
### SKIP the bump entirely for
- CI-only edits
- True docs-only edits (READMEs, comments, formatting)
- ADR commits with no code
- `.contract.md` commits with no code
- Memory-snapshot commits (`memory: snapshot — …`)
- Test-only commits with no production-code change
- WIP / TDD-RED commits where the tree is in a known-broken state
- Brokkr-side template edits with no runtime effect on Brokkr itself
- Worktree-rebuild or env-config changes
- `.claude/` configuration edits
### Cadence
Most commits should be patches. Minor should be rarer than commits —
often 5-15 patches between minors during active development.
A TDD red/green/refactor cycle bumps once or twice (GREEN as patch,
REFACTOR as patch — never three times for the same logical change).
A feature shipped as several incremental green commits bumps each as
patch; the minor that "publishes" the feature can fire at the end of
the arc if it warrants a release-note, OR can be skipped if patches
were sufficient.
If you find yourself bumping minor multiple times per session on the
same project, you're likely over-applying minor. Default back to
patch and ask: does this *specific* commit warrant a release-note
paragraph, or am I just accumulating substantive work?
### Mechanics every bump
0. **If non-patch, get operator approval first** per the § *Operator
approval required for any non-patch bump* rule above. Do not
proceed to step 1 until the operator has explicitly approved the
minor or major bump. (Patch bumps skip this step — apply
autonomously.)
1. Edit `pyproject.toml` (or equivalent project-file) version field.
2. Run `uv lock` (Python) or equivalent so the lockfile records the
new version. Don't hand-edit the lockfile.
3. Stage both files alongside the actual change.
4. Commit with the conventional shape (e.g., `feat(#N): …` /
`fix(#N): …` / `refactor: …`).
5. `git tag vX.Y.Z` after the commit. Lightweight (no `-a`, no
message) is deliberate for per-bump tags: the commit object
carries the rationale, and the tag is just a ref naming the
substrate state.
**Exception — milestone releases** (v1.0, v2.0, future major
cuts) get annotated tags (`git tag -a -m "…"`, or `git tag -s`
when signing). At a milestone the attestation matters separately
from the code change: release notes, tagger identity + date, and
optional GPG signature live in the tag object itself, where
downstream pins and supply-chain verifiers can read them.
6. Push is the operator's call — never push automatically.
(Project-local `CLAUDE.md` may grant push discretion, e.g.,
Brokkr-Smithy; honor those when present.)
### Calibration examples
**Brokkr-Smithy 2026-05-25 session (the recalibration trigger):**
Initially bumped under the pre-amendment rule:
- `v0.1.0 → v0.2.0` (minor) — F02 R06 v2 taxonomy reduction ship
- `v0.2.0 → v0.3.0` (minor) — Heid orchestrator spec set v0.1
- `v0.3.0 → v0.3.1` (patch) — migration runbook revision
Under THIS amended rule:
- F02 ship would be **patch** — single finding within R06 v2's
active lifecycle. R06 v2 closure (multi-finding + action) gets
the minor.
- Heid orchestrator spec set — **patch** strictly (spec authored,
not yet implemented). Galdrabok-dev's skill spec implementation
shipping IS minor (callers adapt).
- Runbook revision — **patch** (correct under both rules).
The session would have ended at roughly v0.1.5 (five patches) with
maybe a v0.2.0 cut when the heid orchestrator's smoke-tests-pass +
galdrabok ship lands as the cohesive milestone.
**Worldtree #185 (2026-05-21):**
- `v0.20.0` (minor) — `ChromaLongTermMemory.store/search/retrieve/
forget` gained required keyword-only `end_user_id`. Pre-v1.x
breaking change; every caller had to be updated. Release-note-
worthy in pre-1.0 framing. **Minor** under both old and new
rules.
- `v0.20.1` (patch) — Heid-flagged drift fixes within the v0.20.0
surface. **Patch** under both rules.
### Retroactive bumps
Don't retroactively bump past commits to align with a tightened
rule. Substrate-state baseline is forward-looking: the bump-cadence
from this point forward reflects the discipline; past versions
record what happened under the previous rule. The pre/post boundary
is the amendment-effective commit, not the rule itself.
## Issue → AFK dispatch hygiene (Sleipnir)
Before marking any issue `ready-for-agent` (the label that gates AFK
dispatch via Sleipnir), it MUST have an issue-numbered contract at
`docs/contracts/issues/<N>.contract.md` AND that contract MUST carry
a `prd:` block in its YAML frontmatter pinning it to:
- the issue body (SHA-256, first 16 hex chars)
- the lock-in comment id + content SHA (or `null` if the issue body
alone is the spec)
- a `pinned_at` timestamp
This is hard policy, not a suggestion. Without the contract Sleipnir's
gate refuses dispatch with `blocked-needs-contract`. Without the `prd:`
block PRD-↔-ship drift becomes invisible — the audit infrastructure
that detects it is a no-op.
Verify before applying the label:
- contract file exists at the expected path
- frontmatter has a populated `prd:` block
- `python scripts/contract_drift_check.py --contract <path>` returns
clean (i.e., the pinned hashes match the live issue + comment)
The full convention — frontmatter shape, required fields, reasoning
behind the four drift entry points — lives in
`docs/contracts/CONTRACT-FORMAT.md` (the project that consumes
Sleipnir owns this file). Module-scoped contracts MAY adopt the same
`prd:` block when amended in response to a specific issue; for
issue-scoped contracts it is required.