Skip to content

Commit 26d9c64

Browse files
Merge pull request #53 from kuudori/HYPERFLEET-930
HYPERFLEET-930 - docs: replace CLAUDE.md with AGENTS.md
2 parents cc6a1a9 + b131c1f commit 26d9c64

3 files changed

Lines changed: 127 additions & 235 deletions

File tree

.github/workflows/ci.yml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ jobs:
1515
steps:
1616
- name: Checkout code
1717
uses: actions/checkout@v6
18+
with:
19+
fetch-depth: 0
1820

1921
- name: Setup Node.js
2022
uses: actions/setup-node@v6
2123
with:
22-
node-version: '20'
24+
node-version: "20"
2325

2426
- name: Install dependencies
2527
run: npm ci
@@ -39,7 +41,24 @@ jobs:
3941
run: |
4042
npx spectral lint schemas/core/openapi.yaml --format github-actions --fail-severity warn
4143
44+
- name: Check for contract changes
45+
id: contract
46+
run: |
47+
BASE_REF="${{ github.event.pull_request.base.sha || github.event.before }}"
48+
if [ -z "$BASE_REF" ] || ! git cat-file -e "$BASE_REF" 2>/dev/null; then
49+
echo "changed=true" >> "$GITHUB_OUTPUT"
50+
exit 0
51+
fi
52+
CHANGED=$(git diff --name-only "$BASE_REF" HEAD -- '*.tsp')
53+
if [ -n "$CHANGED" ]; then
54+
echo "changed=true" >> "$GITHUB_OUTPUT"
55+
else
56+
echo "changed=false" >> "$GITHUB_OUTPUT"
57+
echo "No contract files changed — skipping version bump check"
58+
fi
59+
4260
- name: Check version bump
61+
if: steps.contract.outputs.changed == 'true'
4362
env:
4463
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4564
run: |

AGENTS.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# HyperFleet API Spec
2+
3+
TypeSpec sources that generate the HyperFleet core OpenAPI specification. Published as the `hyperfleet` npm package for provider-specific repos (e.g., `hyperfleet-api-spec-gcp`). Also consumed as a Go module via `schemas/schemas.go` (`embed.FS`).
4+
5+
## Critical path
6+
7+
```bash
8+
npm install # Install TypeSpec compiler + deps
9+
./build-schema.sh # Build core OpenAPI spec
10+
npx spectral lint schemas/core/openapi.yaml --fail-severity warn # Lint (matches CI strictness)
11+
```
12+
13+
**IMPORTANT:** `schemas/core/openapi.yaml` is committed. CI runs `git diff --exit-code schemas/` — if you change TypeSpec sources, rebuild and commit the schema in the same PR.
14+
15+
**IMPORTANT:** Every PR must bump the version in `main.tsp` (the `version` field inside the `@info` decorator). CI compares against the latest release tag and blocks if unchanged or lower.
16+
17+
When bumping version:
18+
1. Edit the `version` field inside the `@info` decorator in `main.tsp`
19+
2. Run `./build-schema.sh` (auto-syncs `package.json` version — never edit `package.json` manually)
20+
3. Update `CHANGELOG.md`: add new `## [X.Y.Z] - YYYY-MM-DD` section, update comparison links
21+
22+
## Source of truth
23+
24+
| Topic | File |
25+
|-------|------|
26+
| Build process | `build-schema.sh` |
27+
| CI checks | `.github/workflows/ci.yml` |
28+
| Release automation | `.github/workflows/release.yml`, `RELEASING.md` |
29+
| Contributing guide | `CONTRIBUTING.md` |
30+
| Changelog format | `CHANGELOG.md` (Keep a Changelog) |
31+
| Spectral rules | `.spectral.yaml` |
32+
| TypeSpec config | `tspconfig.yaml` |
33+
| Go module embed | `schemas/schemas.go` |
34+
35+
## Architecture: shared vs core
36+
37+
```
38+
shared/ → Cross-provider models and services (npm package)
39+
core/ → Core-only models and internal-only services
40+
main.tsp → Entry point (imports shared + core)
41+
schemas/core/ → Generated output (committed)
42+
```
43+
44+
**Where to put new code:**
45+
46+
| What | Where | Why |
47+
|------|-------|-----|
48+
| Models used by all providers | `shared/models/{resource}/model.tsp` | Published as npm package |
49+
| Endpoints for external clients | `shared/services/{resource}.tsp` | Shared across provider contracts |
50+
| Internal-only endpoints (adapters) | `core/services/{resource}-internal.tsp` | Core contract only |
51+
| Core-specific model overrides | `core/models/{resource}/model.tsp` | Not shared |
52+
| Provider-specific models | Separate repo (e.g., `hyperfleet-api-spec-gcp`) | Own contract |
53+
54+
**Directory naming:** `shared/models/` uses plural names (`clusters/`, `nodepools/`, `statuses/`) except `resource/` and `common/`. `core/models/` uses singular names (`cluster/`, `nodepool/`). Follow existing convention per directory.
55+
56+
## TypeSpec conventions
57+
58+
**IMPORTANT: Required decorators on every interface:** `@useAuth(HyperFleet.BearerAuth)`, `@tag("ResourceName")`. Every operation must have `@operationId("operationName")` and `@summary("...")`. Missing `@useAuth` causes the generated spec to omit security requirements — this was a real bug (commit `89b9f9b`).
59+
60+
**Service file boilerplate:**
61+
```tsp
62+
import "@typespec/http";
63+
import "@typespec/openapi";
64+
import "@typespec/openapi3";
65+
// ... model imports ...
66+
67+
using Http;
68+
using OpenAPI;
69+
70+
namespace HyperFleet;
71+
72+
@tag("Resources")
73+
@route("/resources")
74+
@useAuth(HyperFleet.BearerAuth)
75+
interface Resources {
76+
@get
77+
@summary("List resources")
78+
@operationId("getResources")
79+
getResources(...QueryParams): Body<ResourceList> | Error | BadRequestResponse;
80+
}
81+
```
82+
83+
**Model files** do not declare a namespace or `using` statements — just imports and model definitions.
84+
85+
**Naming:**
86+
- Resources: `Cluster`, `NodePool`, `Resource` (singular)
87+
- Create payloads: `ClusterCreateRequest`, `ResourceCreateRequest`
88+
- Patch payloads: `ClusterPatchRequest`, `ResourcePatchRequest`
89+
- Lists: `ClusterList`, `ResourceList`
90+
91+
**Import order:** TypeSpec library imports first, then relative model/service imports.
92+
93+
**Example files:** Each resource has `example_*.tsp` files for `@example` decorators. Example files in `shared/models/` are imported from their resource's `model.tsp`. Example files in `core/models/` are imported from `main.tsp`. Example files do not declare a namespace.
94+
95+
## Boundaries
96+
97+
- **IMPORTANT:** Never edit files in `schemas/` or `tsp-output-core/` directly — they are generated
98+
- `package.json` version is auto-synced by `build-schema.sh` — do not edit manually
99+
- New service files must be imported in `main.tsp` or they won't compile into the schema
100+
101+
## Gotchas
102+
103+
- `@typespec/rest` and `@typespec/versioning` are in `package.json` but not imported in any source file — they may be transitive requirements. Don't remove without testing.
104+
- Spectral linting in CI uses `--fail-severity warn` — all warnings are treated as errors.
105+
- The `go.mod` at repo root exists so downstream Go services can `go get` this module and read schemas via `embed.FS`. Don't remove it.
106+
- The `BearerAuth` model in `main.tsp` uses lowercase `"bearer"` as a workaround for `kin-openapi` library requirements.

CLAUDE.md

Lines changed: 1 addition & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -1,234 +1 @@
1-
# HyperFleet API Spec - AI Agent Context
2-
3-
This repository generates the HyperFleet core OpenAPI specification from TypeSpec definitions. The GCP-specific contract lives in [hyperfleet-api-spec-gcp](https://github.com/openshift-hyperfleet/hyperfleet-api-spec-gcp), which imports shared models from this repo as the `hyperfleet` npm package.
4-
5-
## Quick Reference
6-
7-
**Build commands:**
8-
```bash
9-
npm run build # Generate core OpenAPI spec
10-
./build-schema.sh # Same, via script directly
11-
```
12-
13-
**Validation workflow:**
14-
```bash
15-
npm install # Install dependencies
16-
./build-schema.sh # Build core OpenAPI 3.0
17-
ls -l schemas/core/openapi.yaml # Confirm output exists
18-
```
19-
20-
## Key Concepts
21-
22-
### Repository Layout
23-
24-
```
25-
shared/ # Cross-provider models and services (published as the `hyperfleet` npm package)
26-
core/ # Core-only models and internal services
27-
main.tsp # Entry point — imports shared + core
28-
schemas/core/ # Generated output (committed)
29-
```
30-
31-
**When adding new models:**
32-
- Cross-provider models → `shared/models/`
33-
- Core-only models → `core/models/`
34-
- GCP-specific → separate repo (`hyperfleet-api-spec-gcp`)
35-
36-
### Public vs Internal APIs
37-
38-
Status endpoints are split:
39-
- `shared/services/statuses.tsp` - GET operations (external clients)
40-
- `core/services/statuses-internal.tsp` - PUT operations (internal adapters only) and resource force-delete
41-
42-
The split allows generating different API contracts per audience. Only `statuses.tsp` is imported by default.
43-
44-
## Code Style
45-
46-
### TypeSpec Conventions
47-
48-
**Imports first, namespace second** (applies to service and model files; example `const` files do not declare a namespace):
49-
```typescript
50-
import "@typespec/http";
51-
import "../models/common/model.tsp";
52-
53-
namespace HyperFleet;
54-
```
55-
56-
**Use decorators for HTTP semantics:**
57-
```typescript
58-
@route("/clusters")
59-
interface Clusters {
60-
@get list(): Cluster[] | Error;
61-
@post create(@body cluster: ClusterInput): Cluster | Error;
62-
}
63-
```
64-
65-
**Model naming:**
66-
- Resources: `Cluster`, `NodePool` (singular)
67-
- Inputs: `ClusterInput`, `NodePoolInput`
68-
- Provider-specific: `GCPClusterSpec`, `AWSClusterSpec`
69-
70-
### File Organization
71-
72-
```
73-
shared/models/{resource}/
74-
├── model.tsp # Shared model definitions
75-
└── interfaces.tsp # Optional: shared interfaces
76-
77-
shared/services/
78-
└── {resource}.tsp # Shared service endpoints
79-
80-
core/models/{resource}/
81-
└── model.tsp # Core-specific models
82-
83-
core/services/
84-
└── {resource}-internal.tsp # Internal-only endpoints
85-
```
86-
87-
## Boundaries
88-
89-
**DO NOT:**
90-
- Modify generated files in `schemas/` or `tsp-output-core/` directly
91-
- Add dependencies without checking TypeSpec version compatibility
92-
- Auto-generate documentation - it degrades agent performance per research
93-
- Commit `node_modules/` or build artifacts
94-
95-
**DO:**
96-
- Run `./build-schema.sh` and commit `schemas/core/openapi.yaml` with your changes
97-
- Keep TypeSpec files focused (one resource per service file)
98-
- Use semantic versioning for releases (automated on merge to main)
99-
100-
## Common Tasks
101-
102-
### Add a new endpoint to existing service
103-
104-
```typescript
105-
// shared/services/clusters.tsp
106-
namespace HyperFleet;
107-
108-
@route("/clusters")
109-
interface Clusters {
110-
// ... existing endpoints ...
111-
112-
@get
113-
@route("/{id}/health")
114-
getHealth(@path id: string): HealthStatus | Error;
115-
}
116-
```
117-
118-
### Add a new resource
119-
120-
1. Create model:
121-
```typescript
122-
// shared/models/health/model.tsp
123-
import "@typespec/http";
124-
125-
model HealthStatus {
126-
id: string;
127-
state: "healthy" | "degraded" | "critical";
128-
lastChecked: utcDateTime;
129-
}
130-
```
131-
132-
2. Create service:
133-
```typescript
134-
// shared/services/health.tsp
135-
import "@typespec/http";
136-
import "../models/health/model.tsp";
137-
import "../models/common/model.tsp";
138-
139-
namespace HyperFleet;
140-
141-
@route("/health")
142-
interface Health {
143-
@get check(): HealthStatus | Error;
144-
}
145-
```
146-
147-
3. Import in `main.tsp`:
148-
```typescript
149-
import "./shared/services/health.tsp";
150-
```
151-
152-
4. Build: `npm run build`
153-
154-
### Add provider-specific fields
155-
156-
Provider-specific models live in the provider's own repository (e.g., `hyperfleet-api-spec-gcp`). See that repo for examples of how to extend core shared models.
157-
158-
## Version Bump and Changelog
159-
160-
When bumping the version in `main.tsp`, always update `CHANGELOG.md`:
161-
162-
1. Keep `## [Unreleased]` at the top, then add a new version section as `## [X.Y.Z] - YYYY-MM-DD`
163-
2. List changes under appropriate headings (`Added`, `Changed`, `Fixed`, `Removed`)
164-
3. Update the comparison links at the bottom of the file
165-
4. Follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format
166-
167-
## Validation Checklist
168-
169-
Before submitting changes:
170-
171-
- [ ] Dependencies installed: `npm install`
172-
- [ ] Core schema builds: `./build-schema.sh`
173-
- [ ] Schema file generated: `ls schemas/core/openapi.yaml`
174-
- [ ] No TypeSpec compilation errors (check output)
175-
- [ ] Schema passes linting: `spectral lint schemas/core/openapi.yaml`
176-
- [ ] Changes committed including schema update
177-
- [ ] PR description references related issue
178-
179-
## Build System Details
180-
181-
**The build-schema.sh script:**
182-
1. Extracts the version from `main.tsp` and syncs it into `package.json`
183-
2. Runs `node_modules/.bin/tsp compile main.tsp --output-dir tsp-output-core`
184-
3. Moves output to `schemas/core/openapi.yaml`
185-
186-
**Output locations:**
187-
- TypeSpec temp: `tsp-output-core/schema/openapi.yaml` (auto-deleted)
188-
- Final: `schemas/core/openapi.yaml` (committed)
189-
190-
**Version sync:** `package.json` version is kept in lockstep with `main.tsp` automatically on every build. The CI consistency check (`git diff --exit-code`) enforces that both are committed together.
191-
192-
## VS Code Extension Notes
193-
194-
The TypeSpec extension may show false errors for models resolved only at compile time. Both the CLI and "Emit from TypeSpec" command work correctly.
195-
196-
## Dependencies
197-
198-
All TypeSpec libraries use version `^1.6.0` for consistency:
199-
- `@typespec/compiler` - Core compiler
200-
- `@typespec/http` - HTTP semantics
201-
- `@typespec/openapi` - OpenAPI decorators
202-
- `@typespec/openapi3` - OpenAPI 3.0 emitter
203-
- `@typespec/rest` - REST conventions
204-
- `@typespec/versioning` - API versioning support
205-
206-
**Adding new TypeSpec libraries:**
207-
```bash
208-
npm install --save-dev @typespec/library-name@^1.6.0
209-
```
210-
211-
Match the version range to existing dependencies.
212-
213-
## Release Process
214-
215-
Releases are **fully automated** via GitHub Actions (`.github/workflows/release.yml`).
216-
217-
On every push to `main`, the release workflow:
218-
1. Extracts the version from the `@info` decorator in `main.tsp`
219-
2. Skips if a tag for that version already exists
220-
3. Builds the core OpenAPI schema
221-
4. Creates an annotated Git tag (`vX.Y.Z`)
222-
5. Publishes a GitHub Release with `core-openapi.yaml` attached
223-
224-
The CI workflow (`.github/workflows/ci.yml`) enforces that the version in `main.tsp` is bumped from the latest release tag before a PR can be merged.
225-
226-
To release a new version, simply bump the version in `main.tsp` and merge to `main`.
227-
228-
## Architecture Context
229-
230-
This repository is part of the HyperFleet project. For broader context:
231-
- Architecture repo: https://github.com/openshift-hyperfleet/architecture
232-
- Main API implementation: https://github.com/openshift-hyperfleet/hyperfleet-api
233-
234-
The API implementation consumes the generated OpenAPI specs for validation and documentation.
1+
@AGENTS.md

0 commit comments

Comments
 (0)