1.1 - Anki collection
Editable collection notes, decks, and settings
Current CLI version-1 TOML exported from collection-20261017235526.colpkg.
The export contains 7,218 notes, 7,485 existing cards, five note types and 513
decks: 485 source decks plus 28 Guzhenren grouping decks. Native identities are
saved in state.anki.toml; note files contain editable fields, tags, note-type
references and card assignments. Deck titles derive from literal directory
names, with a decks/ directory between hierarchy levels.
The source archive SHA-256 is
1fc03bb25a00418a19b927adbb3c18f85a93e58724932cd836152df60f83d3a5.
The original archive, media and review history remain local and are not
committed. The original archive remains byte-identical. Note fields and tags,
native note/card identities, templates, presets, due dates, intervals and review
history were verified against a rebuilt archive. Anki recalculates FSRS memory
estimates when moving cards between decks; this build changes 336 FSRS payloads
while leaving due dates, intervals, review counts, queues and other card data
unchanged.
Grammar lessons live under
decks/main/decks/chinese/decks/chinese-grammar-wiki-782551504/decks/.
All 449 lesson decks use 001. , 002. and subsequent three-digit prefixes,
by the earliest contained note creation ID within each proficiency level.
Imported deck timestamps can reflect alphabetical recreation, so only the four
empty source lessons fall back to their deck creation IDs. Existing note files
within each lesson sort by their original note creation IDs. There are 445
populated lessons. B2 contains 142 lessons and 1,342 notes; C1 contains
12 lessons and 96 notes. Their directories are B2/decks/ and C1/decks/ under
the grammar level directory. Two lesson titles replace / with or for valid
literal filenames; article-title fields are unchanged. The filtered decks use
the readable titles Due cards and New or due cards; their search queries
are unchanged. The source’s removed,
empty B1_unsorted deck is absent.
Guzhenren lives under decks/main/decks/chinese/decks/guzhenren/.
Chapter 1 has 133 sentence notes and 714 vocabulary notes. Its sentences and
words parents each have 14 matching numbered groups, from
001. Sentences 001-010 through 014. Sentences 131-133. Sentence files have
global reading-order prefixes, such as 001. 1692893034664.note.anki.toml.
The sequence includes the chapter heading, narrative and author’s closing note.
Vocabulary follows its example passages and uses global 001. through 714.
filename prefixes, preserving numeric note-ID order within each passage group.
Most vocabulary IDs are imported sequence numbers rather than recoverable
wall-clock timestamps. Shared import/edit modification times do not determine
the order. Two incomplete examples use the
associations recorded in the verification.
The state file is the current CLI’s unmodified export snapshot: it binds all
485 source decks, 7,218 notes, five note types with their fields/templates, and
one referenced scheduling preset (Default). Both native presets remain in the
archive. [[moved]] entries in collection.anki.toml connect
those exported addresses to the organized paths. Grouping decks are created by
native reconciliation. Keep the state and moves together to retain identities.
State update writes its collection argument in place. Import state into a copy
of the original archive, inspect the plan, then build a separate organized
archive with the current CLI:
mkdir -p "$PWD/out/anki-refresh"
cp /absolute/path/collection-20261017235526.colpkg \
"$PWD/out/anki-refresh/collection-state-bound.colpkg"
bazel_agent bazel run //projects/anki_as_code -- col state update \
--collection "$PWD/out/anki-refresh/collection-state-bound.colpkg" \
"$PWD/users/simeonwarren/anki/state.anki.toml"
bazel_agent bazel run //projects/anki_as_code -- col plan \
--collection "$PWD/out/anki-refresh/collection-state-bound.colpkg" \
--text "$PWD/users/simeonwarren/anki"
bazel_agent bazel run //projects/anki_as_code -- col build \
--base "$PWD/out/anki-refresh/collection-state-bound.colpkg" \
--text "$PWD/users/simeonwarren/anki" \
--output "$PWD/out/anki-refresh/collection-organized.colpkg"
The native build succeeds, and planning the rebuilt package against this tree
returns an empty plan. All 254 media filenames and decoded contents are
preserved. The text uses the current CLI format; no legacy CLI is required.
Verification is tracked in
refresh-collection-20261017.
1.3 - Host Bot
host-bot.simeonwarren.users.alwaldend.com
Host Bot runs Traefik with mTLS in front of T3 Code. The host firewall blocks
direct external access to T3 Code and accepts new connections only on SSH,
HTTP, and HTTPS ports.
The DNS Terraform package uses the dedicated
src_users_simeonwarren_host_bot AppRole through dns_al. Ansible retains
its existing al configuration and user_simeonwarren authentication.
T3 Code controls permissions per thread. Its Auto mode maps to Codex
Auto-review. The managed Codex requirements allow read-only and host-bot
permissions with on-request approvals, so threads cannot select Full
access or disable approvals. The requirements file only allowlists the
host-bot profile; it must not define the profile because T3 Code supplies a
per-thread config layer with the same name, and Codex rejects profiles defined
by both requirements and config. The normal and isolated provider configs
define host-bot with workspace writes, public command-network access, and
write access to the Bazel cache and Bazelisk directories while keeping local
network binding and loopback connections available for Bazel servers. Other
private network targets remain blocked. T3 Code must select that profile for
the thread. Hosted web search is not restricted by this role.
The deployment installs pinned Bazelisk as ~/.local/bin/bazel, provisions
the repository’s bazel_agent runner in the same directory, and pre-seeds its
content-addressed mcp_cordis and repo_delivery runtimes when the canonical
repository checkout is present. New Codex worktrees can then start Cordis and
run delivery operations without first loading their own Bazel graph.
Two SATA SSDs, selected by their stable ATA IDs, back the sata_ssd volume
group. Its striped bazel_cache logical volume is mounted persistently at
/var/cache/bazel. Every Bazel workspace uses its disk_cache directory and
the managed user bazelrc points its output-user root at the same filesystem.
The managed user bazelrc also sets repo_contents_cache
to /var/cache/bazel/repo_contents_cache. This requires the live bazelrc to
match that file and the cache directory to exist with its Ansible-defined
ownership and permissions. Eligible materialized external repositories are
then shared across output bases: the first cache miss extracts an archive,
and later worktrees reuse the cached contents instead of repeating extraction. The repository cache still
avoids the network archive download. Automatic garbage collection caps the
action disk cache at 700 GiB, leaving room within the 1-TiB filesystem for
output bases, repository contents, and install state; the volume group retains
about 900 GiB for later growth or another logical volume. Because the volume is
striped, either SSD failing invalidates the disposable cache.
The managed bazelrc sets GO_REPOSITORY_EPHEMERAL_MODCACHE=1. Gazelle 0.53.0
uses a temporary Go module cache for each module fetch and deletes it after
materializing the repository. Pinned, reproducible Go repositories can then
be reused through the shared Bazel repository-contents cache across worktrees.
The variable saves duplicate download-cache space; it does not enable Bazel
cache reuse. A repository-contents cache miss downloads the module again,
including after changes to Gazelle or generation inputs. Gazelle’s default
instead retains module ZIPs within each output base while pruning extracted
module trees. Explicit host module-cache settings take precedence over
ephemeral mode.
The managed user bazelrc limits each Bazel invocation to four concurrent jobs
and budgets one quarter of the host CPUs for local actions. These scheduling limits
apply to builds and tests; they do not impose a combined CPU cap across
independent Bazel processes or Codex sessions.
Executable repository tools use /var/cache/bazel/tool_cache. Unlike the
disposable action and output caches, this directory is private to the host-bot
account. Cache entries are keyed by declared source, build configuration,
platform, and dependency pins, and are installed atomically after a Bazel
build on the first miss.
The arXiv MCP server is pinned to version 0.7.2 and runs locally over stdio.
Its downloaded papers and search data live under
/srv/misc/arxiv-mcp-server/papers on a reusable 100-GiB misc logical
volume in the existing sata_ssd volume group. The volume is mounted for
reconstructible non-executable application data. Data with durability or
different reliability requirements belongs on a separate volume.
The managed Codex configuration allows up to 20 concurrent subagents per
session. The root agent is accounted for separately.
Node.js and npm are provisioned through the development VM role’s mutable or
OSTree package backend. OpenSpec 1.14.2 (@fission-ai/openspec) is installed
without npm lifecycle scripts under the managed user’s ~/.local prefix,
providing ~/.local/bin/openspec. Its exact version is controlled by
openspec_version; the openspec tag selects its prerequisites and installation.
Repository workflows continue to use the repository-pinned Bazel CLI wrapper.
Open Computer Use 0.3.3 is then installed without npm
lifecycle scripts under the managed user’s ~/.local prefix. Its skill is
then installed for the whole host under /etc/codex/skills from the matching
immutable upstream revision. The deployment configures its stdio MCP server in
the normal Codex config and the isolated OpenRouter and Abliteration homes.
The MCP can inspect and control the logged-in desktop session, so agents must treat its actions as real user input and obtain approval before
externally visible or destructive actions.
OpenRouter is available through its isolated Codex home. Its
configuration reads its API key from the desktop keyring entry selected by
service=openrouter and application=codex; the key is not stored in the
configuration. Repository work uses the repo-owned $codex-migration skill, while
the user-level Codex instructions require isolated migration testing and
intentional host changes to be mirrored into this role. Existing sessions are
not redirected because provider-bound encrypted reasoning history cannot
safely migrate in place.
T3 Code can use OpenRouter as a separate Codex provider by setting its
CODEX_HOME path to /var/home/simeonwarrenbot/.codex-openrouter. That
isolated home defaults to ~deepseek/deepseek-v4-flash-latest and shares no
conversation state with the ChatGPT-backed Codex provider. Start a new thread
when selecting it.
The OpenRouter no-tools home at ~/.codex-openrouter-no-tools mirrors the
OpenRouter provider with every tool surface disabled. It reuses the same
keyring lookup and model, sets web_search = "disabled", turns off the shell,
unified-exec, image, multi-agent, computer-use, browser, app, plugin, and goal
features, and disables the request_user_input tool and the MCP servers.
Codex then advertises no tools at all, so the model can only reply with text.
This matters beyond tidiness: OpenRouter rejects a request that carries any
tools when its routed endpoint supports none, so a single leftover tool fails
the turn.
T3 Code launches the Codex app server with
-c mcp_servers.t3-code.url=..., so its own MCP server would otherwise
reintroduce the MCP resource tools. The no-tools config therefore declares
[mcp_servers.t3-code] with enabled = false, which still wins over that
launch-time override.
Select it in T3 Code with CODEX_HOME path set to
/var/home/simeonwarrenbot/.codex-openrouter-no-tools and start a new thread.
The Abliteration and standard OpenRouter homes keep their tools.
Abliteration uses a separate home with
CODEX_HOME="$HOME/.codex-abliteration" codex, using the endpoint and model from its
Codex integration guide.
It retrieves its API token at runtime with secret-tool, selecting
service=abliteration and application=codex. Save the token from an
interactive terminal in the same user’s desktop keyring:
secret-tool store --label='Abliteration API token for Codex' service abliteration application codex
Enter the token at the hidden prompt. The configuration contains only the
lookup command. Provider response and tool-call validation require a saved
token. Both providers keep their configuration and conversation state outside
the main Codex home; provider profiles in the main home are removed. For
T3 Code, set its CODEX_HOME path to
/var/home/simeonwarrenbot/.codex-abliteration and start a new thread.
T3_MCP_BEARER_TOKEN authenticates the Codex app-server’s loopback MCP
connection to T3 Code at /mcp. Codex excludes it from model-spawned commands
so those subprocesses do not inherit the credential.
Cache cleaning
bazel_agent bazel clean removes the current workspace’s build outputs and
resets its local action cache. Adding --expunge removes that workspace’s
entire output base and stops its Bazel server. Neither operation purges the
shared action disk cache, repository-contents cache, or archive-download cache,
so another worktree can continue reusing them.
With the pinned Bazel 8.7.0, repository-contents garbage collection removes
entries unused for 14 days after the server has been idle for five minutes,
using the defaults of --repo_contents_cache_gc_max_age and
--repo_contents_cache_gc_idle_delay. Routine manual cleanup of that cache
is unnecessary. The action disk cache uses the managed 700-GiB cap described
above. The archive-download cache at
/var/cache/bazel/output_user_root/cache/repos/v1 has no automatic cleanup
configured and requires separate maintenance to reclaim its space. Gazelle’s
ephemeral module download cache is already deleted after each fetch.
Architecture


The diagram source is t3code-architecture.mmd,
and the SVG beside this page is rendered from it at build time by
//users/simeonwarren/host_bot:rendered_site. There is no committed copy to refresh:
edit the .mmd and the next build renders the new diagram.
Rendering uses the repository’s pinned Mermaid, headless Chrome, and Liberation
font inputs. The site preset renders native light/dark SVG variants with bordered
caption labels and rounded containers; it does not post-process the generated SVG.
The site selects the variant matching its theme menu. The rendered target
retains the original appearance used by the historical blog publication.
Deployment
bazel run //users/simeonwarren/host_bot/ansible
1.3.2 - DNS Terraform
This root packages the owner’s canonical DNS declarations
through the shared DNS module and keeps state in the DNS AppRole’s Vault HTTP
backend. It uses the dedicated dns_al configuration; Ansible retains its
separate al configuration and user_simeonwarren authority.
The root contains DNS resources only. dns_enabled defaults to true after
verified adoption. Keep it enabled to retain records; disabling it would
propose deletion.
The DNS migration procedure owns
credential provisioning, reconciliation, and recovery. The adoption record
contains import and verification evidence. Use the normal explicit
//users/simeonwarren/host_bot/tf:tf wrappers for DNS operations and the
//users/simeonwarren/host_bot/tf:tf_tests.fmt_test target for offline source
validation.
Operational Terraform calls require the DNS AppRole policy grants and credential
fields described by the migration procedure. Provider configuration and
authentication require real credentials and live access.
1.3.3.1.1.1 - architecture-diagram Specification
Purpose
Describe the maintained architecture diagram owned by
users/simeonwarren/host_bot. The diagram replaced the T3code page of the
former central Drawio document. Infrastructure diagrams now live with their
owning projects, with ownerless archived diagrams under infra/archive.
The diagram describes checked-in configuration; it is not a live inventory or
health check.
Requirements
Requirement: Owner-local Mermaid diagram source
The project SHALL keep its architecture diagram as Mermaid source at
t3code-architecture.mmd and SHALL render it through the repository’s
mermaid_svg rule at //users/simeonwarren/host_bot:rendered. The project
SHALL NOT commit a rendered SVG: //users/simeonwarren/host_bot:docs_assets
SHALL take its srcs from :rendered so the documentation site publishes the
build output, and the README SHALL reference that published path. The diagram
SHALL use the shared //tools/mermaid:theme.json appearance rather than a
package-local copy.
Scenario: Publish the diagram
- WHEN the documentation site is built
- THEN
:docs_assets packages the SVG produced by :rendered
- AND the README image reference resolves to that packaged file
- AND no rendered SVG is tracked in the source tree
- WHEN the render action produces the SVG
- THEN it uses the repository’s pinned Mermaid, headless Chrome, and font
inputs
- AND it loads no stylesheet, script, or font from a CDN
- AND the document embeds the pinned handwriting face, so a viewer that
loads no webfont still lays the labels out at their measured widths
Requirement: Diagram content reflects the deployed topology
The diagram SHALL group the remote actor and the shared Ingress in an
Internet cluster, and SHALL show the remote actor reaching Ingress over
HTTPS with mTLS. It SHALL nest the host-bot cluster inside the local
infrastructure cluster, so the local actor, Vault, and the host’s services
sit inside dc1.alwaldend.com rather than beside it, and the two nested cluster
titles SHALL carry the dc1.alwaldend.com and
host-bot.simeonwarren.users.alwaldend.com names respectively. It SHALL group
Traefik, T3 Code, and Codex in the host-bot cluster, SHALL show the local
actor reaching Traefik directly over HTTPS, Ingress forwarding to Traefik
over Wireguard, and Traefik obtaining ACME certificates from Vault. It
SHALL show T3 Code reaching GitHub for source and Codex reaching OpenAI and
OpenRouter for inference, with those providers in a second Internet cluster
because they are off-host. Both actors SHALL be labelled Actor. External
service nodes SHALL carry their own hostnames.
Sources: diagram source and
project documentation.
Scenario: Consult the host-bot architecture
- WHEN a reader opens the architecture section of the project README
- THEN it displays the maintained diagram showing the ingress path, the
in-host services, and the two external service dependencies
Requirement: Legible diagram layout
The diagram SHALL keep node labels short by naming each host once, in the
cluster title that owns it, and SHALL NOT restate a hostname in both a cluster
title and the nodes it contains. Node labels SHALL NOT wrap mid-token. The
diagram SHALL keep subgraph titles clear of node outlines and edges: Mermaid
centers a cluster title on the cluster’s top border, so a title that wraps
extends into the cluster’s node area. The diagram source SHALL describe topology
only and SHALL let the shared theme own appearance and spacing; a per-diagram
directive is warranted only for a specific geometry need the theme cannot
express.
Actors SHALL render as an ordinary rounded node carrying the shared theme’s
hand-drawn stick figure beside the label, rather than as a bare glyph, a pill
outline, or a Mermaid person shape. Mermaid’s person shape draws the head
over the body box, so it is not used.
Sources: diagram source and
theme.
Scenario: Render the maintained layout
- WHEN the renderer draws the diagram
- THEN every node outline is clear of headings, edges, and other nodes
- AND no label is truncated or broken inside a hostname
Requirement: Appearance is owned by the shared theme
Diagram appearance SHALL come from //tools/mermaid:theme.json rather than
package-local copies or per-diagram appearance overrides, so node rounding and
edge-label plates are consistent across every rendered diagram. The theme SHALL
keep shapes semantic, so a rounded bracket node renders rounded while a square
bracket node stays square; rounding SHALL NOT be forced on every node. The
theme SHALL box edge labels by styling the label text, because Mermaid emits an
empty label plate for unlabelled edges and styling that plate directly would
draw a stray box.
Source: theme.
Scenario: Render another diagram with the theme
- WHEN any diagram renders through
mermaid_svg or the mmdc target
- THEN a rounded node renders rounded and a square node stays square
- AND its edge labels use bordered plates
Scenario: Read an edge annotation
- WHEN the renderer draws an edge that carries a label
- THEN the label renders on a bordered plate rather than directly on the
edge line
- AND an edge without a label draws no plate
1.3.3.1.2.1 - owned-dns Specification
Purpose
Define Host Bot DNS management through the owner’s tf root,
including canonical declarations, scoped credentials, and offline source checks
before adopting live records.
Requirements
Requirement: Owner-local DNS configuration
The tf root SHALL consume this owner’s canonical dnsconfig.json through
the shared DNS Terraform module, preserving declared record identities and views.
DNS resources SHALL default to disabled until the shared migration adopts them
and SHALL retain enabled ownership after authorized adoption into the owner’s
state. Reconciliation of adopted records with unchanged declarations SHALL
propose no record additions, changes, replacements, or deletions.
Scenario: Inspect the preparatory configuration
- WHEN the checked-in root is evaluated with default inputs before authorized
adoption
- THEN it reads this owner’s declaration and disables managed DNS records
- AND the package contains the module and declaration inputs
Scenario: Inspect adopted source defaults
- WHEN the adopted host root is evaluated with its checked-in source defaults
- THEN record ownership is enabled for the owner’s declared records and views
- AND the package retains the canonical declaration and shared module inputs
Scenario: Reconcile existing DNS records
- WHEN the host root plans against its adopted state and unchanged declarations
- THEN it proposes no record additions, changes, replacements, or deletions
- AND records outside this owner’s declarations remain unchanged
Requirement: Scoped DNS execution and offline checks
The DNS wrapper SHALL select src_users_simeonwarren_host_bot through the repository AL flow and keep
secret values in injected variables. DNS injection SHALL select the root’s existing
Terraform stage label. Real DNS credentials and Vault policy grants SHALL be
prerequisites for operational Terraform calls. RouterOS DNS credentials SHALL be isolated
from unrelated RouterOS resources. The package SHALL expose a format test that
does not authenticate to Vault or contact DNS providers.
- WHEN the package format test executes
- THEN it checks the packaged Terraform configuration without live credentials
- AND existing non-DNS authentication and backend paths remain unchanged
- WHEN the wrapper selects its Terraform stage labels
- THEN it selects DNS credential injection through the owner AppRole
- AND disabled DNS resources do not imply offline provider configuration