Compare commits
19 changed files with 39 additions and 163 deletions
|
|
@ -53,17 +53,3 @@ jobs:
|
||||||
# protection) — a hit blocks merge. See
|
# protection) — a hit blocks merge. See
|
||||||
# scripts/check-comment-blocks.sh.
|
# scripts/check-comment-blocks.sh.
|
||||||
run: sh scripts/check-comment-blocks.sh
|
run: sh scripts/check-comment-blocks.sh
|
||||||
|
|
||||||
doc-refs:
|
|
||||||
name: doc-pointer lint
|
|
||||||
runs-on: [hive-ci]
|
|
||||||
# Pure git+grep — seconds.
|
|
||||||
timeout-minutes: 5
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: lint
|
|
||||||
# Flags `docs/…md` paths and relative markdown links that no
|
|
||||||
# longer resolve. Pointing at a doc is what the comment-block
|
|
||||||
# lint above pushes people toward, so the pointers need a gate of
|
|
||||||
# their own. See scripts/check-doc-refs.sh.
|
|
||||||
run: sh scripts/check-doc-refs.sh
|
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ docs/tools/hivectl-cli.md
|
||||||
# indentation normalisation, so these files need manual re-wrap before
|
# indentation normalisation, so these files need manual re-wrap before
|
||||||
# adding them back to the formatter scope.
|
# adding them back to the formatter scope.
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
docs/agent-lifecycle/approvals.md
|
docs/approvals.md
|
||||||
docs/process/conventions.md
|
docs/conventions.md
|
||||||
docs/networking/gateway.md
|
docs/gateway.md
|
||||||
docs/integrations/matrix.md
|
docs/matrix.md
|
||||||
docs/agent-lifecycle/persistence.md
|
docs/persistence.md
|
||||||
docs/web-ui/terminal-rendering.md
|
docs/terminal-rendering.md
|
||||||
docs/tools/forge.md
|
docs/tools/forge.md
|
||||||
docs/tools/matrix.md
|
docs/tools/matrix.md
|
||||||
docs/turn-loop/README.md
|
docs/turn-loop/README.md
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ One-shot rows fire once (if past due, on the next worker pass) and are deleted b
|
||||||
### Scheduled prompt delivery: todo, not a broker message
|
### Scheduled prompt delivery: todo, not a broker message
|
||||||
|
|
||||||
An agent target's delivery is `push_todo` (`Coordinator::push_todo`,
|
An agent target's delivery is `push_todo` (`Coordinator::push_todo`,
|
||||||
`docs/scheduler/coordinator.md` covers the mechanism generally), not a broker
|
`docs/coordinator.md` covers the mechanism generally), not a broker
|
||||||
`Message` — a scheduled prompt wakes its target with a todo instead of
|
`Message` — a scheduled prompt wakes its target with a todo instead of
|
||||||
driving an immediate turn, by design. `key = "schedule:<id>"` per
|
driving an immediate turn, by design. `key = "schedule:<id>"` per
|
||||||
target drives `push_todo`'s own upsert-by-key dedup: a re-fire of the
|
target drives `push_todo`'s own upsert-by-key dedup: a re-fire of the
|
||||||
|
|
@ -419,7 +419,7 @@ submitter pushes again (or closes it) to retry.
|
||||||
|
|
||||||
Long-running approval work — `MergeConfigPr`, `UpdateMetaInputs`,
|
Long-running approval work — `MergeConfigPr`, `UpdateMetaInputs`,
|
||||||
`Spawn` — runs as a DAG on the global job queue
|
`Spawn` — runs as a DAG on the global job queue
|
||||||
(`docs/scheduler/coordinator.md::Job queue`), submitted by the approval handler
|
(`docs/coordinator.md::Job queue`), submitted by the approval handler
|
||||||
rather than run inline:
|
rather than run inline:
|
||||||
|
|
||||||
| `ApprovalKind` | DAG submitted | source |
|
| `ApprovalKind` | DAG submitted | source |
|
||||||
|
|
|
||||||
|
|
@ -466,7 +466,7 @@ subvolume creation/deletion above.
|
||||||
|
|
||||||
This is the same subvolume `hivectl agent <name> subvol snapshot push`
|
This is the same subvolume `hivectl agent <name> subvol snapshot push`
|
||||||
sends to the swarm's snapshot store — see
|
sends to the swarm's snapshot store — see
|
||||||
[`docs/networking/snapshot-store.md`](../networking/snapshot-store.md) for what a pushed
|
[`docs/snapshot-store.md`](../networking/snapshot-store.md) for what a pushed
|
||||||
snapshot contains and how the store authenticates a sender.
|
snapshot contains and how the store authenticates a sender.
|
||||||
|
|
||||||
## `/var/lib/swarm-controller/` (swarm-controller host only)
|
## `/var/lib/swarm-controller/` (swarm-controller host only)
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ host-side service on another port — e.g. a dev OTLP collector you want
|
||||||
agents to reach directly — is unreachable. (hyperhive's own telemetry
|
agents to reach directly — is unreachable. (hyperhive's own telemetry
|
||||||
needs none of this: `otel.enable` opens its collector's port itself, and
|
needs none of this: `otel.enable` opens its collector's port itself, and
|
||||||
`otel.endpoint` is the *upstream*, which no agent ever dials. See
|
`otel.endpoint` is the *upstream*, which no agent ever dials. See
|
||||||
`docs/scheduler/observability.md`.)
|
`docs/observability.md`.)
|
||||||
|
|
||||||
`services.hyperhive.network.exposeHostPorts = [ 4318 ];` opens each
|
`services.hyperhive.network.exposeHostPorts = [ 4318 ];` opens each
|
||||||
listed TCP port `P` on the bridge-interface `allowedTCPPorts`, so an
|
listed TCP port `P` on the bridge-interface `allowedTCPPorts`, so an
|
||||||
|
|
@ -274,4 +274,4 @@ loopback — the hive-c0re admin socket is a UDS, not TCP.
|
||||||
|
|
||||||
## Cross-references
|
## Cross-references
|
||||||
|
|
||||||
- `docs/networking/gateway.md` — vhost map + the gateway's other duties
|
- `docs/gateway.md` — vhost map + the gateway's other duties
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ the access-review list.
|
||||||
|
|
||||||
The snapshot covers an agent's **state subvolume**, which is the parent
|
The snapshot covers an agent's **state subvolume**, which is the parent
|
||||||
of `state/`, `claude/` and `harness/` (see
|
of `state/`, `claude/` and `harness/` (see
|
||||||
[`docs/agent-lifecycle/persistence.md`'s btrfs subvolume
|
[`docs/persistence.md`'s btrfs subvolume
|
||||||
section](../agent-lifecycle/persistence.md#btrfs-subvolumes-for-varlibhyperhiveagentsname)
|
section](../agent-lifecycle/persistence.md#btrfs-subvolumes-for-varlibhyperhiveagentsname)
|
||||||
for how and when that subvolume is created). Consequences:
|
for how and when that subvolume is created). Consequences:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ Per-variant fields:
|
||||||
— `agent` is the affected agent (target of the spawn / config
|
— `agent` is the affected agent (target of the spawn / config
|
||||||
commit), not the asker. `description` is the manager's free-text
|
commit), not the asker. `description` is the manager's free-text
|
||||||
blurb shown on the dashboard card. `commit_ref` is the
|
blurb shown on the dashboard card. `commit_ref` is the
|
||||||
kind-specific payload (see `docs/agent-lifecycle/approvals.md::Approval kinds
|
kind-specific payload (see `docs/approvals.md::Approval kinds
|
||||||
(wire shapes)`).
|
(wire shapes)`).
|
||||||
- `Reminder { id, owner, message, due_at, age_seconds }` —
|
- `Reminder { id, owner, message, due_at, age_seconds }` —
|
||||||
`due_at` is the absolute time the scheduler is targeting (RFC
|
`due_at` is the absolute time the scheduler is targeting (RFC
|
||||||
|
|
@ -424,7 +424,7 @@ start runs across the `StopForUpdate → Swap → RebuildBookkeeping`
|
||||||
brace and the tail `Reconcile` node. `flake.nix` itself is no longer
|
brace and the tail `Reconcile` node. `flake.nix` itself is no longer
|
||||||
regenerated host-side on rebuild — it's tracked in the agent's
|
regenerated host-side on rebuild — it's tracked in the agent's
|
||||||
proposed/applied repos and rides along on every fetch (see
|
proposed/applied repos and rides along on every fetch (see
|
||||||
`docs/agent-lifecycle/approvals.md::Two repos per agent`).
|
`docs/approvals.md::Two repos per agent`).
|
||||||
|
|
||||||
Anything that changes per-container state on the host should be
|
Anything that changes per-container state on the host should be
|
||||||
re-applied here so a manual `↻ R3BU1LD` from the dashboard is
|
re-applied here so a manual `↻ R3BU1LD` from the dashboard is
|
||||||
|
|
@ -528,7 +528,7 @@ the state dir at provisioning time:
|
||||||
|
|
||||||
(The matrix profile avatar is **not** a oneshot — `hive-matrix-daemon`
|
(The matrix profile avatar is **not** a oneshot — `hive-matrix-daemon`
|
||||||
sets it over its live authenticated Client; see
|
sets it over its live authenticated Client; see
|
||||||
`docs/agent-lifecycle/persistence.md::matrix avatar`.)
|
`docs/persistence.md::matrix avatar`.)
|
||||||
|
|
||||||
Shape contract — every one of these:
|
Shape contract — every one of these:
|
||||||
|
|
||||||
|
|
@ -549,7 +549,7 @@ Shape contract — every one of these:
|
||||||
5. **Re-runnable**: a second invocation produces the same final
|
5. **Re-runnable**: a second invocation produces the same final
|
||||||
state (idempotent uploads, idempotent config rewrites). Used
|
state (idempotent uploads, idempotent config rewrites). Used
|
||||||
by the `.path` watchers that re-fire on token appearance (see
|
by the `.path` watchers that re-fire on token appearance (see
|
||||||
`docs/agent-lifecycle/persistence.md::Matrix per-agent daemon`).
|
`docs/persistence.md::Matrix per-agent daemon`).
|
||||||
|
|
||||||
The artefact lives under the agent user's home where applicable
|
The artefact lives under the agent user's home where applicable
|
||||||
(`~/.config/tea/config.yml`) and is chown'd to that user, but the
|
(`~/.config/tea/config.yml`) and is chown'd to that user, but the
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@ and fail outright if the host daemon's
|
||||||
`nix.settings.sandbox-fallback` is `false` (nixpkgs default).
|
`nix.settings.sandbox-fallback` is `false` (nixpkgs default).
|
||||||
`nix/agent-modules/default.nix` does `lib.mkForce true` so builds
|
`nix/agent-modules/default.nix` does `lib.mkForce true` so builds
|
||||||
fall back to unsandboxed local builds rather than failing. Security
|
fall back to unsandboxed local builds rather than failing. Security
|
||||||
implications: `docs/trust-boundary/security.md`.
|
implications: `docs/security.md`.
|
||||||
|
|
||||||
### Linking workspace binaries locally needs `nix develop`
|
### Linking workspace binaries locally needs `nix develop`
|
||||||
|
|
||||||
|
|
@ -377,7 +377,7 @@ connects to the compositor at `127.0.0.1:<vnc_port>`.
|
||||||
- **Port allocation**: a **fixed** port (`hyperhive.gui.vncPort`,
|
- **Port allocation**: a **fixed** port (`hyperhive.gui.vncPort`,
|
||||||
default 5900). No per-agent hashing: network isolation is
|
default 5900). No per-agent hashing: network isolation is
|
||||||
unconditional (each agent has its own netns — see
|
unconditional (each agent has its own netns — see
|
||||||
`docs/networking/network.md#container-isolation`), so the VNC port is
|
`docs/network.md#container-isolation`), so the VNC port is
|
||||||
container-local and can't collide across agents. The harness learns
|
container-local and can't collide across agents. The harness learns
|
||||||
the port from the `HIVE_GUI_VNC_PORT` env var (set on the harness
|
the port from the `HIVE_GUI_VNC_PORT` env var (set on the harness
|
||||||
service when `gui.enable`) — no marker file, no runtime hash. (Unlike
|
service when `gui.enable`) — no marker file, no runtime hash. (Unlike
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ build can't hold the runner's single slot indefinitely).
|
||||||
|
|
||||||
- **Private netns, bridge-attached**: the container runs in its own network namespace (`privateNetwork = true`, `hostBridge`) and reaches hive-forge through the gateway at `http://<forge.domain>` (resolved to the bridge IP via `networking.extraHosts`). It cannot reach host-loopback services — the core dashboard at `127.0.0.1:7000` and the raw forge port are unreachable from CI. Requires `forge.behindGateway = true`.
|
- **Private netns, bridge-attached**: the container runs in its own network namespace (`privateNetwork = true`, `hostBridge`) and reaches hive-forge through the gateway at `http://<forge.domain>` (resolved to the bridge IP via `networking.extraHosts`). It cannot reach host-loopback services — the core dashboard at `127.0.0.1:7000` and the raw forge port are unreachable from CI. Requires `forge.behindGateway = true`.
|
||||||
- **Non-ephemeral**: runner credentials persist across restarts (written to container's stateDir on first registration, reused thereafter).
|
- **Non-ephemeral**: runner credentials persist across restarts (written to container's stateDir on first registration, reused thereafter).
|
||||||
- **Sandbox fallback**: nspawn containers can't create user-namespaces, so nix's sandboxing would always fail. Module sets `nix.settings.sandbox-fallback = true` in the container — nix builds run unsandboxed (safe because the container is already isolated). See `docs/process/gotchas.md`.
|
- **Sandbox fallback**: nspawn containers can't create user-namespaces, so nix's sandboxing would always fail. Module sets `nix.settings.sandbox-fallback = true` in the container — nix builds run unsandboxed (safe because the container is already isolated). See `docs/gotchas.md`.
|
||||||
- **Credential isolation**: the forge admin token (`forge-core-token`) never enters the container. hive-c0re holds it and performs all forge API calls (runner validation + registration-token mint, in `forge/ci_runner.rs`); via hive-priv it writes only the runner registration token to the host env-file `/run/hive-ci/runner-token`, which the container bind-mounts read-only.
|
- **Credential isolation**: the forge admin token (`forge-core-token`) never enters the container. hive-c0re holds it and performs all forge API calls (runner validation + registration-token mint, in `forge/ci_runner.rs`); via hive-priv it writes only the runner registration token to the host env-file `/run/hive-ci/runner-token`, which the container bind-mounts read-only.
|
||||||
|
|
||||||
## Auto-registration flow
|
## Auto-registration flow
|
||||||
|
|
@ -235,4 +235,4 @@ GC on the coordinator host won't reclaim space on the builder.
|
||||||
|
|
||||||
- `nix/host-modules/hive-ci.nix`: runner configuration, auto-registration script, container setup.
|
- `nix/host-modules/hive-ci.nix`: runner configuration, auto-registration script, container setup.
|
||||||
- `.forgejo/workflows/ci.yml`: workflow definition.
|
- `.forgejo/workflows/ci.yml`: workflow definition.
|
||||||
- `docs/process/gotchas.md`: nix sandboxing limitations in containers.
|
- `docs/gotchas.md`: nix sandboxing limitations in containers.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# hive-c0re coordinator internals
|
# hive-c0re coordinator internals
|
||||||
|
|
||||||
Architecture notes for the `hive-c0re` coordinator daemon's internal subsystems.
|
Architecture notes for the `hive-c0re` coordinator daemon's internal subsystems.
|
||||||
For the public API surface (dashboard, socket protocol) see `docs/process/conventions.md`
|
For the public API surface (dashboard, socket protocol) see `docs/conventions.md`
|
||||||
and `docs/agent-lifecycle/persistence.md`.
|
and `docs/persistence.md`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -571,7 +571,7 @@ which is almost never desired. Leave off on non-x86 hosts.
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- `docs/agent-lifecycle/approvals.md` — approval flow + scheduled prompts
|
- `docs/approvals.md` — approval flow + scheduled prompts
|
||||||
- `docs/agent-lifecycle/persistence.md` — SQLite schema, state-dir layout
|
- `docs/persistence.md` — SQLite schema, state-dir layout
|
||||||
- `docs/process/conventions.md` — wire protocol, recipient sentinels
|
- `docs/conventions.md` — wire protocol, recipient sentinels
|
||||||
- `docs/agent-lifecycle/agent-hierarchy.md` — topology and parent/child relations
|
- `docs/agent-hierarchy.md` — topology and parent/child relations
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ That is a **different risk from the one the collector fixes**, and strictly
|
||||||
smaller than what preceded it: before, every agent held the upstream credential
|
smaller than what preceded it: before, every agent held the upstream credential
|
||||||
itself, so it could do all of the above *and* use the token anywhere else. The
|
itself, so it could do all of the above *and* use the token anywhere else. The
|
||||||
collector removes the token and keeps the pipe. Agents are inside the trust
|
collector removes the token and keeps the pipe. Agents are inside the trust
|
||||||
boundary (`docs/trust-boundary/security.md`: capability = accepted risk), so an agent being
|
boundary (`docs/security.md`: capability = accepted risk), so an agent being
|
||||||
able to *send* is an accepted extension of that boundary — but it is not
|
able to *send* is an accepted extension of that boundary — but it is not
|
||||||
closed by this design, and nothing here should be read as closing it.
|
closed by this design, and nothing here should be read as closing it.
|
||||||
|
|
||||||
|
|
@ -179,7 +179,7 @@ and point whatever consumes it at `10.42.0.1:5432` rather than loopback: inside
|
||||||
a container, loopback is the *container*. The bridge IP is the host's address on
|
a container, loopback is the *container*. The bridge IP is the host's address on
|
||||||
the `hive-br0` bridge. The service must also bind an address the bridge can
|
the `hive-br0` bridge. The service must also bind an address the bridge can
|
||||||
reach — a `127.0.0.1`-only listener stays unreachable no matter what the
|
reach — a `127.0.0.1`-only listener stays unreachable no matter what the
|
||||||
firewall allows. See `docs/networking/network.md::Reaching host services` for details.
|
firewall allows. See `docs/network.md::Reaching host services` for details.
|
||||||
|
|
||||||
⚠️ **None of this is needed for hyperhive's own telemetry** — `otel.enable`
|
⚠️ **None of this is needed for hyperhive's own telemetry** — `otel.enable`
|
||||||
contributes the collector's port and derives the agent-facing endpoint itself.
|
contributes the collector's port and derives the agent-facing endpoint itself.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ always on), the gateway proxies all operator-facing traffic, and
|
||||||
reachable via the gateway.
|
reachable via the gateway.
|
||||||
- **Agent** — speaks only for itself, only over its per-agent
|
- **Agent** — speaks only for itself, only over its per-agent
|
||||||
unix socket. The socket's identity _is_ the agent (see
|
unix socket. The socket's identity _is_ the agent (see
|
||||||
`docs/process/conventions.md`, "identity = socket"). An agent must not
|
`docs/conventions.md`, "identity = socket"). An agent must not
|
||||||
be able to reach the core daemon's HTTP surface, another
|
be able to reach the core daemon's HTTP surface, another
|
||||||
agent's socket, or another agent's web UI.
|
agent's socket, or another agent's web UI.
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ defence-in-depth layered on top.
|
||||||
|
|
||||||
Network isolation is complete and always on: every agent container
|
Network isolation is complete and always on: every agent container
|
||||||
runs in a private netns behind the hive bridge, and there is no
|
runs in a private netns behind the hive bridge, and there is no
|
||||||
shared-netns mode. See `docs/networking/network.md`.
|
shared-netns mode. See `docs/network.md`.
|
||||||
|
|
||||||
Concretely, the core daemon's dashboard `/api` carries **no
|
Concretely, the core daemon's dashboard `/api` carries **no
|
||||||
application-layer authentication** — operator-authority routes are served
|
application-layer authentication** — operator-authority routes are served
|
||||||
|
|
@ -125,7 +125,7 @@ nginx reaches all of `/run/hive-agent` as a plain host path. Dropping
|
||||||
`o=w` removes that permission rather than qualifying it.
|
`o=w` removes that permission rather than qualifying it.
|
||||||
|
|
||||||
⚠️ **The gateway's nginx and dnsmasq are host services, next to
|
⚠️ **The gateway's nginx and dnsmasq are host services, next to
|
||||||
`hive-c0re`** (see `docs/networking/gateway.md`) — there is no namespace between
|
`hive-c0re`** (see `docs/gateway.md`) — there is no namespace between
|
||||||
them and the rest of the host. That costs no network isolation: nginx
|
them and the rest of the host. That costs no network isolation: nginx
|
||||||
binds the host's `:80`/`:443` and reaches `localhost` upstreams, which a
|
binds the host's `:80`/`:443` and reaches `localhost` upstreams, which a
|
||||||
netns would have to be opened up for anyway.
|
netns would have to be opened up for anyway.
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
cgroup v2 on the host. Polled only while this sub-tab is active. -->
|
cgroup v2 on the host. Polled only while this sub-tab is active. -->
|
||||||
<section class="core-pane" id="core-pane-load" data-tab-pane="load"
|
<section class="core-pane" id="core-pane-load" data-tab-pane="load"
|
||||||
role="tabpanel" aria-labelledby="core-tab-load">
|
role="tabpanel" aria-labelledby="core-tab-load">
|
||||||
<p class="meta">live cpu + memory per agent container, from cgroup v2 on the host. cpu is % of total host capacity (all cores), sampled over ~200ms each refresh; polled every 5s while this tab is open. network is omitted on purpose — agents share the host netns, so there is no per-container counter (see <code>docs/networking/network.md</code>).</p>
|
<p class="meta">live cpu + memory per agent container, from cgroup v2 on the host. cpu is % of total host capacity (all cores), sampled over ~200ms each refresh; polled every 5s while this tab is open. network is omitted on purpose — agents share the host netns, so there is no per-container counter (see <code>docs/network.md</code>).</p>
|
||||||
<div id="container-load-section">
|
<div id="container-load-section">
|
||||||
<p class="meta">loading…</p>
|
<p class="meta">loading…</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -619,7 +619,7 @@ export function renderContainers(s) {
|
||||||
// TCP fallback — the gateway proxies the prefix to the per-agent
|
// TCP fallback — the gateway proxies the prefix to the per-agent
|
||||||
// harness (unix-domain via `agent-sockets.json`, or a computed TCP
|
// harness (unix-domain via `agent-sockets.json`, or a computed TCP
|
||||||
// loopback port while the socket marker is absent). See
|
// loopback port while the socket marker is absent). See
|
||||||
// `docs/web-ui/dashboard.md::Container row` + `docs/networking/gateway.md::Vhost map`.
|
// `docs/web-ui/dashboard.md::Container row` + `docs/gateway.md::Vhost map`.
|
||||||
const gatewayLinks = !!(s && s.gateway_enabled);
|
const gatewayLinks = !!(s && s.gateway_enabled);
|
||||||
// Forge public URL: state.forge_public_url (set by the NixOS module
|
// Forge public URL: state.forge_public_url (set by the NixOS module
|
||||||
// from services.hyperhive.forge.publicUrl) or null — never guessed
|
// from services.hyperhive.forge.publicUrl) or null — never guessed
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
No `.term-input` here — composers are a separate concern, owned by
|
No `.term-input` here — composers are a separate concern, owned by
|
||||||
each page's own CSS. Row taxonomy + layout contract documented in
|
each page's own CSS. Row taxonomy + layout contract documented in
|
||||||
`docs/web-ui/shape.md::Shared terminal pane` and
|
`docs/web-ui/shape.md::Shared terminal pane` and
|
||||||
`docs/web-ui/terminal-rendering.md`. */
|
`docs/terminal-rendering.md`. */
|
||||||
|
|
||||||
.terminal-wrap {
|
.terminal-wrap {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
is now four: the four severities every row already carries. Structural
|
is now four: the four severities every row already carries. Structural
|
||||||
identity (this is a turn boundary, this is a tool call) is carried by
|
identity (this is a turn boundary, this is a tool call) is carried by
|
||||||
the row's icon (`◆`, `🔧`, `✅`, …) and summary text instead of colour —
|
the row's icon (`◆`, `🔧`, `✅`, …) and summary text instead of colour —
|
||||||
see docs/web-ui/terminal-rendering.md. */
|
see docs/terminal-rendering.md. */
|
||||||
.live .level-debug { color: var(--muted); }
|
.live .level-debug { color: var(--muted); }
|
||||||
.live .level-info { color: var(--fg); }
|
.live .level-info { color: var(--fg); }
|
||||||
.live .level-warn { color: var(--amber); border-left-color: var(--amber); }
|
.live .level-warn { color: var(--amber); border-left-color: var(--amber); }
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# The swarm's telemetry collector: one per swarm, in a `swarm-otel`
|
# The swarm's telemetry collector: one per swarm, in a `swarm-otel`
|
||||||
# nixos-container beside the swarm's other shared services. This is the
|
# nixos-container beside the swarm's other shared services. This is the
|
||||||
# **swarm** tier; `otel.nix` is the hive tier that forwards into it.
|
# **swarm** tier; `otel.nix` is the hive tier that forwards into it.
|
||||||
# `docs/scheduler/observability.md` owns the tier model and why the two stay separate
|
# `docs/observability.md` owns the tier model and why the two stay separate
|
||||||
# when co-located.
|
# when co-located.
|
||||||
#
|
#
|
||||||
# A container rather than a second host unit, for the same reason every
|
# A container rather than a second host unit, for the same reason every
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,6 @@
|
||||||
# `docs.nix` (website repo) needs no changes to pick either up — it
|
# `docs.nix` (website repo) needs no changes to pick either up — it
|
||||||
# already walks every subdirectory generically.
|
# already walks every subdirectory generically.
|
||||||
#
|
#
|
||||||
# For crates that is `$out/crates/<crate>.md`, one per workspace crate
|
|
||||||
# with a README.md (mara's ask: "behaves as if hive-core/README.md lives
|
|
||||||
# at docs/crates/hive-core.md" — lint:allow, a path this derivation
|
|
||||||
# creates in $out, so the repo has no such file). The crate's own README
|
|
||||||
# stays the single source of truth.
|
|
||||||
#
|
|
||||||
# Output layout:
|
# Output layout:
|
||||||
# $out/ — the repo docs/ tree verbatim
|
# $out/ — the repo docs/ tree verbatim
|
||||||
# $out/crates/<crate>.md — one per workspace crate README
|
# $out/crates/<crate>.md — one per workspace crate README
|
||||||
|
|
@ -80,7 +74,7 @@ stdenv.mkDerivation {
|
||||||
# Rewrite relative links that were correct from the crate's own
|
# Rewrite relative links that were correct from the crate's own
|
||||||
# position in the repo tree but aren't once the file is
|
# position in the repo tree but aren't once the file is
|
||||||
# virtually one level under docs/ instead:
|
# virtually one level under docs/ instead:
|
||||||
# ../docs/x.md (crate root -> repo docs/) -> ../x.md lint:allow (stand-in name)
|
# ../docs/x.md (crate root -> repo docs/) -> ../x.md
|
||||||
# (crates/ -> docs/ is one directory shallower than
|
# (crates/ -> docs/ is one directory shallower than
|
||||||
# <crate>/ -> docs/ was, so the leading docs/ segment drops)
|
# <crate>/ -> docs/ was, so the leading docs/ segment drops)
|
||||||
# ../<sibling-crate> (that crate's own directory, no file)
|
# ../<sibling-crate> (that crate's own directory, no file)
|
||||||
|
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# CI lint: flags documentation pointers that no longer resolve.
|
|
||||||
#
|
|
||||||
# A pointer at a doc is the hive's preferred alternative to duplicating that
|
|
||||||
# doc's prose next to the code. That trade only pays while the pointer
|
|
||||||
# resolves, and nothing was checking. A `docs/` reorganisation left 15 distinct
|
|
||||||
# dead paths across nix, css, html, js, markdown and .prettierignore.
|
|
||||||
#
|
|
||||||
# Emits a CI error annotation per hit and exits 1 if any pointer is dead, 0
|
|
||||||
# otherwise. Runs as its own job; seconds, pure git+grep.
|
|
||||||
#
|
|
||||||
# Two arms, because a pointer is written two ways:
|
|
||||||
# root — a repo-root-relative path in prose or a comment, `docs/<dir>/<f>.md`
|
|
||||||
# rel — a markdown link resolved against the linking file, [x](../<dir>/<f>.md)
|
|
||||||
# (spelled with placeholders on purpose: a literal example path here would be
|
|
||||||
# a dead pointer this script then reports against itself)
|
|
||||||
#
|
|
||||||
# Scope is every tracked file, deliberately unrestricted by type: prose
|
|
||||||
# describes a path in words, and a stale path in a config file (an ignore
|
|
||||||
# rule, a build input) is a live defect rather than a stale comment.
|
|
||||||
#
|
|
||||||
# Escape hatch: a line containing `lint:allow` is exempt. Reserve it for a
|
|
||||||
# path that is correct while absent — a file another derivation synthesizes,
|
|
||||||
# or a literal example — and keep the reason next to the marker.
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
root_pattern='docs/[A-Za-z0-9._/-]*\.md'
|
|
||||||
rel_pattern='\]\(\.\.*/[A-Za-z0-9._/-]*\.md'
|
|
||||||
|
|
||||||
tmp="$(mktemp -d)"
|
|
||||||
trap 'rm -rf "$tmp"' EXIT
|
|
||||||
|
|
||||||
# Lines carrying the marker, as `file:line`, to skip below.
|
|
||||||
git ls-files -z | xargs -0 -r grep -n 'lint:allow' /dev/null 2>/dev/null \
|
|
||||||
| cut -d: -f1,2 > "$tmp/allow" || true
|
|
||||||
|
|
||||||
exempt() { grep -qxF "$1:$2" "$tmp/allow" 2>/dev/null; }
|
|
||||||
|
|
||||||
git ls-files -z | xargs -0 -r grep -nEo "$root_pattern" /dev/null 2>/dev/null \
|
|
||||||
> "$tmp/root" || true
|
|
||||||
git ls-files -z | xargs -0 -r grep -nEo "$rel_pattern" /dev/null 2>/dev/null \
|
|
||||||
> "$tmp/rel" || true
|
|
||||||
|
|
||||||
dead=0
|
|
||||||
alive=0
|
|
||||||
|
|
||||||
while IFS=: read -r file lineno ref; do
|
|
||||||
[ -n "${ref:-}" ] || continue
|
|
||||||
exempt "$file" "$lineno" && continue
|
|
||||||
if [ -f "$ref" ]; then
|
|
||||||
alive=$((alive + 1))
|
|
||||||
else
|
|
||||||
dead=$((dead + 1))
|
|
||||||
printf '::error file=%s,line=%s::doc pointer does not resolve: %s\n' \
|
|
||||||
"$file" "$lineno" "$ref"
|
|
||||||
fi
|
|
||||||
done < "$tmp/root"
|
|
||||||
|
|
||||||
while IFS=: read -r file lineno match; do
|
|
||||||
[ -n "${match:-}" ] || continue
|
|
||||||
exempt "$file" "$lineno" && continue
|
|
||||||
link="${match#](}"
|
|
||||||
# Resolved from the linking file's directory, so `../` counts the same way
|
|
||||||
# the renderer counts it. `-m` keeps a link that escapes the repo root
|
|
||||||
# resolvable enough to report rather than erroring out here.
|
|
||||||
target="$(cd "$(dirname "$file")" && realpath -m "$link")"
|
|
||||||
if [ -f "$target" ]; then
|
|
||||||
alive=$((alive + 1))
|
|
||||||
else
|
|
||||||
dead=$((dead + 1))
|
|
||||||
printf '::error file=%s,line=%s::relative doc link does not resolve: %s\n' \
|
|
||||||
"$file" "$lineno" "$link"
|
|
||||||
fi
|
|
||||||
done < "$tmp/rel"
|
|
||||||
|
|
||||||
# A lint whose extractor matches nothing passes forever and reports success
|
|
||||||
# while checking not one thing. Both arms therefore have to have found
|
|
||||||
# something that resolves before a clean run means anything: if a rename, a
|
|
||||||
# pattern edit or a tree move silences the search, that is a failure of this
|
|
||||||
# script, not a property of the tree.
|
|
||||||
if [ "$(wc -l < "$tmp/root")" -eq 0 ]; then
|
|
||||||
echo 'check-doc-refs: root-pointer search matched nothing — pattern or tree changed' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ "$(wc -l < "$tmp/rel")" -eq 0 ]; then
|
|
||||||
echo 'check-doc-refs: relative-link search matched nothing — pattern or tree changed' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ "$alive" -eq 0 ]; then
|
|
||||||
echo 'check-doc-refs: not one pointer resolved — the resolver is broken, not the tree' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$dead" -gt 0 ]; then
|
|
||||||
printf 'check-doc-refs: %s dead doc pointer(s); %s resolved\n' "$dead" "$alive" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
exit 0
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Git pre-push hook: runs the tracker-tag, comment-block and doc-pointer
|
# Git pre-push hook: runs the tracker-tag lint and the comment-block
|
||||||
# lints against the working tree before any push lands on the remote.
|
# lint against the working tree before any push lands on the remote.
|
||||||
# Catches issues that would fail CI and require a follow-up commit
|
# Catches issues that would fail CI and require a follow-up commit
|
||||||
# (common failure mode: a nix comment containing a hash-issue-number
|
# (common failure mode: a nix comment containing a hash-issue-number
|
||||||
# tag added mid-session).
|
# tag added mid-session).
|
||||||
|
|
@ -28,11 +28,5 @@ if ! sh "$repo_root/scripts/check-comment-blocks.sh"; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "pre-push: running doc-pointer lint..." >&2
|
|
||||||
if ! sh "$repo_root/scripts/check-doc-refs.sh"; then
|
|
||||||
echo "pre-push: doc-pointer lint FAILED — fix before pushing" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "pre-push: lints passed ✓" >&2
|
echo "pre-push: lints passed ✓" >&2
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue