From 859949a0906baa032a09422dccc2f1f00f913a5c 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: Sat, 15 Aug 2026 23:20:25 -0700 Subject: [PATCH] docs: archive specify-sdk-standards The capability is in the corpus and both source documents point at it. Closes documentation-homes task 3.5, the first of section 3's relocations. Co-Authored-By: Claude Opus 5 (1M context) --- .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/sdk-standards/spec.md | 0 .../tasks.md | 12 +-- .../specify-documentation-homes/tasks.md | 2 +- openspec/specs/sdk-standards/spec.md | 95 +++++++++++++++++++ 7 files changed, 102 insertions(+), 7 deletions(-) rename openspec/changes/{specify-sdk-standards => archive/2026-08-15-specify-sdk-standards}/.openspec.yaml (100%) rename openspec/changes/{specify-sdk-standards => archive/2026-08-15-specify-sdk-standards}/design.md (100%) rename openspec/changes/{specify-sdk-standards => archive/2026-08-15-specify-sdk-standards}/proposal.md (100%) rename openspec/changes/{specify-sdk-standards => archive/2026-08-15-specify-sdk-standards}/specs/sdk-standards/spec.md (100%) rename openspec/changes/{specify-sdk-standards => archive/2026-08-15-specify-sdk-standards}/tasks.md (51%) create mode 100644 openspec/specs/sdk-standards/spec.md diff --git a/openspec/changes/specify-sdk-standards/.openspec.yaml b/openspec/changes/archive/2026-08-15-specify-sdk-standards/.openspec.yaml similarity index 100% rename from openspec/changes/specify-sdk-standards/.openspec.yaml rename to openspec/changes/archive/2026-08-15-specify-sdk-standards/.openspec.yaml diff --git a/openspec/changes/specify-sdk-standards/design.md b/openspec/changes/archive/2026-08-15-specify-sdk-standards/design.md similarity index 100% rename from openspec/changes/specify-sdk-standards/design.md rename to openspec/changes/archive/2026-08-15-specify-sdk-standards/design.md diff --git a/openspec/changes/specify-sdk-standards/proposal.md b/openspec/changes/archive/2026-08-15-specify-sdk-standards/proposal.md similarity index 100% rename from openspec/changes/specify-sdk-standards/proposal.md rename to openspec/changes/archive/2026-08-15-specify-sdk-standards/proposal.md diff --git a/openspec/changes/specify-sdk-standards/specs/sdk-standards/spec.md b/openspec/changes/archive/2026-08-15-specify-sdk-standards/specs/sdk-standards/spec.md similarity index 100% rename from openspec/changes/specify-sdk-standards/specs/sdk-standards/spec.md rename to openspec/changes/archive/2026-08-15-specify-sdk-standards/specs/sdk-standards/spec.md diff --git a/openspec/changes/specify-sdk-standards/tasks.md b/openspec/changes/archive/2026-08-15-specify-sdk-standards/tasks.md similarity index 51% rename from openspec/changes/specify-sdk-standards/tasks.md rename to openspec/changes/archive/2026-08-15-specify-sdk-standards/tasks.md index e18fbf7..6e30327 100644 --- a/openspec/changes/specify-sdk-standards/tasks.md +++ b/openspec/changes/archive/2026-08-15-specify-sdk-standards/tasks.md @@ -7,14 +7,14 @@ ## 2. Point the sources at the capability -- [ ] 2.1 `osapi` — `docs/docs/sidebar/sdk/guidelines.md` keeps its worked +- [x] 2.1 `osapi` — `docs/docs/sidebar/sdk/guidelines.md` keeps its worked examples and points at the capability for the rules -- [ ] 2.2 `osapi` — `CLAUDE.md` drops the SDK naming block, which the capability +- [x] 2.2 `osapi` — `CLAUDE.md` drops the SDK naming block, which the capability now states ## 3. Verification -- [ ] 3.1 Confirm no service method repeats its service name -- [ ] 3.2 Confirm no public signature contains a generated type -- [ ] 3.3 Confirm every exported result field carries a JSON tag -- [ ] 3.4 Confirm no consumer imports the generated package +- [x] 3.1 Confirm no service method repeats its service name +- [x] 3.2 Confirm no public signature contains a generated type +- [x] 3.3 Confirm every exported result field carries a JSON tag +- [x] 3.4 Confirm no consumer imports the generated package diff --git a/openspec/changes/specify-documentation-homes/tasks.md b/openspec/changes/specify-documentation-homes/tasks.md index 202b8d1..d13d5b5 100644 --- a/openspec/changes/specify-documentation-homes/tasks.md +++ b/openspec/changes/specify-documentation-homes/tasks.md @@ -30,7 +30,7 @@ blocks: cross-layer consistency, provider idempotency, broadcast response shape, HTTP verb separation, OpenAPI validation, SDK method naming, and the Go code standards (signatures, `types.go`, test-file naming, mocking) -- [ ] 3.5 `osapi` — propose a capability from `docs/sidebar/sdk/guidelines.md`; +- [x] 3.5 `osapi` — propose a capability from `docs/sidebar/sdk/guidelines.md`; "never expose generated types" and "JSON tags required" bind every consumer of `pkg/sdk`, including `osapi-orchestrator` - [ ] 3.6 `osapi` — resolve the three-way duplication of branching, commit diff --git a/openspec/specs/sdk-standards/spec.md b/openspec/specs/sdk-standards/spec.md new file mode 100644 index 0000000..351c92e --- /dev/null +++ b/openspec/specs/sdk-standards/spec.md @@ -0,0 +1,95 @@ +# sdk-standards Specification + +## Purpose + +Records what the SDK at `pkg/sdk` guarantees the repositories built on it, so a +consumer can read the contract it depends on rather than infer it from another +repository's contributor documentation. + +## Requirements + +### Requirement: A method name does not repeat its service + +A service method SHALL be named for the action alone. The service already +supplies the namespace, so repeating it in the method reads twice at every call +site. + +Methods SHALL use `List`, `Get`, `Create`, `Update`, and `Delete` for operations +on a resource. An operation with no persistent resource — a one-shot action or a +command execution — MAY use a verb naming what it does. + +#### Scenario: A service gains a read method + +- **WHEN** a service is added for a domain +- **THEN** its read method is `Get`, not the domain name followed by `Get` + +#### Scenario: An operation has no resource + +- **WHEN** an operation performs an action rather than acting on a stored + resource +- **THEN** it is named for the action, because none of the five resource verbs + describes it + +### Requirement: Generated types stay inside the SDK + +A public method signature SHALL NOT contain a type from the generated OpenAPI +package. The SDK exists to hide that package; a signature naming one requires +every consumer to import it, and re-exports each regeneration as a breaking +change. + +A consumer needing to import the generated package indicates the SDK is missing +a wrapper, rather than indicating the consumer should import it. + +#### Scenario: A request needs a generated body type + +- **WHEN** a method sends a request whose body is a generated type +- **THEN** the method accepts an SDK-defined type and builds the generated one + internally + +#### Scenario: A consumer reaches for the generated package + +- **WHEN** a consumer cannot express a call without importing the generated + package +- **THEN** the SDK adds the missing wrapper, rather than the consumer adding the + import + +### Requirement: Every exported result field carries a JSON tag + +Every exported field on a result type SHALL carry a `json` tag naming the key in +`snake_case`. + +The tags are load-bearing rather than decorative: results are converted to +generic maps by round-tripping through JSON, and an untagged field arrives under +its Go name, which does not match the key the API returned. + +A field whose absence is meaningful SHALL use `omitempty`. A field a caller must +always be able to read SHALL NOT, so that a false or empty value is +distinguishable from a field that was never set. + +#### Scenario: A result is converted to a map + +- **WHEN** a result is converted to a generic map +- **THEN** its keys match the API's, because each field names its key + +#### Scenario: A mutation reports whether it changed anything + +- **WHEN** a mutation result reports that nothing changed +- **THEN** the field is present and false, rather than omitted + +### Requirement: Errors reaching a consumer carry context + +An error returned from an SDK method SHALL name the operation that produced it. + +A response body SHALL be checked for nil after its status is checked, because a +status alone does not establish that a body was returned. + +#### Scenario: A call fails inside a wrapped client + +- **WHEN** an underlying call fails +- **THEN** the returned error names the SDK operation, so a consumer's log + identifies the call without a stack trace + +#### Scenario: A success status arrives with no body + +- **WHEN** a response carries a success status and no body +- **THEN** the SDK returns an error rather than dereferencing it