Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .claude/skills/add-a-domain/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: add-a-domain
description: Add or extend an osapi API domain — a provider plus every layer it must appear in. Covers the provider implementation and its platform variants, agent processor and registry wiring, the OpenAPI spec and validation tags, the Echo handler with broadcast targeting, handler registration and startup wiring, the SDK service, CLI commands, and the docs and permission tables a new domain must be added to. Use when asked to add a domain, add a provider, add an operation or endpoint to an existing domain, wire a provider into the agent, add an SDK service, add CLI commands for a domain, or when asked what a new domain has to touch, why a domain feels half-finished, or which layer is missing. Also use when reviewing a domain for cross-layer consistency against an existing one.
description: Add or extend an osapi API domain, a provider plus every layer it must appear in. Covers the provider implementation and its platform variants, agent processor and registry wiring, the OpenAPI spec and validation tags, the Echo handler with broadcast targeting, handler registration and startup wiring, the SDK service, CLI commands, and the docs and permission tables a new domain must be added to. Use when asked to add a domain, add a provider, add an operation or endpoint to an existing domain, wire a provider into the agent, add an SDK service, add CLI commands for a domain, or when asked what a new domain has to touch, why a domain feels half-finished, or which layer is missing. Also use when reviewing a domain for cross-layer consistency against an existing one.
compatibility: Requires an osapi checkout with mise and just available. Commands run through `mise exec -- just`.
license: MIT
metadata:
Expand Down Expand Up @@ -46,14 +46,14 @@ Never copy a domain's files wholesale. Read one, then write the new one.
The layers depend on each other in one direction. Going out of order means
regenerating or rewriting.

1. **Provider** — the operations, with its own tests passing.
2. **Agent** — processor and registry, so a job reaches the provider.
3. **OpenAPI spec** — then `just generate`, which produces the server, the
1. **Provider**, the operations, with its own tests passing.
2. **Agent**, processor and registry, so a job reaches the provider.
3. **OpenAPI spec**, then `just generate`, which produces the server, the
combined spec, and the SDK's generated client together.
4. **Handler** — with validation and broadcast, then registration and startup.
5. **SDK service** — wrapping the generated client.
6. **CLI** — wrapping the SDK.
7. **Docs and tables** — feature page, CLI pages, permissions, navbars.
4. **Handler**, with validation and broadcast, then registration and startup.
5. **SDK service**, wrapping the generated client.
6. **CLI**, wrapping the SDK.
7. **Docs and tables**, feature page, CLI pages, permissions, navbars.

A new permission is decided at step 3 and lands in step 7. Write it down when
you choose it; it is the thing most often missed.
Expand Down
8 changes: 4 additions & 4 deletions .claude/skills/add-a-domain/references/agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dispatch and facts wiring.

| What you need to know | Where |
| --- | --- |
| Two files connect a provider, and what does **not** change — `agent/types.go`, `agent/agent.go`, the `JobClient` interface | [FR-009](../../../../components/osapi/domains.md) |
| Two files connect a provider, and what does **not** change: `agent/types.go`, `agent/agent.go`, the `JobClient` interface | [FR-009](../../../../components/osapi/domains.md) |
| The `FactsAware` obligation: embed it, add the compile-time `FactsSetter` check | [FR-010](../../../../components/osapi/domains.md) |

This file holds the shapes and the file names. The rules above are stated once,
Expand Down Expand Up @@ -104,7 +104,7 @@ in full. What matters when adding an operation, and where each rule is stated:
| --- | --- |
| Delivery is at-least-once, and the agent checks for a recorded response before executing | [FR-009](../../../../components/osapi/job-system.md) |
| Which operations make that check load-bearing rather than theoretical | [FR-010](../../../../components/osapi/job-system.md) |
| A job that has run is terminal — a failure is reported, not retried by redelivery | [FR-011](../../../../components/osapi/job-system.md) |
| A job that has run is terminal, a failure is reported, not retried by redelivery | [FR-011](../../../../components/osapi/job-system.md) |
| What happens when the response cannot be written after the work ran | [FR-012](../../../../components/osapi/job-system.md) |
| Which failures terminate a message instead of redelivering it | [FR-013](../../../../components/osapi/job-system.md) |
| The consumer's delivery settings, as defaults a deployment may override | [FR-014](../../../../components/osapi/job-system.md) |
Expand All @@ -113,14 +113,14 @@ in full. What matters when adding an operation, and where each rule is stated:
Two consequences for a new operation, which are yours rather than the system's:

- **Redelivery is not your safety net.** The provider's idempotency is what makes a
repeat safe — the contract's own requirement, not this one.
repeat safe, the contract's own requirement, not this one.
- **`job retry` creates a new job** rather than replaying the old message, so
nothing per-domain handles it.

## Facts

`provider.WireProviderFacts(a.GetFacts, registry.AllProviders()...)` injects
facts into every registered provider — one call, in `internal/agent/agent.go`. A
facts into every registered provider: one call, in `internal/agent/agent.go`. A
provider registered through the registry is covered; one constructed and passed
somewhere else is not. The obligation on the provider struct itself is
[FR-010](../../../../components/osapi/domains.md), and what a provider does with facts is
Expand Down
12 changes: 6 additions & 6 deletions .claude/skills/add-a-domain/references/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ does not: where the files go, what they are called, and the scaffolding to start
from.

The split is deliberate. A rule restated here would drift from the one in the
corpus, and the copy an agent happened to load would win — which is the failure
corpus, and the copy an agent happened to load would win, which is the failure
[003-corpus-backfill](../../../../CONSTITUTION.md)
exists to end.

Expand All @@ -25,7 +25,7 @@ exists to end.
| Path parameters are the trap, and what actually validates one | FR-012 |
| Separate verbs for create and update, and why a combined upsert is forbidden | FR-013 |
| The six API design guidelines, including path versus query parameters | FR-014 |
| What `{hostname}` accepts — a literal, `_any`, `_all`, a label selector | FR-015 |
| What `{hostname}` accepts, a literal, `_any`, `_all`, a label selector | FR-015 |
| Broadcast is mandatory, and both paths return the same collection shape | FR-016 |
| The job client has four methods, and an operation adds none | FR-017 |
| `Handler()` returns route closures, and the `Server` struct does not change | FR-018 |
Expand Down Expand Up @@ -90,7 +90,7 @@ handlers = append(handlers,

Stated here because they are real and nothing else states them. Both belong in
the corpus and neither is there, which is recorded rather than left to be
discovered — the same treatment FR-019 gives the absent `sdk-standards`
discovered, the same treatment FR-019 gives the absent `sdk-standards`
capability.

**A custom validation rule is a registered validator.** It belongs in
Expand All @@ -106,7 +106,7 @@ corpus does not say is that a domain must not write its own target parser.
that differ in how much damage they can do want two permissions however similar
their shape. A new one must be added to the built-in role expansion, the
permission constants, the SDK, and the roles tables in
`features/authentication.md` and `usage/configuration.md` — see
`features/authentication.md` and `usage/configuration.md`, see
[docs.md](docs.md). A permission that exists in the spec but in no role reaches
nobody.

Expand All @@ -115,9 +115,9 @@ nobody.
Testing conventions are osapi's `CONTRIBUTING.md`, under "Testing". What this
layer adds to a public suite:

- `TestXxxHTTP` — raw HTTP through the full Echo middleware stack: valid input
- `TestXxxHTTP`, raw HTTP through the full Echo middleware stack: valid input
succeeds, invalid input returns 400 with the message.
- `TestXxxRBACHTTP` — no token is 401, a token without the permission is 403, a
- `TestXxxRBACHTTP`, no token is 401, a token without the permission is 403, a
token with it succeeds.

Cover validation failure, success, a provider error surfaced from the job, and
Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/add-a-domain/references/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The obligations this layer carries are stated in
| `--json` on every command, and flags rather than positional arguments for IDs | FR-021 |
| `cli.PrintKV` for a single resource, `cli.PrintCompactTable` for rows | FR-021 |
| Every response code the spec declares handled in the status switch | FR-021 |
| What `--target` accepts — a literal, `_any`, `_all`, a label selector | FR-015 |
| What `--target` accepts, a literal, `_any`, `_all`, a label selector | FR-015 |
| What verifies a finished domain, and what Step 8 alone misses | FR-024 |

## Files
Expand Down Expand Up @@ -49,7 +49,7 @@ operators read these side by side.

## Three rules the corpus does not yet hold

Stated here because they are real and nothing else states them — not the corpus,
Stated here because they are real and nothing else states them, not the corpus,
not osapi's `CONTRIBUTING.md`. Recorded as unstated rather than left to be
discovered, the same treatment FR-019 gives the absent `sdk-standards`
capability.
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/add-a-domain/references/docs.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Docs and tables

The Docusaurus site is user-facing: what a feature does, how to call it, what the
SDK exposes. Development guidance is not there — it is in the corpus, and this
SDK exposes. Development guidance is not there: it is in the corpus, and this
skill cites it.

A domain that works but appears in none of these is invisible to everyone who did
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/add-a-domain/references/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ binding rules were "the `sdk-standards` capability in this repository", that the
bound `osapi-orchestrator` too, and that the capability won any disagreement.
Nothing of the sort has been written. The claim was also on osapi's
`adding-an-api-domain.md`, so two documents deferred to a specification that reads
as settled and does not exist — recorded as
as settled and does not exist, recorded as
[FR-019](../../../../components/osapi/domains.md)'s gap rather than repeated here.

What that means in practice: the conventions below and in FR-019 and FR-020 are
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/document/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The first is a page under `components/<name>/`, the second belongs in
`ARCHITECTURE.md`, and a rule every repository follows belongs in
`CONSTITUTION.md`.

[references/voice.md](references/voice.md) carries the writing standard: plain
[VOICE.md](../../../VOICE.md) carries the writing standard: plain
engineering prose, concrete over abstract, say the tradeoff, and the tells to
avoid.

Expand Down
3 changes: 1 addition & 2 deletions .claude/skills/document/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ alone.
## 4. Write it

The voice: an engineer explaining a system to another engineer who has to work on
it. [references/voice.md](references/voice.md) has the specifics and the tells to
avoid.
it. [VOICE.md](../../../VOICE.md) has the specifics and the tells to avoid.

What a page does, in order of what a reader needs:

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/org-status/references/issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ gh pr list --repo "osapi-io/$r" --state open \

An issue named there is in flight, and belongs in the pull request block rather
than in a block of its own. An issue whose fix has already merged is still open
only because nobody closed it — say so, because it reads as outstanding work.
only because nobody closed it: say so, because it reads as outstanding work.

## Trackers

Expand Down
6 changes: 3 additions & 3 deletions .claude/skills/org-status/references/merging.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ repository often are not.

Two bumps that touch the same file cannot both be merged from the state they
were built in. The first merge moves the default branch and the second is now
based on something that no longer exists, so it conflicts — or worse, merges
based on something that no longer exists, so it conflicts, or worse, merges
cleanly and drops the first one's edit. `mergeable` says `CLEAN` for both right
up until the first one lands, which is what makes this easy to get wrong.

Expand All @@ -56,9 +56,9 @@ So decide by the files, not by the colour of the tick:
gh pr view <number> --repo "osapi-io/$r" --json files -q '.files[].path'
```

- **No overlap** — merge them together. Four bumps each touching a different
- **No overlap**, merge them together. Four bumps each touching a different
`examples/<name>/go.mod` do not interact.
- **Overlap** — serialize, one merge at a time:
- **Overlap**, serialize, one merge at a time:
1. merge the first
2. `@dependabot rebase` the next, and wait for its checks
3. merge it, and repeat
Expand Down
8 changes: 4 additions & 4 deletions .claude/skills/org-status/references/pull-requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ gh pr list --repo "osapi-io/$r" --state open \

## Fields worth reporting

- `isDraft` — a draft is not waiting on review. Say so rather than counting it
- `isDraft`, a draft is not waiting on review. Say so rather than counting it
as pending.
- `mergeable` — `CONFLICTING` means it needs a rebase before anything else.
- `mergeable`: `CONFLICTING` means it needs a rebase before anything else.
`UNKNOWN` means GitHub is still computing it, so re-query rather than
reporting it as a problem.
- `reviewDecision` — empty string means no review has been requested or given.
- `reviewDecision`, empty string means no review has been requested or given.
`APPROVED` means it is ready to merge.
- `createdAt` — sort oldest first. Age is the signal.
- `createdAt`, sort oldest first. Age is the signal.

## Checks on a PR

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/org-status/references/quality.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ go directive: the fix is the same command in each, and a new linter release puts
all of them behind at once.

A stale pin is not cosmetic. A linter predating the toolchain crashes rather
than reporting findings — v2.12.2 died inside `buildir` under Go 1.27 — and the
than reporting findings, v2.12.2 died inside `buildir` under Go 1.27, and the
crash arrives as a red build on an unrelated pull request.

This drift was invisible for as long as it existed, because the shared `deps`
Expand Down
Loading
Loading