From 45d9f573000992324483220b4a0ab8cc54a977b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D7=A0=CF=85=CE=B1=CE=B7=20=D7=A0=CF=85=CE=B1=CE=B7=D1=95?= =?UTF-8?q?=CF=83=CE=B7?= Date: Wed, 2 Sep 2026 17:16:55 -0700 Subject: [PATCH 1/4] docs: cut AI tells from the hand-written markdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applies the unslop rules to the markdown this repository writes itself. The bulk is punctuation: em dashes become sentence breaks, commas, or colons, which forces several sentences carrying two ideas to become two sentences. Bold and code-span lead-ins change from "X — text" to "X. Text", the form the rules allow. Inside tables the em dash was a separator between a field and its description, so it becomes a comma. A lone em dash in a cell means "not applicable" and is data, so it stays. Two paragraphs are shared with every other repository in the organization and now read the same in all of them: the design-records pointer to osapi-io/specs, and the pull request bullet that asked for a "comprehensive description" without saying what a reviewer needs. CODE_OF_CONDUCT.md and AI_POLICY.md are untouched. The first is the Contributor Covenant, a standard text that rewording would misrepresent, and the second is byte-identical across the organization. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013oR5Gt9WPTTdPKdnShdL7L --- AGENTS.md | 8 ++-- CLAUDE.md | 2 +- CONTRIBUTING.md | 106 ++++++++++++++++++++++++------------------------ 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3428364e6..3cf586762 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,7 +3,7 @@ Test: `just test` | Before committing: `just ready` Read @CONTRIBUTING.md first. It covers prerequisites, setup, project structure, -code standards, testing, input validation, and how to add an API domain — all of +code standards, testing, input validation, and how to add an API domain. All of which apply to agents exactly as they apply to people. This file carries only what is specific to agents. @@ -17,7 +17,7 @@ mise exec -- just test `mise` is active in a person's shell and supplies the versions `.mise.toml` declares. An agent's shell has no activation, so a bare `just` resolves to -whatever is installed globally — usually an older version. +whatever is installed globally, usually an older version. The symptom is a check that fails here and passes in continuous integration, on a file nobody edited. When that happens, establish which version ran before @@ -27,13 +27,13 @@ treating the failure as real. `go build ./...` and `go test ./...` fail in this repository. The UI is embedded via `//go:embed dist/*`, which requires `ui/dist/` to hold files at compile -time. Use `just build`, `just test`, or `just ready` — each builds the UI first. +time. Use `just build`, `just test`, or `just ready`. Each builds the UI first. See @CONTRIBUTING.md under "Building and running". ## Where the rules come from @CONTRIBUTING.md names the specification under "Before you start". When a -convention here and the specification disagree, the specification wins — say so +convention here and the specification disagree, the specification wins. Say so rather than following the code. ## Commit trailer diff --git a/CLAUDE.md b/CLAUDE.md index 4365898c8..b337e5b72 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # Claude Code -All agent guidance lives in @AGENTS.md — how to invoke tools, why the Go +All agent guidance lives in @AGENTS.md: how to invoke tools, why the Go toolchain is never called directly, where the rules come from, the commit trailer, and task tracking. This file exists so Claude Code finds it; the content is upstream. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 185739d9e..eb043796a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,26 +10,26 @@ workflow. - Read the [Code of Conduct](CODE_OF_CONDUCT.md). It applies to every interaction in this repo. -- **Design records** — The conventions binding this repository are specified in +- **Design records.** The conventions binding this repository are specified in [osapi-io/specs](https://github.com/osapi-io/specs) under `components/osapi/`, whose `.specify/memory/` is the standing record. Design reasoning for a change - lives there too, not here — a design document kept in this repository goes - stale the moment the code moves past it, with nothing to catch the drift. + lives there, not here. A design document kept in this repository goes stale + the moment the code moves past it, and nothing catches the drift. -- **Get familiar with the project** — Read the docs in this order: +- **Get familiar with the project.** Read the docs in this order: - 1. [Guiding Principles][principles] — design philosophy and project values - 2. [System Architecture][system-architecture] — REST API, NATS, CLI - 3. [API Design Guidelines][api-guidelines] — REST conventions and endpoint + 1. [Guiding Principles][principles]. Design philosophy and project values + 2. [System Architecture][system-architecture]. REST API, NATS, CLI + 3. [API Design Guidelines][api-guidelines]. REST conventions and endpoint structure - 4. [Job System Architecture][job-architecture] — KV-first job processing, + 4. [Job System Architecture][job-architecture]. KV-first job processing, subject routing, and the agent pipeline -- **Check existing work** — Is there an existing PR? Are there issues discussing +- **Check existing work.** Is there an existing PR? Are there issues discussing the feature/change you want to make? Please make sure you consider/address these discussions in your work. -- **Backwards compatibility** — Will your change break existing OSAPI files? It +- **Backwards compatibility.** Will your change break existing OSAPI files? It is much more likely that your change will be merged if it is backwards compatible. Is there an approach you can take that maintains this compatibility? If not, consider opening an issue first so that API changes can @@ -43,17 +43,17 @@ Install tools using [mise]: mise install ``` -- **[Go]** — OSAPI is written in Go. We always support the latest two major Go +- **[Go].** OSAPI is written in Go. We always support the latest two major Go versions, so make sure your version is recent enough. -- **[Node.js]** — Runtime for tools like `@redocly/cli`, for building the +- **[Node.js].** Runtime for tools like `@redocly/cli`, for building the Docusaurus docs site, and for building the embedded React UI in `ui/`. -- **[Bun]** — JavaScript package manager and script runner used for the +- **[Bun].** JavaScript package manager and script runner used for the Docusaurus docs and the React UI. -- **[just]** — Task runner used for building, testing, formatting, and other +- **[just].** Task runner used for building, testing, formatting, and other development workflows. -- **[uv]** — Python package runner. `just md-fmt` formats repository markdown +- **[uv].** Python package runner. `just md-fmt` formats repository markdown with [mdformat] via `uvx`; nothing is installed into the repo. -- **[NATS CLI]** — Command-line tools for interacting with NATS. Useful for +- **[NATS CLI].** Command-line tools for interacting with NATS. Useful for debugging and monitoring during development. Install with `brew install nats-io/nats-tools/nats`. @@ -66,7 +66,7 @@ marketplace: /plugin install commit-commands@claude-plugins-official ``` -- **commit-commands** — provides `/commit` and `/commit-push-pr` slash commands +- **commit-commands.** provides `/commit` and `/commit-push-pr` slash commands that follow the project's commit conventions automatically. **Do not use superpowers.** Spec Kit governs specification, planning, and @@ -86,30 +86,30 @@ just deps ## Project structure -- **`cmd/`** — Cobra CLI commands (`client`, `node agent`, `controller.api`, +- **`cmd/`.** Cobra CLI commands (`client`, `node agent`, `controller.api`, `nats server`). -- **`internal/controller/api/`** — Echo REST API. Node-targeted handlers nest +- **`internal/controller/api/`.** Echo REST API. Node-targeted handlers nest under `node/{domain}/`; controller-only handlers are top-level (`job/`, `health/`). Each domain has its own `gen/` with an OpenAPI spec; the combined spec is `internal/controller/api/gen/api.yaml`. -- **`internal/job/`** — Job domain types and subject routing. `client/` holds - the high-level operations. -- **`internal/agent/`** — Node agent: the consumer/handler/processor pipeline - for job execution. -- **`internal/provider/`** — Operation implementations, organized by category +- **`internal/job/`.** Job domain types and subject routing. `client/` holds the + high-level operations. +- **`internal/agent/`.** Node agent: the consumer/handler/processor pipeline for + job execution. +- **`internal/provider/`.** Operation implementations, organized by category then domain. -- **`internal/config/`** — Viper-based config from `osapi.yaml`. -- **`internal/telemetry/`** — Tracing, metrics, and agent self-metrics. -- **`internal/controller/notify/`** — Condition notification system. -- **`pkg/sdk/`** — Go SDK for programmatic REST API access. -- **`ui/`** — React 19 + TypeScript + Vite + Tailwind CSS v4, embedded into the +- **`internal/config/`.** Viper-based config from `osapi.yaml`. +- **`internal/telemetry/`.** Tracing, metrics, and agent self-metrics. +- **`internal/controller/notify/`.** Condition notification system. +- **`pkg/sdk/`.** Go SDK for programmatic REST API access. +- **`ui/`.** React 19 + TypeScript + Vite + Tailwind CSS v4, embedded into the Go binary at build time. [System Architecture][system-architecture] describes the package layout, handler structure, and provider pattern in full. `nats-client` and `nats-server` are sibling repositories, consumed as pinned -module versions in `go.mod` — there is no `replace` directive. +module versions in `go.mod`. There is no `replace` directive. ## Code style @@ -123,7 +123,7 @@ just go-vet # Run linter ``` The linters that run are declared in `.golangci.yml`. Read them there rather -than looking for a list here — a copied list goes stale the first time the +than looking for a list here. A copied list goes stale the first time the configuration changes. Generated files (`*.gen.go`, `*.pb.go`) are excluded from formatting. @@ -142,7 +142,7 @@ just docusaurus-fmt # Format the site ### Function signatures -Functions with parameters use multi-line format — one parameter per line, with +Functions with parameters use multi-line format, one parameter per line, with the closing parenthesis and the return types on a line of their own: ```go @@ -169,7 +169,7 @@ Name a file for what it holds. Avoid `helpers.go`, `utils.go`, and names of that kind: they describe where code was put rather than what it is, and they accumulate whatever has no other home. -`types.go` holds only type declarations — structs, interfaces, constants, and +`types.go` holds only type declarations: structs, interfaces, constants, and aliases. A function belongs in a file named for what it does. A test file is named for the production file it tests. Where tests grow too @@ -202,7 +202,7 @@ package mocks The generator is resolved through the module's tool dependencies, so every checkout runs the version `go.mod` records. Destination files end in `.gen.go` -and are committed. Do not use `gen/` for mocks — that name is taken by API code +and are committed. Do not use `gen/` for mocks. That name is taken by API code generation. When the interface is **unexported**, a sibling package cannot work: the mock @@ -227,7 +227,7 @@ type. The generated mock is still what satisfies the interface. Three doubles are written by hand, because generating them buys nothing: -- One standing in for a standard library interface — `net.Conn`, `fs.File`, +- One standing in for a standard library interface: `net.Conn`, `fs.File`, `io.Writer`, `slog.Handler`. Those do not move when our code does. - One carrying a real implementation of the behavior under test, such as signing with a genuinely generated key pair. @@ -241,20 +241,20 @@ The conventions below are specific to OSAPI. All logging uses Go's `log/slog` structured logger. -- **Subsystem labels** — Every component that holds a logger MUST wrap it with +- **Subsystem labels.** Every component that holds a logger MUST wrap it with `logger.With(slog.String("subsystem", "..."))` at construction time, which auto-tags every line from that component. Examples: `"agent"`, `"agent.seed"`, `"api.schedule"`, `"provider.file"`, `"job.client"`, `"metrics"`, `"controller.heartbeat"`. -- **Typed attributes** — Use `slog.String("key", val)`, `slog.Int`, `slog.Bool`, +- **Typed attributes.** Use `slog.String("key", val)`, `slog.Int`, `slog.Bool`, `slog.Any`. Never use positional pairs like `"key", val`; they compile but bypass type safety. -- **Standard field names** — `error` for errors, `hostname` for hosts, `path` - for file paths, `job_id` for job IDs, `name` for entry names, `addr` for +- **Standard field names.** `error` for errors, `hostname` for hosts, `path` for + file paths, `job_id` for job IDs, `name` for entry names, `addr` for addresses. -- **Error fields** — `slog.String("error", err.Error())` for string context, or +- **Error fields.** `slog.String("error", err.Error())` for string context, or `slog.Any("error", err)` to preserve the error type. -- **Log levels** — `Debug` for operation dispatch and idempotency skips, `Info` +- **Log levels.** `Debug` for operation dispatch and idempotency skips, `Info` for lifecycle events and state changes, `Warn` for degraded but functional states, `Error` for failures that need attention. @@ -265,7 +265,7 @@ Components use a non-blocking lifecycle: `Start()` returns immediately, and ### Filesystem access -Use [avfs] — `memfs.New()` for in-memory work and `failfs.New()` for targeted +Use [avfs]: `memfs.New()` for in-memory work and `failfs.New()` for targeted error injection. Never use `afero`. ## Testing @@ -285,7 +285,7 @@ just go-unit-cov-check # Report coverage and fail below the target ``` The target is declared in `.github/codecov.yml` and in the shared `go` justfile -module — change both together. +module. Change both together. ### Test file conventions @@ -296,7 +296,7 @@ module — change both together. - Suite naming: `*_public_test.go` → `{Name}PublicTestSuite`, `*_test.go` → `{Name}TestSuite`. - `testify/suite` with table-driven cases. -- One suite method per function under test — success, errors, and edge cases are +- One suite method per function under test. Success, errors, and edge cases are rows in one table, not separate methods. - `export_test.go` exposes unexported symbols to external tests, by alias or by setter. Do not use an alias to re-cover behavior the caller's own test already @@ -304,11 +304,11 @@ module — change both together. ### Test layers -- **Unit tests** (`*_test.go`, `*_public_test.go`) — fast, mocked dependencies. +- **Unit tests** (`*_test.go`, `*_public_test.go`). Fast, mocked dependencies. Public suites also carry HTTP wiring methods (`TestXxxHTTP`, `TestXxxRBACHTTP`) that send raw HTTP through the full Echo middleware stack with mocked backends. -- **Integration tests** (`test/integration/`) — build and start a real `osapi` +- **Integration tests** (`test/integration/`). Build and start a real `osapi` binary and exercise CLI commands end-to-end. Guarded by a `//go:build integration` tag. The harness allocates random ports, generates a JWT, and starts the server, so no external setup is required. @@ -390,7 +390,7 @@ if errMsg, ok := validation.Struct(request.Body); !ok { ``` This applies to action endpoints (power, docker stop) where an empty body is -valid — unlike update endpoints, which must use `AtLeastOneField`. +valid, unlike update endpoints, which must use `AtLeastOneField`. ## Adding a new API domain @@ -463,13 +463,13 @@ be reasonable to split it in a few). Git squash and rebase is your friend! ## Submitting a PR -- **Describe your changes** — Ensure that you provide a comprehensive - description of your changes. -- **Issue/PR links** — Link any previous work such as related issues or PRs. +- **Describe your changes.** Say what changed and why. A reviewer should not + have to read the diff to learn the reason for it. +- **Issue/PR links.** Link any previous work such as related issues or PRs. Please describe how your changes differ to/extend this work. -- **Examples** — Add any examples or screenshots that you think are useful to +- **Examples.** Add any examples or screenshots that you think are useful to demonstrate the effect of your changes. -- **Draft PRs** — If your changes are incomplete, but you would like to discuss +- **Draft PRs.** If your changes are incomplete, but you would like to discuss them, open the PR as a draft and add a comment to start a discussion. Using comments rather than the PR description allows the description to be updated later while preserving any discussions. @@ -477,7 +477,7 @@ be reasonable to split it in a few). Git squash and rebase is your friend! ## AI usage This repo is written with AI assistance. All contributions are subject to the -[AI Usage Policy](AI_POLICY.md) — disclose the tool you used, and make sure you +[AI Usage Policy](AI_POLICY.md). Disclose the tool you used, and make sure you can explain what your change does without the aid of AI tools. ## FAQ From 156b499725c5a02247213cfff35600adc3ccdbbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D7=A0=CF=85=CE=B1=CE=B7=20=D7=A0=CF=85=CE=B1=CE=B7=D1=95?= =?UTF-8?q?=CF=83=CE=B7?= Date: Wed, 2 Sep 2026 17:42:34 -0700 Subject: [PATCH 2/4] docs: leave code blocks alone The unslop pass reached inside fenced blocks and rewrote them as if they were prose. An aligned file tree lost its alignment and gained a leading comma; code comments were reflowed. A fenced block is code or output, not writing, so restore every one of them. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013oR5Gt9WPTTdPKdnShdL7L --- ui/docs/architecture.md | 102 ++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/ui/docs/architecture.md b/ui/docs/architecture.md index cb1f6cbf5..089abf101 100644 --- a/ui/docs/architecture.md +++ b/ui/docs/architecture.md @@ -5,15 +5,15 @@ and operations builder for [OSAPI](https://github.com/osapi-io/osapi). ## Tech Stack -- **React 19** — UI framework -- **TypeScript** — type-safe codebase -- **Vite** — build tool and dev server -- **Tailwind CSS v4** — utility-first styling via CSS-based `@theme` -- **class-variance-authority (cva)** — component variant patterns -- **clsx + tailwind-merge** — conditional class composition (`cn()` helper) -- **lucide-react** — icons -- **React Router v7** — client-side routing -- **orval** — OpenAPI SDK generation +- **React 19**. UI framework +- **TypeScript**. Type-safe codebase +- **Vite**. Build tool and dev server +- **Tailwind CSS v4**. Utility-first styling via CSS-based `@theme` +- **class-variance-authority (cva)**. Component variant patterns +- **clsx + tailwind-merge**. Conditional class composition (`cn()` helper) +- **lucide-react**. Icons +- **React Router v7**. Client-side routing +- **orval**. OpenAPI SDK generation ## Application Structure @@ -63,10 +63,10 @@ User opens app OSAPI issues JWTs via `osapi token generate`. The token contains: -- `roles` claim — array of role strings (`admin`, `write`, `read`) +- `roles` claim, array of role strings (`admin`, `write`, `read`) - Standard JWT claims (sub, exp, iat) -The UI decodes the token client-side (no verification — that's the server's job) +The UI decodes the token client-side (no verification, that's the server's job) to extract roles. The token is sent as a Bearer header on every API request via the fetch mutator. @@ -85,10 +85,10 @@ Permissions use `resource:verb` format matching osapi's Go model: `agent:read`, ### Permission Gating -- **Configure blocks** — each block type maps to a required permission in +- **Configure blocks**. Each block type maps to a required permission in `BLOCK_PERMISSIONS`. Unauthorized blocks show greyed out with a lock icon. -- **Agent drain/undrain** — requires `agent:write`. -- **Role override dropdown** — lets users preview what other roles see without +- **Agent drain/undrain**. Requires `agent:write`. +- **Role override dropdown**. Lets users preview what other roles see without changing the token. ## SDK Generation @@ -140,41 +140,41 @@ Pages (Dashboard, Configure, Roles, SignIn) Framework-level reusable components. These define the visual language: -| Component | Purpose | -| ---------------------------------------- | -------------------------------------------------------------- | -| Text | Styled text with variant/size props — the default for all text | -| Card, CardHeader, CardTitle, CardContent | Container with variant borders/shadows | -| Button | Primary/secondary/ghost/destructive with sizes | -| Badge | Status indicators (ready/pending/running/error/applied/muted) | -| Input | Form input with label, autofill suppression | -| Dropdown | Custom popover dropdown (replaces native select) | -| FactInput | Input with @fact. reference autocomplete | -| FormField | Label + Input wrapper for consistent form layout | -| PageHeader | Page title + subtitle + optional actions slot | -| SectionLabel | Uppercase section header with optional icon | -| StatCard | Label + big value + detail text in a card | -| DataTable | Typed table with header/rows inside a card | -| HealthDot | Colored status dot (ok/error/muted) | -| ErrorBanner | Error message with icon (sm/md sizes) | -| MetricValue | Formatted metric display (label + value) | -| CodeBlock | Styled code/pre block with border | -| IdBadge | Monospace ID pill for identifiers | -| IconButton | Icon-only button with ghost/danger/accent variants | -| CollapsibleSection | Togglable section with chevron, icon, right content | -| Modal | Dialog overlay with close button | -| EmptyState | Centered message with dashed border and optional icon | -| SearchBox | Inline search input with close button | -| ScrollButton | Directional scroll arrow (left/right) | -| InfoBox | Subtle container for hints and info text | -| KeyValue | Inline key:value display pair | -| LabelTag | Accent-colored key:value label pill | -| ConditionAlert | Warning condition with triangle icon | -| Popover, PopoverItem, PopoverPanel | Floating popover menu system | +| Component | Purpose | +| ---------------------------------------- | ------------------------------------------------------------- | +| Text | Styled text with variant/size props, the default for all text | +| Card, CardHeader, CardTitle, CardContent | Container with variant borders/shadows | +| Button | Primary/secondary/ghost/destructive with sizes | +| Badge | Status indicators (ready/pending/running/error/applied/muted) | +| Input | Form input with label, autofill suppression | +| Dropdown | Custom popover dropdown (replaces native select) | +| FactInput | Input with @fact. reference autocomplete | +| FormField | Label + Input wrapper for consistent form layout | +| PageHeader | Page title + subtitle + optional actions slot | +| SectionLabel | Uppercase section header with optional icon | +| StatCard | Label + big value + detail text in a card | +| DataTable | Typed table with header/rows inside a card | +| HealthDot | Colored status dot (ok/error/muted) | +| ErrorBanner | Error message with icon (sm/md sizes) | +| MetricValue | Formatted metric display (label + value) | +| CodeBlock | Styled code/pre block with border | +| IdBadge | Monospace ID pill for identifiers | +| IconButton | Icon-only button with ghost/danger/accent variants | +| CollapsibleSection | Togglable section with chevron, icon, right content | +| Modal | Dialog overlay with close button | +| EmptyState | Centered message with dashed border and optional icon | +| SearchBox | Inline search input with close button | +| ScrollButton | Directional scroll arrow (left/right) | +| InfoBox | Subtle container for hints and info text | +| KeyValue | Inline key:value display pair | +| LabelTag | Accent-colored key:value label pill | +| ConditionAlert | Warning condition with triangle icon | +| Popover, PopoverItem, PopoverPanel | Floating popover menu system | ### Text Component The `Text` component is the standard way to render styled text. Never write -inline Tailwind text classes — always use `Text` with the appropriate variant: +inline Tailwind text classes, always use `Text` with the appropriate variant: ```tsx Secondary text @@ -193,14 +193,14 @@ Available sizes: `xs` (default), `sm`, `base`. Business logic components specific to OSAPI: -- **Block system** — BlockCard, BlockStack, ApplyButton, ResultCard, +- **Block system**. BlockCard, BlockStack, ApplyButton, ResultCard, SaveStackDialog, StackBar -- **Block forms** — one per operation type (CommandBlock, CronBlock, - DockerBlock, DockerExecBlock, FileBlock, FileUploadBlock, FileDeleteBlock, - CronDeleteBlock, ContainerActionBlock, DnsUpdateBlock, SingleInputBlock) -- **Pickers** — TargetPicker (agents/labels), ObjectPicker (files), +- **Block forms**. One per operation type (CommandBlock, CronBlock, DockerBlock, + DockerExecBlock, FileBlock, FileUploadBlock, FileDeleteBlock, CronDeleteBlock, + ContainerActionBlock, DnsUpdateBlock, SingleInputBlock) +- **Pickers**. TargetPicker (agents/labels), ObjectPicker (files), ContainerPicker, CronPicker -- **Dashboard** — AgentCard, ComponentRow, JobDetail, HostGroupHeader +- **Dashboard**. AgentCard, ComponentRow, JobDetail, HostGroupHeader ### Hooks (`src/hooks/`) From e16d7ae388c5db59886a8340d046715d5d80aa70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D7=A0=CF=85=CE=B1=CE=B7=20=D7=A0=CF=85=CE=B1=CE=B7=D1=95?= =?UTF-8?q?=CF=83=CE=B7?= Date: Wed, 2 Sep 2026 17:49:42 -0700 Subject: [PATCH 3/4] docs: apply the rest of the unslop rules The earlier pass treated unslop as an em dash pass and stopped there. Running every rule over every file finds what it skipped. Headings take sentence case, except where the words are a name: "Adding a New Collector" becomes "Adding a new collector", while macOS, VMware, Apple Silicon and Max Headroom keep their capitals. Decorative emojis come off headings, which the rules ask for and the first pass left alone. "Feel free to open a Discussion" loses the chatbot opener, and reads the same in every repository. gohai is no longer "designed to be embedded", it embeds, and it uses Ohai's knowledge rather than leveraging it. Fenced blocks are left as they are throughout. A code sample or a file tree is not writing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013oR5Gt9WPTTdPKdnShdL7L --- CONTRIBUTING.md | 2 +- README.md | 8 ++++---- ui/README.md | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb043796a..039d65dc3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -490,7 +490,7 @@ answer questions. > I'm stuck, where can I get help? -If you have questions, feel free to open a [Discussion] on GitHub. +If you have questions, open a [Discussion] on GitHub. [adding-an-api-domain]: docs/docs/sidebar/development/adding-an-api-domain.md [api-guidelines]: docs/docs/sidebar/architecture/api-guidelines.md diff --git a/README.md b/README.md index 14563ebda..68873db36 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@ them to be used as appliances. OSAPI demo -## 📖 Documentation +## Documentation [Getting Started] | [API] | [Usage] | [SDK] -## 🔗 Sister Projects +## Sister Projects | Project | Description | | -------------------- | ------------------------------------------------------------------- | @@ -39,12 +39,12 @@ them to be used as appliances. | [nats-server] | A Go package for running an embedded NATS server | | [osapi-orchestrator] | Declarative infrastructure orchestration DSL built on the OSAPI SDK | -## 🤝 Contributing +## Contributing See the [Contributing](CONTRIBUTING.md) guide for prerequisites, setup, conventions, and the PR workflow. -## 📄 License +## License The [MIT] License. diff --git a/ui/README.md b/ui/README.md index 4776aa569..e41a121fa 100644 --- a/ui/README.md +++ b/ui/README.md @@ -19,7 +19,7 @@ language. Configure

-## ✨ Features +## Features | Feature | Description | | ----------------- | ----------------------------------------------------------------------------------------------- | @@ -30,13 +30,13 @@ language. | @fact. References | Auto-complete fact references in DNS and network fields from live API | | Generated SDK | Typed fetch functions from OSAPI's OpenAPI spec via [orval](https://orval.dev/) | -## 🤝 Contributing +## Contributing See the [Development](docs/development.md) guide for prerequisites, setup, and conventions. See the [Contributing](docs/contributing.md) guide before submitting a PR. -## 📄 License +## License The [MIT] License. From ee689bc3160098b38cf3e4eb67acf17933041b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D7=A0=CF=85=CE=B1=CE=B7=20=D7=A0=CF=85=CE=B1=CE=B7=D1=95?= =?UTF-8?q?=CF=83=CE=B7?= Date: Wed, 2 Sep 2026 17:55:14 -0700 Subject: [PATCH 4/4] docs: put headings in sentence case The rules ask for sentence case and the earlier passes left title case alone, so 587 headings still read "Collected Fields", "Platform Support", "Quick Start". Names keep their capitals: Claude Code, Spec Kit, macOS, Apple Silicon, Xeon, M2 Pro, Homebrew, Sonoma, VMware, Ohai, NKey, OSAPI Orchestrator, and every acronym. The word after them is the one that changes. Anchors are unaffected, since a heading anchor is lowercased already. Checked: every internal link in every repository still resolves. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013oR5Gt9WPTTdPKdnShdL7L --- README.md | 2 +- ui/docs/architecture.md | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 68873db36..5de971d95 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ them to be used as appliances. [Getting Started] | [API] | [Usage] | [SDK] -## Sister Projects +## Sister projects | Project | Description | | -------------------- | ------------------------------------------------------------------- | diff --git a/ui/docs/architecture.md b/ui/docs/architecture.md index 089abf101..c35289857 100644 --- a/ui/docs/architecture.md +++ b/ui/docs/architecture.md @@ -3,7 +3,7 @@ OSAPI UI is a React single-page application that provides a management dashboard and operations builder for [OSAPI](https://github.com/osapi-io/osapi). -## Tech Stack +## Tech stack - **React 19**. UI framework - **TypeScript**. Type-safe codebase @@ -15,7 +15,7 @@ and operations builder for [OSAPI](https://github.com/osapi-io/osapi). - **React Router v7**. Client-side routing - **orval**. OpenAPI SDK generation -## Application Structure +## Application structure ``` src/ @@ -45,9 +45,9 @@ src/ pages/ — Route page components ``` -## Authentication & Authorization +## Authentication & authorization -### Auth Flow +### Auth flow ``` User opens app @@ -59,7 +59,7 @@ User opens app → Auto-authenticate → Dashboard ``` -### JWT Token +### JWT token OSAPI issues JWTs via `osapi token generate`. The token contains: @@ -70,7 +70,7 @@ The UI decodes the token client-side (no verification, that's the server's job) to extract roles. The token is sent as a Bearer header on every API request via the fetch mutator. -### RBAC Model +### RBAC model Three built-in roles with hierarchical permissions: @@ -83,7 +83,7 @@ Three built-in roles with hierarchical permissions: Permissions use `resource:verb` format matching osapi's Go model: `agent:read`, `file:write`, `command:execute`, `docker:execute`, etc. -### Permission Gating +### Permission gating - **Configure blocks**. Each block type maps to a required permission in `BLOCK_PERMISSIONS`. Unauthorized blocks show greyed out with a lock icon. @@ -91,12 +91,12 @@ Permissions use `resource:verb` format matching osapi's Go model: `agent:read`, - **Role override dropdown**. Lets users preview what other roles see without changing the token. -## SDK Generation +## SDK generation The TypeScript SDK is generated from OSAPI's combined OpenAPI spec using [orval](https://orval.dev/). -### Generation Flow +### Generation flow ``` osapi repo osapi-ui repo @@ -116,7 +116,7 @@ osapi repo osapi-ui repo `src/sdk/gen/api.yaml` and runs orval to produce typed fetch functions and schema types. -### Fetch Mutator +### Fetch mutator `src/sdk/fetch.ts` is the only hand-written file in `src/sdk/`. It: @@ -125,7 +125,7 @@ osapi repo osapi-ui repo - Sends it as a `Bearer` header - Wraps responses in `{ data, status, headers }` for orval -## Component Architecture +## Component architecture ### Layers @@ -136,7 +136,7 @@ Pages (Dashboard, Configure, Roles, SignIn) └─ Tailwind CSS theme tokens ``` -### UI Primitives (`src/components/ui/`) +### UI primitives (`src/components/ui/`) Framework-level reusable components. These define the visual language: @@ -171,7 +171,7 @@ Framework-level reusable components. These define the visual language: | ConditionAlert | Warning condition with triangle icon | | Popover, PopoverItem, PopoverPanel | Floating popover menu system | -### Text Component +### Text component The `Text` component is the standard way to render styled text. Never write inline Tailwind text classes, always use `Text` with the appropriate variant: @@ -189,7 +189,7 @@ Available variants: `default`, `muted`, `label`, `mono`, `mono-muted`, Available sizes: `xs` (default), `sm`, `base`. -### Domain Components (`src/components/domain/`) +### Domain components (`src/components/domain/`) Business logic components specific to OSAPI: @@ -214,7 +214,7 @@ Business logic components specific to OSAPI: | useObjects | Fetch file objects for pickers | | useFacts | Fetch @fact. keys from `/facts/keys` | -## Feature Flags +## Feature flags Feature flags gate unreleased functionality via env vars: