# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What This Repo Is

Benac is two things in one workspace:

1. **A GraphMD specification repository** — `records/` holds YAML-frontmatter documents that define requirements (`req.*`) and terms (`term.*`). Node tooling compiles these into glossary and SyRS docs, then validates the dataset.

2. **The C1 Hello World PWA** — a static Rust/WASM browser app with no backend, built with Trunk and Leptos.

The operator sidecar (`tools/gravitational_lens`) is a separate Rust package that provides tickets, code navigation, and an MCP endpoint. Its durable state (tickets, tokens) lives in `.gravitational_lens/`.

## Commands

### Docs pipeline
```bash
npm ci
npm test   # rebuilds glossary + SyRS, then validates the dataset
```

### Rust (native)
```bash
cargo test --workspace
cargo fmt --check
cargo clippy --workspace --all-targets -- -D warnings
bash scripts/check-cid-grep-gates.sh   # DASL CID + DRISL grep-gate guard
bash scripts/check-no-canonical-in-source.sh  # source-side terminology gate (49f14a3d)
```

### C1 PWA full build sequence
```bash
rustup target add wasm32-unknown-unknown
cargo build -p hello-world-wasm --target wasm32-unknown-unknown --release
cargo run -p xtask -- build-hello-world-fixtures
trunk build apps/pwa/index.html --release --dist dist
cargo run -p xtask -- finalize-pwa-dist dist   # writes sw.js + health-static.txt
cargo run -p xtask -- validate-pwa-dist dist
```

### Containerized build (cache-aware, uses podman)
```bash
scripts/build-pwa-container.sh           # build only
scripts/build-pwa-container.sh --sync-root  # build + copy dist artifacts to repo root
```

Cache directories default to `/tmp/benac-build-cache`; override with `BENAC_CARGO_HOME`, `BENAC_RUSTUP_HOME`, `BENAC_BUILD_CACHE_ROOT`, `BENAC_TRUNK_HOME`, or `BENAC_XDG_CACHE_HOME`.

### gravitational_lens (ticket/operator sidecar)
```bash
.gravitational_lens/run-local.sh   # starts server at http://localhost:8787
```
Tickets are at `/tickets`, MCP endpoint at `/operator-mcp`, dashboard at `/dashboard`.

## Operator Ticket Workflow

Use the operator MCP surface for ticket work:

```text
https://benac.benac.dev/operator-mcp
```

Before relying on a remembered tool shape, call `tools/list` on
`/operator-mcp`. The ticket lifecycle pattern proven on Benac is:

```text
create_ticket              -> caller creates a request
get_ticket                 -> handler fetches full context before responding
handler_respond_ticket     -> handler status transitions
add_ticket_comment         -> relevant non-status ticket discussion only
user_confirm_resolution    -> caller accepts or rejects proposed resolution
user_cancel_ticket         -> caller cancels
user_change_ticket_status  -> caller defers, supersedes, or resumes
archive_closed_tickets     -> remove terminal tickets from default lists
```

When acting as the handler, use `handler_respond_ticket` for lifecycle
changes:

```text
in_progress
awaiting_clarification
proposed_resolution
```

Use `add_ticket_comment` only when the comment is directly relevant to the
ticket and does not change lifecycle state. Do not use product tickets as a log
for unrelated tooling, deployment, or UI-support work. If a change is not part
of the ticket's requested work, keep it out of that ticket's history.

For long-running ticket-queue work, keep a watch stream open with a Bearer
token:

```bash
curl -N https://benac.benac.dev/tickets/watch \
  -H 'authorization: Bearer <token>'
```

Treat `ticket_created` and `ticket_comment` events as actionable. Fetch the
ticket with `get_ticket` before replying. If a deploy or network drop kills the
stream, reconnect and continue.

## Crate Architecture

```
benac-core          Pure domain logic — no browser, no WASM bindings
benac-browser       Browser host adapters (IndexedDB, WASM runner, file I/O)
benac-conformance   Conformance test suites against benac-core interfaces
benac-fixtures      Deterministic test fixtures (hello-world capsule, signed claims)
apps/pwa            Trunk entry point; wires together all crates into the browser app
packages/hello-world-wasm  Standalone WASM package compiled to provide the hello-world capsule
xtask               Build automation: fixture generation, sw.js finalization, dist validation
tools/gravitational_lens   Separate Rust package (operator sidecar); not part of the main workspace
```

`benac-core` has no platform dependencies (no `web-sys`, no `wasm-bindgen`). Browser-specific implementations belong in `benac-browser`.

## Records / GraphMD Dataset

`records/` is a flat directory of subdirectories named by record id (e.g. `req.benac-auth-001`, `term.station`). Each record is a Markdown file with YAML frontmatter matching its type schema defined in `types/`.

The Node tools in `tools/` read these records and produce compiled output; `npm test` is the authoritative check that the dataset is consistent.

## Commit Conventions

Commits are enforced by commitlint (`.commitlintrc.cjs`) and gitlint (`.gitlint`):

- **Conventional Commits format required**: `type: subject` with a body separated by a blank line
- **Body is mandatory** — explain what changed and why
- **No Co-Authored-By or AI attribution trailers** — single authorship principle
- **No email addresses anywhere in commit messages**
- Valid types: `feat fix perf refactor docs chore test build ci style revert`

## Deployment

The deployable artifact is `dist/`. CI uploads it as a static release to:
```
/srv/benac/releases/<git-sha>/
/srv/benac/current -> releases/<git-sha>
```
Required secrets: `BENAC_DEPLOY_HOST`, `BENAC_DEPLOY_USER`, `BENAC_DEPLOY_PATH`, `BENAC_DEPLOY_SSH_KEY`.

The `validate-pwa-dist` xtask enforces that `dist/` contains `index.html`, `manifest.webmanifest`, `sw.js`, icons, and at least one `.wasm` file, and that `index.html` contains no `/api/` or `telemetry` references.
