Record Vuong Hoang's name + roles (Founder/Principal Developer/Chief Systems Architect of Corviduo AI and PhaseFinal, Inc; Partner + Chief Systems Architect at VastBlue AI) so identity surfaces in every Claude Code session, not just brokkr-smithy's per-project auto-memory.
714 lines
34 KiB
Markdown
714 lines
34 KiB
Markdown
# 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.
|
||
|
||
## Operator identity
|
||
|
||
The operator is **Vuong Hoang** (lkraven@lkraven.com), addressed as
|
||
"Vuong". Roles across three organizations:
|
||
|
||
- **Corviduo AI** — Founder, Principal Developer, Chief Systems Architect.
|
||
- **PhaseFinal, Inc** — Founder, Principal Developer, Chief Systems Architect.
|
||
- **VastBlue AI** — Partner, Chief Systems Architect.
|
||
|
||
He is the final authority on architectural / material-consequence
|
||
calls. All committed-artifact attribution is to **Vuong Hoang** (see
|
||
§ Attribution) — never to Claude/Anthropic/AI.
|
||
|
||
## 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.
|
||
|
||
## Althing async-reply monitoring
|
||
|
||
When you ship an althing message that you expect to come back with a
|
||
reply — a consult (`/heid*`), a question to a peer agent, a request
|
||
awaiting an answer — and you do **not** already have an althing
|
||
monitor running for this session, **always start one**
|
||
(`/althing:monitor`). The dispatch and the monitor are one action: you
|
||
opened a loop, so arm the thing that closes it.
|
||
|
||
Why: in an interactive session the inbox banner (📬) only fires on the
|
||
*next* user prompt. If the operator steps away after you dispatch an
|
||
async consult, the reply can sit unseen indefinitely — the loop you
|
||
opened never closes on its own. A monitor actively pings when the
|
||
reply lands, so the await-reply promise ("reply will land in your
|
||
inbox") is backed by a real notification, not by hoping the operator
|
||
prompts again.
|
||
|
||
Scope: only messages where you're waiting on a response. Pure
|
||
fire-and-forget posts (an FYI ping, a post-1.0 downstream-consumer
|
||
announcement) don't need a monitor. When the exchange is done, sign
|
||
off (`/althing:sign-off`) so monitors don't accumulate — stop a stuck
|
||
one with `althing-light-monitor --stop`, never `pkill` (that kills
|
||
every agent's monitor, not just yours).
|
||
|
||
## 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.
|
||
|
||
**Graphify the blast radius BEFORE writing the contract** — for ANY
|
||
contract that touches existing code (not greenfield). Run a graphify
|
||
pass over every symbol the contract will change, delete, or rename
|
||
(`graphify explain <symbol>` for callers/neighbors, `graphify path`
|
||
for reach) so the contract's scope section enumerates the *true* set
|
||
of affected files and call-sites — not an under-count that detonates
|
||
mid-implementation. **Cross-check with grep, always.** Graphify is
|
||
blind to function-local and DI-injected imports (`from .x import y`
|
||
inside a function body, store objects passed through constructors) —
|
||
the exact pattern that hides cross-module callers. Graphify confirms
|
||
the call structure; grep catches what its AST extraction misses;
|
||
neither alone is sufficient (graphify-only under-counts, grep-only
|
||
misses transitive reach). Both, before the contract — the scope
|
||
section is only as honest as the blast-radius pass behind it. (Lesson:
|
||
a contract that names 3 files when the rip touches 6 is a scope miss
|
||
the up-front pass would have caught.)
|
||
|
||
### 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>'`.
|
||
|
||
- **Graphify** — open-source knowledge-graph skill for AI coding assistants
|
||
(`uv tool install graphifyy`; CLI `graphify`, MCP `graphify-mcp`). Turns a
|
||
repo into a queryable graph. The **free, deterministic path** is
|
||
`graphify update <path>`: tree-sitter AST extraction + Leiden clustering,
|
||
**zero LLM / zero tokens**, ~25s for ~900 files; writes
|
||
`graphify-out/{graph.json,GRAPH_REPORT.md}`. Highest-value free output is
|
||
the **God Nodes** list (core-abstraction surfacing) plus `graphify explain
|
||
<symbol>` / `query` / `affected` / `path` traversals over `EXTRACTED` call
|
||
edges. The LLM layer (community **labeling** = low-caliber, and **semantic
|
||
extraction** of docs/INFERRED edges = higher-caliber + noisier) is optional
|
||
and points at any backend via `~/.graphify/providers.json` — pair labeling
|
||
with the cheap LiteLLM/Granite endpoint below; leave deep semantic
|
||
extraction off unless needed (its INFERRED/AMBIGUOUS edges fight the
|
||
explicit-over-implicit floor). Complements a hand-authored `docs/CODEBASE.md`
|
||
(curated geography), doesn't replace it. Pilot-validated on Worldtree
|
||
2026-06-10.
|
||
|
||
- **LiteLLM gateway (Granite 4.1 + friends)** — OpenAI-compatible gateway at
|
||
`http://10.250.50.70:4000/v1` (Logs UI `:4000/ui`) fronting vLLM services.
|
||
Essentially-**free local compute** for low-caliber, high-volume,
|
||
parallelizable LLM work. The **always-available summarizer / classifier
|
||
endpoint** is `granite-4.1-8b` — the go-to for summarization, naming/
|
||
labeling, classification, and triage (fast, parallelizes well on vLLM):
|
||
served FP8 on ana-ml2 GPU 1, production-stable, 131k context (rebalanced
|
||
2026-06-13). Treat it as a standing dependency you can reach for any time.
|
||
Also behind the gateway: `qwen3.5-9b-fp8` (vision / multimodal — image +
|
||
text chat, GPU 1), `qwen3-embedding`, `qwen3-reranker`, and `glm-5.1` /
|
||
`glm-4.7` (via z.ai passthrough). The gateway 401s without a **virtual API
|
||
key**. **Shared all-agents key** — internal-only gateway, scoped to the *free
|
||
local* models only (granite + qwen-vision + embed/rerank, NOT the paid GLM):
|
||
`sk-eA_XOdcs6nIkyYXonohtEQ` (alias `all-agents-local`). Use it directly for
|
||
the always-available local endpoints — no per-project provisioning needed.
|
||
Example: `curl http://10.250.50.70:4000/v1/chat/completions -H "Authorization:
|
||
Bearer sk-eA_XOdcs6nIkyYXonohtEQ" -d '{"model":"granite-4.1-8b","messages":[…]}'`.
|
||
For the **paid** GLM passthroughs or broader scope, still request a
|
||
project-scoped key from infra-ops via althing (the shared key deliberately
|
||
can't spend z.ai cost). Rotatable via infra-ops if it leaks. Reach for
|
||
this before spending premium API tokens on tasks a small fast local model
|
||
handles fine (the LLM-last-resort floor still applies — deterministic first).
|
||
|
||
## 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.
|
||
|
||
## Roadmap discipline — v1 target + parking lot (anti-creep)
|
||
|
||
Projects sprawl at v0 when features land with **no v1 target to gate
|
||
them against** — every good idea, lacking a home, becomes v0 scope by
|
||
default, and the project never converges. This is the feature-axis
|
||
analog of two disciplines already in force: **value-of-information**
|
||
for probes (don't-measure-what-won't-change-behavior) and
|
||
**patch-default** for version bumps. Same posture —
|
||
*asymmetric default toward not-doing* — applied to features.
|
||
|
||
**Every project carries a `ROADMAP.md`** (a screenful, not a PRD):
|
||
|
||
1. **v1 target** — the small, explicit "what must be true to cut
|
||
1.0": **3–7 capabilities**. A big v1 is itself the creep; keep it
|
||
tight. This is the done-definition AND the anchor every feature is
|
||
gated against.
|
||
|
||
2. **Parking lot (post-v1 / vNext / spinout)** — every deferred idea,
|
||
named, with a home. Creep lands *here* instead of silently
|
||
becoming v0 scope. A "feature" that's really its own project is a
|
||
**spinout**, not v1 scope.
|
||
|
||
3. **The gate (the anti-creep mechanism):** a proposed feature → *on
|
||
the v1 path? → in; else → parking lot.* **Default = parking lot.**
|
||
When both "v1" and "park" are defensible, park it — the same
|
||
tie-break-to-the-cheaper-tier asymmetry as the SemVer patch-default.
|
||
Applies regardless of who proposed the feature (operator included);
|
||
the gate is about v1-path-fit, not provenance.
|
||
|
||
4. **Creep-check at checkpoints:** when surfacing progress, ask "is
|
||
what we're building on the v1 path?" Off-path-but-worth-keeping →
|
||
parking lot, surfaced to the operator, never silently into v0.
|
||
|
||
The per-project `ROADMAP.md` file shape + location is defined in
|
||
`corviduo-project-template` as a copied-in **skeleton** (like
|
||
`persistent-memory.md` — new projects copy it, then the content
|
||
diverges per project), NOT a byte-identical canonical. The skeleton
|
||
carries the shape; the discipline itself lives in this rule (loaded
|
||
every session), so no byte-sync of content is needed. This mirrors
|
||
the SemVer global-rule + per-project-version-file split: the version
|
||
*field* is conventional, its *value* diverges per project — nobody
|
||
canonical-syncs version values, and likewise nobody syncs roadmap
|
||
content. v1.0 is cut when the ROADMAP's v1 target is met — tying
|
||
directly into the SemVer "v1.0 = discipline-tightening event."
|
||
|
||
## 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.
|